始、Makefile的規則
關鍵:規則的嵌套——會先完成規則的先驗條件
變量使用時都用${ }包圍起來,才能取到變量的實際值,否則取到的就是一個值為變量名的東西了。變量能賦值的,要不然怎么叫變量呢
這里的變量在實際執行時,會被換成它們的實際值,變量名起簡化和占位符的作用。
一、變量與函數
1. 函數notdir,wildcard和patsubst
這三個都是函數名,函數格式為:return = $(func arg1,arg2,..),即 $ 和 () 的結合。
例:將當前文件夾中所有以 .c為后綴的文件名替換為以 .o為后綴,並返回一個以空格為間隔的列表
SRC = $(patsubst %.c,%.o,${./})
2. 邏輯判斷 ——參考博客
ifeq (arg1, arg2):當arg1 == arg2 時,為true。
# 1. 連續的結構,只需要一個endif ifeq (${mode}, TE) EXECUTABLE := te else ifeq (${mode}, TR) EXECUTABLE := tr endif # 2. 非連續的兩個結構,每個結構都需要一個endif ifeq (${mode}, TE) EXECUTABLE := te endif ifeq (${mode}, TR) EXECUTABLE := tr endif
可以邏輯判斷實現條件編譯,如下,不同mode值將執行不同的規則。
ifeq (${mode}, train} .PHONY : target ${TRAIN} target : ${TRAIN} else ifeq (${mode}, test} .PHONY : target ${TEST} target : ${TEST} endif
二、Makefile中的自動化變量$@, $^, $< , $?, $%, $+, $* 等的含義 ——參考博客
$@ 表示目標文件 $^ 表示所有的依賴文件 $+ 這個變量很像“$^”,也是所有依賴目標的集合。只是它不去除重復的依賴目標 $< 表示第一個依賴文件 $? 表示比目標還要新的依賴文件列表 $% 僅當目標是函數庫文件時,表示規則中的目標成員名。例如,如果一個目標是“foo.a(bar.o)”,那么,“$%”就是“bar.o”,“$@”就是“foo.a”。如果目標不是函數庫文件(Unix下是[.a],Windows下是[.lib]),那么,其值為空 $* 這個變量表示目標模式中“%”及其之前的部分。如果目標是“dir/a.foo.b”,並且目標的模式是“a.%.b”,那么,“$*”的值就是“dir/a.foo”。這個變量對於構造有關聯的文件名是比較有較。
如果目標中沒有模式的定義,那么“$*”也就不能被推導出,但是,如果目標文件的后綴是make所識別的,那么“$*”就是除了后綴的那一部分。例如:如果目標是“foo.c”,因為“.c”是make所能識別的后綴名,所以,“$*”的值就是“foo”。
這個特性是GNU make的,很有可能不兼容於其它版本的make,所以,你應該盡量避免使用“$*”,除非是在隱含規則或是靜態模式中。如果目標中的后綴是make所不能識別的,那么“$*”就是空值。
三、規則 ——參考 博客
1. makefile中的規則的基本格式如下:
targets:dependeds
commands
targets:要生成的文件(多個文件則以空格分開),或者是一個動作,可用稱為偽目標;若target為空(未賦值的變量),則該規則無效
dependeds:執行此規則所必須的依賴,可以是其它target,形成遞歸嵌套;所有依賴都滿足后才會執行command生成target;
command:規則所執行的命令,以tab鍵開始;規則后的所有連續的 以tab鍵開始的行 都會被視作command。tab、多個空格 二者是不等效的。
eg:
... main.o:main.cpp gcc -o main.o ...
test:test.o
...
.PHONY:clean
clean:
rm -rf ...
一次執行makefile,有一個最終目標,這個最終目標可能會帶動其它依賴目標的生成。
2. 規則的執行:
1)直接執行 make 時,先查找makefile中的第一個規則,以這個規則的目標(若有多個目標,則以第一個)為最終目標,完成這個規則的執行; ==》如果要執行其它規則,需要指定目標,如 make test、 make clean。
2)依賴項dependeds將從左往右檢查是否存在,如果需要執行其它嵌套的規則,會先執行,若不存在且無生成規則,則停止並放棄檢查后面的依賴項。
3)除了第一個規則或者指定的規則,.PHONY指定的目標也會在執行時生成。
3. 偽目標
上面的clean是偽目標,所以也不會真正生成clean文件。由於它沒有依賴項,make無法判斷偽目標的依賴關系,所以要執行相關命令需要顯式地指定目標,如:make clean,這種規則每次指定時都一定會執行。
.PHONY可以將目標顯示地聲明為偽目標。即使真的有一個文件和偽目標同名,.PHONY指定的偽目標的規則在執行時也不會生成文件。
.PHONY也可以用於指定一個偽目標,該偽目標依賴於多個真實目標,如下:
.PHONY:all # .PHONY后面跟着all,all沒有具體的生成命令,但是all依賴於aa和bb,故而aa、bb會先生成
all:aa bb
aa: xxx
...
偽目標每次要生成時都會生成,無論是否有更改;其它規則如果是未更改則不會重新生成。
* 遇到一個問題,以上面的.PHONY:all為例,會出現 make: Nothing to be donefor'all'.
解決:改為以下寫法即可達到每次make都重新執行規定的目標
.PHONY:all aa bb
all:aa bb
* .PHONY不影響第一規則為默認規則,若.PHONY所在規則不是第一規則,其指定的規則用make是不會默認執行的。
下面寫法里,執行 make,則aa,bb都會生成,.PHONY行 和 偽目標all行可以調換順序,等效。
.PHONY:all aa bb
all:aa bb
aa: xxx
...
下面的例子中,執行 make,只有aa會生成。
aa: xxx
...
.PHONY:all aa bb
all:aa bb
四、參數傳遞
一個例子 :這個用 -D 傳遞進去是一個宏,這個宏也可以賦值 更多
-DDEBUG #相當於在代碼里面定義了 #define DEBUG 1
-DDEBUG=2 #相當於在代碼里面定義了 #define DEBUG 2。
另一個例子
Makefile文件: ARCH := $(shell arch) ifeq ($(ARCH), "x86_64") SETTING := 1 else SETTING := 0 endif CXX := g++ -std=c++11 CFLAGS := -Wall -g -O2 -D${SETTING} ALL = main OBJECT = main.o LIBS = %.o:%.cpp $(CXX) -c $^ -o $@ $(LIBS) $(CFLAGS) $(ALL):$(OBJECT) $(CXX) -o $@ $^ $(LIBS) $(CFLAGS) .PHONY:clean clean: rm -rf $(ALL) $(OBJECT)
// main.cpp
#include <iostream> using namespace std; int main(){ cout << "Hello World!" << endl; #ifdef SETTING cout<<"setting = 1"<<endl; #else cout<<"setting = 0"<<endl; #endif return 0; }
也可以用傳統的傳參方法,傳給main函數的argv。
