一文學(xué)會(huì)利用Makfile給多文件、多目錄C源碼建立工程
0. 前言
粉絲留言,想知道如何使用Makefile給多個(gè)文件和多級(jí)目錄建立一個(gè)工程,必須安排!
關(guān)于Makefile的入門(mén)參考文章,可以先看這篇文章:
《Makefile入門(mén)教程》
為了讓大家有個(gè)更加直觀的感受,一口君將之前寫(xiě)的一個(gè)小項(xiàng)目,本篇在該項(xiàng)目基礎(chǔ)上進(jìn)行修改。
該項(xiàng)目詳細(xì)設(shè)計(jì)和代碼,見(jiàn)下文:
《從0寫(xiě)一個(gè)《電話號(hào)碼管理系統(tǒng)》的C入門(mén)項(xiàng)目【適合初學(xué)者】》
一、文件
好了,開(kāi)始吧!
我們將該項(xiàng)目的所有功能函數(shù)放到以該函數(shù)名命名的c文件,同時(shí)放到對(duì)應(yīng)名稱的子目錄中。
比如函數(shù)allfree(),存放到 allfree/allfree.c中
最終目錄結(jié)構(gòu)如下圖所示:
peng@ubuntu:/mnt/hgfs/code/phone$ tree .
.
├── allfree
│ ├── allfree.c
│ └── Makefile
├── create
│ ├── create.c
│ └── Makefile
├── delete
│ ├── delete.c
│ └── Makefile
├── display
│ ├── display.c
│ └── Makefile
├── include
│ ├── Makefile
│ └── phone.h
├── init
│ ├── init.c
│ └── Makefile
├── login
│ ├── login.c
│ └── Makefile
├── main
│ ├── main.c
│ └── Makefile
├── Makefile
├── menu
│ ├── Makefile
│ └── menu.c
├── scripts
│ └── Makefile
└── search
├── Makefile
└── search.c
11 directories, 22 files
直接看下編譯結(jié)果吧:
peng@ubuntu:/mnt/hgfs/code/phone$ make
make[1]: Entering directory '/mnt/hgfs/code/phone/allfree'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/allfree'
make[1]: Entering directory '/mnt/hgfs/code/phone/create'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/create'
make[1]: Entering directory '/mnt/hgfs/code/phone/delete'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/delete'
make[1]: Entering directory '/mnt/hgfs/code/phone/display'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/display'
make[1]: Entering directory '/mnt/hgfs/code/phone/init'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/init'
make[1]: Entering directory '/mnt/hgfs/code/phone/login'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/login'
make[1]: Entering directory '/mnt/hgfs/code/phone/menu'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/menu'
make[1]: Entering directory '/mnt/hgfs/code/phone/search'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/search'
make[1]: Entering directory '/mnt/hgfs/code/phone/main'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/mnt/hgfs/code/phone/main'
gcc -Wall -O3 -o phone allfree.o create.o delete.o display.o init.o login.o menu.o search.o main.o -lpthread
phone make done!
運(yùn)行結(jié)果如下:
二、Makefile常用基礎(chǔ)知識(shí)點(diǎn) [0] 符號(hào)'@' '$' '$$' '-' '-n '的說(shuō)明'@'
通常makefile會(huì)將其執(zhí)行的命令行在執(zhí)行前輸出到屏幕上。如果將‘@’添加到命令行前,這個(gè)命令將不被make回顯出來(lái)。例如:@echo --compiling module----; // 屏幕輸出 --compiling module----
echo --compiling module----; // 沒(méi)有@ 屏幕輸出echo --compiling module----
' - '
通常刪除,創(chuàng)建文件如果碰到文件不存在或者已經(jīng)創(chuàng)建,那么希望忽略掉這個(gè)錯(cuò)誤,繼續(xù)執(zhí)行,就可以在命令前面添加 -,
-rm dir;
-mkdir aaadir;
' $ '美元符號(hào)$,主要擴(kuò)展打開(kāi)makefile中定義的變量
' $$ '$$ 符號(hào)主要擴(kuò)展打開(kāi)makefile中定義的shell變量
[1] wildcard
說(shuō)明:列出當(dāng)前目錄下所有符合模式“ PATTERN”格式的文件名,并且以空格分開(kāi)! PATTERN”使用shell可識(shí)別的通配符,包括“ ?”(單字符)、“ *”(多字符)等。示例:
$(wildcard *.c)
返回值為當(dāng)前目錄下所有.c 源文件列表。
[2] patsubst
說(shuō)明:把字串“ x.c.c bar.c”中以.c 結(jié)尾的單詞替換成以.o 結(jié)尾的字符。示例:
$(patsubst %.c,%.o,x.c.c bar.c)
函數(shù)的返回結(jié)果是
x.c.o bar.o
[3] notdir
說(shuō)明:去除文件名中的路徑信息示例:
SRC = ( notdir ./src/a.c )
去除文件a . c 的路徑信息 , 使用 (notdir ./src/a.c) 去除文件a.c的路徑信息,使用 (notdir./src/a.c)去除文件a.c的路徑信息,使用(SRC)得到的是不帶路徑的文件名稱,即a.c。
[4] 包含頭文件路徑
使用-I+頭文件路徑的方式可以指定編譯器的頭文件的路徑示例:
INCLUDES = -I./inc
$(CC) -c $(INCLUDES) $(SRC)
[5] addsuffix
函數(shù)名稱:加后綴函數(shù)—addsuffix。語(yǔ)法:
$(addsuffix SUFFIX,NAMES…)
函數(shù)功能:為“NAMES…”中的每一個(gè)文件名添加后綴“SUFFIX”。參數(shù)“NAMES…”為空格分割的文件名序列,將“SUFFIX”追加到此序列的每一個(gè)文件名的末尾。返回值:以單空格分割的添加了后綴“SUFFIX”的文件名序列。函數(shù)說(shuō)明:示例:
$(addsuffix .c,foo bar)
返回值為
foo.c bar.c
[6] 包含另外一個(gè)文件:include
在Makefile使用include關(guān)鍵字可以把別的Makefile包含進(jìn)來(lái),這很像C語(yǔ)言的#include,被包含的文件會(huì)原模原樣的放在當(dāng)前文件的包含位置。比如命令
include file.dep
即把file.dep文件在當(dāng)前Makefile文件中展開(kāi),亦即把file.dep文件的內(nèi)容包含進(jìn)當(dāng)前Makefile文件
在 include前面可以有一些空字符,但是絕不能是[Tab]鍵開(kāi)始。
[7] foreach
foreach函數(shù)和別的函數(shù)非常的不一樣。因?yàn)檫@個(gè)函數(shù)是用來(lái)做循環(huán)用的語(yǔ)法是:
$(foreach
這個(gè)函數(shù)的意思是,把參數(shù)中的單詞逐一取出放到參數(shù)所指定的變量中,然后再執(zhí)行所包含的表達(dá)式。
每一次會(huì)返回一個(gè)字符串,循環(huán)過(guò)程中,的所返回的每個(gè)字符串會(huì)以空格分隔,最后當(dāng)整個(gè)循環(huán)結(jié)束時(shí),所返回的每個(gè)字符串所組成的整個(gè)字符串(以空格分隔)將會(huì)是foreach函數(shù)的返回值。
所以,最好是一個(gè)變量名,可以是一個(gè)表達(dá)式,而中一般會(huì)使用這個(gè)參數(shù)來(lái)依次枚舉中的單詞。
舉例:
names := a b c d
files := $(foreach n,$(names),$(n).o)
上面的例子中,$(name)中的單詞會(huì)被挨個(gè)取出,并存到變量“n”中,“$(n).o”每次根據(jù)“$(n)”計(jì)算出一個(gè)值,這些值以空格分隔,最后作為foreach函數(shù)的返回,所以,$(files)的值是“a.o b.o c.o d.o”。
注意,foreach中的參數(shù)是一個(gè)臨時(shí)的局部變量,foreach函數(shù)執(zhí)行完后,參數(shù)的變量將不在作用,其作用域只在foreach函數(shù)當(dāng)中。
[8] call
“ call”函數(shù)是唯一一個(gè)可以創(chuàng)建定制化參數(shù)函數(shù)的引用函數(shù)。使用這個(gè)函數(shù)可以實(shí)現(xiàn)對(duì)用戶自己定義函數(shù)引用。我們可以將一個(gè)變量定義為一個(gè)復(fù)雜的表達(dá)式,用“ call”函數(shù)根據(jù)不同的參數(shù)對(duì)它進(jìn)行展開(kāi)來(lái)獲得不同的結(jié)果。
函數(shù)語(yǔ)法:
$(call variable,param1,param2,...)
函數(shù)功能:在執(zhí)行時(shí),將它的參數(shù)“ param”依次賦值給臨時(shí)變量“ $(1)”、“ $(2)” call 函數(shù)對(duì)參數(shù)的數(shù)目沒(méi)有限制,也可以沒(méi)有參數(shù)值,沒(méi)有參數(shù)值的“ call”沒(méi)有任何實(shí)際存在的意義。執(zhí)行時(shí)變量“ variable”被展開(kāi)為在函數(shù)上下文有效的臨時(shí)變量,變量定義中的“ $(1)”作為第一個(gè)參數(shù),并將函數(shù)參數(shù)值中的第一個(gè)參數(shù)賦值給它;變量中的“ $(2)”一樣被賦值為函數(shù)的第二個(gè)參數(shù)值;依此類(lèi)推(變量**$(0)**代表變量“ variable”本身)。之后對(duì)變量“ variable” 表達(dá)式的計(jì)算值。
返回值:參數(shù)值“ param”依次替換“ $(1)”、“ $(2)”…… 之后變量“ variable”定義的表達(dá)式的計(jì)算值。
函數(shù)說(shuō)明:
函數(shù)中“ variable”是一個(gè)變量名,而不是變量引用。因此,通! call”函數(shù)中的“ variable”中不包含“ $”(當(dāng)然,除非此變量名是一個(gè)計(jì)算的變量名)。當(dāng)變量“ variable”是一個(gè) make 內(nèi)嵌的函數(shù)名時(shí)(如“ if”、“ foreach”、“ strip”等),對(duì)“ param”參數(shù)的使用需要注意,因?yàn)椴缓线m或者不正確的參數(shù)將會(huì)導(dǎo)致函數(shù)的返回值難以預(yù)料。函數(shù)中多個(gè)“ param”之間使用逗號(hào)分割。變量“ variable”在定義時(shí)不能定義為直接展開(kāi)式!只能定義為遞歸展開(kāi)式。
函數(shù)示例:
reverse = $(2)$(1)
foo = $(call reverse,a,b)
all:
@echo "foo=$(foo)"
執(zhí)行結(jié)果:
foo=ba
即a替代了替代了(2)
三、編譯詳細(xì)說(shuō)明
我們?cè)诟夸浵聢?zhí)行make命令后,詳細(xì)步驟如下:
include scripts/Makefile :將文件替換到當(dāng)前位置,使用默認(rèn)的目標(biāo)all,該目標(biāo)依賴于$(Target)$(Target) 在scripts/Makefile中定義了,即phone而$(Target)依賴于mmmm這個(gè)目標(biāo)會(huì)執(zhí)行@ $(foreach n,$(Modules),$(call modules_make,$(n)))
Modules是所有的目錄名字集合,foreach 會(huì)遍歷字符串$(Modules)中每個(gè)詞語(yǔ),每個(gè)詞語(yǔ)會(huì)賦值給n,同時(shí)執(zhí)行語(yǔ)句:
call modules_make,$(n)
modules_make 被$(MAKE) -C $(1)所替代,
$(MAKE) 有默認(rèn)的名字make-C:進(jìn)入子目錄執(zhí)行make$(1) :是步驟4中$(n),即每一個(gè)目錄名字
最終步驟4的語(yǔ)句就是進(jìn)入到每一個(gè)目錄下,執(zhí)行每一個(gè)目錄下的Makefile
進(jìn)入某一個(gè)子目錄下,執(zhí)行Makefile默認(rèn)目標(biāo)是all,依賴ObjsObjs := $(patsubst %.c,%.o,$(Source))
patsubst 把字串$ource中以.c 結(jié)尾的單詞替換成以.o 結(jié)尾的字符而
Source := $(wildcard ..c)
wildcard 會(huì)列舉出當(dāng)前目錄下所有的.c文件
所以第6步最終就是將子目錄下的所有的.c文件,編譯生成對(duì)應(yīng)文件名的.o文件
$(CC) $(CFLAGS) -o $(Target) $(AllObjs) $(Libs)
這幾個(gè)變量都在文件scripts/Makefile中定義$(CC) :替換成gcc,制定編譯器$(CFLAGS) :替換成-Wall -O3,即編譯時(shí)的優(yōu)化等級(jí)-o $(Target):生成可執(zhí)行程序phone$(AllObjs) :
AllObjs := $(addsuffix .o,$(Modules))
addsuffix 會(huì)將 .o追加到$(Modules)中所有的詞語(yǔ)后面,也就是我們之前在子目錄下編譯生成的所有的.o文件$(Libs) :替換為-lpthread,即所需要的動(dòng)態(tài)庫(kù)
大家可以根據(jù)這個(gè)步驟,來(lái)分析一下執(zhí)行make clean時(shí),執(zhí)行步驟
發(fā)表評(píng)論
請(qǐng)輸入評(píng)論內(nèi)容...
請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字
最新活動(dòng)更多
-
10月31日立即下載>> 【限時(shí)免費(fèi)下載】TE暖通空調(diào)系統(tǒng)高效可靠的組件解決方案
-
即日-11.13立即報(bào)名>>> 【在線會(huì)議】多物理場(chǎng)仿真助跑新能源汽車(chē)
-
11月28日立即報(bào)名>>> 2024工程師系列—工業(yè)電子技術(shù)在線會(huì)議
-
12月19日立即報(bào)名>> 【線下會(huì)議】OFweek 2024(第九屆)物聯(lián)網(wǎng)產(chǎn)業(yè)大會(huì)
-
即日-12.26火熱報(bào)名中>> OFweek2024中國(guó)智造CIO在線峰會(huì)
-
即日-2025.8.1立即下載>> 《2024智能制造產(chǎn)業(yè)高端化、智能化、綠色化發(fā)展藍(lán)皮書(shū)》
推薦專(zhuān)題
- 1 【一周車(chē)話】沒(méi)有方向盤(pán)和踏板的車(chē),你敢坐嗎?
- 2 特斯拉發(fā)布無(wú)人駕駛車(chē),還未迎來(lái)“Chatgpt時(shí)刻”
- 3 特斯拉股價(jià)大跌15%:Robotaxi離落地還差一個(gè)蘿卜快跑
- 4 馬斯克給的“驚喜”夠嗎?
- 5 大模型“新星”開(kāi)啟變現(xiàn)競(jìng)速
- 6 海信給AI電視打樣,12大AI智能體全面升級(jí)大屏體驗(yàn)
- 7 打完“價(jià)格戰(zhàn)”,大模型還要比什么?
- 8 馬斯克致敬“國(guó)產(chǎn)蘿卜”?
- 9 神經(jīng)網(wǎng)絡(luò),誰(shuí)是盈利最強(qiáng)企業(yè)?
- 10 比蘋(píng)果偉大100倍!真正改寫(xiě)人類(lèi)歷史的智能產(chǎn)品降臨
- 高級(jí)軟件工程師 廣東省/深圳市
- 自動(dòng)化高級(jí)工程師 廣東省/深圳市
- 光器件研發(fā)工程師 福建省/福州市
- 銷(xiāo)售總監(jiān)(光器件) 北京市/海淀區(qū)
- 激光器高級(jí)銷(xiāo)售經(jīng)理 上海市/虹口區(qū)
- 光器件物理工程師 北京市/海淀區(qū)
- 激光研發(fā)工程師 北京市/昌平區(qū)
- 技術(shù)專(zhuān)家 廣東省/江門(mén)市
- 封裝工程師 北京市/海淀區(qū)
- 結(jié)構(gòu)工程師 廣東省/深圳市