March 3, 2015 8:19 PM
Makefile 文件的編寫學習前的準備需要準備的工程目錄結(jié)構(gòu)如下:
.├── add│?? ├── add_float.c│?? ├── add.h│?? └── add_int.c├── main.c└── sub ├── sub_float.c ├── sub.h └── sub_int.c文件編譯為可執(zhí)行文件cacuNOTE:需要的源代碼:MakefileExample.tar
Makefile的介紹使用 GCC 的命令行進行程序編譯在單個文件下是比較方便的,當工程中的文件逐漸增多,甚至變得十分龐大的時候,使用 GCC 命令編譯就會變得力不從心。linux 中的 make 工具提供了一種管理工程的功能,可以方便的進行程序的編譯,對更新的文件進行重編譯。
Makefile的基本格式為:
TARGET... : DEPENDEDS...COMMAND ... ...如果在命令行下手動編譯該程序比較麻煩,需要先編譯每個文件,生成目標文件,最后再將5個目標文件編譯成可執(zhí)行文件。
#get add_int.o targetgcc -c add/add_int.c -o add/add_int.o -ggdb#get add_float.o targetgcc -c add/add_float.c -o add/add_float.o -ggdb#get sub_float.o targetgcc -c sub/sub_float.c -o sub/sub_float.o -ggdb#get sub_int.o targetgcc -c sub/sub_int.c -o sub/sub_int.o -ggdb#get main.o targetgcc -c main.c -o main.o -Iadd -Isub -ggdb#get cacu bin filegcc -o cacu add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main.o -ggdb#get main.S targetgcc -S add/add_int.o add/add_float.o sub/sub_int.o sub/sub_float.o main.o2.多文件的Makefile使用make進行項目管理,需要編寫Makefile。在編譯時,make程序按照順序從Makefile文件中讀取指令,依次執(zhí)行!
#get cacu bin filecacu:add_int.o add_float.o sub_int.o sub_float.o main.ogcc -o cacu add/add_int.o add/add_float.o /sub/sub_int.o sub/sub_float.o main.o -ggdb#get add_int.o targetadd_int.o:add/add_int.c add/add.hgcc -c -o add/add_int.o add/add_int.c -ggdb#get add_float.o targetadd_float.o:add/add_float.c add/add.hgcc -c -o add/add_float.o add/add_float.c -ggdb#get sub_int.o targetsub_int.o:sub/sub_int.c sub/sub.hgcc -c -o sub/sub_int.o sub/sub_int.c -ggdb#get sub_float.o targetsub_float.o:sub/sub_float.c sub/sub.hgcc -c -o sub/sub_float.o sub/sub_float.c -ggdb#get main.o targetmain.o:main.c add/add.h sub/sub.hgcc -c -o main.o main.c -Iadd -Isub -ggdb#clean PRojectclean:rm -f cacu add/add_int.o add/add_float.o /sub/sub_int.o sub/sub_float.o main.o當需要編譯工程時,直接在工程目錄中執(zhí)行make即可。如果想清除編譯過程中生成的目標文件和cacu,執(zhí)行make clean即可。
在Makefile文件中,用戶可以自定義變量,方便用戶修改參數(shù)。使用變量后,原本冗長的文件可以化簡為:
CC = gccCFLAGS = -Iadd -Isub -O2OBJS = add/add_int.o add/add_float.o sub/sub_float.o sub/sub_int.o main.oTARGET = cacuRM = rm -f$(TARGET):$(OBJS)$(CC) -o $(TARGET) $(OBJS) $(CFLAGS)$(OBJS):%o:%c$(CC) -c $(CFLAGS) $< -o $@clean:$(RM) $(TARGET) $(OBJS)NOTE:$(OBJS):%.o:%.c中 %.o:%.c 是將 $(OBJS) 中以 .o 結(jié)尾的文件替換成以 .c 結(jié)尾的文件。
其中 $< 和 $@ 是自動化變量,下一節(jié)會介紹。
4.使用預(yù)定義變量的Makefile在Makefile中還有一些變量是系統(tǒng)預(yù)定義的,用戶可以直接使用。
Makefile中經(jīng)常使用的變量及含義
| 變量名 | 含 義 | 默 認 值 |
|---|---|---|
| AR | 生成靜態(tài)庫庫文件的程序名稱 | ar |
| AS | 匯編編譯器的名稱 | as |
| CC | C語言編譯器的名稱 | cc |
| CPP | C語言預(yù)編譯器的名稱 | /$(CC) -E |
| CXX | C++語言編譯器的名稱 | g++ |
| FC | FORTRAN語言編譯器的名稱 | f77 |
| RM | 刪除文件程序的名稱 | rm -f |
| ARFLAGS | 生成靜態(tài)庫庫文件程序的選項 | 無默認值 |
| ASFLAGS | 匯編語言編譯器的編譯選項 | 無默認值 |
| CFLAGS | C語言編譯器的編譯選項 | 無默認值 |
| CPPFLAGS | C語言預(yù)編譯器的編譯選項 | 無默認值 |
| CXXFLAGS | C++語言編譯器的編譯選項 | 無默認值 |
| FFLAGS | FORTRAN語言編譯器的編譯選項 | 無默認值 |
因此,前面的Makefile文件可以改寫成:
CFLAGS = -Iadd -Isub -O2OBJS = add/add_int.o add/add_float.o / sub/sub_int.o sub/sub_float.o main.oTARGET = cacu$(TARGET):$(OBJS)$(CC) -o $(TARGET) $(OBJS) $(CFLAGS)clean:-$(RM) $(TARGET) $(OBJS)其中變量$(CC) $(RM)可以直接使用,默認值分別是cc和rm -f。另外CFLAGS等變量是調(diào)用編譯器時的默認選項配置,在生成main.o時沒有指定編譯選項,make程序自動調(diào)用了文件中定義的CFLAGS選項來增加頭文件的搜索路徑。
還記得上面出現(xiàn)的 /$< 和 /$@ 嗎?它們是Makefile中的自動變量,分別代表依賴項和目標項。下面是一些常見的自動變量及其含義:
Makefile 中常見的自動變量和含義
| 變量 | 含義 |
|---|---|
| * | 表示目標文件的名稱,不包含目標文件的擴展名 |
| + | 表示所有的依賴文件,這些依賴文件之間以空格分開,按照出現(xiàn)的先后為順序,其中可能包含重復(fù)的依賴文件 |
| < | 表示依賴項中第一個依賴文件的名稱 |
| ? | 依賴項中,所有目標文件時間戳晚的依賴文件,依賴文件之間以空格分開 |
| @ | 目標項中目標文件的名稱 |
| ^ | 依賴項中,所有不重復(fù)的依賴文件,這些文件之間以空格分開 |
由此,對上面的Makefile文件進行重寫,代碼如下:
CFLAGS = -Iadd -Isub -O2OBJS = add/add_int.o add/add_float.o / sub/sub_int.o sub/sub_float.o main.oTARGET = cacu$(TARGET):$(OBJS)$(CC) $^ -o $@ $(CFLAGS)$(OBJS):%o:%c$(CC) -c $< -o $@ $(CFLAGS)clean:-$(RM) $(TARGET) $(OBJS)6.設(shè)置搜索路徑在大的系統(tǒng)中,通常存在很多目錄,手動添加目錄的方法不僅十分笨拙而且容易造成錯誤。Make 的目錄搜索功能提供了一個解決此問題的方法,指定需要搜索的目錄, make 會自動找到指定文件的目錄并添加到文件上, VPATH 變量可以實現(xiàn)此目的。VPATH 變量的使用方法如下:
VPATH = path1:path2:...VPATH 右邊是冒號(:)分割路徑名稱,例如下面的指令:
VPATH = add:subadd_int.o:%.o:%.c$(CC) -c -o $@ $<Make 的搜索路徑包含 add 和 sub 目錄。add_int.o 規(guī)則自動擴展成如下代碼:
add_int.o:add/add_int.ccc -c -o add_int.o add/add_int.c用添加路徑的方法改寫上面的 Makefile 文件,代碼如下:
CFLAGS = -Iadd -Isub -O2OBJDIR = objsVPATH = add:sub:.OBJS = add_int.o add_float.o sub_int.o sub_float.o main.oTARGET = cacu$(TARGET):$(OBJSDIR) $(OBJS)$(CC) -o $(TARGET) $(OBJDIR)/*.o $(CFLAGS)$(OBJDIR):mkdir -p ./$@$(OBJS):%.o:%.c $(CC) -c $(CFLAGS) $< -o $(OBJDIR)/$@clean:-$(RM) $(TARGET)-$(RM) $(OBJDIR)/*.o -r7.自動推導(dǎo)規(guī)則使用命令 make 編譯擴展名為 .c 的 C 語言文件的時候,源文件的編譯規(guī)則不用明確地給出。這是因為 make 進行編譯的時候會使用一個默認的編譯規(guī)則,按照默認規(guī)則完成對 .c 文件的編譯,生成對應(yīng)的 .o 文件。它執(zhí)行命令 cc -c 來編譯 .c 源文件。在 Makefile 中只需要給出需要重建的目標文件(一個 .o 文件),make 會自動為這個 .o 文件尋找合適的依賴文件(對應(yīng)的 .c 文件),并且使用默認的命令來構(gòu)建這個目標文件。
對于上邊的例子,默認規(guī)則是使用命令cc -c main.c -o main.o來創(chuàng)建文件 main.o 。對一個目標文件是“文件名.o“,依賴文件是”文件名.c“的規(guī)則,可以省略其編譯規(guī)則的命令行,由 make 命令決定如何使用編譯命令和選項。此默認規(guī)則稱為 make 的隱含規(guī)則。
這樣,在書寫 Makefile 時,就可以省略掉描述 .c 文件和 .o 依賴關(guān)系的規(guī)則,而只需要給出那些特定的規(guī)則描述(.o 目標所需要的 .h 文件)。因此上面的例子可以使用更加簡單的方式書寫, Makefile 文件的內(nèi)容如下:
CFLAGS = -Iadd -Isub -O2VPATH = add:subOBJS = add_int.o add_float.o sub_int.o sub_float.o main.oTARGET = cacu$(TARGET):$(OBJS)$(CC) -o $(TARGET) $(OBJS) $(CFLAGS)clean:-$(RM) $(TARGET)-$(RM) $(OBJS)8.遞歸 make對于規(guī)模比較大的程序,需要多個人在多個目錄下進行開發(fā)。如果只用一個 Makefile 來維護就會比較麻煩,因此可以在每個目錄下建立自己的 Makefile ,然后在總控 Makefile 中調(diào)用子目錄的 Makefile 文件。
目錄結(jié)構(gòu)如下:
.├── add│?? ├── add_float.c│?? ├── add.h│?? ├── add_int.c│?? └── Makefile├── main.c├── Makefile└── sub ├── Makefile ├── sub_float.c ├── sub.h └── sub_int.c1.遞歸調(diào)用的方式
add:cd add && $(MAKE)它等價于
add:$(MAKE) -C add2.總控Makefile
CC = gccCFLAGS = -O2TARGET = cacuexport OBJSDIR = $(shell pwd)/objs$(TARGET):$(OBJSDIR) main.o$(MAKE) -C add$(MAKE) -C sub$(CC) -o $(TARGET) $(OBJSDIR)/*.o$(OBJSDIR):mkdir -p $@main.o:%.o:%.c$(CC) -c $< -o $(OBJSDIR)/$@ $(CFLAGS) -Iadd -Isubclean:-$(RM) $(TARGET)-$(RM) $(OBJSDIR)/*.o如果總控 Makefile 中的一些變量需要傳遞給下層的 Makefile,可以使用 export 命令。如:export OBJSDIR = ./objs
3.子目錄Makefile的編寫
Add 目錄下的 Makefile 如下:
OBJS = add_int.o add_float.oall:$(OBJS)$(OBJS):%.o:%.c$(CC) -c $< -o $(OBJSDIR)/$@ $(CFLAGS)clean:$(RM) $(OBJS)Sub 目錄下的 Makefile 如下:
OBJS = sub_int.o sub_float.oall:$(OBJS)$(OBJS):%.o:%.c$(CC) -c $< -o $(OBJSDIR)/$@ $(CFLAGS)clean:$(RM) $(OBJS)Makefile 中的函數(shù)1.獲取匹配模式的文件名wildcard這個函數(shù)的功能是查找當前目錄下所有符合模式 PATTERN 的文件名,其返回值是以空格分割的、當前目錄下的所有符合模式 PATTERN 的文件名列表。其原型如下:
$(wildcard PATTERN)
例如,如下模式返回當前目錄下所有擴展名位 .c 的文件列表。
$(wildcard *.c)
這個函數(shù)的功能是查找字符串 text 中按照空格分開的單詞,將符合模式 pattern 的字符串替換成 replacement。 Pattern 中的模式可以使用通配符, % 代表 0 個到 n 個字符,當 pattern 和 replacement 中都有 % 時,符合條件的字符將被 replacement 中的替換。函數(shù)的返回值是替換后的新字符串。其原型如下:
$(patsubst pattern, replacement, text)
例如,需要將 C 文件替換為 .o 的目標文件可以使用如下模式:
$(patsubst %.c, %.o, add.c)
上面的模式將 add.c 字符串作為輸入,當擴展名為 .c 時符合模式 %.c ,其中 % 在這里代表 add,替換為 add.o,并作為輸出字符串。
$(patsubst %.c, %.o, $(wildcard *.c))
輸出的字符串將當前擴展名為 .c 的文件替換成 .o 的文件列表。
3.循環(huán)函數(shù)foreach這個函數(shù)的原型為:
$(foreach VAR, LIST, TEXT)
函數(shù)的功能為 foreach 將 LIST 字符串中一個空格分割的單詞,先傳給臨時變量 VAR ,然后執(zhí)行 TEXT 表達式, TEXT 表達式處理結(jié)束后輸出。其返回值是空格分割表達式 TEXT 的計算結(jié)果。
例如,對于存在 add 和 sub 的兩個目錄,設(shè)置 DIRS 為 "add sub ./" 包含目錄 add、sub 和當前目錄。表達式 $(wildcard $(dir)/*.c) ,可以取出目錄 add 和 sub 及當前目錄中的所有擴展名為 .c 的C語言源文件:
DIRS = sub add ./FILES = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))利用上面幾個函數(shù)對原有的 Makefile 文件進行重新編寫,使新的 Makefile 可以自動更新各個目錄下的C語言源文件:
CC = gccCFLAGS = -O2 -Iadd -IsubTARGET = cacuDIRS = sub add .FILES = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))OBJS = $(patsubst %.c, %.o, $(FILES))$(TARGET):$(OBJS)$(CC) -o $(TARGET) $(OBJS)clean:-$(RM) $(TARGET)-$(RM) $(OBJS)總結(jié)至此,已經(jīng)可以閱讀大部分軟件的 Makefile 了~~~
新聞熱點
疑難解答