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