一:基本概念
- 目標(target): 位於‘:’的前面,其名字可以是由字母和下劃線‘_’組成。
- 假目標(phony target):用於解決所定義的目標與所存在的文件是同名的的問題。實例:.PHONY: clean
- 先決條件(prerequisites):生成某個目標時,所依賴的目標。通常位於':'後面。
- 命令(commands):通常位於目標下面。每條規則中的命令和操作系統 Shell 的命令行是一致的。make 會按順序一條一條地執行命令,每條命令的開頭必須以[Tab]鍵開頭,除非,命令是緊跟在依賴規則後面的分號後的。在命令行之間中的空格或是空行會被忽略,但是,如果該空格或空行是以 Tab 鍵開頭的,那麼 make 會認爲其是一個空命令。
- 規則:
- 由目標(targets)、先決條件(prerequisites)以及命令(commands)所組成的。
- Makefile的基本單元。
注:
目標和先決條件之間表達的就是依賴關係(dependency),這種依賴關係指明在構建目標之前,必須保證先決條件先滿足(或構建)。而先決條件可以是其它的目標,當先決條件是目標時,其必須先被構建出來。 還有就是一個規則中目標可以有多個,當存在多個目標,且這一規則是 Makefile 中的第一個規則時,如果我們運行 make 命令不帶任何目標,那麼規則中的第一個目標將被視爲是缺省目標。
- 實例
源文件:
//foo.c
#include <stdio.h>
void foo ()
{
printf (“This is foo ()!\n”);
}
//main.c
extern void foo ();
int main ()
{
foo ();
return 0;
}
makefile初版1
#-c:表示只編譯不鏈接。
all: main.o foo.o
gcc -o simple main.o foo.o
main.o: main.c
gcc -o main.o -c main.c
foo.o: foo.c
gcc -o foo.o -c foo.c
clean:
rm simple.exe main.o foo.o
二:變量
- 概念:一個變量的定義很簡單,就是一個名字(變量名)後面跟上一個等號,然後在等號的後面放這個變量所期望的值。對 於變量的引用,則需要採用$(變量名)或者${變量名}這種模式。
- 變量的分類:
1. 自動變量:用於解決目標和先決條件的名字會在規則的命令中多次出現的問題。
$@:用於表示一個規則中的目標。當我們的一個規則中有多個目標時, $@所指的是其中任何造成命令被運行的目標。
$^:則表示的是規則中的所有先擇條件。
$<:表示的是規則中的第一個先決條件。
2. 特殊變量:make程序定義好的變量。
CC = cc #c語言編譯器的名稱
CPP = $(cc) -E #c文件預處理器的名稱
CFLAGS #C文件的編譯選項
CPPFLAGS #C文件預處理的編譯選項
CXXFLAGS #CPP文件的編譯選項
LDFLAGS #連接的動態庫
CURDIR := /home/zxy/... #當前路徑
MAKEFLAGS = p #make命令選項
RM = rm -f
VPATH #文件的搜索路徑
MAKE:指make命令名是什麼。當我們需要在 Makefile 中調用另一個 Makefile 時需要用到這個變量, 採用這種方式,有利於寫一個容易移植的 Makefile。
MAKECMDGOALS: 指的是用戶輸入的目標。
- 變量的類別(賦值)
1. 遞歸擴展變量(recursively expanded variable):使用“=”操作符進行變量定義和賦值,遞歸擴展變量的引用是遞歸的。 其值爲推導到最後一次的賦值值。
2. 簡單擴展變量(simply expanded variables):使用“:=”操作符來定義的。 對於這種變量, make 只對其進行一次掃描和 替換。
3. 條件賦值符:使用“?=”操作符來定義的。條件賦值的意思是當變量以前沒有定義時,就定義它並且將左邊的值賦值給
它,如果已經定義了那麼就不再改變其值。條件賦值類似於提供了給變量賦缺省值的功能。
4. 追加賦值:使用“+=”操作符來定義的。原變量用追加一個新值。
5. 多行變量
define two-lines
echo foo
echo $(bar)
endef
- 變量及其值的來源:
1 . 對於前面所說到的自動變量,其值是在每一個規則中根據規則的上下文自動獲得變量值的。
2 .可以在運行 make 時,在 make 命令行上定義一個或多個變量。如果採用 make bar=x 運行 Makefile,則得到的結果將完 全不同。在 make 命令行中定義的變量及其值同樣在 Makefile 中是可見的。其實,我們可以通過在 make 命令行中定義 變量的方式從而覆蓋 Makefile 中所定義的變量的值。
3 .變量還可以來自於 Shell 環境, 如,採用 Shell 中的 export 命令定義了一個 bar變量。
- 高級變量引用功能:
在斌值的同時完成後綴替換操作。如:
PHONY: all
foo = a.o b.o c.o
bar := $(foo:.o=.c)
all:
@echo "bar = $(bar)"
- override 指令:
我們可以採用在 make 命令行上定義變量的方式, 使得 Makefile 中定義的變量被覆蓋掉,從而不起作用。 可能,在設計 Makefile 時,我們並不希望用戶將我們在 Makefile 中定義的某個變量覆蓋掉,那就得用 override 指令了。
#更新makefile2:
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o
$(EXE): $(OBJS)
$(CC) -o $@ $^
main.o: main.c
$(CC) -o $@ -c $^
foo.o: foo.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
三:模式
- 解決問題:
如果對於每一個目標文件都得寫一個不同的規則來描述,那會是一種“體力活”,太繁了!對於一個大型項目, 就更不用說了。 Makefile 中的模式就是用來解決我們的這種煩惱的。
# 更新makefile3:
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o
$(EXE): $(OBJS)
$(CC) -o $@ $^
#模式應用
%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
四:條件判斷
使用條件判斷,可以讓make根據運行時的不同情況選擇不同的執行分支。條件表達式可以是比較變量的值,或是比較變量和常量的值。
注意:
make是在讀取Makefile時就計算條件表達式的值,並根據條件表達式的值來選擇語句,所以,你最好不要把自動化變量(如“$@”等)放入條件表達式中,因爲自動化變量是在運行時纔有的。
條件表達式的語法爲:
# 111
<conditional-directive>
<text-if-true>
endif
# 222
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
# 333
ifeq (<arg1>, <arg2>)
<text-if-true>
else ifeq (<arg3>, <arg4>)
<text-if-true>
else
<text-if-true>
endif
其中<conditional-directive>表示條件關鍵字,如“ifeq”。這個關鍵字有四個。
(1)第一個是“ifeq”:
注:ifeq 後面參數要加$(), 因爲是值引用, 值可以爲數值或字符串
ifeq (<arg1>, <arg2> )
比較參數“arg1”和“arg2”的值是否相同。當然,參數中還可以使用make的函數。如:
ifeq ($(strip $(foo)),)
<text-if-empty>
endif
這個示例中使用了“strip”函數,如果這個函數的返回值是空(Empty),那麼<text-if-empty>就生效。
(2)第二個條件關鍵字是“ifneq”。語法是:
ifneq (<arg1>, <arg2> )
其比較參數“arg1”和“arg2”的值是否相同,如果不同,則爲真。和“ifeq”類似。
(3)第三個條件關鍵字是“ifdef”。語法是:
ifdef <variable-name>
如果變量<variable-name>是否有值,那表達式爲真。否則,表達式爲假。當然,<variable-name>同
樣可以是一個函數的返回值。注意,ifdef只是測試一個變量是否有值,其並不會把變量擴展到當前位置。還是來看兩個例子:
注:ifdef後的變量名不能加 $()。
示例一:
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif
示例二:
foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif
第一個例子中,“$(frobozz)”值是“yes”,第二個則是“no”。
(4)第四個條件關鍵字是“ifndef”。其語法是:
ifndef <variable-name>
和“ifdef”是相反的意思。
在<conditional-directive>這一行上,多餘的空格是被允許的,但是不能以[Tab]鍵做爲開始(不然
就被認爲是命令)。而註釋符“#”同樣也是安全的。“else”和“endif”也一樣,只要不是以[Tab]鍵開始就行了。
feq ($(TARGET_ARCH), arm)
LOCAL_SRC_FILES := ...
else ifeq ($(TARGET_ARCH), x86)
LOCAL_SRC_FILES := ...
else ifeq ($(TARGET_ARCH), mips)
LOCAL_SRC_FILES := ...
else
LOCAL_SRC_FILES := ...
endif
五:函數
- addprefix 函數
addprefix 函數是用來在給字符串中的每個子串前加上一個前綴,其形式是:
$(addprefix prefix, names...)
實例代碼:
.PHONY: all
without_dir = foo.c bar.c main.o
with_dir := $( addprefix objs/, $(without_dir))
all:
@echo $(with_dir)
- filter函數
filter 函數用於從一個字符串中,根據模式得到滿足模式的字符串,其形式是:
$(filter pattern..., text)
實例代碼:
.PHONY: all
sources = foo.c bar.c baz.s ugh.h
sources := $(filter %.c %.s, $(sources))
all:
@echo $(sources)
- filter-out函數
filter-out 函數用於從一個字符串中根據模式濾除一部分字符串,filter 與 filter-out 是互補的。其形式是:
$(filter-out pattern..., text)
實例代碼:
.PHONY: all
objects = main1.o foo.o main2.o bar.o
result = $(filter-out main%.o, $(objects))
all:
@echo $(result)
- patsubst函數
patsubst 函數是用來進行字符串替換的,其形式是:
$(patsubst pattern, replacement, text)
實例代碼:
.PHONY: all
mixed = foo.c bar.c main.o
objects := $(patsubst %.c, %.o, $(mixed))
all:
@echo $(objects)
- strip函數
strip 函數用於去除變量中的多餘的空格,其形式是:
$(strip string)
實例代碼:
.PHONY: all
original = foo.c bar.c
stripped := $(strip $(original))
all:
@echo "original = $(original)"
@echo "stripped = $(stripped)"
- wildcard 函數
wildcard 是通配符函數,通過它可以得到我們所需的文件,這個函數如果我們在 Windows 或是Linux 命令行中的“*”。 其形式是:
$(wildcard pattern)
實例代碼:
.PHONY: all
SRCS = $(wildcard *.c)
all:
@echo $(SRCS)
# 更新makefile4
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
$(EXE): $(OBJS)
$(CC) -o $@ $^
%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)