make工具和Makefile基础语法(含有交叉编译、pthread_create()的处理)
目錄
- 含有交叉編譯、pthread_create()處理
- 初次使用
- 1、編寫Makefile文件
- 2、make
- 3、make clean
- Makefile基本語法
- 1、Makefile規(guī)則格式
- 2、變量
- 3、變量賦值符(=、:=、?=、+=)
- 4、模式規(guī)則(通配符)
- 5、自動(dòng)化變量
- 6、偽目標(biāo)
- 7、條件判斷
- 8、函數(shù)使用
- 1、函數(shù) subst
- 2、函數(shù) patsubst
- 3、函數(shù) dir
- 4、函數(shù) notdir
- 5、函數(shù) foreach
- 6、函數(shù) wildcard
含有交叉編譯、pthread_create()處理
摘自:使用makefile編譯含有pthread_create()函數(shù)時(shí)報(bào)錯(cuò):對‘pthread_create’未定義的引用
交叉編譯的話直接將Makefile里面的gcc替換成arm-linux-gnueabihf-gcc即可。
在linux應(yīng)用程序中使用了多線程編程,但是makefile編譯卻報(bào)如下錯(cuò)誤:
/tmp/cc5i6uH7.o:在函數(shù)‘main’中: tcpSever.c:(.text+0x62):對‘modRegInit’未定義的引用 tcpSever.c:(.text+0x76):對‘ryS’未定義的引用 tcpSever.c:(.text+0xc0):對‘ryS’未定義的引用 tcpSever.c:(.text+0x10a):對‘ryS’未定義的引用 tcpSever.c:(.text+0x14e):對‘ryS’未定義的引用 collect2: error: ld returned 1 exit status網(wǎng)上查找解決方案,發(fā)現(xiàn)修改makefile即可。如下
test: rySys.o tcpSever.ogcc -o test rySys.o tcpSever.o -lpthread rySys.o: rySys.c rySys.hgcc -c rySys.c -lpthread tcpSever.o: tcpSever.c rySys.c rySys.hgcc -c tcpSever.c -lpthread clean:rm *.orm test初次使用
當(dāng)文件有幾十、上百甚至上萬個(gè)的時(shí)候用終端輸入 gcc 命令的方法顯然是不現(xiàn)實(shí)的,為此提出了一個(gè)解決大工程編譯的工具:make,描述哪些文件需要編譯、哪些需要 重新編譯 的文件就叫做 Makefile。使用的時(shí)候只需要一個(gè) make 命令即可完成整個(gè)工程的自動(dòng)編譯,極大的提高了軟件開發(fā)的效率。
使用make工具可以自動(dòng)完成編譯工作,這些工作包括:
- 如果修改了某幾個(gè)源文件,則只重新編譯這幾個(gè)源文件;
- 如果某個(gè)頭文件被修改了,則重新編譯所有包含該頭文件的源文件。
1、編寫Makefile文件
在工程目錄下創(chuàng)建Makefile文件:
Makefile 里面是由一系列的規(guī)則組成的,這些規(guī)則格式如下:
目標(biāo)…... : 依賴文件集合……命令1命令2……Makefile 和 .c 文件是處于同一個(gè)目錄的,在Makefile文件中輸入如下代碼:
main: main.o fun1.o fun2.ogcc -o main main.o fun1.o fun2.o main.o: main.cgcc -c main.c fun1.o: fun1.cgcc -c fun1.c fun2.o: fun2.cgcc -c fun2.cclean:rm *.orm main上述代碼中所有行首需要空出來的地方一定要使用 “TAB” 鍵!不要使用空格鍵!這是 Makefile 的語法要求
2、make
Makefile 編寫好以后我們就可以使用make命令來編譯我們的工程了,直接在命令行中輸入make即可,make 命令會(huì)在當(dāng)前目錄下查找是否存在 “Makefile” 這個(gè)文件,如果存在的話就會(huì)按照 Makefile 里面定義的編譯方式進(jìn)行編譯:
make
此時(shí)我們修改一下 fun1.c 的文件源碼,再make:
可以看出因?yàn)槲覀冃薷牧薴un1.c這個(gè)文件,所以fun1.c和最后的可執(zhí)行文件main重新編譯了,其它沒有修改過的文件就沒有編譯,而且我們只需要輸入make命令即可,非常方便。
3、make clean
make cleanMakefile基本語法
1、Makefile規(guī)則格式
目標(biāo)...: 依賴文件集合...命令1命令2......!!命令列表中的每條命令必須以 TAB 鍵開始,不能使用空格!
- 比如:
這條規(guī)則的目標(biāo)是main,main.o、fun1.o、fun2.o是生成main的依賴文件,如果要更新目標(biāo)main,就必須先更新它的所有依賴文件,如果依賴文件中的任何一個(gè)有更新(更改時(shí)間),那么目標(biāo)也必須更新,“更新”就是執(zhí)行一遍規(guī)則中的命令列表。make命令會(huì)為Makefile中的每個(gè)以 TAB 開始的命令創(chuàng)建一個(gè) Shell 進(jìn)程去執(zhí)行。
- 示例:
make命令在執(zhí)行這個(gè)Makefile的時(shí)候其執(zhí)行步驟如下:
首先更新第一條規(guī)則中的main,第一條規(guī)則的目標(biāo)成為默認(rèn)目標(biāo),只要默認(rèn)目標(biāo)更新了那么就認(rèn)為Makefile的工作完成。在第一次編譯的時(shí)候由于main還不存在,因此第一條規(guī)則會(huì)執(zhí)行,第一條規(guī)則依賴于文件main.o、fun1.o和fun2.o這個(gè)三個(gè).o文件,這三個(gè).o文件目前還都沒有,因此必須先更新這三個(gè)文件。make 會(huì)查找以這三個(gè).o文件為目標(biāo)的規(guī)則并執(zhí)行。
以main.o為例,發(fā)現(xiàn)更新main.o的是第二條規(guī)則,因此會(huì)執(zhí)行第二條規(guī)則,第二條規(guī)則里面的命令為“gcc –c main.c”,這行命令就是不鏈接編譯main.c,生成main.o。
最后一個(gè)規(guī)則目標(biāo)是clean,它沒有依賴文件,因此會(huì)默認(rèn)為依賴文件都是最新的,所以其對應(yīng)的命令不會(huì)執(zhí)行,當(dāng)我們想要執(zhí)行clean的話可以直接使用命令make clean,執(zhí)行以后就會(huì)刪除當(dāng)前目錄下所有的.o文件以及main,因此clean的功能就是完成工程的清理工作。
- 總結(jié)make執(zhí)行過程:
① make命令會(huì)在當(dāng)前目錄下查找以 Makefile(或makefile) 命名的文件。
② 當(dāng)找到 Makefile 文件以后就會(huì)按照 Makefile 中定義的規(guī)則去編譯生成最終的目標(biāo)文件。
③ 當(dāng)發(fā)現(xiàn)目標(biāo)文件不存在,或者目標(biāo)所依賴的文件比目標(biāo)文件新(也就是最后修改時(shí)間比目標(biāo)文件晚)的話就會(huì)執(zhí)行后面的命令來更新目標(biāo)。
!!注意: 除了 Makefile 的“終極目標(biāo)”所在的規(guī)則以外,其它規(guī)則的順序在 Makefile中是沒有意義的,“終極目標(biāo)”就是指在使用 make 命令的時(shí)候沒有指定具體的目標(biāo)時(shí),make 默認(rèn)的那個(gè)目標(biāo),它是 Makefile 文件中第一條規(guī)則的目標(biāo),如果 Makefile 中的第一個(gè)規(guī)則有多個(gè)目標(biāo),那么這些目標(biāo)中的第一個(gè)目標(biāo)就是 make 的“終極目標(biāo)”。
2、變量
跟 C 語言一樣 Makefile 也是支持變量的,如前面的例子:
main: main.o fun1.o fun2.ogcc -o main main.o fun1.o fun2.o上述 Makefile 語句中,main.o、fun1.o、fun2.o 這三個(gè)依賴文件,我們輸入了兩遍,我們這個(gè) Makefile 比較小,如果 Makefile 復(fù)雜的時(shí)候這種重復(fù)輸入的工作就會(huì)非常費(fèi)時(shí)間,Makefile 加入了變量支持,類似 C 語言中的宏。使用變量將上面的代碼修改:
# Makefile 變量的使用 objects = main.o fun1.o fun2.o main: $(objects)gcc -o main $(objects)Makefile 的注釋用#。
我們定義了一個(gè)變量objects,并且給這個(gè)變量進(jìn)行了賦值,其值為字符串main.o fun1.o fun2.o,Makefile 中變量的引用方法是 $(變量名),如本例中: $(objects)。
我們在定義變量objects 的時(shí)候使用“=”對其進(jìn)行了賦值,Makefile
變量的賦值符還有其它兩個(gè)“:=”和“?=”,我們來看一下這三種賦值符的區(qū)別:
3、變量賦值符(=、:=、?=、+=)
使用“=”在給變量的賦值的時(shí)候,不一定要用已經(jīng)定義好的值,也可以使用后面定義的值,比如如下代碼:
① 賦值符=:
name = lcx curname = $(name) name = licxprint:@echo curname: $(curname)第一行定義了一個(gè)變量name,值為lcx,第二行定義了變量curname,值也為lcx,第三行變量name的值改為licx,第五、六行是輸出變量curname的值。在 Makefile 要輸出一串字符的話使用echo。(第六行中的echo前面加了個(gè) @符號,因?yàn)?Make 在執(zhí)行的過程中會(huì)自動(dòng)輸出命令執(zhí)行過程,在命令前加上@就不會(huì)輸出命令執(zhí)行過程)執(zhí)行結(jié)果:
可以看出,curname的值不是lcx,而是變量name最后一次賦值的結(jié)果。
② 賦值符:=:
name = lcx curname := $(name) name = licxprint:@echo curname: $(curname)執(zhí)行結(jié)果:
顯然,此時(shí)curname值為lcx,因?yàn)橘x值符:=只能使用前面定義的值。
③ 賦值符?=:
curname ?= licx上述代碼的意思是,如果變量curname前面沒有被賦值,那么此變量就是licx,如果前面已經(jīng)賦過值了,那么就使用前面賦的值。
④ 變量追加符+=:
Makefile 中的變量是字符串,有時(shí)候我們需要給前面已經(jīng)定義好的變量添加一些字符串,此時(shí)就要使用變量追加符號+=,如:
objects = main.o fun1.o objects += fun2.oprint:@echo objects: $(objects)
可以看出,objects最后的值為main.o fun1.o fun2.o。
4、模式規(guī)則(通配符)
如下示例代碼:
objects = main.o fun1.o fun2.o main: $(objects)gcc -o main $(objects)main.o: main.cgcc -c main.c fun1.o: fun1.cgcc -c fun1.c fun2.o: fun2.cgcc -c fun2.cclean:rm *.orm main顯然,如果工程中.c文件很多,這樣做就很不方便,我們可以使用 Makefile 中的模式規(guī)則,使用一條規(guī)則來將所有的.c文件編譯為對應(yīng)的.o文件。
模式規(guī)則中,至少在規(guī)則的目標(biāo)定義中要包含%,否則就是一般規(guī)則,目標(biāo)中的%表示對文件名的匹配,%表示長度任意的非空字符串,比如%.c就是所有的以.c結(jié)尾的文件,類似于通配符。
示例可以改成如下形式:
objects = main.o fun1.o fun2.o main: $(objects)gcc -o main $(objects)%.o: %.c#命令clean:rm *.orm main第六行的命令我們需要借助下面的自動(dòng)化變量 ↓↓↓
5、自動(dòng)化變量
上面講的模式規(guī)則中,目標(biāo)和依賴都是一系列的文件,每一次對模式規(guī)則進(jìn)行解析的時(shí)候都會(huì)是不同的目標(biāo)和依賴文件,而命令只有一行,那么如何通過一行命令來從不同的依賴文件中生成對應(yīng)的目標(biāo)?自動(dòng)化變量就是完成這個(gè)功能的!
自動(dòng)化變量就是這種變量會(huì)把模中所定義的一系列的文件自動(dòng)的挨個(gè)取出,直至所有的符合模式的文件都取完,自動(dòng)化變量只應(yīng)該出現(xiàn)在規(guī)則的命令中,常用的自動(dòng)化變量如下:
常用的三種: $@、 $< 和 $^
那么上述代碼我們可以完善:
objects = main.o fun1.o fun2.o main: $(objects)gcc -o main $(objects)%.o: %.cgcc -c $<clean:rm *.orm main6、偽目標(biāo)
偽目標(biāo)的主要是為了避免 Makefile 中定義的執(zhí)行命令的目標(biāo)和工作目錄下的實(shí)際文件出現(xiàn)名字沖突,有時(shí)候我們需要編寫一個(gè)規(guī)則用來執(zhí)行一些命令,但是這個(gè)規(guī)則不是用來創(chuàng)建文件的,比如文章示例有如下代碼用來完成清理工程的功能:
clean:rm *.orm main上述規(guī)則中并沒有創(chuàng)建clean文件的命令,因此工作目錄下永遠(yuǎn)都不會(huì)存在文件clean,當(dāng)我們輸入make clean以后,后面的rm *.o和rm main總是會(huì)執(zhí)行。如果我們在工作目錄下創(chuàng)建一個(gè)名為clean(重名)的文件,當(dāng)執(zhí)行make clean的時(shí)候,規(guī)則因?yàn)闆]有依賴文件,所以目標(biāo)被認(rèn)為是最新的 (剛創(chuàng)建了一個(gè)clean文件,是最新的),因此后面的rm命令也就不會(huì)執(zhí)行,如圖所示:
為了避免這個(gè)問題,我們可以將clean聲明為偽目標(biāo),聲明方式如下:
.PHONY:clean那么我們可以將示例代碼修改為:
.PHONY : cleanclean:rm *.orm main7、條件判斷
在 C 語言中我們通過條件判斷語句來根據(jù)不同的情況來執(zhí)行不同的分支,Makefile 也支持條件判斷,語法有兩種如下:
<條件關(guān)鍵字> <條件為真時(shí)執(zhí)行的語句> endif以及:
<條件關(guān)鍵字> <條件為真時(shí)執(zhí)行的語句> else <條件為假時(shí)執(zhí)行的語句> endif其中條件關(guān)鍵字有 4 個(gè):ifeq、ifneq、ifdef 和 ifndef,這四個(gè)關(guān)鍵字其實(shí)分為兩對、ifeq 與ifneq、ifdef 與 ifndef,先來看一下 ifeq 和 ifneq,ifeq 用來判斷是否相等,ifneq 就是判斷是否不相等,ifeq 用法如下:
ifeq (<參數(shù) 1>, <參數(shù) 2>) ifeq ‘<參數(shù) 1 >’,‘ <參數(shù) 2>’ ifeq “<參數(shù) 1>”, “<參數(shù) 2>” ifeq “<參數(shù) 1>”, ‘<參數(shù) 2>’ ifeq ‘<參數(shù) 1>’, “<參數(shù) 2>”上述用法中都是用來比較“參數(shù) 1”和“參數(shù) 2”是否相同,如果相同則為真,“參數(shù) 1”和“參數(shù) 2”可以為函數(shù)返回值。ifneq 的用法類似,只不過 ifneq 是用來了比較“參數(shù) 1”和“參數(shù) 2”是否不相等,如果不相等的話就為真。
ifdef 和 ifndef 的用法如下:
ifdef <變量名>如果“變量名”的值非空,那么表示表達(dá)式為真,否則表達(dá)式為假。“變量名”同樣可以是一個(gè)函數(shù)的返回值。ifndef 用法類似,但是含義用戶 ifdef 相反。
8、函數(shù)使用
Makefile 支持函數(shù),類似 C 語言一樣,Makefile 中的函數(shù)是已經(jīng)定義好的,我們直接使用,不支持我們自定義函數(shù)。make 所支持的函數(shù)不多,函數(shù)的用法如下:
$(函數(shù)名 參數(shù)集合)或者:
${函數(shù)名 參數(shù)集合}可以看出,調(diào)用函數(shù)和調(diào)用普通變量一樣,使用符號 “$ ” 來標(biāo)識(shí)。參數(shù)集合是函數(shù)的多個(gè)參數(shù),參數(shù)之間以逗號“,”隔開,函數(shù)名和參數(shù)之間以“空格”分隔開,函數(shù)的調(diào)用以“$”開頭。
接下來我們介紹幾個(gè)常用的函數(shù),來保證后面的學(xué)習(xí)。其它的函數(shù)大家可以參考《跟我一起寫 Makefile》這份文檔。
1、函數(shù) subst
函數(shù) subst 用來完成字符串替換,調(diào)用形式如下:
$(subst <from>,<to>,<text>)此函數(shù)的功能是將字符串< text>中的內(nèi)容替換為< to>,函數(shù)返回被替換以后的字符串,比如如下示例:
$(subst zzk,ZZK,my name is zzk把字符串“my name is zzk”中的“zzk”替換為“ZZK”,替換完成以后的字符串為“my name is ZZK”。
2、函數(shù) patsubst
函數(shù) patsubst 用來完成模式字符串替換,使用方法如下:
$(patsubst <pattern>,<replacement>,<text>)此函數(shù)查找字符串< text>中的單詞是否符合模式< pattern>,如果匹配就用< replacement>來替換掉,< pattern>可以使用通配符“%”,表示任意長度的字符串,函數(shù)返回值就是替換后的字符串。如果< replacement>中也包涵“%”,那么< replacement>中的“%”將是< pattern>中的那個(gè)“%”所代表的字符串,比如:
$(patsubst %.c,%.o,a.c b.c c.c)將字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,替換為“%.o”,替換完成以后的字符串為“a.o b.o c.o”。
3、函數(shù) dir
函數(shù) dir 用來獲取目錄,使用方法如下:
$(dir <names…>)此函數(shù)用來從文件名序列< names>中提取出目錄部分,返回值是文件名序列< names>的目錄部分,比如:
$(dir </src/a.c>)提取文件“/src/a.c”的目錄部分,也就是“/src”。
4、函數(shù) notdir
函數(shù) notdir 看名字就是知道去除文件中的目錄部分,也就是提取文件名,用法如下:
$(notdir <names…>)此函數(shù)用與從文件名序列< names>中提取出文件名非目錄部分,比如:
$(notdir </src/a.c>)提取文件“/src/a.c”中的非目錄部分,也就是文件名“a.c”。
5、函數(shù) foreach
foreach 函數(shù)用來完成循環(huán),用法如下:
$(foreach <var>, <list>,<text>)此函數(shù)的意思就是把參數(shù)中的單詞逐一取出來放到參數(shù) < var> 中, 然后再執(zhí)行 < text > 所包含的表達(dá)式。每次 < text > 都會(huì)返回一個(gè)字符串,循環(huán)的過程中,< text > 中所包含的每個(gè)字符串會(huì)以空格隔開,最后當(dāng)整個(gè)循環(huán)結(jié)束時(shí),< text > 所返回的每個(gè)字符串所組成的整個(gè)字符串將會(huì)是函數(shù) foreach 函數(shù)的返回值。
6、函數(shù) wildcard
通配符“%”只能用在規(guī)則中,只有在規(guī)則中它才會(huì)展開,如果在變量定義和函數(shù)使用時(shí),通配符不會(huì)自動(dòng)展開,這個(gè)時(shí)候就要用到函數(shù) wildcard,使用方法如下:
$(wildcard PATTERN…)比如:
$(wildcard *.c)上面的代碼是用來獲取當(dāng)前目錄下所有的.c 文件,類似“%”。
總結(jié)
以上是生活随笔為你收集整理的make工具和Makefile基础语法(含有交叉编译、pthread_create()的处理)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Word转pdf文件使用技巧:怎么安装虚
- 下一篇: 扫雷外挂(扫雷辅助程序)