1. 程式人生 > >makefile(四):makefile規則中的命令

makefile(四):makefile規則中的命令

規則中的命令被傳遞給shell進行解析執行。除跟在依賴後面的第一條命令以外,其他命令必須與tab鍵開頭。如下:

目標:依賴;命令1
    命令2

通常情況下也並不推薦這種寫法,推薦的寫法還是將命令1,另起一行,並以tab鍵開頭,這樣做的好處,就是便於觀察,但是對於空命令除外,詳見空命令一節

一.命令的回顯

makefile在執行命令的時候,通常會將命令顯示在標準輸出中,如果不想顯示在標準輸出中,可以使用下面的方法
1.在每個命令的前面使用@字串如下:

wanbiao:
    @echo $(name)

這樣在執行命令的時候,就不會講這個命令回顯在標準輸出中
2.使用命令列引數


在make執行的命令輸入中,鍵入-s,–silent選項,既可,它表示所有的命令都不會回顯在標準輸出中。
具體使用哪一種方式,由具體是用來定,通常情況下@更加靈活
3.使用.SLIENT
給.SLIENT目標新增依賴,那麼出現在依賴列表中的目標,重建的時候,將不會回顯命令

二.命令的執行

由不同的tab鍵開始的命令符,將使用不同的shell執行緒,即第一行,和第二行使用的shell執行緒將不同,因此,第一行切換了目錄之後,在第二行之後就不會再次在第一行切換的目錄下,如果要在切換目錄之後正常使用,則必須寫在同一行中,如果想要換行,可以使用‘\’加換行符進行換行,舉例如下:

all:
cd prog cp -a * out

上例中,cd到prog目錄之後,在第二行的cp命令執行時,並沒有在prog目錄下,因此如果想要達到在prog目錄下,應該寫在同一行中。如下

all:
    cd prog;cp -a * out

因為shell中以分號進行分割命令,因此第一個命令和第二個命令之間使用分號進行分割,如果想要寫在多行中,可以使用反斜線,如下:

all:
    cd prog;\
cp -a * out

三.命令的錯誤

當make的命令執行錯誤的時候,會退出當前的執行環境,並報錯。在某些時候,命令的執行錯誤並不影響終極目標的生成。如,rm命令,當要刪除的檔案已經存在,那麼rm命令刪除返回成功,當要刪除的檔案不存在,那麼rm將會報錯。但是對於這種錯誤並不影響終極目標的建立,因此我們可以忽略這種錯誤。makefile中忽略錯誤有三種方法
1.同使用@符號一樣,我們使用符號-來忽略命令的錯誤。


2.同使用-s選項一樣,我們使用-i選項和–igoner-errors來忽略所有的命令錯誤
3.使用.IGNORE目標來忽略後面命令執行的錯誤,具體用法同.SILENT類似
當遇到錯誤之後,make會退出,這樣當遇到第一個錯誤就退出,後面沒有執行的,就不知道是否還有錯誤,只有把第一個錯誤修改之後,才能知道後面是否有錯誤,這樣做非常的麻煩。為了避免這種麻煩,我們可以使用在命令列中使用–keep-going,來告訴make遇到錯誤的時候,暫時不退出,繼續執行後面的命令,直到無法繼續執行後續目標的情況下才退出。這樣就可以在一次修改多個原始檔的情況下,最大限度的直到哪些修改還有問題。

注意:雖然使用–keep-going有好處,但是他生成的中間檔案,已經被更新了時間,但是他的內容很有可能是不對的,因此在下一次make之前,請先刪掉這些生成的中間檔案。通常情況下,每個編譯系統中,都會有一個clean的偽目標。可以直接make
clean,刪除相應的中間檔案。

四.定義命令包(makefile的自定義函式)

在大部分語言中,除了使用內建函式以外,還可以使用自定義函式。一個函式就是一些列執行語句的集合。在makefile中也可以定義執行語句的集合。它的格式如下:

define 變數名
執行語句
endef

注意:這裡的叫做變數名,不叫做函式名,是因為在makefile的手冊裡面,這個語法的本質定義是定義多行變數,即上面的執行語句,是變數的值。

舉例如下:

define name
wanbiao
zhangsan
lisi
endef

定義了一個變數name,它的值有三行,分別是

wanbiao
zhangsan
lisi

makefile的變數引用就相當於c語言的巨集替換,因此,我們可以在這裡將變數的值替換成各種執行語句,這樣,配合自動化變數和內建的call函式,就實現,其他語言的函式功能了。舉例如下

define catfunction
$(1)$(2).png
endef
$(warning $(call catfunction,wanbiao,20171016))

直接執行上面的程式碼,就能夠得到如下的輸出:

wanbiao20171016.png

關於call函式,和warning函式的詳細描述,可以參考後面的內建函式一小節
此語法的高階特性,是配合call函式和,eval函式,實現更多具有模組化的功能。詳細內容請參考後面的介紹

五.控制命令的執行

1.使用引數,進行命令的並行執行
make的選項引數中,提供了一個-j或者–job的引數選項,來控制命令的執行,使用幾個執行緒。當沒有使用這個引數的時候,預設使用一個執行緒。-j後面跟一個正整數,表示使用幾個執行緒。多執行緒可以提高make的執行效率,但是這並不代表,執行緒數越多,效率越高。作業系統線上程上面的切換,需要消耗一定的資源。因此合理的分配執行緒數,是提供make編譯效率的前提。
當我們,在編譯一個專案的時候,可能需要消耗cpu和記憶體的很大部分資源。那麼,當我們需要在同一臺電腦上做其他事情,有可能非常的卡頓,因此,我們可以給make指定一個最大負載,當超過這個最大負載,make就算通過了-j指定了執行緒數,也不會重新建立新的執行緒。
使用引數-l或者–max-load加一個浮點數,來表示最大的負載。沒有跟浮點數的這個引數,表示取消前面的負載限制。
2.取消命令的執行
在make執行的時候,可以按住ctrl+c鍵取消當前make的執行。make被中斷之後,會自動的刪除,建立的中間檔案。這樣就能保證在下一次執行的正確性。

六.make的遞迴執行

make的遞迴指的是:在命令列中使用make命令,呼叫其他的makefile檔案。

注意:這裡的遞迴和c語言裡面的遞迴有些差別,c語言裡面的遞迴是指,函式自己呼叫自己。

make的遞迴舉例如下:

subsystem:
cd subdir && $(MAKE)

表示的是:進入subdir目錄,然後執行make命令,其等價於下面的規則。

subsystem:
$(MAKE) -C subdir

注意:這裡的變數MAKE是對make命令的一個引用,雖然這裡可以直接使用make命令,但是並不推薦這種用法,有如下原因:
1.當我們在頂層make中指定了一個make程式之後,使用MAKE變數,在下層make中將會使用同一個make程式,如果不使用MAKE變數,則使用的是預設的make程式,有可以能與頂層的make程式不同。
2.當使用選項-t(–touch),-n(–just-print),-q(–question)的時候,所有的命令都不不會被執行,但是使用了MAKE變數的命令除外。一旦使用了make命令來替代MAKE變數,那麼在下層makefile中,將不會被執行,對應的-t,-n,-q選項,這不是我們想要的效果。

六,變數的向下傳遞和截斷

在頂層makefile中定義的變數,將不會被傳遞給底層的makefile檔案中。因此完全可以命名同名的兩個變數在不同層級的makefiile中。
一旦我們想要將頂層的makefile中變數傳遞給下層的makefile中,可以使用下面的方法:
使用export關鍵字,將需要傳遞給下一層的makefile的變數加入的環境變數中。如果下層的變數名和上層的變數名相同,並不會覆蓋下層的變數名。如果想要覆蓋下層的環境變數,可以使用-e(–environment-overrides)引數。

用法:export 變數

不帶任何變數的export表示將所有的變數都變成環境變數,然後傳遞給下層makefile。如果不希望將某個變數傳遞給下層,可以使用unexport關鍵字

用法:unexport 變數

需要特別注意的是:1.通過命令列宣告的變數也會傳遞給下層makefile中。2.MAKEFILES、MAKEFLAGS和SHELL變數始終會傳遞給下層makefile中,除非使用unexport關鍵字進行取消。
其實上面的注意中,第一項可以放置到第二項的MAKEFLAGS中,因為通過命令列引數指定的變數被儲存在了MAKEOVRRIDES變數中,而MAKEOVRRIDES變數被MAKEFLAGS變數所引用。MAKEFLAGS變數的值為命令列裡面的引數,但是有幾個引數卻不會賦值給MAKEFALGS,他們是 “-C”、“ -f”、“ -o” 和“ -W”。這幾個引數的具體含義參見後面的makefile引數說明章節