釋出
2012年12月17日 (最後更新: 2012年12月17日)

Make

評分: 3.7/5 (185 票)
*****

簡介

Make 是一個用於構建其他程式或文件的程式。Make 適用於任何具有可能完成或未完成的中間步驟的過程。

Make 不會重新構建已更新的專案。此外,它知道如何構建中間目標。您可以使用 Make 檔案來指示 Make。

Make 程式不止一個。C/C++ 開發人員最常遇到的是 GNU Make。我將首先用示例描述 GNU Make,然後介紹 BSD Make,最後對它們之間的區別進行一些評論。

GNU Make 入門

GNU Make 內建了許多規則。它們被稱為內建規則。讓我們看一個示例,hello.c
1
2
3
4
5
6
7
#include <stdio.h>

int main()
{
	printf("hello world\n:");
	return 0;
}


要構建程式,只需鍵入
make hello
它會執行
cc     hello.c   -o hello

因此,GNU Make 已知道可以從 .c 檔案構建程式,並且知道如何編譯和連結它。

讓我們再試一個,這次包含更多檔案。這次我們需要一個 makefile。GNU Make 檔案可以命名為 GNUmakefile、Makefile 或 makefile。在這些示例中我將使用 GNUmakefile,因為稍後我們將介紹 BSD Make。
calc.c
1
2
3
4
5
6
7
8
#include <stdio.h>
#include "engine.h"

int main()
{
	printf("%d\n", calc("2+3"));
	return 0;
}


engine.h
1
2
3
#pragma once

int calc(const char* expression);


engine.c
1
2
3
4
5
6
#include "engine.h"

int calc(const char *expression)
{
	return 5;
}


GNUmakefile
1
2
3
4
5
OBJ = calc.o engine.o

calc: $(OBJ)

$(OBJ): engine.h


我們定義了一個宏 OBJ,它包含我們的目標檔案列表。請記住,GNU Make 知道如何從 C 程式建立目標檔案。

接下來,我們告訴 GNU Make 我們的程式 calc 由這些 OBJ 檔案組成。當我們執行 make 時,它會執行
cc    -c -o calc.o calc.c
cc    -c -o engine.o engine.c
cc   calc.o engine.o   -o calc

最後,我們告訴 GNU Make,如果 engine.h 發生更改,我們必須重新編譯目標檔案。這稱為依賴項。

現在,我們來介紹 C++。讓我們定義一個新檔案:calcpp.cc
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
extern "C"
{
#include "engine.h"
}

int main()
{
	std::cout << calc("2+3") << std::endl;
	return 0;
}


這是我們修改後的 GNUmakefile
1
2
3
4
5
6
OBJ = calcpp.o engine.o

calc: $(OBJ)
	$(LINK.cc) $^ -o $@

$(OBJ): engine.h


那麼有什麼改變?我們需要告訴 GNU Make 我們正在連結一個 C++ 程式。我們使用 GNU Make 宏 LINK.cc 來告訴它我們正在連結一個 C++ 程式。

$^ 表示所有依賴項,在此情況下為 calcpp.o 和 engine.o。

$@ 表示我們正在構建的目標,在此情況下為 calc。

當我們執行 make 時,它會執行
c++    -c -o calcpp.o calcpp.cc
c++     calcpp.o engine.o -o calc

那編譯 engine.c 呢?嗯,我們之前已經編譯過它了,make 知道它不需要重新編譯。

BSD Make

BSD Make 早於 GNU Make,並且它有所不同。BSD Make 沒有很多內建規則。它需要在 make 檔案中被告知很多東西。正如您在上面看到的,GNU Make 知道很多。

BSD Make 知道依賴項和構建這些依賴項的規則,但它沒有所有那些內建的預設規則,因此需要被告知。一個使用 BSD Make 的系統,而不是被告知所有內容,而是提供一組包含檔案,這些檔案定義了在該系統上如何完成事物。例如,在 FreeBSD 上,如果您想編寫程式,則包含 <bsd.prog.mk>。這包含了編譯、安裝、解除安裝、歸檔……開發程式所需的所有規則。

GNU Make 和 BSD Make 的比較

請記住,GNU 是自由軟體基金會的工具集。GNU 的意思是 GNU 不是 Unix。這很重要。當 GNU 專案啟動時,它的目標是提供 Unix 程式的免費可用替代品。並且它們透過新增有用的、一致的行為來改進它們。

BSD Make 只知道依賴項、目標、規則和宏。GNU Make 將內建規則新增到其中,以便為開發人員提供便利。

事實證明,系統的細節是衝突的,並且無法提前知道。BSD Make 透過讓系統定義其引數來處理系統依賴項。GNU Make 的內建規則被證明是不夠的。Auto Tools 專案(AutoConf、AutoMake、LibTools)是為了彌補這一點而開發的。它透過在配置時發現系統來工作,然後生成 makefiles。

我更喜歡 BSD 的解決方案。很明顯,在這個例子中它的設計更好。我發現 BSD 通常都是如此。

示例

此示例使用了 3 個 C++ 原始檔(main.cc、config.cc、dispatch.cc)和 2 個頭檔案(config.hpp、dispatch.hpp)。我沒有新增依賴項,在現實世界中,它們是自動生成的。

GNUmakefile
1
2
3
4
5
6
CXXFLAGS = -g

all: goodlistener

goodlistener: main.o config.o dispatch.o
	$(LINK.cc) -o $@ $^


BSDmakefile
1
2
3
4
5
6
7
PROG_CXX = goodlistener
SRCS = main.cc config.cc dispatch.cc
MAN = goodlistener.1
MAKEOBJDIR = work
CXXFLAGS=-g

.include <bsd.prog.mk>