什麼是makefile?或許不少Winodws的程序員都不知道這個東西,由於那些Windows的IDE都爲你作了這個工做,但我以爲要做一個好的和professional的程序員,makefile仍是要懂。這就好像如今有這麼多的HTML的編輯器,但若是你想成爲一個專業人士,你仍是要了解HTML的標識的含義。特別在Unix下的軟件編譯,你就不能不本身寫makefile了,會不會寫makefile,從一個側面說明了一我的是否具有完成大型工程的能力。c++
由於,makefile關係到了整個工程的編譯規則。一個工程中的源文件不計數,其按類型、功能、模塊分別放在若干個目錄中,makefile定義了一系列的規則來指定,哪些文件須要先編譯,哪些文件須要後編譯,哪些文件須要從新編譯,甚至於進行更復雜的功能操做,由於makefile就像一個Shell腳本同樣,其中也能夠執行操做系統的命令。程序員
makefile帶來的好處就是——「自動化編譯」,一旦寫好,只須要一個make命令,整個工程徹底自動編譯,極大的提升了軟件開發的效率。make是一個命令工具,是一個解釋makefile中指令的命令工具,通常來講,大多數的IDE都有這個命令,好比:Delphi的make,Visual C++的nmake,Linux下GNU的make。可見,makefile都成爲了一種在工程方面的編譯方法。編輯器
在這篇文檔中,將以C/C++的源碼做爲咱們基礎,因此必然涉及一些關於C/C++的編譯的知識,相關於這方面的內容,還請各位查看相關的編譯器的文檔。這裏所默認的編譯器是UNIX下的GCC和CC。ide
target ... : prerequisites ...
command
...
...
target: 目標文件,能夠是Object File,也能夠是執行文件。還能夠是一個標籤(Label)工具
prerequisites: 要生成那個target所依賴的文件或是目標。優化
command: 也就是make須要執行的命令。(任意的Shell命令)ui
這是一個文件的依賴關係,也就是說,target這一個或多個的目標文件依賴於prerequisites中的文件,其生成規則定義在command中。說白一點就是說,prerequisites中若是有一個以上的文件比target文件要新的話,command所定義的命令就會被執行。這就是Makefile的規則。也就是Makefile中最核心的內容。spa
簡單Makefile展現以下:操作系統
include ../Make.defines PROGS = test01 \ test02 all: ${PROGS} test01: test01.o ${CC} ${CFLAGS} -o $@ test01.o ${LIBS} test02: test02.o ${CC} ${CFLAGS} -o $@ test02.o ${LIBS} clean: rm -f ${PROGS} ${CLEANFILES}
事例中幾點注意以下:命令行
1.首行導入了其餘的Makefile相關文件,事例假定此文件爲二級目錄,導入文件爲一級目錄下的Make.define文件
2.第二行顯示定義了變量PROGS, test01後面的 \爲換行符,在參數較多時方便代碼查看。
3.all表示一個標籤,在此例中表示全部目標,即${PROGS}所指定的全部執行文件。clean同理。
4.命令行必定要以TAB開頭(Makefile規定)
5.命令行定義中使用了隱晦規則,$@表示目標文件,在此例中即指代test01或test02.
6.命令行中的變量均爲Make.define文件中定義,表示編譯所用的參數
3.Makefile工做方式
爲方便說明,簡化上述Makefile以下:
include ../Make.defines test01: test01.o ${CC} ${CFLAGS} -o $@ test01.o ${LIBS} clean: rm -f ${PROGS} ${CLEANFILES}
在默認的方式下,在make命令後:
一、make會在當前目錄下找名字叫「Makefile」或「makefile」的文件。
二、若是找到,它會找文件中的第一個目標文件(target),即顯示定義的test01。他會找到「test01」這個文件,並把這個文件做爲最終的目標文件。
三、若是test01文件不存在,或是test01所依賴的後面的 .o 文件的文件修改時間要比all這個文件新,那麼,他就會執行後面所定義的命令來生成test01這個文件。
四、若是test01所依賴的.o文件也存在,那麼make會在當前文件中找目標爲.o文件的依賴性,若是找到則再根據那一個規則生成.o文件。(這有點像一個堆棧的過程)
五、固然,你的C文件和H文件是存在的啦,因而make會生成 .o 文件,而後再用 .o 文件生命make的終極任務,也就是執行文件test01了。
4.Makefile中一些雜項說明
4.1 文件包含
Makefile支持文件包含功能,相似於c/c++中的#include功能,最開始的例子已經使用,實例模型能夠參考。其具體使用方法介紹以下:
include <filename> # filename能夠是當前操做系統Shell的文件模式(能夠保含路徑和通配符)
在include前面能夠有一些空字符,可是毫不能是[Tab]鍵開始。include和<filename>;能夠用一個或多個空格隔開。舉個例子,你有這樣幾個Makefile:a.mk、b.mk、c.mk,還有一個文件叫foo.make,以及一個變量$(bar),其包含了e.mk和f.mk,那麼,下面的語句:
include foo.make *.mk $(bar)
等價於:
include foo.make a.mk b.mk c.mk e.mk f.mk
make命令開始時,會把找尋include所指出的其它Makefile,並把其內容安置在當前的位置。就好像C/C++的#include指令同樣。若是文件都沒有指定絕對路徑或是相對路徑的話,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,其做用和這一個是同樣的。
4.2 幾種賦值符號差別
Makefile中賦值符號中四種(= := ?= +=),區別以下:
1. = 是最基本的賦值,他會將整個Makefile展開後,將最後一個值付給變量。
x = foo y = $(x) bar x = xyz 在上例中,y的值將會是 xyz bar ,而不是 foo bar 。
2. := 是覆蓋以前的值,無需所有展開,在賦值處直接覆蓋。
x := foo y := $(x) bar x := xyz 在上例中,y的值將會是 foo bar ,而不是 xyz bar 了。
3. ?= 是若是沒有被賦值過就賦予等號後面的值(容易理解)
4. += 是添加等號後面的值(字面含義,直接追加)
4.3 編譯參數整理
4.3.1: GCC參數整理:
在Makefile編譯命令行中(即commend),第一個例子的${CCFLAG}. 變量中定義了一些編譯器須要處理的工做(如:自動導出頭文件包含 -M; 添加GDB調試:-g; 優化等級:-o ~ -o2), gcc選項以下
用法:gcc [選項] 文件...
選項:
-pass-exit-codes 在某一階段退出時返回最高的錯誤碼
--help 顯示此幫助說明
--target-help 顯示目標機器特定的命令行選項
(使用‘-v --help’顯示子進程的命令行參數)
-dumpspecs 顯示全部內建 spec 字符串
-dumpversion 顯示編譯器的版本號
-dumpmachine 顯示編譯器的目標處理器
-print-search-dirs 顯示編譯器的搜索路徑
-print-libgcc-file-name 顯示編譯器伴隨庫的名稱
-print-file-name=<庫> 顯示 <庫> 的完整路徑
-print-prog-name=<程序> 顯示編譯器組件 <程序> 的完整路徑
-print-multi-directory 顯示不一樣版本 libgcc 的根目錄
-print-multi-lib 顯示命令行選項和多個版本庫搜索路徑間的映射
-print-multi-os-directory 顯示操做系統庫的相對路徑
-Wa,<選項> 將逗號分隔的 <選項> 傳遞給彙編器
-Wp,<選項> 將逗號分隔的 <選項> 傳遞給預處理器
-Wl,<選項> 將逗號分隔的 <選項> 傳遞給連接器
-Wall 打開全部編譯警告
-Xassembler <參數> 將 <參數> 傳遞給彙編器
-Xpreprocessor <參數> 將 <參數> 傳遞給預處理器
-Xlinker <參數> 將 <參數> 傳遞給連接器
-combine 將多個源文件一次性傳遞給彙編器
-save-temps 不刪除中間文件
-pipe 使用管道代替臨時文件
-time 爲每一個子進程計時
-specs=<文件> 用 <文件> 的內容覆蓋內建的 specs 文件
-std=<標準> 指定輸入源文件遵循的標準
--sysroot=<目錄> 將 <目錄> 做爲頭文件和庫文件的根目錄
-B <目錄> 將 <目錄> 添加到編譯器的搜索路徑中
-b <機器> 爲 gcc 指定目標機器(若是有安裝)
-V <版本> 運行指定版本的 gcc(若是有安裝)
-v 顯示編譯器調用的程序
-### 與 -v 相似,但選項被引號括住,而且不執行命令
-E 僅做預處理,不進行編譯、彙編和連接
-S 編譯到彙編語言,不進行彙編和連接
-c 編譯、彙編到目標代碼,不進行連接
-o <文件> 輸出到 <文件>
-x <語言> 指定其後輸入文件的語言容許的語言包括:c c++ assembler none
‘none’意味着恢復默認行爲,即根據文件的擴展名猜想
源文件的語言
以 -g、-f、-m、-O、-W 或 --param 開頭的選項將由 gcc 自動傳遞給其調用的不一樣子進程。若要向這些進程傳遞其餘選項,必須使用 -W<字母> 選項。
4.3.2: make參數整理(摘自跟我一塊兒寫Makefile)
下面列舉了全部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發現有未定義的變量,那麼就輸出警告信息。
4.4 Makefile規則檢查
有時候,咱們不想讓咱們的makefile中的規則執行起來,咱們只想檢查一下咱們的命令,或是執行的序列。因而咱們可使用make命令的下述參數: 「-n」 「--just-print」 「--dry-run」 「--recon」 不執行參數,這些參數只是打印命令,無論目標是否更新,把規則和連帶規則下的命令打印出來,但不執行,這些參數對於咱們調試makefile頗有用處。 「-t」 「--touch」 這個參數的意思就是把目標文件的時間更新,但不更改目標文件。也就是說,make僞裝編譯目標,但不是真正的編譯目標,只是把目標變成已編譯過的狀態。 「-q」 「--question」 這個參數的行爲是找目標的意思,也就是說,若是目標存在,那麼其什麼也不會輸出,固然也不會執行編譯,若是目標不存在,其會打印出一條出錯信息。 「-W <file>;」 「--what-if=<file>;」 「--assume-new=<file>;」 「--new-file=<file>;」 這個參數須要指定一個文件。通常是是源文件(或依賴文件),Make會根據規則推導來運行依賴於這個文件的命令,通常來講,能夠和「-n」參數一同使用,來查看這個依賴文件所發生的規則命令。