[轉載請註明出處] http://kezeodsnx.pixnet.net/blog

作者: kezeodsnx

規則(rule)

Makefile是幫助compile的工具,透過Makefile,可以簡化build的流程。特別是當程式愈寫愈大,source file愈來愈多時,更要用Makefile來管理整個project的開發。在這個open source的時代,在某種程度上,Makefile也可用來了解整包source在幹麻,並分析其流程。

範例如下:

CC=gcc
CFLAGS = -g -Wall
.PHONY: clean
objs:=main.o test1.o test2.o
test: $(objs)
        $(CC) -o test ${objs}

main.o: main.c test1.o test2.o
        $(CC) -c main.c

test1.o: test1.c
        $(CC) -c test1.c

test2.o: test2.c
        $(CC) -c test2.c

clean:
        rm *.o test

Makefile是由許多規則組成,其格式為:

Target : [dependence] [dependence2] […]
<tab>[command]
<tab>[command2]
<tab>[…]

所以上述的Makefile有5個rule: test, main.o, test1.o, test2.o 及clean。main.o depend on main.c, test1.o及test2.o。dependence通常是檔案或是object file。depend on的意思是存在與否,若main.c不存在,則該rule就fail。若是object file不存在,則會去尋找是否有該object的rule,並執行。

因此,當make的時候,若無指定target,則會找第一個rule (test)。然後發現main.o不存在,就執行rule main.o的command: $(CC) -c main.c,以產生main.o。依此類推,當所有dependence都存在後,即可完成compile。

一些進階的用法,可讓Makefile更簡潔,不過當然更難懂。上述的Makefile也可以變成下面這個樣子:

CC=gcc
CFLAGS = -g -Wall
.PHONY: clean

objs:=main.o test1.o test2.o
test: $(objs)
        $(CC) -o test ${objs}

%.o: %.c
        $(CC) -c $< -o $@

clean:
        rm *.o test

其中,%是一個字串取代,代表dependence的檔名。比如說,test depend on main.o,main.o的檔名就是main,也就是%所代表的字串,取代完就跟前例是一樣的意思。接下來,$<就是該rule dependence那串的第一個值。在該例中,就是main.c。$@是target的名稱,也就是main.o。因此,可展開如下:

main.o: main.c

        $(CC) -c main.c -o main.o

這樣可以少寫很多一樣的code,這種用法還蠻常見的。

$?表示dependence中,時間比target還新的部份。比如在make時,改了test1.c及test2.c,沒改main.o,則$?會是test1.o及test2.o,因為只有這兩個被重編。

$*就是target,若有副檔名,就是拿掉副檔名後的那串。若沒有,如make clean,則為空字串。

Makefile是設計是當有錯誤發生時停止,以免浪費時間在無意義的編譯。以<tab>開頭的,都會用shell執行,若失敗,即停止編譯。也因此所有指令需在一行完成。

all:

        if [ -f ./status/kernel.compile ] ; then rm ./status/kernel.compile; fi; make

並要小心邏輯上的錯誤:

all:

        cd dir1; make

此情形下,若dir1不存在,一樣會執行make,而造成不可預期的錯誤。此時應該用(cd dir1 && make)。既然是shell,當然也可以用||:cd /test || cd /home && ls

 

 

Timestamp

並非每次make時,所有的rule都會重新compile,而是看時間的先後。比較的對像,是rule及其dependence。當dependence比rule新時,該rule才會執行。大致上是這樣啦,當然,如果dependence的object不存在,就一定是會執行的。

 

變數

變數的assign: "="和" :=",其差異是變數assign的時機。前者(recursively expand variable)是用到時才給值;後者(simply expand variable)是執行到時給值。

var1=aaa

var2=$(var1) 123

var1=bbb

echo $(var2) ==> 要拿來echo了,此時var1的值根據最後結果assign

==>var2為bbb 123

var1=aaa

var2:=$(var1) 123 ==>此時已將var1當時的值assign給var 2

var1=bbb

echo $(var2)

==>var2為aaa 123

另外還有"?=" (conditional assignment)和"+=" (append operator)。這兩個意義就比較直接,前者當變數沒有值(未定義)assign,後者為簡單的append。

var1?=aaa

var1 未定義,var1=aaa;若 var1 已定義,var1不變

var1=aaa

var1+=bbb

則var1為aaa bbb

也可就不同target,對變數設不同的值:

var1=aaa
var2=$(var1) 123
objs:=main.o test1.o test2.o
test1.o: var1=bbb
test2.o: var1=ddd
test: $(objs)
        $(CC) -o test ${objs}

main.o: main.c test1.o test2.o
        $(CC) -c main.c

test1.o: test1.c
        @echo $(var2)==>var2為bbb 123
        $(CC) -c test1.c

test2.o: test2.c
        @echo $(var2)==>var2為ddd 123
        $(CC) -c test2.c

其他

1.

.PHONY: 是無條件執行。為避免當rule與檔名重覆時,被make誤判為該target已存在而沒有執行。如果有個檔案叫clean,在執行make clean時,clean已經存在,因此就不執行。如果用.PHONY: clean,則該rule會無條件執行。

其他的build-in target:

GNU `make': 4.9 Special Built-in Target Names

 

2.

@: 不要顯示該行指令

-: 不要停止編譯,即使該行錯誤

Reference:

http://tetralet.luna.com.tw/index.php?op=ViewArticle&articleId=185&blogId=1

http://www.gnu.org/software/automake/manual/make/

 

 

 

kezeodsnx 發表在 痞客邦 PIXNET 留言(0) 人氣()