a.c #include #include "a.h" int main() { printf("hello world\n"); printf("A= %d\n",A); test_fun(); return 0; } a.h #define A 1 b.c #include int test_f... ...
a.c
#include<stdio.h> #include "a.h" int main() { printf("hello world\n"); printf("A= %d\n",A); test_fun(); return 0; }
a.h
#define A 1
b.c
#include <stdio.h> int test_fun() { printf("it is B\n"); return 0; }
編譯test_Makefile的方法:
a. gcc -o test a.c b.c
對於a.c: 預處理、編譯、彙編
對於b.c:預處理、編譯、彙編
最後鏈接
優點:命令簡單
缺點:如果文件很多,即使你只修改了一個文件,但是所有的文件文件都要重新"預處理、編譯、彙編"
效率低
b. 寫Makefile
核心:規則
目標:依賴1 依賴2
命令
命令執行的條件:
i. "依賴"文件 比 "目標"文件 新
ii.沒有"目標"這個文件
這是一個文件的依賴關係,也就是說,target這一個或多個的目標文件依賴於dependencies中的文件,其生成規則定義在command中。dependencies 中如果有一個以上的文件時間要比target文件要新的話,command所定義的命令就會被執行。這就是 Makefile的規則。也就是Makefile中最核心的內容。在Makefile中的命令,必須要以[Tab]鍵開始。
2.2 變數定義
Makefile中變數的定義一般有兩種: =和:=。 =符號定義的變數叫延時變數,只有在使用的時候才擴展開來; :=符號定義的變數為立即變數,一旦定義就擴展。 使用=定義的變數不能追加新值,使用:=定義的變數可以使用+=追加新值
2.3 文件指示
在Makefile使用include關鍵字可以把別的Makefile包含進來,這很像C語言的#include,被包含的文件會原模原樣的放在當前文件的包含位置。include的語法是:include <filename> filename可以是當前操作系統Shell的文件模式(可以保含路徑和通配符)
2.4 偽目標
偽目標並不是一個文件,只是一個標簽,由於偽目標不是文件,所以make無法生成它的依賴關係和決定 它是否要執行。我們只有通過顯示地指明這個目標才能讓其生效。當然,偽目標的取名不能和文件名重名,不然其就失去了偽目標的意義了。當然,為了避免和文件重名的這種情況,我們可以使用一個特殊的標記.PHONY來顯示地指明一個目標是偽目標,向make說明,不管是否有這個文件,這個目標就是偽目標。
2.5 自動化變數
$< 第一個依賴文件的名稱
$? 所有的依賴文件,以空格分開,這些依賴文件的修改日期比目標的創建日期晚
$@ 目標的完整名稱
$^ 所有的依賴文件,以空格分開,不包含重覆的依賴文件
Makefile_1
test: a.c b.c a.h
gcc -o test a.c b.c
只要有任一文件變化,就執行gcc -o test a.c b.c效率低
Makefile_2
test : a.o b.o gcc -o test a.o b.o a.o : a.c gcc -c -o a.o a.c b.o : b.c gcc -c -o b.o b.c
文件不會隨著某個文件變化而全部編譯
Makefile_3
test : a.o b.o gcc -o test a.o b.o a.o : a.c a.h %.o : %.c gcc -c -o $@ $<
加通通配符,通配符%,$@表示目標值,$<表示第一個依賴,$^表示所有依賴
不能生成依賴文件
頂層Makefile 頂層Makefile.build 子目錄Makefile
編譯過程:
從頂層開始遞歸進入子目錄,當進入到一個目錄的最底層時,開始使用GCC編譯,再將該層的所有.o文件打包成build-in.o,返回它的上一層目錄再遞歸進入子目錄,當編譯完所有的子目錄後,就開始編譯頂層的.c文件,最後將頂層的.o文件和頂層每個子目錄的build-in.o鏈接成我們的目標文件
電子書Makefile
CROSS_COMPILE = arm-linux- AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld CC = $(CROSS_COMPILE)gcc CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump export AS LD CC CPP AR NM export STRIP OBJCOPY OBJDUMP編譯選項
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
鏈接選項
LDFLAGS := -lm -lfreetype
export CFLAGS LDFLAGS
假目標
PHONY := __build
__build:
obj-y :=
subdir-y :=
包含當前目錄下的Makefileinclude Makefile
工程必備:
頂層Makefile 頂層Makefile.build 子目錄Makefile
編譯過程:
從頂層開始遞歸進入子目錄,當進入到一個目錄的最底層時,開始使用GCC編譯,再將該層的所有.o文件打包成build-in.o,返回它的上一層目錄再遞歸進入子目錄,當編譯完所有的子目錄後,就開始編譯頂層的.c文件,最後將頂層的.o文件和頂層每個子目錄的build-in.o鏈接成我們的目標文件
頂層Makefile解析(隨工程而變):
#----------------------------------------------指定編譯工具鏈---------------------------------------------------
CROSS_COMPILE = #指定編譯器種類
AS = $(CROSS_COMPILE)as #
LD = $(CROSS_COMPILE)ld #鏈接工具
CC = $(CROSS_COMPILE)gcc #編譯工具
CPP = $(CC) -E #
AR = $(CROSS_COMPILE)ar #打包工具
NM = $(CROSS_COMPILE)nm #
STRIP = $(CROSS_COMPILE)strip #優化工具
OBJCOPY = $(CROSS_COMPILE)objcopy #
OBJDUMP = $(CROSS_COMPILE)objdump #
export AS LD CC CPP AR NM #將定義的變數導出,方便其他makefile使用
export STRIP OBJCOPY OBJDUMP #將定義的變數導出,方便其他makefile使用
CFLAGS := -Wall -O2 -g #編譯器參數
CFLAGS += -I $(shell pwd)/include #指定編譯器頭文件(根據實際項目手動修改)
LDFLAGS := -lm -lfreetype -lvga #指定編譯器鏈接庫(根據實際項目手動修改)
export CFLAGS LDFLAGS #將定義的變數導出,方便其他makefile使用
TOPDIR := $(shell pwd) #獲得當前程式的頂層目錄
export TOPDIR #輸出頂層目錄
TARGET := show_file #編譯後的程式名(根據實際項目手動修改)
#-------------------------頂層要生成的.o文件以及頂層文件夾(根據實際項目手動修改)------------------
obj-y += main.o
obj-y += display/
obj-y += draw/
obj-y += encoding/
obj-y += fonts/
#--------------------------------------------頂層的第一個規則(預設規則)-----------------------------------------
all :
make -C ./ -f $(TOPDIR)/Makefile.build #進入當前目錄,使用頂層的makefile.build進行編譯
$(CC) $(LDFLAGS) -o $(TARGET) built-in.o #將編譯好的built-in.o文件鏈接生成我們的目標文件
#------------------------------------------------頂層的清除規則-------------------------------------------------------
clean:
rm -f $(shell find -name "*.o") #刪除所有的.o文件
rm -f $(shell find -name "*.d") #刪除所有的.d文件
rm -f $(TARGET) #刪除目標文件
.PHONY:all clean
頂層Makefile.build解析(無需改動):
PHONY := __build #定義一個PHONY變數
__build: #開頭說明__build偽目標,使其成為Makefile.build的第一個目標
obj-y := #定義當前目錄的目標變數,初始值為空
subdir-y := #定義當前目錄的子目錄變數,初始值為空
include Makefile #將當前目錄的Makefile包含進來,初始化obj-y
#obj-y:=a.o b.o c/ d/
__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) #篩選出當前目錄的目標變數中的子目錄,並且去掉/
#$(filter %/, $(obj-y)):c/ d/
#__subdir-y:c d
subdir-y += $(__subdir-y) #將開始定義的subdir-y賦值為__subdir-y
#subdir-y:c d
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o) #對於subdir-y裡面的每一個值(目錄),增加一個相應的目錄/built-in.o的變數值
#subdir_objs:c/built-in.o d/built-in.o
cur_objs := $(filter-out %/, $(obj-y)) #得到obj-y中的.o文件
#cur_objs:a.o b.o
dep_files := $(foreach f,$(cur_objs),.$(f).d) #對於所有的.o文件,定義它的依賴文件名
#dep_files: .a.d .b.d
dep_files := $(wildcard $(dep_files))
ifneq ($(dep_files),) #根據依賴文件名,判斷依賴文件是否存在,存在就包含就來
include $(dep_files)
endif
PHONY += $(subdir-y) #將$(subdir-y)也加入到變數PHONY中
--------------------------------------------Makefile. build的第一個規則--------------------------------------------------------------
__build : $(subdir-y) built-in.o #第一個規則
$(subdir-y): #第一個規則的第一個依賴規則
make -C $@ -f $(TOPDIR)/Makefile.build #依次進入該子目錄變數裡面存儲的值,使用的Makefile.build進行編譯
built-in.o : $(cur_objs) $(subdir_objs) #第一個規則的第二個依賴規則
$(LD) -r -o $@ $^ #該規則的命令:將該目錄下的.o和$(subdir_obj)打包成built-in.o文件
dep_file = [email protected] #
%.o : %.c #第一個規則的第二個依賴規則的依賴規則
$(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<#用於將目錄下所有的.c文件編譯成.o文件
.PHONY : $(PHONY) #將PHONY聲明為偽目標
本程式的Makefile分為3類:
1. 頂層目錄的Makefile
2. 頂層目錄的Makefile.build
3. 各級子目錄的Makefile
一、各級子目錄的Makefile:
它最簡單,形式如下:
obj-y += file.o
obj-y += subdir/
"obj-y += file.o"表示把當前目錄下的file.c編進程式里,
"obj-y += subdir/"表示要進入subdir這個子目錄下去尋找文件來編進程式里,是哪些文件由subdir目錄下的Makefile決定。
註意: "subdir/"中的斜杠"/"不可省略
二、頂層目錄的Makefile:
它除了定義obj-y來指定根目錄下要編進程式去的文件、子目錄外,主要是定義工具鏈、編譯參數、鏈接參數──就是文件中用export導出的各變數。
三、頂層目錄的Makefile.build:
這是最複雜的部分,它的功能就是把某個目錄及它的所有子目錄中、需要編進程式去的文件都編譯出來,打包為built-in.o
詳細的講解請看視頻。
四、怎麼使用這套Makefile:
1.把頂層Makefile, Makefile.build放入程式的頂層目錄
2.修改頂層Makefile
2.1 修改工具鏈
2.2 修改編譯選項、鏈接選項
2.3 修改obj-y決定頂層目錄下哪些文件、哪些子目錄被編進程式
2.4 修改TARGET,這是用來指定編譯出來的程式的名字
3. 在各一個子目錄下都建一個Makefile,形式為:
obj-y += file1.o
obj-y += file2.o
obj-y += subdir1/
obj-y += subdir2/
4. 執行"make"來編譯,執行"make clean"來清除,執行"make distclean"來徹底清除