Makefile的重建与include指令
Makefile的重建與include指令
include指令
當make看到include指令時,會事先對通配符以及變量引用進行擴展,然后試著讀引入文件(include file).
如果這個文件存在,則整個過程會繼續下去;如果不存在,則make會匯報此問題(如果你用的不是-include)并且讀取其余的Makefile.
當所有的讀取動作完成后,make會從規則數據庫中尋找任何可以用來更新引入文件的規則,如果找到了一個相符的規則,make就會按照正常的步驟來更新目標。
如果任何一個引入文件被規則更新,那么make會接著清除它的內部數據庫并且重新讀取整個Makefile.
在完成了讀取、更新、重新讀取的過程后,如果仍有文件不存在而導致include指令執行失敗,那么make就會報告錯誤并終止執行。
下面的一個例子可以幫助我們理解這個過程。這個例子修改自《GNU Make 項目管理》。
假設我們有一個Makefile,里面的內容如下
另外我們還有一個bar.mk文件,它的內容很簡單,只有如下一行
$(warning Reading FILENAME)要理解這個Makefile,先解釋幾個知識點。
warning函數
函數的格式是
$(warning text)這里的text只是舉例,可以換成任何你想要顯示的信息。
warning函數的輸出中包含了當前Makefile的文件名,當前行的行號,以及要顯示的信息內容。warning函數擴展之后會變成空字符串,所以它幾乎可以用在任何地方。
關于m4
What is m4?
M4 can be called a “template language”, a “macro language” or a “preprocessor language”. The name “m4” also refers to the program which processes texts in this language: this “preprocessor” or “macro processor” takes as input an m4 template and sends this to the output, after acting on any embedded directives, called macros.
At its most basic, it can be used for simple embedded text replacement. If m4 receives the input
define(AUTHOR, William Shakespeare)A Midsummer Night's Dreamby AUTHORthen it outputs
A Midsummer Night's Dreamby William Shakespeare簡而言之,m4將輸入拷貝到輸出,同時將宏展開。
下載地址:ftp://ftp.gnu.org/gnu/m4/
上面的第7行,作用就是將bar.mk文件的內容拷貝到foo.mk文件中,同時替換FILENAME為$@,也就是foo.mk。所以說
m4 --define=FILENAME=$@ bar.mk>$@這行命令生成了foo.mk文件,其內容就是:
$(warning Reading foo.mk)掌握了上面兩個知識點后,我們繼續看Makefile文件。
執行make命令后。輸出如下
根據輸出結果,我們可以窺見make的處理流程:
1. 讀Makefile文件,從頭開始處理;
2. 執行include foo.mk,但是沒有找到這個文件,于是輸出foo.mk: 沒有那個文件或目錄;
3. 繼續讀取文件,直到讀取整個文件結束。然后,make會從規則數據庫中尋找任何可以用來更新引入文件的規則,找到了
于是根據這個規則,生成了foo.mk文件;
4. 因為foo.mk文件被更新了,所以make會清除它的內部數據庫并且重新讀取整個Makefile,于是再次輸出Reading Makefile;
5. 當遇到include foo.mk的時候,找到了foo.mk這個文件,于是讀入它,所以輸出Reading foo.mk;
6. 繼續讀完剩下的部分;在讀取完成后也會去試圖更新所有的已經讀取的makefile文件,包括foo.mk,但是foo.mk不會再次被重建,因為它比bar.mak新;
7. 解析已經讀取的makefile文件并執行必要的動作。根據依賴樹,因為foo.mk比bar.mk新,所以不需要執行命令,于是輸出“foo.mk”是最新的。
無限循環的Makefile
對于上面的第6點,我們可以在命令中更新bar.mak的時間戳,讓foo.mk比bar.mak舊,于是foo.mk會再次被重建,也就是說foo.mk被更新了(比之前的它要新),這樣就會再回到第4步,構成一個無限循環。
如何達到這個效果呢?Makefile的內容改成下面的(第8~9行是新增加的):
1 $(warning Reading Makefile) 2 3 include foo.mk 4 $(warning Finished include) 5 6 foo.mk :bar.mk 7 m4 --define=FILENAME=$@ bar.mk>$@ 8 sleep 1 9 touch bar.mk第8行:表示延時1秒
第9行:更新bar.mak
之所以要延時,是因為要讓bar.mak比foo.mk新得“明顯”;否則盡管bar.mk比foo.mk新,但是只新那么一丁點(小于系統時間的最小分辨度),于是它們在時間戳上是一樣的,這樣就達不到我們的目的。
此時我們執行make命令(執行前請刪除foo.mk文件),如愿以償,看到一個死循環,輸出結果如下圖:
書中還提到:
現在是讓你知道“make也可以把Makefile本身作為一個可能的工作目標”這件事的好時機。當make讀進整個makefile之后,make將會試著尋找可以用來重新當前所執行的makefile的規則。如果找到了,make將會處理此規則,然后檢查makefile是否已經更新。如果已經更新,make將清除它的內部狀態,重新讀進此makefile,重新完成整個分析動作。
作者還舉了一個無限循環的例子,其內容如下:
.PHONY: dummymakefile: dummytouch $@執行結果如下圖:
其實并沒有無限循環。
我把這個例子修改如下:
1 $(warning read me...) 2 .PHONY:dummy 3 Makefile:dummy 4 sleep 1 5 touch $@執行結果如下圖:
對執行過程的解釋:
1. make讀入Makefile,解析目標Makefile與依賴dummy的時間戳,因為dummy是偽目標,偽目標總是尚未更新,所以Makefile也需要更新,所以就執行touch命令,這樣會更新Makefile的時間戳,于是make會重進讀入Makefile;
2. make讀入Makefile,解析目標Makefile與依賴dummy的時間戳……(循環到第1步)
實驗結果說明第4行是非常有必要的,如果不延時的話,因為上次touch更新了Makefile,所以再次讀入,再執行touch,但是這兩次touch之間的時間間隔太短了,短于系統時間的最小分辨度,這樣就會導致第二次touch后,make對比Makefile的時間戳,發現時間戳一樣,認為它沒有被更新,所以不會再次讀入它。這樣就無法看到無限循環的效果。
總結
以下文字摘自《GNU make中文手冊》的3.7節,作為本文的總結。
make在讀入所有makefile文件之后,首先將所讀取的每個makefile作為一個目標,尋找更新它們的規則。如果存在一個更新某一個 makefile文件明確規則或者隱含規則,就去更新對應的makefile文件。完成對所有的makefile文件的更新之后,如果之前所讀取的任何一個makefile文件被更新,那么make就清除本次執行的狀態重新讀取一遍所有的makefile文件(此過程中,同樣在讀取完成以后也會去試圖更新所有的已經讀取的makefile文件,但是一般這些文件不會再次被重建,因為它們在時間戳上已經是最新的)。讀取完成以后再開始解析已經讀取的makefile文件并開始執行必要的動作。
參考資料
[0] 《GNU Make 項目管理》
[1] Notes on the M4 Macro Language
[2] 徐海兵:《GNU make中文手冊》
總結
以上是生活随笔為你收集整理的Makefile的重建与include指令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python的深造方向_自动化深造方向有
- 下一篇: 输入两个长度相同的字符串,比较两个数在相