您當前的位置:首頁 > 體育

快速的理解MakeFile+讀懂一個MakeFile

作者:由 kaiyuan 發表于 體育時間:2021-02-10

是否工程開發中遇到了如下情況:

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)。

快速的理解MakeFile+讀懂一個MakeFile

這些過程中用得比較多的就是:原始碼(。c 。h)、機器碼(。o)、靜態庫(。a)以及執行檔案。是不是所有編譯工作都能用編譯器(g++/gcc/cc)完成? 是的,可以的,對於簡單的工程來說你完全可以寫個。sh檔案完成。那為什麼用MakeFile?

省事、準確

。對於大的工程(檔案多,一個。c對應了一個。o檔案,最後還涉及。o檔案和。a檔案連結)編譯彙編工作如果手動操作就會變得繁瑣且重複,那麼makefile出現能極大的簡化這個重複繁瑣的過程,而且對於不同的作業系統,也需要讓編譯自動適應。

1。2 MakeFile怎麼運作

一句話:

MakeFile裡面大致是先處理一些環境變數或者引數,然後從某一個位置開始執行命令集。

對於一個初學者,大概瀏覽一個makefile:

1、區分哪些是進行的

前處理/變數處理

(根據規則定義或處理引數) 。

2、找到

target:

包含了冒號(colon :)找到他們,target都是頂格抒寫的, “ : <***> ” , target下面的帶[tab]縮排的行,就是它包含的命令,找到所有的target。

3、執行target: 一般是實現

第一個target

,(也可以用make 指定,如make clean,就只執行“clean” 這個target)。

來個示例:

快速的理解MakeFile+讀懂一個MakeFile

其中引數就定義了兩個,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 () else endif”,

3.2

“定義命令包,可以重複使用”,

3.3

“ifneq () endif, ifdef endif”,

3.4

“$(error )”,

3.5

“判斷變數的來源,比如是undefined 、default、file、command line等”,

3.6

“$(if ) 如果條件非空就執行then-part”,

3.7

“$(foreach ) var在list中取值然後執行text”,

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 ) $(patsubst ) ”,

3.20

。o 的目標的依賴目標會自動推導為 。cc 或是 。C”,

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

標簽: hello  target  makefile  make  檔案