前面我們編譯內(nèi)核時(shí),那么多.c文件最終生成一個(gè)zImage鏡像。其實(shí)是make工具通過解析Makefile文件進(jìn)行一系列編譯操作,最終生成我們想要的鏡像文件。Makefile文件中描述了個(gè)工程所有文件的編譯順序、編譯規(guī)則以及依賴關(guān)系,決定了工程中文件是否需要編譯,以及這些文件的編譯順序。Make工具還可以通過比較文件最后修改時(shí)間,來決定哪些文件需要更新哪些不需要更新,更改了某個(gè)文件之后,只對(duì)依賴此文件的目標(biāo)文件進(jìn)行重新編譯更新,這就大大減少了編譯時(shí)間。
打開源碼目錄可以看到,Makefile文件不僅存在于源碼根目錄下,在其他的子目錄下也基本都有Makefile文件。在執(zhí)行編譯時(shí),Make工具會(huì)解析根目錄下Makefile 文件進(jìn)行編譯,而根目錄的Makefile會(huì)調(diào)用子目錄下的Makefile,子目錄下又有子目錄,層層調(diào)用。
Makefile需要按照一定的格式語(yǔ)法規(guī)則進(jìn)行書寫。如果你是做Linux應(yīng)用開發(fā)的人員,那么寫Makefile就是必備技能,就需要深諳Makefile語(yǔ)法規(guī)則。對(duì)于我們Linux平臺(tái)初級(jí)開發(fā)人員,很少需要我們?nèi)懸粋€(gè)復(fù)雜的Makefile文件,只是在做平臺(tái)移植的時(shí)候可能需要去簡(jiǎn)單修改或者閱讀Makefile文件,所以呢,下面我們就簡(jiǎn)單了解一下Makefile基本格式規(guī)則。
目標(biāo)和依賴
目標(biāo)就是我們需要生成的文件,依賴就是生成目標(biāo)文件所需要的其他文件,稱為依賴文件。基本語(yǔ)法規(guī)則如下:
targets … :dependent_files … (tap)command … |
舉例一
我們先創(chuàng)建一個(gè)簡(jiǎn)單的app.c文件:
elf@ubuntu:~/work/tmp$ touch?app.c |
在文件中輸入如下代碼:
#include <stdio.h> int main(void) { ????????printf("my first app !!! rn"); ????????return 0; } |
按照基本規(guī)則建立一個(gè)簡(jiǎn)單的Makefile文件,app為我們需要的目標(biāo)文件,app.c為所要生成的app的依賴文件,gcc –o app app.c就是生成目標(biāo)app需要執(zhí)行的命令。
建立一個(gè)Makefile文件:
elf@ubuntu:~/work/tmp$ touch makefile |
文件中輸入如下內(nèi)容:
注意:復(fù)制粘貼過程中可能會(huì)出現(xiàn)格式問題
app:app.c ????????gcc -o app app.c |
使用make命令,可以看到執(zhí)行了命令gcc -o app app.c,ls命令查看文件,發(fā)現(xiàn)在該目錄下生成了app目標(biāo)文件:
elf@ubuntu:~/work/tmp$ make gcc -o app app.c elf@ubuntu:~/work/tmp$ ls app app.c makefile |
執(zhí)行app,即可看到app.c中我們寫的打印信息:
elf@ubuntu:~/work/tmp$ ./app my first app !!! |
然后我們?cè)賵?zhí)行make命令,會(huì)發(fā)現(xiàn)出現(xiàn)如下信息,‘a(chǎn)pp’ is up to date,說明app已經(jīng)是最新的,沒必要再重新生成:
elf@ubuntu:~/work/tmp$ make make: ‘a(chǎn)pp’ is up to date. |
我們做一下稍微的改動(dòng),修改app.c文件中的內(nèi)容或者使用touch命令更改一下app.c的時(shí)間屬性,然后再進(jìn)行make看看:
elf@ubuntu:~/work/tmp$ touch app.c elf@ubuntu:~/work/tmp$ make gcc -o app app.c |
可以看到又重新執(zhí)行了gcc -o app app.c命令,重新生成了app。也就是說,make工具會(huì)判斷依賴文件的時(shí)間戳是否比目標(biāo)文件時(shí)間戳更新,來決定是否重新生成目標(biāo)文件。
舉例二
假如我們有多個(gè)文件需要編譯,比如我們創(chuàng)建多個(gè)文件fun1.c,fun1.h,fun2.c,fun2.h,app.c。
fun1.c:
#include <stdio.h> #include "fun1.h" void fun1(char *s) { ????????printf("%srn",s); } |
fun1.h:
#ifndef _FUN1_H #define _FUN1_H void fun1(char *s); #endif |
fun2.c:
#include "fun2.h" int fun2(int x,int y) { ????????return x+y; } |
fun2.h:
#ifndef _FUN2_H #define _FUN2_H int fun2(int x,int y); #endif |
創(chuàng)建app.c文件,該文件引用fun1.c和fun2.c中定義的函數(shù):
#include <stdio.h> #include "fun1.h" #include "fun2.h" int main(void) { ????????fun1("I am fun1 !"); ????????printf("fun2 return value=%d n",fun2(1,2)); ????????return 0; } |
最后創(chuàng)建makefile文件,其中app是我們的最終目標(biāo)文件,app的依賴文件為app.o,fun1.o,fun2.o,這幾個(gè).o文件稱為中間目標(biāo)文件,它們又有各自所依賴的.c和.h文件,最后的clean也是一個(gè)目標(biāo)文件,但是這個(gè)目標(biāo)沒有任何依賴,也不會(huì)生成真正文件,只是執(zhí)行一條命令,我們稱之為偽目標(biāo)。我們可以看到clean執(zhí)行的是刪除.o和app的命令。
注意:復(fù)制粘貼過程中可能會(huì)出現(xiàn)格式問題
app:app.o fun1.o fun2.o ????????gcc -o app app.o fun1.o fun2.o app.o:app.c ????????gcc -c app.c fun1.o:fun1.c fun1.h ????????gcc -c fun1.c fun2.o:fun2.c fun2.h ????????gcc -c fun2.c clean: ?????????rm -rf *.o app ?????????????????? |
創(chuàng)建完成之后,我們直接make:
elf@ubuntu:~/work/tmp$ make gcc -c app.c gcc -c fun1.c gcc -c fun2.c gcc -o app app.o fun1.o fun2.o elf@ubuntu:~/work/tmp$ ls app ?app.c ?app.o ?fun1.c ?fun1.h ?fun1.o ?fun2.c ?fun2.h ?fun2.o ?makefile ?????????? |
上圖可以看到生成最終目標(biāo)app和一些中間目標(biāo)文件,這個(gè)過程經(jīng)過了以下四個(gè)步驟:
gcc -c app.c gcc -c fun1.c gcc -c fun2.c gcc -o app app.o fun1.o fun2.o |
執(zhí)行app,可以看到執(zhí)行成功:
elf@ubuntu:~/work/tmp$ ./app I am fun1 ! fun2 return value=3 |
現(xiàn)在修改其中一個(gè)文件fun2.h后,比如更改一下fun2.h的時(shí)間屬性,然后再次編譯:
elf@ubuntu:~/work/tmp$ touch fun2.h elf@ubuntu:~/work/tmp$ make gcc -c fun2.c gcc -o app app.o fun1.o fun2.o |
可以看到只用了兩個(gè)步驟,app的其他依賴文件并沒有被重新編譯生成。
最后,執(zhí)行make clean清除中間文件和app:
elf@ubuntu:~/work/tmp$ make clean rm -rf *.o app elf@ubuntu:~/work/tmp$ ls app.c ?fun1.c ?fun1.h ?fun2.c ?fun2.h ?makefile |
變量定義
變量是在Makefile中定義的名字,用來代替一個(gè)文本字符串,該文本字符串稱為該變量的值。變量名是不包括“:”、“#”、“=”結(jié)尾空格的任何字符串。變量名是大小寫敏感的。推薦在Makefile內(nèi)部使用小寫字母作為變量名。
變量賦值方式有以下幾種:
遞歸賦值
變量的名稱 = 變量值
變量的值將會(huì)是整個(gè)Makefile中最后被指定的值。
舉例,建立一個(gè)makefile文件,定義兩個(gè)變量a和b,引用變量使用$(),使用@echo進(jìn)行打?。?/p>
注意:復(fù)制粘貼過程中可能會(huì)出現(xiàn)格式問題
a = 123 b = $(a) string a = 678 target: ????????@echo "a = $(a)" ????????@echo "b = $(b)" |
可以看到b的值為678 string,而不是123 string
elf@ubuntu:~/work/tmp$ make a = 678 b = 678 string |
簡(jiǎn)單賦值
變量的名稱 := 變量值
變量的值決定于它在Makefile中的位置,而不是整個(gè)Makefile展開后最終的值。
注意:復(fù)制粘貼過程中可能會(huì)出現(xiàn)格式問題
a := 123 b := $(a) string a := 678 target: ????????@echo "a = $(a)" ????????@echo "b = $(b)" |
make后可以看到b的值為123 string:
elf@ubuntu:~/work/tmp$ make a = 678 b = 123 string |
條件賦值
變量的名稱 ?= 變量值
如果變量未定義,則使用該變量值定義變量。如果該變量已經(jīng)被定義賦值,則該賦值語(yǔ)句無效,使用原有值。
注意:復(fù)制粘貼過程中可能會(huì)出現(xiàn)格式問題
a ?= 123 b ?= $(a) string a ?= 678 target: ????????@echo "a = $(a)" ????????@echo "b = $(b)" |
make后,a的值是第一次被賦予的值:
elf@ubuntu:~/work/tmp$ make a = 123 b = 123 string |
追加賦值
變量的名稱 += 變量值
舉例:
注意:復(fù)制粘貼過程中可能會(huì)出現(xiàn)格式問題
a := 123 b := $(a) string a += $(b) target: ????????@echo "a = $(a)" ????????@echo "b = $(b)" |
make之后結(jié)果:
elf@ubuntu:~/work/tmp$ make a = 123 123 string b = 123 string |
系統(tǒng)變量
系統(tǒng)變量或者叫環(huán)境變量,包含了常見編譯器、匯編器的名稱及其編譯選項(xiàng),我們?cè)诰幾g之前使用source /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi主要就是設(shè)置這些系統(tǒng)環(huán)境變量的值。
下表列出了Makefile中常見系統(tǒng)變量及其部分默認(rèn)值:
AR:庫(kù)文件維護(hù)程序的名稱,默認(rèn)值為ar;
AS:匯編程序的名稱,默認(rèn)值為as;
CC:C編譯器的名稱,默認(rèn)值為cc;
CPP:C預(yù)編譯器的名稱,默認(rèn)值為$(CC) –E;
CXX:C++編譯器的名稱,默認(rèn)值為g++;
FC:FORTRAN編譯器的名稱,默認(rèn)值為f77;
RM:文件刪除程序的名稱,默認(rèn)值為rm –f;
ARFLAGS:庫(kù)文件維護(hù)程序的選項(xiàng),無默認(rèn)值;
ASFLAGS:匯編程序的選項(xiàng),無默認(rèn)值;
CFLAGS:C編譯器的選項(xiàng),無默認(rèn)值;
CPPFLAGS:C預(yù)編譯的選項(xiàng),無默認(rèn)值;
CXXFLAGS:C++編譯器的選項(xiàng),無默認(rèn)值;
FFLAGS:FORTRAN編譯器的選項(xiàng),無默認(rèn)值;
條件表達(dá)式的語(yǔ)法
(1)比較arg1和arg2值是否相同,如果相同則執(zhí)行text-if-true,否則執(zhí)行text-if-false。
ifeq (<arg1>, <arg2> ) text-if-true else text-if-false endif |
比較arg1和arg2值是否相同,如果不同則執(zhí)行text-if-true,否則執(zhí)行text-if-false,與上面的ifeq相反。
ifneq (<arg1>, <arg2> ) text-if-true else text-if-false endif |
(2)判斷variable是否為空,如果非空則為真,執(zhí)行text-if-true,否則執(zhí)行text-if-false。
ifdef <variable > text-if-true else text-if-false endif |
判斷variable是否為空,如果為空則為真,執(zhí)行text-if-true,否則執(zhí)行text-if-false。
ifndef <variable > text-if-true else text-if-false endif |