(內容源於陳浩CSDN博客中的跟我一起寫 Makefile的一系列文章)
- 跟我一起寫 Makefile(一)
- 跟我一起寫 Makefile(二)
- 跟我一起寫 Makefile(三)
- 跟我一起寫 Makefile(四)
- 跟我一起寫 Makefile(五)
- 跟我一起寫 Makefile(六)
- 跟我一起寫 Makefile(七)
- 跟我一起寫 Makefile(八)
- 跟我一起寫 Makefile(九)
- 跟我一起寫 Makefile(十)
- 跟我一起寫 Makefile(十一)
- 跟我一起寫 Makefile(十二)
- 跟我一起寫 Makefile(十三)
- 跟我一起寫 Makefile(十四)
規則
makefile基本格式
target ... : prerequisites ...
command
...
...格式說明
- target: 個目標文件或者標簽(Label)
- prerequisites: 生成 target 所需要的文件
- command: make 需要執行的命令(任意的Shell命令)
這是一個文件的依賴關系,target 這個目標文件依賴於 prerequisites 中的文件,其生成規則定義在 command 中.prerequisites 中如果有一個以上的文件比 target 文件要新的話,command 所定義的命令就會被執行 (makefile中最核心的內容)
例子:
a : b.o c.o d.o gcc -o a b.o c.o d.o b.o : b.c b.h gcc -c b.c c.o : c.c c.h gcc -c c.c d.o : d.c d.h gcc -c d.c clean : rm a b.o c.o d.o
文件名規則
默認的情況下,make 命令會在當前目錄下按順序找尋文件名為 “GNUmakefile”、“makefile”、“Makefile” 的文件,找到了解釋這個文件.在這三個文件名中,最好使用 “Makefile” 這個文件名,因為,這個文件名第一個字符為大寫,這樣有一種顯目的感覺.最好不要用 “GNUmakefile”,這個文件是 GNU 的 make 識別的.有另外一些 make 只對全小寫的 “makefile” 文件名敏感,但是基本上來說,大多數的 make 都支持 “makefile” 和 “Makefile” 這兩種默認文件名.
當然,你可以使用別的文件名來書寫 Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX” 等,如果要指定特定的 Makefile,你可以使用 make 的 “-f” 和 “--file” 參數,如:make -f Make.Linux 或 make --file Make.AIX.
內容
makefile里主要包含了五個東西:顯式規則、隱晦規則、變量定義、文件指示和注釋.
- 顯式規則:顯式規則說明了,如何生成一個或多的的目標文件.這是由 makefile 的書寫者明顯指出,要生成的文件,文件的依賴文件,生成的命令.
- 隱晦規則:由於我們的 make 有自動推導的功能,所以隱晦的規則可以讓我們比較簡略地書寫 makefile, 這是由 make 所支持的.
- 變量定義:在 makefile 中我們要定義一系列的變量,變量一般都是字符串,這個有點像你 C 語言中的宏,當 makefile 被執行時,其中的變量都會被擴展到相應的引用位置上.
- 文件指示:其包括了三個部分,一個是在一個 makefile 中引用另一個 makefile, 就像 C 語言中的 include 一樣;另一個是指根據某些情況指定 makefile 中的有效部分,就像 C 語言中的預編譯 #if 一樣;還有就是定義一個多行的命令.有關這一部分的內容,我會在后續的部分中講述.
- 注釋:makefile中只有行注釋,和UNIX的Shell腳本一樣,其注釋是用“#”字符,這個就像C/C++中的“//”一樣.如果你要在你的makefile中使用“#”字符,可以用反斜框進行轉義,如:“\#”.
變量
makefile中使用變量
說明:
- 變量在聲明時需要給與初值,而在使用時,需要給在變量名前家上 "$" 符號,但最好用小括號 "()" 或者大括號 "{}" 把變量給括起來,如果你要使用真實的 "$ "字符,那么你需要用 "$$" 來表示
- 在 Makefile 中的定義的變量,就像是 C/C++ 語言中的宏一樣,他代表了一個文本字串,在 Makefile 中執行的時候其會自動原模原樣地展開在所使用的地方.其與 C/C++ 所不同的是,你可以在 Makefile 中改變其值.
- 變量的命名字可以包含字符、數字,下划線 (可以是數字開頭),但不應該含有 ":" 、"#"、"=" 或是空字符(空格、回車等).變量是大小寫敏感.
使用:
- 變量的定義:變量名 = 變量實際內容
- 使用變量:$(變量名)
例子:
objects = b.o c.o d.o a : $(objects) cc -o a $(objects) b.o : b.c b.h cc -c b.c c.o : c.c c.h cc -c c.c d.o : d.c d.h cc -c d.c clean : rm a $(objects)自動化變量
- $@
表示規則的目標文件名.如果目標是一個文檔文件(Linux中,一般稱.a文件為文檔文件,也稱為靜態庫文件),那么它代表這個文檔的文件名.在多目標模式規則中,它代表的是哪個觸發規則被執行的目標文件名.- $%
當規則的目標文件是一個靜態庫文件時,代表靜態庫的一個成員名.例如,規則的目標是“foo.a(bar.o)”,那么,“$%”的值就為“bar.o”,“$@”的值為“foo.a”.如果目標不是靜態庫文件,其值為空.- $<
規則的第一個依賴文件名.如果是一個目標文件使用隱含規則來重建,則它代表由隱含規則加入的第一個依賴文件.- $?
所有比目標文件更新的依賴文件列表,空格分割.如果目標是靜態庫文件名,代表的是庫成員(.o文件).- $^
規則的所有依賴文件列表,使用空格分隔.如果目標是靜態庫文件,它所代表的只能是所有庫成員(.o文件)名.一個文件可重復的出現在目標的依賴中,變量“$^”只記錄它的一次引用情況.就是說變量“$^”會去掉重復的依賴文件.- $+
類似“$^”,但是它保留了依賴文件中重復出現的文件.主要用在程序鏈接時庫的交叉引用場合.- $*
這個變量表示目標模式中"%"及其之前的部分.如果目標是"dir/a.foo.b",並且目標的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo".這個變量對於構造有關聯的文件名是比較有較.如果目標中沒有模式的定義,那么"$*"也就不能被推導出,但是,如果目標文件的后綴是make所識別的,那么"$*"就是除了后綴的那一部分.例如:如果目標是"foo.c",因為".c"是make所能識別的后綴名,所以,"$*"的值就是"foo".這個特性是GNU make的,很有可能不兼容於其它版本的make,所以,你應該盡量避免使用"$*",除非是在隱含規則或是靜態模式中.如果目標中的后綴是make所不能識別的,那么"$*"就是空值.例子:
test:main.o hello.o g++ -o $@ main.o hello.o main.o:main.cpp hello.h g++ -c $< -Iinclude hello.o:hello.cpp hello.h g++ -c $< -Iinclude .PHONY:clean clean: -rm test hello.o默認變量
- AR :檔案管理程序,默認為:ar
- AS :匯編編譯程序,默認為:as
- CC :C 語言編譯程序,默認為:cc
- CXX :C++ 編譯程序,默認為:g++
- CPP :帶有標准輸出的 C 語言預處理程序,默認為:$(CC) -E
- RM :刪除文件的命令:,默認為: rm -f
偽目標
說明
為了讓目標不是作為目標文件而是一個標簽而已,從而不生成目標文件,讓 make 無法生成它的依賴關系和決定它是否執行,只有顯示第指明這個"目標"才能讓其生效.當然"偽目標"的取名不能和文件名重名,不然就失去了"偽目標"的意義
為了避免和文件重名的這種情況,可以使用一個特殊的表示 .PHONY 來顯示指明一個目標是"偽目標",向 make 說明不給是否有這個文件,這個目標就是 "偽目標"
例子
.PHONY : clean clean : -rm edit $(objects)前面說過,.PHONY 意思表示 clean 是一個“偽目標”,而在 rm 命令前面加了一個小減號的意思就是,也許某些文件出現問題,但不要管,繼續做后面的事.當然,clean 的規則不要放在文件的開頭,不然,這就會變成 make 的默認目標,相信誰也不願意這樣.不成文的規矩是——“clean 從來都是放在文件的最后”.
工作
引用其它的Makefile
說明
在 Makefile 使用 include 關鍵字把別的 Makefile 包含進來,被包含的文件會原模原樣的放在當前文件的包含位置
語法格式
include file
注解:file 可以是當前操作系統 Shell 的文件模式(可含路徑和通配符)在 include 前可有一些空字符,但絕不能是 [Tab] 鍵開始.include 和 file 可以用一個或多個空格隔開
例子:
目錄結構 -------------------------------------------
| -- makefile
| -- a.mk
| -- b.mk
`-- src
| -- c.mk
`-- d.mk
makefile--------------------------------------------include *.mk src/*.mk #等價於:include a.mk b.mk src/c.mk src/d.mk工作方式:
make 命令開始時,會把找尋 include 所指出的其它 Makefile,並把其內容安置在當前的位置.如果文件都沒有指定絕對路徑或是相對路徑的話,make 會在當前目錄下首先尋找,如果當前目錄下沒有找到,那么,make 還會在下面的幾個目錄下找:
- 如果 make 執行時,有 “-I” 或 “--include-dir” 參數,那么make就會在這個參數所指定的目錄下去尋找.
- 如果目錄 <prefix>/include(一般是:/usr/local/bin 或 /usr/include)存在的話,make 也會去找.
如果有文件沒有找到的話,make 會生成一條警告信息,但不會馬上出現致命錯誤.它會繼續載入其它的文件,一旦完成 makefile 的讀取,make 會再重試這些沒有找到,或是不能讀取的文件,如果還是不行,make 才會出現一條致命信息.如果你想讓 make 不理那些無法讀取的文件,而繼續執行,你可以在 include 前加一個減號 "-" .如:
-include <filename>
其表示,無論 include 過程中出現什么錯誤,都不要報錯繼續執行.和其它版本 make 兼容的相關命令是 sinclude,其作用和這一個是一樣的.
文件搜索
VPATH
說明:
makefile 文件中有個特殊的變量 VPATH ,這個變量的作用是在 make 在但前目錄找不到的情況下,VPATH 變量中保存的路徑中去找尋文件,若沒有指明這個變量, make 只會在當前的目錄下去找尋依賴文件和目標文件,如果有多個目錄可以以 ":" 分隔多個目錄
例子:
VPATH = src:../headers #在本目錄下的 src 目錄下去找和在上級目錄的 headers 目錄下去找依賴文件
vpath
說明:
vpath 是 make 中的關鍵字,它的作用和上面的 VPATH 作用相似,但是它更為靈活.它可以在不同的搜索目錄中指定不同的文件
使用方法:
- vpath pattern dir 為符合模式 pattern 的文件指定搜索目錄 dir ,如果有多個目錄可以以 ":" 分隔多個目錄
- vpath pattern 清除符合模式 pattern 的文件搜索目錄
- vpath 清除所有已被設置好了的文件搜索目錄
例子:
vpath %.h ../headers #到上級目錄下的 headers 目錄下去找尋所有以 ".h" 表示的頭文件, "%" 字符表示匹配一個以上的字符
例子:
文件目錄結構--------------------------------------------------
|-- Makefile
|-- main.cpp
|-- include
| `-- hello.h
`-- src
`-- hello.cpp程序源碼-------------------------------------------------------
/****************************************/ //hello.h: #ifndef _HELLO_H__ #define _HELLO_H__ #include<iostream> #endif using namespace std; void hello(); #endif /****************************************/ //hello.cpp: #include"hello.h" void hello() { cout<<"Hello world"<<endl; } /****************************************/ //main.cpp: #include"hello.h" int main() { hello(); return 0; }makefile內容 --------------------------------------------------
#VPATH=include:src vpath %.cpp src vpath %.h include test:main.o hello.o g++ -o $@ main.o hello.o main.o:main.cpp hello.h g++ -c $< -I include #----------------------------------------------- #"$<" 為自動化變量,它的作用是把 make 找到的源文件用正確的路徑形式表示在 g++ 的命令中 #如這里的 "$<" 表示的就是 "main.ccp" 而下面一個命令中的 "$<" 表示的就是 "src/hello.cpp" #----------------------------------------------- #雖然前面 vpath %.h include 告訴le make ,所依賴的頭文件在在本目錄和子目錄 include 下找 #源文件在本目錄和子目錄 src 下找,但是不會把這個消息告訴 gcc 或 g++ #所以還是要手動把頭文件所在目錄告訴給 g++ 或 gcc ,即: I -inlude hello.o:hello.cpp hello.h g++ -c $< -I include .PHONY:clean clean: -rm test hello.o
參考文獻
Makefile中自動化變量 http://blog.sina.com.cn/s/blog_45497dfa0100jk09.html
Makefile VPATH和vpath的使用 http://blog.csdn.net/changli_90/article/details/7881905
跟我一起寫 Makefile(四)http://blog.csdn.net/haoel/article/details/2889
make工作流程
objects = b.o c.o d.o a : $(objects) cc -o a $(objects) b.o : b.c b.h cc -c b.c c.o : c.c c.h cc -c c.c d.o : d.c d.h cc -c d.c clean : rm a $(objects)
- make 會在當前目錄下找名字叫“makefile”或“makefile”的文件.
- 如果找到,它會找文件中的第一個目標文件(target),在上面的例子中,他會找到 “a” 這個文件,並把這個文件作為最終的目標文件.
- 如果 a 文件不存在,或是 a 所依賴的后面的 .o 文件的文件修改時間要比 a 這個文件新,那么,他就會執行后面所定義的命令來生成 a 這個文件.
- 如果 a 所依賴的.o文件也不存在,那么 make 會在當前文件中找目標為 .o 文件的依賴性,如果找到再根據那一個規則生成 .o 文件.(這有點像一個堆棧的過程)
- 當然,你的 C 文件和 H 文件存在的,於是 make 會生成 .o 文件,然后再用 .o 文件生成 make 的終極任務,也就是執行文件 a 了.
這就是整個 make 的依賴性, make 會一層又一層地去找文件的依賴關系,直到最終編譯出第一個目標文件.在找尋的過程中,如果出現錯誤,比如最后被依賴的文件找不到,那么 make 就會直接退出,並報錯,而對於所定義的命令的錯誤,或是編譯不成功,make 根本不理.make 只管文件的依賴性,即,如果在我找了依賴關系之后,冒號后面的文件還是不在,那么 make 就不會工作.
make 工作執行步驟
- 讀入所有的 makefile.
- 讀入被 include 的其它 makefile.
- 初始化文件中的變量.
- 推導隱晦規則,並分析所有規則.
- 為所有的目標文件創建依賴關系鏈.
- 根據依賴關系,決定哪些目標要重新生成.
- 執行生成命令.
make自動推導
只要 make 看到一個[.o]文件,它就會自動的把[.c]文件加在依賴關系中,如果 make 找到一個 b.o,那么 b.c 就會是 b.o的依賴文件.並且 gcc -c b.c 也會被推導出來,於是,我們的 makefile 再也不用寫得這么復雜.
objects = b.o c.o d.o a : $(objects) cc -o a $(objects) b.o : b.c b.h c.o : c.c c.h d.o : d.c d.h clean : rm a $(objects)
make命令
make的參數
下面列舉了所有GNU make 3.80版的參數定義.其它版本和產商的make大同小異,不過其它產商的make的具體參數還是請參考各自的產品文檔.
“-b”
“-m”
這兩個參數的作用是忽略和其它版本make的兼容性.“-B”
“--always-make”
認為所有的目標都需要更新(重編譯).“-C <dir>”
“--directory=<dir>”
指定讀取makefile的目錄.如果有多個“-C”參數,make的解釋是后面的路徑以前面的作為相對路徑,並以最后的目錄作為被指定目錄.如:“make –C ~hchen/test –C prog”等價於“make –C ~hchen/test/prog”.“—debug[=<options>]”
輸出make的調試信息.它有幾種不同的級別可供選擇,如果沒有參數,那就是輸出最簡單的調試信息.下面是<options>的取值:
a —— 也就是all,輸出所有的調試信息.(會非常的多)
b —— 也就是basic,只輸出簡單的調試信息.即輸出不需要重編譯的目標.
v —— 也就是verbose,在b選項的級別之上.輸出的信息包括哪個makefile被解析,不需要被重編譯的依賴文件(或是依賴目標)等.
i —— 也就是implicit,輸出所以的隱含規則.
j —— 也就是jobs,輸出執行規則中命令的詳細信息,如命令的PID、返回碼等.
m —— 也就是makefile,輸出make讀取makefile,更新makefile,執行makefile的信息.“-d”
相當於“--debug=a”.“-e”
“--environment-overrides”
指明環境變量的值覆蓋makefile中定義的變量的值.“-f=<file>”
“--file=<file>”
“--makefile=<file>”
指定需要執行的makefile.“-h”
“--help”
顯示幫助信息.“-i”
“--ignore-errors”
在執行時忽略所有的錯誤.“-I <dir>”
“--include-dir=<dir>”
指定一個被包含makefile的搜索目標.可以使用多個“-I”參數來指定多個目錄.“-j [<jobsnum>]”
“--jobs[=<jobsnum>]”
指同時運行命令的個數.如果沒有這個參數,make運行命令時能運行多少就運行多少.如果有一個以上的“-j”參數,那么僅最后一個“-j”才是有效的.(注意這個參數在MS-DOS中是無用的)“-k”
“--keep-going”
出錯也不停止運行.如果生成一個目標失敗了,那么依賴於其上的目標就不會被執行了.“-l <load>”
“--load-average[=<load]”
“—max-load[=<load>]”
指定make運行命令的負載.“-n”
“--just-print”
“--dry-run”
“--recon”
僅輸出執行過程中的命令序列,但並不執行.“-o <file>”
“--old-file=<file>”
“--assume-old=<file>”
不重新生成的指定的<file>,即使這個目標的依賴文件新於它.“-p”
“--print-data-base”
輸出makefile中的所有數據,包括所有的規則和變量.這個參數會讓一個簡單的makefile都會輸出一堆信息.如果你只是想輸出信息而不想執行makefile,你可以使用“make -qp”命令.如果你想查看執行makefile前的預設變量和規則,你可以使用“make –p –f /dev/null”.這個參數輸出的信息會包含着你的makefile文件的文件名和行號,所以,用這個參數來調試你的makefile會是很有用的,特別是當你的環境變量很復雜的時候.“-q”
“--question”
不運行命令,也不輸出.僅僅是檢查所指定的目標是否需要更新.如果是0則說明要更新,如果是2則說明有錯誤發生.“-r”
“--no-builtin-rules”
禁止make使用任何隱含規則.“-R”
“--no-builtin-variabes”
禁止make使用任何作用於變量上的隱含規則.“-s”
“--silent”
“--quiet”
在命令運行時不輸出命令的輸出.“-S”
“--no-keep-going”
“--stop”
取消“-k”選項的作用.因為有些時候,make的選項是從環境變量“MAKEFLAGS”中繼承下來的.所以你可以在命令行中使用這個參數來讓環境變量中的“-k”選項失效.“-t”
“--touch”
相當於UNIX的touch命令,只是把目標的修改日期變成最新的,也就是阻止生成目標的命令運行.“-v”
“--version”
輸出make程序的版本、版權等關於make的信息.“-w”
“--print-directory”
輸出運行makefile之前和之后的信息.這個參數對於跟蹤嵌套式調用make時很有用.“--no-print-directory”
禁止“-w”選項.“-W <file>”
“--what-if=<file>”
“--new-file=<file>”
“--assume-file=<file>”
假定目標<file>需要更新,如果和“-n”選項使用,那么這個參數會輸出該目標更新時的運行動作.如果沒有“-n”那么就像運行UNIX的“touch”命令一樣,使得<file>的修改時間為當前時間.“--warn-undefined-variables”
只要make發現有未定義的變量,那么就輸出警告信息.參考鏈接
跟我一起寫 Makefile(十一)http://blog.csdn.net/haoel/article/details/2896