一.makefile格式
1.makefile 文件一般命名 為makefile 即可送淆,放在目錄下面税产,直接調(diào)出 terminal ,輸入make 即會執(zhí)行makefile文件的第一個命令,如果需要執(zhí)行制定的命令xxx可以 make xxx.
2.makefile 文件的基本語法:
1.目標
<target>:<prerequisites>
<table><commands>
解釋:target 部分叫目標辟拷,也就是make 執(zhí)行的xxx撞羽;prerequisties 叫著前置條件;第二行必須用table鍵起衫冻,緊接著是具體的執(zhí)行命令诀紊。target 是必須的;prequisites隅俘,和command是可選的邻奠,但是兩者必須有一個。每條指令的意思
例如:
new:
touch a.txt
2.偽目標
如果在當前目錄下已經(jīng)存在 文件,并且文件名(new)和目標(new)是一致的時候,使用make new命令時,并不會執(zhí)行指令.例如下面的輸出,顯示new 已經(jīng)是最新的,并不執(zhí)行new的指令.
為了避免對已經(jīng)存在和目標相同的文件,而不執(zhí)行目標的情況,有一種叫"偽目標"的語法出現(xiàn)了.寫法如下
.PHONY: new
new:
touch new
這樣每次執(zhí)行make new ,都會執(zhí)行.如下:
3.前置條件
前置條件通常是一組文件名为居,以空格分開碌宴。他指定目標是否需要重新構(gòu)建的標準.
1.前置條件如果是空,則每次都會觸發(fā);
2.有一個或者多個前置條件的,只需要有一個前置條件不存在,或者有更新過(通常根據(jù)lastmodification的時間戳比目標的時間戳新)蒙畴,目標就會重新構(gòu)建唧喉。
例如:
result.txt:a.txt
cp a.txt result.txt
a.txt:
echo "這是A" > a.txt
開始時候,沒有創(chuàng)建任何文件忍抽,輸出一下內(nèi)容:
/media/leonzhu/document/工作文檔/makefile$ make
echo "這是A" > a.txt
cp a.txt result.txt
修改下makefile的 內(nèi)容八孝,連續(xù)再次執(zhí)行make 指令,由于a.txt 沒有任何改動鸠项,所以這次目標不會執(zhí)行
result.txt:a.txt
cp a.txt result.txt
a.txt:
echo "這是B" > a.txt
輸出結(jié)果:
/media/leonzhu/document/工作文檔/makefile$ make
make: 'result.txt' is up to date.
查看result.txt 文件干跛,可知里面的內(nèi)容并沒有改變成B.
4.命令
命令默認是用table鍵開頭,緊接著由一行或多行的Shell命令組成命令.
- 如果不想用table開頭怎么辦?
- 如果需要執(zhí)行多個命令怎么辦?
- 如果需要使用上條命令的值怎么辦?
1.考慮到用table鍵開頭,空格可能不習(xí)慣.可以采用
.RECIPEPREFIX = >
all:
>touch all
這里的 > 指的是自己想代替table鍵的字符,理論上可以采用單個任意的非定義的特殊字符.例如"<",")"等等.
2.執(zhí)行多條命令:
.RECIPEPREFIX =>
.PHONY:all
all:
>touch all
>echo "ddasfaafdsa" > all
先創(chuàng)建 all 文件,再往文件里面寫入一些字符串"ddasfaafdsa".
需要注意的是,每一行的命令都是在單獨的shell中執(zhí)行,這些命令之間沒有繼承關(guān)系.
3.獲取前條指令的值的方法,有三種:
- 1 把多條命令寫在一行中,用分號(;)隔開
var-kept:
export foo="foood";echo "foo=[$$foo]";export foo2=$$foo"dd";echo "foo2=[$$foo2]"
輸出結(jié)果:
- 2 用連接符[;\],下條指令按正常格式換行.
var-kept:
export foo="foood";\
echo "foo=[$$foo]";\
export foo2=$$foo"dd";\
echo "foo2=[$$foo2]"
輸出結(jié)果:
- 3 使用 .ONESHELL:
.ONESHELL:
var-kept:
export foo="foood"
echo "foo=[$$foo]"
export foo2=$$foo"dd"
echo "foo2=[$$foo2]"
輸出結(jié)果:
二.makefile 文件語法
2.1 注釋
注釋用# 表示.
2.2 回聲
正常情況下,makefile會在控制臺打印每一條命令,這叫回聲.在第一行命令前增加 @ 符號,就可以關(guān)閉回聲,這樣就不會打印命令.
回聲:
.ONESHELL:
var-kept:
# 這是注釋
export foo="foood"
echo "foo=[$$foo]"
export foo2=$$foo"dd"
echo "foo2=[$$foo2]"
輸出結(jié)果:
沒錯,最后的輸出結(jié)果把所有的命令都輸出了,甚至是注釋文件都出來了.
關(guān)閉回聲的做法
.ONESHELL:
var-kept:
@# 這是注釋
export foo="foood"
echo "foo=[$$foo]"
export foo2=$$foo"dd"
echo "foo2=[$$foo2]"
2.3 通配符
通配符(wildcard)用來指定一組符合條件的文件名。Makefile 的通配符與 Bash 一致祟绊,主要有星號(*)楼入、問號(?)和 [...] 牧抽。比如嘉熊, *.o 表示所有后綴名為o的文件。
clean:
rm -f *.o
2.4 模式匹配
makefile可以用% 來匹配makefile 文件中的目標.%表示任何非空字符串. % 和通配符中的* 的區(qū)別在于他們使用的作用域不一樣:* 指的運用在系統(tǒng)中,而%運用在makefile文件中.
例如:對于模式規(guī)則“%.o : %.c”扬舒,它表示的含義是:所有的.o文件依賴于對應(yīng)的.c文件阐肤。我們可以使用模式規(guī)則來定義隱含規(guī)則。
2.5 變量和賦值
定義變量通常只需要寫變量名=xxx,在后面的調(diào)用中用$(變量) 即可.
#變量
testvar=你好嗎?
printvar:
@echo $(testvar)
如果使用shell變量,需要$$,因為makefile 在執(zhí)行shell變量的時候會對$轉(zhuǎn)義如下
VAR=3
target:
echo $(VAR) #(1)
VAR=4 #(2)
echo $(VAR) # (3)
echo $$VAR # (4)
有時,一個變量可以指向另一個變量:
var1=$(var2)
這里會帶來一個問題var1的值是在定義的時候擴展(靜態(tài)擴展)還是在運行時擴展(動態(tài)擴展).不同的時候擴展結(jié)果會不一樣.
為了解決類似問題讲坎,Makefile一共提供了四個賦值運算符 (=孕惜、:=、晨炕?=衫画、+=),它們的區(qū)別請看
VARIABLE = value
# 在執(zhí)行時擴展瓮栗,允許遞歸擴展削罩。
VARIABLE := value
# 在定義時擴展瞄勾。
VARIABLE ?= value
# 只有在該變量為空時才設(shè)置值。
VARIABLE += value
# 將值追加到變量的尾端弥激。
1> 遞歸展開變量(=):用=或define關(guān)鍵字都可以定義這種變量丰榴,如果變量的定義引用了其它的變量,那么引用會一直展開下去秆撮,直到找到被引用的變量的最新的定義,并以此作為改變量的值返回换况。
var = I love
variable = linux
var += $(variable)
variable = magic
variable = last
allvar:
echo $(var)
這里首先賦值variable=linux,然后var 引用了variable,最后variable=last,由于引用,最后輸出l love last
2>簡單擴展變量(:=) 用這種方式定義的變量,會在定義處按照變量擴展開,值不會在后面隨著變量的賦值而變化.
m = mm
x = $(m)
y := $(x) bar
m=xx
x = later
simpleEqual:;echo $(x) $(y)
由于y變量采用簡單擴展,所以y的值為mm bar,如果后面對y引用的變量值進行改變都不會對y值有影響.例如改變m的值(xx),改變x的值(later) 對后面的$(y)一直是 mm bar.
輸出結(jié)果為
3>可以通過+=為已定義的變量添加新的值
當變量從前沒有被定義過职辨, +=和=是一樣的,它定義一個遞歸展開的變量戈二,但是舒裤,當變量已經(jīng)有定義的時候,+=只是簡單
的進行字符的添加工作觉吭。
如果起初你用:=定義變量腾供,那么+=只是利用變量的當前值進行添加
如果起初用=定義變量,+=的行為就變得有些古怪鲜滩,它并不會在使用+=的地方馬上進行變量展開伴鳖,而是會把展開工作推后,
直到它找到最后變量的定義徙硅,這和=定義變量的行為是類似的
:=情況
m = mm
x := $(m)
y = $(x) bar
m=mm2
y+=y later
simpleEqual:;echo $(x) $(y)
=的情況
m = mm
x = $(m)
y = $(x) bar
m=mm2
y+=y later
simpleEqual:;echo $(x) $(y)
4> ?=
賦默認值榜聂,如果沒有初始化該變量,就給它賦上默認值嗓蘑。如果已經(jīng)賦值了便不會起作用.
# ?=測試
ARCH=arm
ARCH ?= i386
questionEqual:;echo $(ARCH)
2.6 內(nèi)置變量(Implicit Variables)
在隱含規(guī)則中的命令中须肆,基本上都是使用了一些預(yù)先設(shè)置的變量。你可以在你的makefile中改變這些變量的值桩皿,或是在make的命令行中傳入這些值豌汇,或是在你的環(huán)境變量中設(shè)置這些值,無論怎么樣泄隔,只要設(shè)置了這些特定的變量拒贱,那么其就會對隱含規(guī)則起作用。當然佛嬉,你也可以利用make的“-R”或“--no– builtin-variables”參數(shù)來取消你所定義的變量對隱含規(guī)則的作用柜思。例如,第一條隱含規(guī)則——編譯C程序的隱含規(guī)則的命令是“$(CC) –c $(CFLAGS) $(CPPFLAGS)”巷燥。Make默認的編譯命令是“cc”赡盘,如果你把變量“$(CC)”重定義成“gcc”,把變量“$(CFLAGS)”重定義成 “-g”缰揪,那么陨享,隱含規(guī)則中的命令全部會以“gcc –c -g $(CPPFLAGS)”的樣子來執(zhí)行了葱淳。
內(nèi)置變量分兩種:一種是命令相關(guān)的,例如"CC" ;
一種是參數(shù)相關(guān)的如"CPPFLAGS"
1、關(guān)于命令的變量抛姑。
AR
函數(shù)庫打包程序赞厕。默認命令是“ar”。
AS
匯編語言編譯程序定硝。默認命令是“as”皿桑。
CC
C語言編譯程序。默認命令是“cc”蔬啡。
CXX
C++語言編譯程序诲侮。默認命令是“g++”。
CO
從 RCS文件中擴展文件程序箱蟆。默認命令是“co”沟绪。
CPP
C程序的預(yù)處理器(輸出是標準輸出設(shè)備)。默認命令是“$(CC) –E”空猜。
FC
Fortran 和 Ratfor 的編譯器和預(yù)處理程序绽慈。默認命令是“f77”。
GET
從SCCS文件中擴展文件的程序辈毯。默認命令是“get”坝疼。
LEX
Lex方法分析器程序(針對于C或Ratfor)。默認命令是“l(fā)ex”谆沃。
PC
Pascal語言編譯程序移剪。默認命令是“pc”串塑。
YACC
Yacc文法分析器(針對于C程序)。默認命令是“yacc”。
YACCR
Yacc文法分析器(針對于Ratfor程序)凳怨。默認命令是“yacc –r”俺叭。
MAKEINFO
轉(zhuǎn)換Texinfo源文件(.texi)到Info文件程序链峭。默認命令是“makeinfo”赃额。
TEX
從TeX源文件創(chuàng)建TeX DVI文件的程序。默認命令是“tex”卓舵。
TEXI2DVI
從Texinfo源文件創(chuàng)建軍TeX DVI 文件的程序南用。默認命令是“texi2dvi”。
WEAVE
轉(zhuǎn)換Web到TeX的程序掏湾。默認命令是“weave”裹虫。
CWEAVE
轉(zhuǎn)換C Web 到 TeX的程序。默認命令是“cweave”融击。
TANGLE
轉(zhuǎn)換Web到Pascal語言的程序筑公。默認命令是“tangle”。
CTANGLE
轉(zhuǎn)換C Web 到 C尊浪。默認命令是“ctangle”匣屡。
RM
刪除文件命令封救。默認命令是“rm –f”。
2捣作、關(guān)于命令參數(shù)的變量
下面的這些變量都是相關(guān)上面的命令的參數(shù)誉结。如果沒有指明其默認值,那么其默認值都是空券躁。
ARFLAGS
函數(shù)庫打包程序AR命令的參數(shù)惩坑。默認值是“rv”。
ASFLAGS
匯編語言編譯器參數(shù)也拜。(當明顯地調(diào)用“.s”或“.S”文件時)以舒。
CFLAGS
C語言編譯器參數(shù)。
CXXFLAGS
C++語言編譯器參數(shù)搪泳。
COFLAGS
RCS命令參數(shù)。
CPPFLAGS
C預(yù)處理器參數(shù)扼脐。( C 和 Fortran 編譯器也會用到)岸军。
FFLAGS
Fortran語言編譯器參數(shù)。
GFLAGS
SCCS “get”程序參數(shù)瓦侮。
LDFLAGS
鏈接器參數(shù)艰赞。(如:“l(fā)d”)
LFLAGS
Lex文法分析器參數(shù)。
PFLAGS
Pascal語言編譯器參數(shù)肚吏。
RFLAGS
Ratfor 程序的Fortran 編譯器參數(shù)方妖。
YFLAGS
Yacc文法分析器參數(shù)。
2.7自動變量(Automatic Variables)
(1)$@
指代當前目標,就是make中的當前執(zhí)行的目標.例如 make Foo,$@ 表示 Foo
# $@ 測試
touchA.txt touchB.txt:
touch $@
(2)$<
指代第一個前置條件. 比如規(guī)則 p: a1 a2 那么$< 表示a1.
(3) $?
指代比目標更新的(時間戳比目標時間戳大)所有前置條件.
(4) $^
指代所有的前置條件,之間用空格隔開
(5)$*
指代 匹配符中$的部分.
(6)$(@D) ,$(@F)
$(@D) 指代 $@變量的目錄部分,也就是目標的目錄部分.
$(@F) 指代$@的文件部分,也就是目標的文件部分
"$(D)""$(F)"和上面所述的同理罚攀,也是取文件的目錄部分和文件部分党觅。對于上面的那個例子,"$(D)"返回"dir"斋泄,而"$(F)"返回"foo"
"$(%D)""$(%F)"分別表示了函數(shù)包文件成員的目錄部分和文件部分杯瞻。這對于形同"archive(member)"形式的目標中的"member"中包含了不同的目錄很有用。
"$(<D)" "$(<F)"分別表示依賴文件的目錄部分和文件部分炫掐。
"$(^D)" "$(^F)"分別表示所有依賴文件的目錄部分和文件部分魁莉。(無相同的)
"$(+D)" "$(+F)"分別表示所有依賴文件的目錄部分和文件部分。(可以有相同的)
"$(?D)""$(?F)"分別表示被更新的依賴文件的目錄部分和文件部分募胃。
最后想提醒一下的是旗唁,對于"$<",為了避免產(chǎn)生不必要的麻煩痹束,我們最好給$后面的那個特定字符都加上圓括號检疫,比如,"$(< )"就要比"$<"要好一些祷嘶。
dest/%: src/%
@[ -d dest ] || mkdir dest
cp $< $(@D)
這段代碼的意思是把 src中的目標文件拷貝到 dest目錄下,其中目標文件 為 make dest/test.txt 中的test.txt ,因為% 作為makefile文件中的目標匹配,表示 src中的 文件名;
第三行中的 $< 表示前置的第一個元素也就是 src/%==>src/test.txt;
$(@D) 表示目標的 目錄 即dest/% 的目錄==>dest
2.8 判斷和循環(huán)
makefile中的判斷 一般采用ifxxx ... else ... endif 的形式;
條件判斷 有 ifeq ... else ...endif, ifneq ... else ... endif ,ifdef ...else ... endif,ifndef ... else ... endif
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
循環(huán),
for/do/done
LIST = one two three
allLoop:
@for i in $(LIST); do echo $$i; done
while/do/done
# while/do/done
n ?= 0
loopwhile:
@n=$(n); \
while [ $${n} -lt 10 ] ; do \
echo $$n ; \
n=`expr $$n + 1 `;\
done; \
其中@n=$(n) 表示在定義一個命令變量n= 之前的n,所以后面用到的命令n都是這時定義的n,n=expr $$n + 1
表示一個表達式賦值給n,值為n的值+1.結(jié)果如下:
2.9 函數(shù)
makefile 使用函數(shù)的格式
${function arg1 arg2} 或者 $(function arg1 arg2)
常見的內(nèi)置函數(shù)
包括
- 字符串處理函數(shù)
1.字符串替換 $(subst from,to,text)
$(subst ee,EE,feet on the street)
結(jié)果 ‘fEEt on the strEEt’.
2.模式匹配替換 $(patsubst pattern,replacement,text)
$(patsubst %.c,%.o,x.c.c bar.c)
有寫簡寫
比如:$(VAR:PATTERN=REPLACEMENT)
objects = foo.o
testPatsub2:
test=$(objects:.o=.c)
echo $${test}
這里面,看 demo var 變量可以傳多個 字符,但是實際測試無法使用,不清楚原因
https://www.gnu.org/software/make/manual/html_node/Text-Functions.html#Text-Functions