快速的理解MakeFile+讀懂一個MakeFile
是否工程開發中遇到了如下情況:
1、平時幾乎不寫makefile,在開源的專案或者先輩們的工程中看到了一個makefile檔案,然後懵圈了;
2、上次寫makefile已經是好幾年前了,很多語法都忘了。。。;
3、嘗試寫個用例,執行make馬上報錯,還看不懂。
本文的目的主要是側重”讀“而不是“寫”makefile,總結點入門指南,給有需要的同學:
~~~下面分級了,挑自己合適的級別~~~
## 零級:從未用過,初次接觸;
## 初級:略懂原理,開始下手;
## 中級:以前寫過,似曾相識;
——————————————————————————————————————————-
1 零級
1。1 Makefile的出現
MakeFile是一個GNU推出的編譯開發工具,能為一些編譯過程提供服務。舉個例子,你寫了很多。c、。cpp、。h檔案,怎麼把他們編譯成最後能夠執行的。exe檔案,
你可以手動的一步一步的編譯,也可以用MakeFile來輔助你編譯,用了MakeFile除了能提升效率,還能避免人為操作導致錯誤。
為什麼是一步一步? 用慣了IDE(整合開發環境 如MVS、Qt)的同學,先回顧一下linux shell上編譯套路:
一個hello。c 程式碼如下:
#include
int
main
()
{
printf
(
“Hello!”
);
return
0
;
}
生成hello(hello。exe)所需要執行的bash命令:gcc -v -o hello hello。c 。
該過程可拆解為4個步驟:預處理、編譯、彙編、連結。大部分情況下,不需要拆分成這四步來完成檔案的生成,一般都是直接生成二進位制。o檔案,然後連結成可執行檔案(或者動態庫。so 。dll 或者靜態庫。a 。lib)。
這些過程中用得比較多的就是:原始碼(。c 。h)、機器碼(。o)、靜態庫(。a)以及執行檔案。是不是所有編譯工作都能用編譯器(g++/gcc/cc)完成? 是的,可以的,對於簡單的工程來說你完全可以寫個。sh檔案完成。那為什麼用MakeFile?
更
省事、準確
。對於大的工程(檔案多,一個。c對應了一個。o檔案,最後還涉及。o檔案和。a檔案連結)編譯彙編工作如果手動操作就會變得繁瑣且重複,那麼makefile出現能極大的簡化這個重複繁瑣的過程,而且對於不同的作業系統,也需要讓編譯自動適應。
1。2 MakeFile怎麼運作
一句話:
MakeFile裡面大致是先處理一些環境變數或者引數,然後從某一個位置開始執行命令集。
對於一個初學者,大概瀏覽一個makefile:
1、區分哪些是進行的
前處理/變數處理
(根據規則定義或處理引數) 。
2、找到
target:
包含了冒號(colon :)找到他們,target都是頂格抒寫的, “
3、執行target: 一般是實現
第一個target
,(也可以用make 指定,如make clean,就只執行“clean” 這個target)。
來個示例:
其中引數就定義了兩個,target有三個,MakeFile 可以有多個“target”,target之間可以獨立,也可以相互關聯/依賴,但只能從某一個開始執行,預設情況下從第一個target執行。例子中的target0由兩個子target組成。
可以類比一下C語言, “變數的處理”相當於宏引數的定義,“找target並執行” 類似於找一個main函式開始執行命令;
上面舉例了一個makefile的基本的內容, 省略了很多細節,比如什麼是“。PHONY:”,這些都是在這個基礎上慢慢加的。
——————————————————————————————————————————-
2 初級
2。1 第一個用例(執行一個target)
配合上面的hello。c,然後建立一個makefile 的檔案如下:
$(warning Demo1)
VAR = “I‘m variable”
hello:hello。c
$(CXX) $^ -o $@
foo:
@echo $(VAR)
clean:
rm -f hello
在該目下(包含hello。c makefile)執行:make 之後就看到了
hello
可執行的檔案。
說明1:上面的程式碼target有三個(找冒號),target就執行第一個hello: ,而 foo、clean不會執行,要想執行需要指定 比如“make foo” 或者“make clean”
說明2: $(warning <。。。>) 這句不管執行哪個target 都會執行。
說明3:新手難懂的hello 這個target裡面的“$(CXX) $^ -o $@ ” 暫時先不管 ,翻譯出來是“g++ hello。c -o hello”,其中$(CXX) 預設是g++,此處也可以用 $(CC) 預設是cc命令。
2。2 第二個用例(執行多個target)
上列中Makefile的內容稍作修改:
$(warning Demo 2)
VAR = “I’m variable”
all: hello foo
hello:hello。c
$(CC) $^ -o $@
foo:
@echo $(VAR)
clean:
rm -f hello
說明:執行make後,預設執行的target是all,而all包含了hello foo ,按順序依次執行。
2。3 第三個用例(省事的抒寫方式,一次編譯多個。o檔案)
還是上面的用例基礎,把hello。c檔案複製一份,重新命名為hello_copy。c。檔案裡面包含(hello。c hello_copy。c makefile), makefile的內容如下:
$(warning Demo 2)
obj = hello。o hello_copy。o
all:$(obj)
$(obj):%。o: %。c
$(CC) $< -o $@
。PHONY: clean
clean:
rm -f $(obj)
說明1: 第一個target是all,而all由兩個。o檔案構成,對應的兩個。c檔案可以同時編譯;
說明2:target可以是變數,例子中的obj是一個變數。
說明3:這裡加上了一個。PHONY,究竟是啥呢?它不是一個真正的target。作用就是防止make裡面檔案與target重名,。。。。$%^&*&……%¥”自己搜尋一下。
初級就來三個用例吧,多了講不清,因為你缺少makefile的語法知識,“$^”是啥?if條件怎麼寫?loop怎麼寫?看中級吧
——————————————————————————————————————————
3 中級
中級的水平應該是基本能看懂的水平,需要對Makefile的語法有個基本的瞭解。根據自己的狀態讀一下makefile的編寫語法吧,ABC:
A
時間充足,英語比較好,看原版:
B
英語吃力的同學,推薦看看這個翻譯:
C
時間不足的同學,就是實際的遇到問題,再查吧。
判斷你有沒有中級畢業,過一下測試,看能答對多少?
part1-格式與啟動:
1。1 “獨立行的可執行命令必須以什麼開頭?”
1。2 “如何讓某些命令執行但不顯示命令的內容?”
1。3 “make命令指定一個Makefile檔案的方法,如make。txt?預設的搜尋名稱順序是什麼?”
1。4 “target執行的順序”
1。5 “make只顯示命令不執行命令?”
1。6 “執行相關的兩條命令,比如cd 。。/。。/ 然後ls -h,命令是否能抒寫在兩行?”
1。7 “命令之前加‘-’的作用?”
1。8 “在makefile中巢狀另一個makefile,執行子目錄的make方法?”
1。9 “如何把父makefile裡的變數傳遞給子目錄的make?”
part-1 答案:
1.1
“Tab鍵,非空格。”,
1.2
“以@開頭即可”,
1.3
‘’‘make –f make。txt 一般用預設查詢順序“GNUmakefile”、“makefile”和“Makefile”’‘’,
1.4
“一般執行第一個目標,除非指定 比如 make clean”,
1.5
“make -n 或者 make ——just-print”,
1.6
“不能獨立的成為兩行,要用‘;’分割。”,
1.7
“不管命令是否錯誤都認為是成功的。”,
1.8
“cd subdir && $(MAKE) 或者 $(MAKE) -C subdir”,
1. 9
“export value ,若要傳遞全部變數,只寫export,不跟引數。”,
part2-字元與變數
2。1 “對一個變數取值的抒寫格式?”
2。2 “輸出字元$ 和# 方式?”
2。3 “%萬用字元表示什麼?”
2。4 “s。%。c匹配的是什麼?”
2。5 “。c。o: 含義?”
2。6 “賦值方式中 =、 :=、+=、?=的區別?”
2。7 “$(foo:。o=。c)的含義?”
2。8 “$($(var)_objects:。o=。c)含義?”
2。9 “%。o : CFLAGS = -g”
2。10 “$@ $< $^ $+ $? 的含義?”
part2-答案:
2.1
“${} 或者$()”,
2.2
“\$$ \#”,
2.3
“表示一個或多個任意字元”,
2.4
“s。 開頭, 。c 結尾的檔名, (s。。c也算)”,
2.5
“相當於 %o : %c”,
2.6
“= 賦值, := 覆蓋之前的值,?= 若沒有定義過就賦值,+= 追加值”,
2.7
“把foo變數裡面以。o結尾的字串替換為。c字串”,
2.8
“var_objects 裡面的。o 替換為。c”,
2.9
“設定taget(%。o)的CFLAGS變數為-g,全域性的設定將被忽略”,
2.10
“$@編譯的目標檔案/檔案集合 $< 取第一個依賴(多個按照順序)$^ 取全部依賴(去除重複) $+取全部(不去除重複) $?比目標新的依賴集合 ”,
part3-常見函式:
3。1 “ifeq語句的抒寫格式?”
3。2 “define command enddef 的作用?”
3。3 “判斷兩個量不相等的關鍵字,或者判斷是否定義了某個變數?”
3。4 “錯誤提示函式”
3。5 “$(origin
3。6 “if函式如何寫? ”
3。7 “foreach函式的抒寫”
3。8 “$(join aaa bbb , 111 222 333) 返回值?”
3。9 “$(addprefix src/,foo bar) 返回值是? ”
3。10 “$(addsuffix 。c,foo bar) 返回值是 ?”
3。11 “$(suffix src/foo。c src-1。0/bar。c hacks) 返回值是 ?”
3。12 “$(subst ee,EE,feet on the street)返回結果?”
3。13 “$(patsubst %。c,%。o,x。c。c bar。c)返回結果?”
3。14 “$(sort )作用?”
3。15“$(filter %。c %。s,$(sources))作用”?
3。16 “$(findstring
3。17 “$(strip
3。18 “$(wildcard *。c) 作用?”
3。19 “例舉常見的字串替換函式?”
3。20 “$(CXX) –c $(CPPFLAGS) $(CFLAGS) 的隱含規則?”
part3-答案
3.1
“ifeq (
3.2
“定義命令包,可以重複使用”,
3.3
“ifneq (
3.4
“$(error
3.5
“判斷變數的來源,比如是undefined 、default、file、command line等”,
3.6
“$(if
3.7
“$(foreach ,,
3.8
“aaa111 bbb222 333”,
3.9
“src/foo src/bar”,
3.10
“foo。c bar。c”,
3.11
“。c 。c”,
3.12
“fEEt on the strEEt”,
3.13
“x。c。o bar。o”,
3.14
“對list排序。”,
3.15
“返回sources裡面。c和。s結尾的詞”,
3.16
“在
3.17
“去掉前後空格”,
3.18
“返回所有。c的檔案”,
3.19
“$(subst
3.20
“
part4-常見的預設引數:
4。1 “AS引數的含義?”
4。2 “CXX引數的含義?”
4。3 “CC引數的含義?”
4。4 “RM引數的含義?”
4。5 “預設的C語言編譯器引數?”
4。6 “AR引數?”
4。7 “$(AR) r $@ $*。o 含義?”
part4-答案
4.1
“組合語言編譯程式。預設命令是 as。as -o hello。o hello。s”,
4.2
“ C++語言編譯程式。預設命令是 g++”,
4.3
“C語言編譯程式。預設命令是 cc”,
4.4
“預設命令是 rm –f”,
4.5
“CFLAGS,預設值是空”,
4.6
“函式庫檔案的打包命令(Archive),函式庫檔案是對Object檔案(程式編譯的中間檔案)的打包檔案,將目標檔案打包為靜態連結庫*。a”,
4.7
“。o檔案打包成目標”,
————————————————————————————————————————
makefile的讀,沒有寫“
高階
”。高階的能“讀”懂makefile,需要真正的“寫”,孰能生巧,寫10個以上的中小型專案吧,GitHub上面資源豐富。
最後補充點:
makefile的新手坑
常見錯誤1 :Makefile:2: *** recipe commences before first target。 Stop。
原因:在寫目標前,寫了shell命令。
復現:
echo
“hello!
改正:
target:
echo ”hello!“
常見錯誤2:Makefile:2: *** missing separator。 Stop。
原因:命令前面不是TAB輸入。是空格。
復現:
target:
echo ”hello!“
改正:
target:
echo ”hello!“
常見錯誤3:/bin/sh: -c: line 0: *****
原因:bash命令寫得有問題。
比如:/bin/sh: -c: line 0: syntax error near unexpected token `fi‘
if [ ${var} -eq 2 ]; then\ # then後面需要一個空格。
echo ”hello“;\
fi
if的空格問題:
/bin/sh: line 0: [: 5-eq: unary operator expected
/bin/sh: [5: command not found
原因:if判斷沒有寫對。中括號裡面的空格丟失
if [ ${var3} -eq 5 ]; then echo ”hello“; fi