Autools学习总结(一)
一、Makefile
簡介
?? ?在編寫C/C++程序的時候,我們經常需要編譯并運行代碼。在程序規模較小的情況下,可以簡單地直接調用編譯器來完成這項工作。然而,在很多情況下程序往往包括大量的代碼文件,手動調用編譯器變得麻煩無比。尤其要命的是,編譯的時候需要考慮源文件之間的依賴情況。例如對于下面的三個文件:
文件main.c:
1?#include?<xxx.h>2?blabla
?? ?文件xxx.h:
1?declared?some?functions?or?classes?? ?文件xxx.cpp:
1?#include?<xxx.h>2?implemented?some?functions?or?classes
?? ?上面三個文件的依賴情況分別為:
1?main.c?=>?xxx.h2?xxx.cpp?=>?xxx.h
?? ?因此,在xxx.h文件被改變時,將導致main.c和xxx.cpp都需要重新編譯,而main.c和xxx.cpp被改變時則只需要編譯相應的文件,并執行鏈接操作即可(這個例子也說明了為什么.h文件不能輕易變動,可以把.h文件視為模塊提供的接口,如同契約一樣已經制定就不能輕易變動)。
?? ?這個問題如何解決呢?可以不考慮依賴關系,把所有源文件重新編譯——這種方式在我們編寫一些玩具代碼的時候的確可以,然而在一個中等規模的項目就會成為一個噩夢。。C/C++代碼的編譯速度本來就慢(這一點自己編譯過開源代碼同學應該深有體會),如果每次都徹底重新編譯的話會浪費大量的時間。Make工具的產生一開始就是用來解決這一問題的(當然不限于此)。
?? ?先上一個最簡單的Make用例:
hello.c文件
1?#include?<stdio.h>2?int?main()
3?{
4?????printf(“hello?world!\n”);
5?????return?0;
6?} 1?$>make?hello?&&?./hello
2?cc?hello.c?????-o????hello
3?hello?world
規則和命令
?? ?Makefile允許我們定義一系列的依賴規則,并根據該依賴規則來執行相應的命令。下面是一個最簡單的Makefile:
Makefile文件
1?#?簡單的依賴文件2?hello:?hello.c
3?? ??gcc?-o?hello?hello.c
?? ?其中第零行是一行注釋,同shell腳本一樣,Makefile使用#來定義注釋。第一行定義了一條依賴規則,冒號左邊為需要生成的目標,冒號右邊則為生成該目標所需要的文件。值得注意的是,規則的目標和依賴項都可以是多個文件,多個文件之間使用空格分開,形如a b: c d的規則表示目標a和b均依賴于c和d。
?? ?文件的第二行為一個命令。Make工具根據目標和依賴項的修改時間戳來判斷是否需要執行命令來生成新的目標,即:如果目標的修改時間較所有的依賴項更新,則表明無需執行命令來重新生成目標;而如果任一依賴項的修改時間較目標的更新,則說明有必要執行命令來重新生成目標。make根據命令的程序退出狀態來判斷是否執行成功(0為成功,否則失敗),如果執行失敗則停止生成過程并報錯。可以通過設定命令行前綴來改變這一過程,命令行前綴包括以下兩種:
-:make忽略該命令的執行情況,即使失敗仍然繼續生成
@:make不打印該命令,通常在執行echo命令時使用該前綴
?? ?下面是一個使用命令行前綴的示例:
1?dash:2?? ??-rm?/some/file/not/exist
3?at:
4?? ??@echo?“hello”
?? ?當執行make dash時,rm命令執行失敗,但是make的生成目標過程不會停止,而執行make at時,將只在屏幕上打印“hello“,不會打印出echo命令的執行情況。
?? ?在makefile中,規則后面不一定要跟一條命令,即我們可以定義一條啥事也不干的規則。而判斷一條語句是規則還是命令是根據行開頭是否包含TAB字符,注意,一定要是TAB字符,不能是四個空格或者其它看起來和TAB一樣的東東。。這個設計被認為是Unix有史以來最糟糕的設計修補(TAOUP P358)。(幸運的是,VIM能很好地處理這一問題,在我設置了expandtab的情況下還能正確地在命令的第一列插入TAB字符。)
變量的定義和使用
?? ?Makefile中變量的定義和使用方式如下所示:
1?dash:2?? ??-rm?/some/file/not/exist
3?at:
4?? ??@echo?“hello”
?? ?變量的定義非常簡單,變量名和值用符號“=”分開即可,使用變量則用$(valname)的方式。在make程序解析Makefile時,會對變量進行替換。這里有一個需要注意的地方:make實際上會對Makefile掃描兩次——第一次替換所有的變量,第二次才是根據規則執行命令。這意味著對于變量在多處定義時,最終替換的變量值是最后一次對變量指定的值。下面舉一個例子:
Makefile文件
1?val?=?a2?test:
3?? ??echo?$(val)
4?val?=?b
?? ?執行make test時,顯示的結果將是b而不是a。
?? ?除了我們自定義的變量之外,make還包含了一些預定義的變量。例如,變量CC表示系統默認的C編譯器(默認值為cc),因此定義編譯文件的命令時通常使用的方式是$(CC) -o main $(src)而不是直接使用gcc。這一特點在后面引入Autoconf時非常有用——Autoconf檢查系統的默認C編譯器并設置CC變量的值,從而使得代碼可以在包含不同C編譯器的系統上順利編譯。完整的預定義變量可以參見make用戶手冊。
?? ?在上面提到的例子中,src變量被使用了兩次:作為規則的依賴項和命令的參數。為了貫徹執行DRY原則,make還定義了一種稱為“自動變量”的變量,用來表示規則中的特定部分。使用自動變量時,上面的例子可以中寫為如下方式:
1?src?=?main.c?xxx.c2?main:?$(src)
3?? ??$(CC)?-o?$@?$+
?? ?其中$@是$(@)的簡寫形式,表示規則的目標部分,而$+則表示規則的所有依賴項。相對于前面的方式,現在的Makefile是不是干凈了一些呢?完整的自動變量列表同樣包括在make用戶手冊中。
SUFFIXES和PHONY
?? ?假設我們的項目中只有一個文件main.c,那么Makefile很簡單:
1?main?:?main.o2?? ??gcc?-o?main?main.o
3?main.o:?main.c
4?? ??gcc?-c?main.c
?? ?可是在有100個甚至更多.c文件的情況下,需要對每一個.c文件進行編譯,就不得不寫很多次如下的代碼:
1?xxx.o:?xxx.c2?? ??gcc?-c?xxx.c
?? ?幸運的是,make提供了后綴匹配功能,從而使用如下代碼就可一完成上述功能:
1?.c.o:2?????gcc?-c?$<
?? ?類似的規則成為后綴規則,make在解析Makefile時將掃描Makefile文件所在目錄下的匹配文件,并執行相應的命令。然而這里存在一個問題,即可能存在一個名為“.c.o“的文件,make如何判斷這條規則是一條普通的規則還是后綴規則呢?這里就要用到特殊的目標SUFFIXES了:
1?.SUFFIXES:?.c?.o?? ?這條規則告訴make將目標為.c.o的規則視為后綴規則,而非名為.c.o的文件。
?? ?與此類似的另一個特殊目標是PHONY,用來定義一些“偽目標”。在Makefile中,我們除了定義一些需要生成的文件外,還可能會執行一些操作,而這些操作是不會生成目標文件的。例如,目前廣泛使用make clean清理臨時文件,make install用來安裝程序。我們需要定義一些沒有依賴項的規則:
1?clean:2?????rm?*.o
?? ?當執行make clean時,make將查找當前目錄,發現沒有名為clean的文件,同時該文件也不需要任何依賴項,這種情況下make將總是執行規則對應的命令。然而,如果不幸地在當前目錄下剛好存在一個名為clean的文件呢?這時make發現該文件沒有任何依賴項,從而認為該目標已是最新的,因此將跳過命令執行階段。
?? ?特殊目標PHONY就是用來解決這一問題的:
1?.PHONY:?clean?? ?以上規則將告知make程序,clean目標為一個偽目標,從而make程序無需再去檢查目錄下clean文件的存在與否,而總是執行clean目標所對應的命令。
?? ?(未完待續。。。)
?
轉載于:https://www.cnblogs.com/zyobi/archive/2010/10/31/1865703.html
總結
以上是生活随笔為你收集整理的Autools学习总结(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《正义联盟》里的超人和蝙蝠侠哪个更有权势
- 下一篇: app开发多少钱啊?