https://blog.atime.me/note/makefile.html
總結GNU Make的一些基礎知識和技巧,以下内容均基于Ubuntu 14.04 x86_64平台的GNU Make 3.81。
變量
變量類型
make支援兩種風格的變量定義方式:
- 遞歸擴充變量(recursively expanded variables)
- 使用
或define指令定義=
-
會導緻無限遞歸CFLAGS = $(CFLAGS) -O
- 變量每次被展開的時候,所使用的函數都會重新被執行,會降低make的效率,更嚴重的是,wildcard等函數會傳回預料之外的結果。
- 使用
- 簡單擴充變量(simply expanded varialbes)
-
或:=
定義::=
- 在變量定義的時候展開一次
-
shell變量
shell變量應使用
@
轉義,比如變量
${var}
要改為
$${var}
。
自動變量
下面是一些常用的自動變量
-
: 第一個依賴$<
-
: 目标$@
-
: 修改時間在目标之後的依賴$?
-
: 所有的依賴$^
-
: 所有的order-only依賴$|
預定義變量
Makefile中預定義了一些變量,通常用于Implicit Rules中,詳細清單見Implicit Variables。其中常用的有:
-
: C編譯器,預設是CC
cc
-
: C++編譯器,預設是CXX
g++
-
: 傳遞給C編譯器的編譯參數CFLAGS
-
: 傳遞給C++編譯器的編譯參數CXXFLAGS
-
: 傳遞給編譯器的連結參數LDFLAGS
函數和指令
wildcard
wildcard函數和直接使用通配符
*
的差別在于,wildcard函數可以在變量定義的時候立即展開(即搜尋相應的比對項),而
*
隻有在規則中才會被展開。1
subst
sust函數用于替換字元串,格式是
$(subst from,to,text)
,其中from是要被替換的子串,to是要替換成的子串,text是需要被替換的字元串。注意from和to之間的逗号左右不要随意添加空格,否則也會被視為需要替換的内容。
patsubst
patsubst函數可以比對并保留第一個沒被
\
轉義的
%
字元,然後在替換成的字元串裡使用,比如
$(patsubst %.css,%.min.css,hello.css)
替換的結果就是
hello.min.css
,同樣的,逗号左右不要随意添加空白字元。類似的功能還可以用Substitution References實作,文法更簡潔一些。
filter
filter函數可以按一定的規則過濾目标,然後傳回符合規則的資料。
規則
order-only依賴
a: b | c
command
上面的例子中,a是目标,b是正常依賴,c是order-only依賴。當a存在時,即便c的修改時間晚于a,該規則也不會更新a。
order-only依賴定義于規則的右側,與正常依賴用
|
隔開。當目标存在時,不管其是否因order-only依賴而過期,均不更新目标。
static pattern rules
當有大量類似的目标時,static pattern rules會很有用,格式如下
targets ...: target-pattern: prereq-patterns ...
recipe
...
用下面的一個簡單例子說明
a.o b.o c.o: %.o: %.c
gcc -c -o $@ $<
這裡targets是
a.o b.o c.o
,target pattern是
%.o
,prereq-patterns(隻有一個)是
%.c
。target pattern首先從targets裡比對出一個個的目标,然後按照類似于patsubst的方式替換prereq-patterns中的
%
字元,生成相應的依賴。在規則的指令中,可以像普通的規則一樣使用
$<
(第一個依賴)和
$@
(目标)等自動變量。
上面的規則可以拆成幾條簡單的規則:
a.o:a.c
gcc -c -o $@ $<
b.o:b.c
gcc -c -o $@ $<
c.o:c.c
gcc -c -o $@ $<
或者使用字元串替換,更友善一些
source = ${wildcard *.c}
objects = ${source:%.c=%.o}
all: ${objects}
${objects}: %.o: %.c
gcc -c -o $@ $<
其它
特殊的符号
-
: 用于規則中的指令之前,可以在make時隻顯示指令的輸出而不顯示指令的内容,比如@
隻輸出hello,不會輸出all: @echo hello
這條指令的内容。echo hello
傳遞變量
可以在指令行裡設定Makefile的變量值,對如下的makefile
TARGET=test
create:
mkdir -p ${TARGET}
可以通過如下的指令修改TARGET的值:
make create TARGET=another
.PHONY僞目标
盡可能使用
.PHONY
标示所有的僞目标,以避免潛在的問題。
常見的一例問題如下:
debug:
mkdir -p debug
cd debug && make -f ../Makefile
make debug
指令時可能會提示
Nothing to be done for debug
,即debug目标已是最新。然而debug是僞目标,理論上每次執行都應該運作才對。問題出在debug目标的名稱和本地的debug檔案夾同名,使用
.PHONY
辨別debug為僞目标即可解決此問題。
.PHONY: debug
不要濫用空白字元
-
,這裡${objects}變量的值是`a.o b.o '(包含後面的一個空格)。objects = a.o b.o # object files
-
,這裡${str}變量的值是` 123de'(包含開頭的一個空格)。str = ${subst abc, 123,abcde}
-
,這裡${str}變量的值還是str = ${subst abc ,123,abcde}
,因為要替換的子串abcde
(包含最後的空格)沒有找到。abc
例子
假設有以下的場景,在
src
目錄下有許多css檔案,我們需要用yui-compressor将其壓縮并輸出到
output
目錄,壓縮後css的檔案名不變,檔案名字尾由
.css
改為
.min.css
,可以如下定義makefile
# 查找所有的css源檔案
CSS_SOURCE_FILES = ${wildcard src/*.css}
# 建構目标名
CSS_OUTPUT_FILES = ${subst src/,output/,${patsubst %.css,%.min.css,${CSS_SOURCE_FILES}}}
all: ${CSS_OUTPUT_FILES}
${CSS_OUTPUT_FILES}: output/%.min.css: src/%.css
@[ -d output ] || mkdir -p output
yui-compressor -o $@ $<
clean:
rm -rf output
.PHONY: all clean
閱讀資料
- GNU Make自動變量,函數和指令索引
- GNU Make函數說明和清單
- Passing additional variables from command line to make
腳注
- GNU Make Manual: The Function wildcard,引用于2014-05-12。