目錄
- Makefile
- Makefile規則與示例
- 為什麼需要Makefile
- Makefile樣式
- 先介紹Makefile的兩個函數
- 完善Makefile
- 通用Makefile的使用
- 通用的Makefile解釋
- 零星知識點
- make指令的使用
- 即時變量與臨時變量
- 變量的導出(export)
- Makefile中的shell
- Makefile中放置第一個指令
- 假想目标
- 小知識
- 常用函數
- *通用Makefile設計思想
- 零星知識點
- Makefile規則與示例
- 一個通用Makefile
- 本程式的Makefile分為3類:
- 源碼(注釋版)
- Makefile.build
- 使用說明
- 1. 各級子目錄的Makefile
- 注意
- 2. 頂層目錄的Makefile
- 3. 頂層目錄的Makefile.build **
- 使用提示
- Makefile附件
- 一些符号
- Makefile附件
- 1. 各級子目錄的Makefile
- 參考
meke指令一般用于編譯程式,而make指令都依賴于 Makefile 檔案。
最簡單的Makefile如下:
hello: hello.c
gcc -o hello hello.c
clean:
rm -f hello
注:縮進使用 tab 鍵 , 因為Makefile 會為每個以 tab 鍵開頭的指令建立一個 shell 去執行。
具體規則參考 《GUN Make 使用手冊》
以下為粗略筆記
提高編譯效率。
一個簡單的Makefile檔案包含一系列"規則":
目标...:依賴...
<tab>指令
- 指令被執行的兩個簡單條件:
- 目标檔案還沒有生成
- 依賴檔案比目标檔案新
-
$(foreach var,list,text)
for each var in list,change it to text。
對list中的每一個元素,取出指派給var,然後把var改為text所描述的形式。
例子:
objs := a.o b.o
dep_files := $(foreach f, $(objs), .$(f).d) // 最終 dep_files := .a.o.d .b.o.d
-
$(wildcard pattern)
把存在的 pattern 檔案列出來
src_files := $(wildcard *.c)
- 先來一個簡單粗暴、效率低的:
test: main.c hello.c hello.h
gcc -o test main.c hello.c
- 再來一個Makefile,效率高、精煉,支援自動檢測頭檔案
objs := main.o hello.o
test : $(objs)
gcc -o test $^
# 需要判斷是否存在依賴檔案
# .main.o.d .hello.o.d
dep_files := $(foreach f, $(objs), .$(f).d)
dep_files := $(wildcard $(dep_files))
# 把依賴檔案包含進來
ifneq ($(dep_files),)
include $(dep_files)
endif
%.o : %.c
gcc -Wp,-MD,[email protected] -c -o $@ $<
clean:
rm *.o test -f
distclean:
rm $(dep_files) *.o test -f
了解自動化變量。
參考linux核心的Makefile來編寫一個通用的Makefile,特點:
- 支援多個目錄、多層目錄、多個檔案;
- 支援給所有檔案設定編譯選項;
- 支援給目錄設定編譯選項;
- 支援給某個檔案單獨設定編譯選項;
- 簡單、好用。
執行make指令時,它會去目前目錄下查找名為Makefile的檔案,并根據它的隻是去執行操作,生成第一個目标。
也可以用 -f 選項指定檔案,如:
make -f Makefile.build
指定檔案的名字随意定義。
可以使用 -C 指定目錄,切換到其他目錄去,如:
make -C a/ abc.def
可以指定目标,不再預設生成第一個目錄,如:
make -C a/ abc.def other_target
變量定義文法:
形式 | 說明 |
---|---|
A = xxx | 延時變量 |
B ?= xxx | 延時變量,隻有第一次定義時指派才成功,若曾被定義過,則此指派無效。 |
C := xxx | 立即變量 |
D += yyy | 如果D在前面是延時變量,那麼現在它還是延時變量 如果D在前面是立即變量,那麼它現在還是立即變量 |
延時變量:
使用時才确定該值。
如:
A = $@
tets:
@echo $A
輸出 A 的值為 test。
即時變量:
定義時立即确定該值。
A := $@
tets:
@echo $A
輸出 A 的值為 空。
export 是供給子目錄的 Makefile 使用的(即 sub-make),同一級的makefile是通路不到的。
可以通過makefile中的内置變量MAKELEVEL可以檢視目前的makefile的level。
Makefile中可以使用shell指令,如:
TOPDIR := $(shell_pwd)
執行目标時,如果不指定目标,則預設執行第一個目标。
是以,第一個目标的位置很重要。有時候不太友善把第一個目标完整地放在檔案的前面,這時可以在檔案的前面直接放置目标,在後面再完善它的依賴和指令。比如:
First_target: // 這句話放在前面
........ // 其他代碼,比如include其它檔案得到後的 xxx 變量
First_target : $(xxx) $(xxx)
command
Makefile中可能會有這樣的目标:
clean:
rm -f $(shell find -name "*.o")
rm -f $(TARGET)
注:如果目前目錄下更好有個名為“clean”的檔案,那麼執行“make clean”時就不會執行makefile中目标clean的内容,是以要把clean設定為假想目标即可:
.PHONY : clean
PHONY 是 phoney,即僞造的意思。PHONY後面的target是僞造的target,不是真實存在的檔案。同時,注意make指令中後面的target預設是檔案。
-
$(foreach var, list, text)
for each var in list,change it to text.
直接上例子:
objs := a.o b.o
dep_files := $(foreach f, $(objs), .$(f).d) // dep_files 的最終結果為“**.a.o.d .b.o.d**”
- 列出 pattern 存在的檔案。
src_files = $(wildcard *.c) // src_files的值為目前目錄下所有 .c 檔案。
-
$(filter pattern..., text)
把text中符合pattern格式的内容留下來。
obj-y := a.o b.o c/ d/
DIR := $(filter %/, $(obj-y)) //結果為:c/ d/
-
$(filter-out pattern..., text)
把text中符合pattern格式的内容删除掉。
obj-y := a.o b.o c/ d/
DIR := $(filter-out %/, $(obj-y)) // 結果為:a.o b.o
-
$(patsubst pattern, replacement, text)
尋找text中符合pattern格式的内容,用replacement代替他們。
subdir-y := a.o b.o c/ d/
subdir := $(patsubst %/, %, $(obj-y)) // 結果為:c d
- 在Makefile檔案中确定要編譯的檔案、目錄,比如:
obj-y += main.o
obj-y += a/
Makefile 檔案總是被 Makefile.build 包含的。
- 在 Makefile.build 中設定編譯規則,有 3 條編譯規則:
- 怎麼編譯子目錄?
- 進入子目錄編譯即可:
- 怎麼編譯子目錄?
$(subdir-y):
make -C $@ -f $(TOPDIR)/Makefile.build
2. 如何編譯目前目錄中的目标檔案?
%.o : %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
3. 目前目錄下的 **.o** 和子目錄下的 **built-in.o** 要打包起來:
$(TARGET) : built-in.o
$(CC) $(LDFLAGS) -o $(TARGET) built-in.o
思想總結:
- Makefile檔案确定要編譯的檔案和目錄
- Makefile.build檔案包含規則
- 每個需要編譯的子目錄都放個Makefile檔案,把需要編譯的檔案編譯成built-in.o
- 再把所有子目錄的built-in.o連結到頂層的built-in.o
- 最後把頂層built-in.o連結到APP
最好重新分析一下通用Makefile
- 頂層目錄的Makefile
- 頂層目錄的Makefile.build
- 各級子目錄的Makefile
CROSS_COMPILE = # 交叉編譯工具頭,如:arm-linux-gnueabihf-
AS = $(CROSS_COMPILE)as # 把彙編檔案生成目标檔案
LD = $(CROSS_COMPILE)ld # 連結器,為前面生成的目标代碼配置設定位址空間,将多個目标檔案連結成一個庫或者一個可執行檔案
CC = $(CROSS_COMPILE)gcc # 編譯器,對 C 源檔案進行編譯處理,生成彙編檔案
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar # 打包器,用于庫操作,可以通過該工具從一個庫中删除或則增加目标代碼子產品
NM = $(CROSS_COMPILE)nm # 檢視靜态庫檔案中的符号表
STRIP = $(CROSS_COMPILE)strip # 以最終生成的可執行檔案或者庫檔案作為輸入,然後消除掉其中的源碼
OBJCOPY = $(CROSS_COMPILE)objcopy # 複制一個目标檔案的内容到另一個檔案中,可用于不同源檔案之間的格式轉換
OBJDUMP = $(CROSS_COMPILE)objdump # 檢視靜态庫或則動态庫的簽名方法
# 共享到sub-Makefile
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP
# -Wall : 允許發出 GCC 提供的所有有用的報警資訊
# -O2 : “-On”優化等級
# -g : 在可執行程式中包含标準調試資訊
# -I : 指定頭檔案路徑(可多個)
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
# LDFLAGS是告訴連結器從哪裡尋找庫檔案,這在本Makefile是連結最後應用程式時的連結選項。
LDFLAGS :=
# 共享到sub-Makefile
export CFLAGS LDFLAGS
# 頂層路徑
TOPDIR := $(shell pwd)
export TOPDIR
# 最終目标
TARGET := test
# 本次整個編譯需要源 檔案 和 目錄
# 這裡的“obj-y”是自己定義的一個格式,和“STRIP”這些一樣,*但是 一般核心會搜集 ”obj-”的變量*
obj-y += main.o # 需要把目前目錄下的 main.c 編程序式裡
obj-y += sub.o # 需要把目前目錄下的 sub.c 編程序式裡
obj-y += subdir/ # 需要進入 subdir 這個子目錄去尋找檔案來編程序式裡,具體是哪些檔案,由 subdir 目錄下的 Makefile 決定。
#obj-y += $(patsubst %.c,%.o,$(shell ls *.c))
# 第一個目标
all : start_recursive_build $(TARGET)
@echo $(TARGET) has been built !
# 處理第一個依賴,**轉到 Makefile.build 執行**
start_recursive_build:
make -C ./ -f $(TOPDIR)/Makefile.build
# 處理最終目标,把前期處理得出的 built-in.o 用上
$(TARGET) : built-in.o
$(CC) -o $(TARGET) built-in.o $(LDFLAGS)
# 清理
clean:
rm -f $(shell find -name "*.o")
rm -f $(TARGET)
# 徹底清理
distclean:
rm -f $(shell find -name "*.o")
rm -f $(shell find -name "*.d")
rm -f $(TARGET)
注意,include 指令,相對路徑為 執行本 Makefile.build 的路徑
# 僞目标
PHONY := __build
__build:
# 清空需要的變量
obj-y :=
subdir-y :=
EXTRA_CFLAGS :=
# 包含同級目錄Makefile
# 這裡要注意,相對路徑為 執行本 Makefile.build 的路徑
include Makefile
# 擷取目前 Makefile 需要編譯的子目錄的目錄名
# obj-y := a.o b.o c/ d/
# $(filter %/, $(obj-y)) : c/ d/
# __subdir-y : c d
# subdir-y : c d
__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y += $(__subdir-y)
# 把子目錄的目标定為以下注釋
# built-in.o d/built-in.o
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)
# 擷取目前目錄需要編程序式的檔案名作為,并寫為目标
# a.o b.o
cur_objs := $(filter-out %/, $(obj-y))
# 使修改頭檔案 .h 後,重新make後可以重新編譯(重要)
dep_files := $(foreach f,$(cur_objs),.$(f).d)
# 列出存在的檔案
dep_files := $(wildcard $(dep_files))
ifneq ($(dep_files),)
include $(dep_files)
endif
PHONY += $(subdir-y)
# 第一個目标
__build : $(subdir-y) built-in.o
# 優先編譯 子目錄的内容
$(subdir-y):
make -C $@ -f $(TOPDIR)/Makefile.build
# 連結成 目标
built-in.o : $(cur_objs) $(subdir_objs)
$(LD) -r -o $@ $^
dep_file = [email protected]
# 生成 cur_objs 目标
%.o : %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
.PHONY : $(PHONY)
- 本程式的Makefile分為3類:
- 頂層目錄的 Makefile
- 頂層目錄的 Makefile.build
- 各級子目錄的 Makefile
- 參考:
# 可以不添加下面兩個變量
EXTRA_CFLAGS :=
CFLAGS_test.o :=
obj-y += test.o
obj-y += subdir/
- obj-y += file.o 表示把目前目錄下的file.c編程序式裡,
- obj-y += subdir/ 表示要進入subdir這個子目錄下去尋找檔案來編程序式裡,是哪些檔案由subdir目錄下的Makefile決定。
- EXTRA_CFLAGS, 它給目前目錄下的所有檔案(不含其下的子目錄)設定額外的編譯選項, 可以不設定
- CFLAGS_xxx.o, 它給目前目錄下的xxx.c設定它自己的編譯選項, 可以不設定
- "subdir/"中的斜杠"/"不可省略
- 頂層Makefile中的CFLAGS在編譯任意一個.c檔案時都會使用
- CFLAGS EXTRA_CFLAGS CFLAGS_xxx.o 三者組成xxx.c的編譯選項
主要作用:
- 整個工程的參數初期定義
- 架構
- 工具鍊
- 編譯工具
- 編譯參數
- 需要導出的變量
- 等等
- 定義最終目标
- 跳轉到 Makefile.build
注意:該檔案雖然放在頂層,但是也是提供給各級子目錄使用的。
主要功能:
- 把某個目錄及它的所有子目錄中、需要編程序式去的檔案都編譯出來,把各個subdir/built-in.o和目前目錄的目标 .o 合并打包為目前目錄的built-in.o 。
- 執行"make"來編譯,執行 make clean 來清除,執行 make distclean 來徹底清除。
符号 | |
---|---|
$@ | 表示規則中的目标檔案集 |
$% | 當目标為函數庫的時候,則表示規則中的目标成員名。反之為空。如一個目标為"foo.a(bar.o)",那麼,"$%"就是"bar.o",以空格分隔開。 |
$< | 依賴檔案集合中的第一個檔案,如果依賴檔案以"%"形式出現,則表示符合模式的一系列檔案集合 |
$? | 所有比目标新的依賴集合,以空格分隔開。 |
$^ | 所有依賴檔案集合,以空格分隔開。如果依賴有相同,則取其一。 |
$+ | 和 "$^"類同,但是不會把相同的删除掉。 |
$* | 這個變量表示目标模式中 "%"及其之前的部分,如果目标是 test/a.test.c,目标模式為 a.%.c, 那麼 "$* " 就是 test/a.test。 |
- 以上筆記為學習和整理韋東山老師資料而來的
- 李柱明部落格:https://www.cnblogs.com/lizhuming/
- 本文連結:https://www.cnblogs.com/lizhuming/p/13956017.html