【Makefile】Makefile與shell命令為何總是藕斷絲連?
1 寫在前面
博主最近在項(xiàng)目實(shí)踐過程中,需要深度定制項(xiàng)目的Makefile,其中有些復(fù)雜的流程必須得借助shell腳本才能高效地完成,為此博主特意深入學(xué)習(xí)了在Makefile種調(diào)用shell命令的方法。
大家都知道shell命令是Linux的神器,學(xué)會(huì)靈活應(yīng)用它可以大大提升執(zhí)行的自動(dòng)化效率。
Makefile也是GNU的另一大神器,大家要知道,大名鼎鼎的Linux內(nèi)核的編譯就是靠Makefile來支撐的,連這么龐大、復(fù)雜的系統(tǒng)編譯它都可以輕松駕馭,可想而知,它也是牛逼哄哄的存在。
我們?cè)囅胍幌拢绻袽akefile和shell命令這兩大神器聯(lián)合在一起,會(huì)有怎樣的效果,我們一起來看下。
2 什么是Makefile?
這個(gè)問題,我在之前的博文中就有介紹過,感興趣的可以點(diǎn)過去看一看:【Linux + Makefile】十分鐘教你學(xué)會(huì)Makefile的FORCE。
下面我再總結(jié)下,到底什么是Makefile?
Makefile的基本形式如下所示:
TARGET : DEPENDENCES CMD
# TARGET:生成的目標(biāo),可以是一個(gè)文件,也可以是一個(gè)虛擬符號(hào)(非真實(shí)文件) # DEPENDENCES: 生成目標(biāo)的所有依賴,它是一個(gè)集合,可以只有一個(gè)文件,或者很多文件;也可以是虛擬符號(hào) # CMD:把所有依賴生成目標(biāo)的執(zhí)行命令,其中CMD前面是一個(gè)TAB鍵,而不能是空格 Makefile的基本規(guī)則,歸納起來就是一句話:【當(dāng)一個(gè)TARGET (欲生成的目標(biāo))比它的任何一個(gè)DEPENDENCES(依賴的文件)舊時(shí),這個(gè)TARGET就要重新生成】。
這段話讀起來很簡(jiǎn)單,就是Makefile在執(zhí)行編譯的時(shí)候,會(huì)先去判斷TARGET和其依賴文件的時(shí)間差,如果TARGET的時(shí)間比較舊,意味著依賴文件已經(jīng)更新了,那么TARGET就要重新生成;反之,如果TARGET的時(shí)間比較新,意味著在TARGET生成后,依賴文件是沒有改變過的,自然就不用重新去生成TARGET。
說白了,就是搞清楚【TARGET 和 DEPENDENCES 的時(shí)間關(guān)系】,弄明白了這個(gè),你就可以讀懂Makefile了。
3 如何調(diào)試Makefile?
以下Makefile給大家演示下如何在Makefile中寫注釋以及如何調(diào)試Makefile的變量。
# This is an annotation for this Makefile.
# Call shell cmd in these ways ...
CUR_PATH = `pwd`
LS_FILES = "`ls`"
LS_TEST_FILES = "$(shell ls test*)"
DEBUG_VARIABLE = "debug"
# Debug your Makefile variables ...
$(info "This is a info msg ...")
$(warning "This is a warning msg ... DEBUG_VARIABLE=$(DEBUG_VARIABLE)")
$(error "This is a error msg ... It will make this Makefile to exist beacuse of an 'error' accur.")
# Begin you targets and dependences
info:
@echo "CUR_PATH = $(CUR_PATH)"
@echo "LS_FILES = $(LS_FILES)"
@echo "LS_TEST_FILES = $(LS_TEST_FILES)"
我們執(zhí)行下make info可以看下,輸出信息:
分別對(duì)應(yīng)info,warning,error;這三個(gè)函數(shù)是Makefile中內(nèi)置的,在任意Makefile中都可以使用。但是需要注意的,這三個(gè)函數(shù)可以在Makefile的任意位置調(diào)用,但是必須符合語法規(guī)則,比如在CMD部分(見第一章節(jié)的引用內(nèi)容)調(diào)用時(shí),必須時(shí)TAB開頭。
我們?cè)囅逻@個(gè)的效果:
4 Makefile定義變量的時(shí)候調(diào)用shell命令
還是拿上面的Makefile來分析,我們一開始定義了幾個(gè)變量:
CUR_PATH = `pwd`
LS_FILES = "`ls`"
LS_TEST_FILES = "$(shell ls test*)"
其中CURPATH是保存當(dāng)前Makefile的路徑,我們都知道shell命令有個(gè)pwd可以獲取到當(dāng)前目錄的路徑,所以 CURPATH 變量正是利用了這個(gè)命令來取得當(dāng)前路徑。其中,值得注意的是 表示的是獲取pwd命令的返回值,這個(gè),不是分號(hào) ' ',而是鍵盤左上角數(shù)字1左邊的那個(gè)鍵值,新手一般非常容易搞錯(cuò)這一點(diǎn)。
第二個(gè)變量LS_FILES的定義,使用同樣的方式調(diào)用了 ls 命令,但是它在返回值的左右,加了 “ ”;這樣做的主要目的是,ls的返回值通常是一個(gè)字符串,中間每個(gè)文件名用空格隔開的;如果我們想把這一系列的字符串連起來而不是分散的,我們就必須使用 " "把它們包起來,這樣Makefile變量就會(huì)把它當(dāng)作一個(gè)字符串整體來處理。
第三個(gè)變量LSTESTFILES,它就換了一種方式來調(diào)用shell命令,它的格式是 $(shell xxx),其中xxx就是一個(gè)可以在shell環(huán)境下執(zhí)行的shell命令,比如本例子中,xxx就是 ls test*。
兩者方法沒有本質(zhì)的區(qū)別,結(jié)合自己的實(shí)際情況來使用即可,從易讀性來說,推薦使用$(shell xxx)的調(diào)用方式。
5 Makefile在執(zhí)行命令的時(shí)候調(diào)用shell命令
以下面的Makefile為例子:
# This is an annotation for this Makefile.
shell_cmd:
@ls test*
@pwd
@echo "shell_cmd test ..."
執(zhí)行make shell_cmd,我們可以看到輸出:
只要是shell執(zhí)行的命令,都可以運(yùn)行,這就方便我們做一些自動(dòng)化操作,比如創(chuàng)建目錄、刪除文件、下載文件、上傳文件等等。
6 Makefile以函數(shù)的形式調(diào)用shell命令
請(qǐng)看下面的Makefile內(nèi)容
# This is an annotation for this Makefile.
DEBUG_VARIABLE = "debug"
# This is a shell function.
define function_test_shell
param1=$1;\
local_variable=12345678;\
debug_variable=$(DEBUG_VARIABLE);\
echo "param1=$$param1";\
echo "local_variable=$$local_variable";\
echo "debug_variable=$$debug_variable";\
i=0;\
cnt=10;\
for i in `seq $$cnt`; do\
echo "i=$$i";\
done;\
ls -al;\
ls -al test*
endef
# Begin you targets and dependences
shell_function:
@$(call function_test_shell,$(DEBUG_VARIABLE))
我們執(zhí)行make shell_function可以看到輸出:
對(duì)應(yīng)就是shell函數(shù)里面的操作。
在Makefile調(diào)用shell函數(shù)的寫法上,有幾點(diǎn)特別需要注意:
1)Makefile定義函數(shù)是使用define 和 endef,務(wù)必配對(duì)使用;函數(shù)里面的實(shí)現(xiàn)可以調(diào)用shell命令,也可以調(diào)用Makefile內(nèi)置的函數(shù),也可以調(diào)用自定義的Makefile函數(shù);
2)Makefile函數(shù)的調(diào)用方式是$(call function,xx,yy,zz),其中function是函數(shù)名,也就是define后面的名字,xx/yy/zz分別是函數(shù)的三個(gè)入?yún)ⅲ划?dāng)然有幾個(gè)參數(shù)就填幾個(gè),每一個(gè)使用 , 隔開;
3) 在函數(shù)里面使用$1 $2 $3獲取輸入的參數(shù),即$+數(shù)字的方式獲取入?yún)ⅲ?/p>
4)如果函數(shù)里面使用的是shell組合命令,請(qǐng)務(wù)必在每一條shell命令后面加上 ; 符號(hào),并且每一行加上 \ 連起來;這樣對(duì)于shell解析器而言,它會(huì)認(rèn)為是一條長(zhǎng)長(zhǎng)的組合輸入命令,而不報(bào)語法錯(cuò)誤;
5)函數(shù)內(nèi)部使用shell的變量,比如是變量a,正確的寫法是$$a;而不是常規(guī)的shell寫法$a,也不是Makefile獲取變量的方法$(a)或${a};這一點(diǎn)特別需要注意。
6)如果函數(shù)內(nèi)部訪問的是Makefile定義的變量,比如示例中的訪問DEBUGVARIABLE變量,還是采用$(DEBUGVARIABLE)的方式。
好了,本期分享就到這里,有疑問的話,歡迎聯(lián)系我。
7 更多分享
歡迎關(guān)注我的github倉庫01workstation,日常分享一些開發(fā)筆記和項(xiàng)目實(shí)戰(zhàn),歡迎指正問題。
同時(shí)也非常歡迎關(guān)注我的CSDN主頁和專欄:
【CSDN主頁-架構(gòu)師李肯】
【RT-Thread主頁-架構(gòu)師李肯】
【GCC專欄】
【信息安全專欄】
【RT-Thread開發(fā)筆記】
【freeRTOS開發(fā)筆記】
【BLE藍(lán)牙開發(fā)筆記】
【ARM開發(fā)筆記】
【RISC-V開發(fā)筆記】
有問題的話,可以跟我討論,知無不答,謝謝大家。
審核編輯:湯梓紅
-
Linux
+關(guān)注
關(guān)注
87文章
11304瀏覽量
209503 -
Makefile
+關(guān)注
關(guān)注
1文章
125瀏覽量
19184 -
Shell
+關(guān)注
關(guān)注
1文章
365瀏覽量
23375
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論