粗體:語法
一般:範例
VARIABLE_NAME = VARIABLE_VALUE
$( VARIABLE_NAME )
若:
targets = foo
$(targets): common.h
gcc -o $(targets) foo.c
則等同於:
foo: common.h
gcc -o foo foo.c
VARIABLE_NAME := VARIABLE_VALUE
如果用"="的話,make file會將整個檔案展開後再決定最後的變數值,若中途要變更則要用":="
x = foo
y = $(x) bar
x = xyz
# y 的值為 xyz bar
x := foo
y := $(x) bar
x := xyz
# y 的值為 foo bar
Q1:如果混用會發生什麼事?
VARIABLE_NAME ?= VARIABLE_VALUE
如果未定義,則此assign成立,否則無效。
若:
targets ?= foo1
$(targets): common.h
gcc -o $(targets) foo.c
則等同於:
foo1: common.h
gcc -o foo foo.c
若:
targets = foo0 或 targets := foo0
targets ?= foo1
$(targets): common.h
gcc -o $(targets) foo.c
則等同於:
foo0: common.h
gcc -o foo foo.c
VARIABLE_NAME += VARIABLE_VALUE
把變數值加到變數的最後端
CFLAGS = -Wall -g
CFLAGS += -O2
最後 CFLAGS 為 "-Wall -g -O2"
define ... endef
跟"="功能類似,但可以多行
若:
define foo
uname -a
echo $$SHELL
endef
all:
$(foo)
則等同於:
foo = uname -a; echo $$SHELL
all:
$(foo)
上面利用了"$$",讓 "$" 能傳到 Shell 中。
Q2:是類似於"="還是類似於":="?
Q3:"target" 是什麼?為什麼要用all:在裡面重新指定?
echo $(VARIABLE_NAME)
@echo $(VARIABLE_NAME)
把值輸出到 shell
從環境變數中尋找同 VARIABLE_NAME 的變數
wildcard
一般而言,make file中的通用符號會自動展開。
但是在變數定義或函數呼叫時就會失效,所以需要用到wildcard來強制展開。
objects=$(wildcard *.o)
規則(Rule):
make file會依此決定要如何compile
主要架構:
target: dependencies
<Tab>Commands
或:
target: dependencies; Commands
<Tab>Commands
target:所要建立的檔案< /td>
dependencies:相依項目。 make 會據此決定是否要重新編譯 target。
Commands:建立 target 的指令。
target
目標檔案,必須以" : "結尾
foo.o: common.h
gcc -c foo.c
若非檔案,則為 fake 項目,建議用" .PHONY "來指定他是 fake 。
如果未指定,make file會去找這個檔案。
若不巧檔案中又剛好有同名的,make file就會一直以為是up-to-date,此項目永遠不會被執行!
.PHONY: clean
clean:
rm *.o
另外,如果某個非 fake 項目的 target 的 dependencies 包含了 fake 項目的話,因為 make 一定會執行 fake 項目,這樣一來,這個非 fake 項目的 target 一定也會被執行。這可能不是理想的做法。
dependencies
是指定在建立 target 之前,必須先檢查的項目。可以是任意檔案類型(.o,.h...),也可以不指定。
會檢查他們的日期是否比target晚,如果是,就會compile target。
Commands
必須以<tab>開頭。使用 Shell Script 語法。在 Makefile 裡,只要以 <Tab> 開頭都將會被視為 Shell Script 執行。
每條法則必須寫在同一行。每條 Command 會啟動一個新的 Shell,預設為 /bin/sh。若執行完某條 Command 但傳回了錯誤值,make 就會中斷執行。
因為每條 Command 會啟動一個新的 Shell,所以有時執行的指令必須寫在同一行,像是使用 if 來進行條件判斷,此時可以用 ; 來分隔指令。
all:
if [ -f foo ]; then rm foo; fi
錯誤示範:
all:
cd subdir; $(MAKE)
這時因為 make 只會檢查最後一個指令的傳回值,所以在以上指令中,即使 subdir 不存在,但 make 並不會因而中斷執行,並會繼續執行 $(MAKE) 指令,而產生了不可預期的結果。
為了避免這個問題,可以利用 && 來檢查其中某個指令是否成功執行,再決定是否執行下個指令。
all:
cd subdir && $(MAKE)
@
不要顯示執行的指令。
-
表示即使該行指令出錯,也不會中斷執行。
.PHONY: clean
clean:
@echo "Clean..."
-rm *.o
特殊規則:
foo.o: common.h
gcc -c foo.c
因為是用gcc來編譯,所以可以簡化為:
foo.o: common.h
若不想啟動這個規則,則使用空白命令放在最後一行
foo.o: common.h
<tab>
內部變數:
$?:
代表已被更新的 dependencies 的值。也就是 dependencies 中,比 targets 還新的值。
$@:代表 targets 的值。
$<:代表第一個 dependencies 的值。
$* :代表 targets 所指定的檔案,但不包含副檔名。
內部函數:
使用make時所支援的函數
http://www.gnu.org/savannah-checkouts/gnu/make/manual/html_node/Functions.html
條件判斷:
ifeq
比較變數1&2是否相等
ifneq
ifdef
檢查此變數是否為空
ifndef
ifeq (value1, value2)
...
else
...
endif
ifdef variable
...
else
...
endif
引入檔案:
把此檔案內容全部插入此make file中
可以同時引入多個檔案,也可以使用萬用字元。
include foo.in common*.in $(MAKEINCS)
子目錄:
如果該專案有多個目錄,且每一個目錄中都有 Makefile,則利用以下指令來進入子目錄並進行編譯。
cd dir && $(MAKE)
範例:
SUBDIRS = dir1 dir2 dir3
all:
for i in $(SUBDIRS); do
(cd $$i && make);
done
clean:
for i in $(SUBDIRS); do
(cd $$i && make clean);
done
install:
for i in $(SUBDIRS); do
(cd $$i && make install);
done
呼叫Make時可用的參數:
可以在shell呼叫make時直接用指令改變參數:
make CFLAGS="-g -O2"
若不想被改變,可在make file中加入:
override CFLAGS = -Wall -g
也可以指定要編譯的 target
make clean
A1:
A2:
A3:
參考資料:
今天的 Tetralet 又在唧唧喳喳了:http://tetralet.luna.com.tw/?op=ViewArticle&articleId=185