Makefile总结
?
gcc編譯流程
1、編寫C程序源代碼
????? 2、預處理 (加入文件到源文件 include,)
????? 3、編譯 (目標文件.o)
4、鏈接? (可執行文件)???
gcc? 選項
-c
編譯、匯編到目標代碼(.o),不進行鏈接。如上圖所示。
?
-o outfile
輸出到指定的文件。
?
Makefile的內容
Makefile里主要包含:
1、顯式規則。顯式規則說明了,如何生成一個或多個的目標文件。這是由Makefile的書寫者明顯指出,要生成的文件,文件的依賴文件,生成的命令。
2、隱晦規則。由于我們的make有自動推導的功能,所以隱晦的規則可以讓我們比較粗糙地簡略地書寫Makefile,這是由make所支持的。
3、變量的定義。在Makefile中我們要定義一系列的變量,變量一般都是字符串,這個有點像C語言中的宏,當Makefile被執行時,其中的變量都會被擴展到相應的引用位置上。
4、文件指示。其包括了三個部分,一個是在一個Makefile中引用另一個Makefile,就像C語言中的include一樣;另一個是指根據某些情況指定Makefile中的有效部分,就像C語言中的預編譯#if一樣;還有就是定義一個多行的命令
5、注釋。Makefile中只有行注釋,和UNIX的Shell腳本一樣,其注釋是用“#”字符,這個就像C/C++中的“//”一樣。(如果你要在你的Makefile中使用“#”字符,可以用反斜杠進行轉義,如:“\#”)
Makefile的文件名
大多數的make都支持“makefile”和“Makefile”這兩種默認文件名。
三、makefile如何知道自己該干嘛
一句話:從目標到依賴關系,從依賴關系到目標,再從目標到依賴關系。
四、引用其它的Makefile
include的語法是:
??? include <filename>
注意事項:filename可以是當前操作系統Shell的文件模式(可以包含路徑和通配符)
include前面可以有一些空字符,但是絕不能是[Tab]鍵開始。include和<filename>可以用一個或多個空格隔開。
如果文件都沒有指定絕對路徑或是相對路徑的話,make會在當前目錄下首先尋找,如果當前目錄下沒有找到,那么,make還會在下面的幾個目錄下找:
??? 1、如果make執行時,有“-I”或“--include-dir”參數,那么make就會在這個參數所指定的目錄下去尋找。
??? 2、如果目錄<prefix>/include(一般是:/usr/local/bin或/usr/include)存在的話,make也會去找。
五、環境變量 MAKEFILES
如果你的當前環境中定義了環境變量MAKEFILES,那么,make會把這個變量中的值做一個類似于include的動作。這個變量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,從這個環境變量中引入的Makefile的“目標”不會起作用,如果環境變量中定義的文件發現錯誤,make也會不理。
建議不要使用這個環境變量,因為只要這個變量一被定義,那么當你使用make時,所有的Makefile都會受到它的影響。這也提醒我們,也許有時候你的Makefile出現了怪事,那時候就可以看看當前環境中有沒有定義這個變量。如果是的話就只需要取消那些環境變量即可。
六、makefile的的工作過程總結
GNU的make工作時的執行步驟如下:
??? 1、讀入所有的Makefile。
??? 2、讀入被include的其它Makefile。
??? 3、初始化文件中的變量。
??? 4、推導隱晦規則,并分析所有規則。
??? 5、為所有的目標文件創建依賴關系鏈。
??? 6、根據依賴關系,決定哪些目標要重新生成。
??? 7、執行生成命令。
其中1-5為第一階段,6、7為第二階段。第一階段中,如果定義跌變量被使用,那么make會把它展開在使用的位置,如果定義出現在依賴關系之中,那么僅當這條依賴被決定需要使用了,變量才會在其內部展開。
Makefile一般采用的結構式從上而下,下層目標是用來讓上層目標保持在最新狀態。
?
簡單程序介紹:
一、一個makefile的實例
test:main.o
??????? gcc -o test main.o
main.o:main.c
??????? gcc -c main.c -o main.o
clean:
??????? @rm -vf main.o test
?
二、Makefile的書寫規則
1)如果這個工程沒有編譯過,那么我們的所有C文件都要編譯并被鏈接。
??? 2)如果這個工程的某幾個C文件被修改,那么我們只編譯被修改的C文件,并鏈接目標程序。
??? 3)如果這個工程的頭文件被改變了,那么我們需要編譯引用了這幾個頭文件的C文件,并鏈接目標程序。
規則包括兩部分,一個是依賴關系,一個是生成目標的方法。
在Makefile中,規則的順序是很重要的,Makefile中只應該有一個最終目標,其它的目標都是被這個目標所連帶出來的,所以一定要讓make知道你的最終目標是什么。一般來說,定義在Makefile中的目標可能會有很多(至少有兩個目標),但是第一條規則中的目標將被確立為最終的目標。如果第一條規則中的目標有很多個,那么,第一個目標會成為最終的目標。整個make所完成的也就是這個目標。
三、規則舉例
??? foo.o : foo.c defs.h
??????????? gcc -c -g foo.c
這個規則告訴我們兩件事:
1、文件的依賴關系,foo.o依賴于foo.c和defs.h的文件,如果foo.c和defs.h的時間戳要比foo.o的要新,或是foo.o不存在,那么依賴關系發生。
??? 2、如果生成(或更新)foo.o文件。也就是那個gcc命令,其說明了,如何生成foo.o這個文件。(當然foo.c文件include了defs.h文件)
四、規則的語法
targets : prerequisites
??????? command
??????? ...
????? 或是這樣:
????? targets : prerequisites ; command
??????????? command
??????????? ...
Targets(即目標)多數情況下是文件名,以空格分開,可以使用通配符。目標基本上是一個文件,也可以是許多個文件。
注意:目標可以使用任何名稱,并不一定非得是真實的文件名。
command是命令行,如果其不與“target:prerequisites”在一行,那么,必須以[Tab鍵]開頭,如果和prerequisites在一行,那么可以用分號做為分隔。第二種寫法,目前見過的大多數都采用第一種。
prerequisites也就是目標所依賴的文件(或依賴目標)。如果其中的某個文件要比目標文件要新,那么,目標就被認為是“過時的”,被認為是需要重生成的。
注意:當冒號的右邊沒有指定依賴關系時會怎么樣——只有在目標代表的文件不存在時才會進行更新的動作。
如果命令太長(這涉及到代碼的規范化問題,即增進代碼的可讀性),你可以使用反斜框(‘\’)作為換行符。make對一行上有多少個字符沒有限制。規則告訴make兩件事,文件的依賴關系和如何成成目標文件。
五、在規則中使用通配符
如果我們想定義一系列比較類似的文件,即可使用通配符,當意欲創建適應能力較強的makefile時,通配符就很有用了。make支持三個通配符:“*”,“?”和“[...]”。這是和Unix的B-Shell是相同的。波浪號(“~”)字符在文件名中也有比較特殊的用途。如果是“~/test”,這就表示當前用戶的$HOME目錄下的test目錄。通配符代替了一系列的文件,如“*.c”表示所以后綴為c的文件。一個需要我們注意的是,如果我們的文件名中有通配符,如:“*”,那么可以用轉義字符“\”,如“\*”來表示真實的“*”字符,而不是任意長度的字符串。
??? 一個例子:
clean:
????????????? rm?-f?*.o
????? 很常見的,這是操作系統Shell所支持的通配符。這是在命令中的通配符。
??? print:?*.c
????????????? lpr?-p?$?
????????????? touch?print
?????? 上面這個例子說明了通配符也可以在我們的規則中,目標print依賴于所有的[.c]文件。注意:其中的“$?”是一個自動化變量。
????? objects?=?*.o
??? 上面這個例子,表示了,通符同樣可以用在變量中。并不是說[*.o]會展開,不!objects的值就是“*.o”。Makefile中的變量其實就是C/C++中的宏。如果你要讓通配符在變量中展開,也就是讓objects的值是所有[.o]的文件名的集合,那么,你可以這樣:
??? objects?:=?$(wildcard?*.o)
??? 這種用法由關鍵字“wildcard”指出。
wildcard把 指定目錄 ./ 和 ./sub/ 下的所有后綴是c的文件全部展開。
六、文件搜尋
在一些大的工程中,有大量的源文件,我們通常的做法是把這許多的源文件分類,并存放在不同的目錄中。所以,當make需要去找尋文件的依賴關系時,你可以在文件前加上路徑,但最好的方法是把一個路徑告訴make,讓make自動去找。
方法一:
Makefile文件中的特殊變量“VPATH”就是完成這個功能的,如果沒有指明這個變量,make只會在當前的目錄中去找尋依賴文件和目標文件。
VPATH = src:../headers
上面的的定義指定兩個目錄,“src”和“../headers”,make會按照這個順序進行搜索。目錄之間由“冒號”分隔。(當然,當前目錄永遠是最高優先搜索的地方)
方法二:
另一個設置文件搜索路徑的方法是使用make的“vpath”關鍵字(注意這里全小寫的),這不是變量,這是一個make的關鍵字,這和上面提到的那個VPATH變量很類似,但是它更為靈活。它可以指定不同的文件在不同的搜索目錄中。這是一個很靈活的功能。它的使用方法有三種:
1、vpath?<pattern>;?<directories>;
為符合模式<pattern>;的文件指定搜索目錄<directories>;。
?????? 2、vpath?<pattern>;
?????? 清除符合模式<pattern>;的文件的搜索目錄。
?????? 3、vpath
?????? 清除所有已被設置好了的文件搜索目錄。
??? vapth使用方法中的<pattern>;需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”結尾的文件。<pattern>;指定了要搜索的文件集,而<directories>;則指定了<pattern>;的文件集的搜索的目錄。例如:
????vpath?%.h?../headers
該語句表示,要求make在“../headers”目錄下搜索所有以“.h”結尾的文件。(如果某文件在當前目錄沒有找到的話)我們可以連續地使用vpath語句,以指定不同搜索策略。如果連續的vpath語句中出現了相同的<pattern>;,或是被重復了的<pattern>;,那么,make會按照vpath語句的先后順序來執行搜索。如:
????vpath?%.c?foo
????vpath?%??blish
????vpath?%.c?bar
其表示“.c”結尾的文件,先在“foo”目錄,然后是“blish”,最后是“bar”目錄。
????vpath?%.c?foo:bar
????vpath?%???blish
而上面的語句則表示“.c”結尾的文件,先在“foo”目錄,然后是“bar”目錄,最后才是“blish”目錄。
?
七、偽目標(也稱“假想目標”)
先看這兩行代碼:
clean:
????????????rm?*.o?temp
??? 正像我們前面例子中的“clean”一樣,我們并不生成“clean”這個文件。“偽目標”并不是一個文件,只是一個標簽,由于“偽目標”不是文件,所以make無法生成它的依賴關系和決定它是否要執行。我們只有通過顯示地指明這個“目標”才能讓其生效。當然,“偽目標”的取名不能和文件名重名,不然其就失去了“偽目標”的意義了。
??? 當然,為了避免和文件重名的這種情況(make無法區分文件形式的目標和偽目標),我們可以使用 “.PHONY”來顯示地指明一個目標是“偽目標”,向make說明,不管是否有這個文件,這個目標就是“偽目標”,還讓make知道,不應該像處理一般規則那樣,從源文件來建立以下以工作目標為名的文件。因此,make可以優化它的一般規則搜索程序以提高性能。
????.PHONY?:?clean
??? 只要有這個聲明,不管是否有“clean”文件,要運行“clean”這個目標,只有“make?clean”這樣。于是整個過程可以這樣寫:
?????.PHONY:?clean
????clean:
????????????rm?*.o?temp
??? 偽目標一般沒有依賴的文件。但是,我們也可以為偽目標指定所依賴的文件。偽目標同樣可以作為“默認目標”,只要將其放在第一個。一個示例就是,如果你的Makefile需要一口氣生成若干個可執行文件,但你只想簡單地敲一個make完事,并且,所有的目標文件都寫在一個Makefile中,那么你可以使用“偽目標”這個特性:
????all?:?prog1?prog2?prog3
????.PHONY?:?all
????prog1?:?prog1.o?utils.o
????????????cc?-o?prog1?prog1.o?utils.o
????prog2?:?prog2.o
????????????cc?-o?prog2?prog2.o
????prog3?:?prog3.o?sort.o?utils.o
????????????cc?-o?prog3?prog3.o?sort.o?utils.o
??? 我們知道,Makefile中的第一個目標會被作為其默認目標。我們聲明了一個“all”的偽目標,其依賴于其它三個目標。由于偽目標的特性是,總是被執行的,所以其依賴的那三個目標就總是不如“all”這個目標新。所以,其它三個目標的規則總是會被決議。也就達到了我們一口氣生成多個目標的目的。“.PHONY?:?all”聲明了“all”這個目標為“偽目標”。
??? 隨便提一句,從上面的例子我們可以看出,目標也可以成為依賴。所以,偽目標同樣也可成為依賴。看下面的例子:
????.PHONY:?cleanall?cleanobj?cleandiff
????cleanall?:?cleanobj?cleandiff
????????????rm?program
????cleanobj?:
????????????rm?*.o
????cleandiff?:
????????????rm?*.diff
“make?clean”將清除所有要被清除的文件。“cleanobj”和“cleandiff”這兩個偽目標有點像“子程序”的意思。我們可以輸入“make?cleanall”和“make?cleanobj”和“make?cleandiff”命令來達到清除不同種類文件的目的。
clean沒有依賴模塊,因為沒有時間標記可供比較,所以它總被執行;它的實際意圖是引出后面的rm命令來刪除某些目標文件。我們看到rm命令以-開頭,這時即使表示make將忽略命令結果,所以即使沒有目標供rm命令刪除而返回錯誤時,make clean依然繼續向下執行。
?
?
?摘自百度文庫
轉載于:https://www.cnblogs.com/wnnily/p/4558815.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Makefile总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ping CAP CTO、Codis作者
- 下一篇: Java for LeetCode 20