首先介紹make實(shí)用程序的語法和常用選項(xiàng),然后細(xì)剖makefile文件的組成。

make命令

make [選項(xiàng)] [目標(biāo)] [宏定義]
通過命令行參數(shù)中的target,可指定make要編譯的目標(biāo),并且允許同時(shí)定義編譯多個(gè)目標(biāo),操作時(shí)按照從左向右的順序依次編譯target選項(xiàng)中指定的目標(biāo)文件。如果命令行中沒有指定目標(biāo),則系統(tǒng)默認(rèn)target指向描述文件中第一個(gè)目標(biāo)文件。
-d    顯示調(diào)試信息
-n    不運(yùn)行任何makefile文件,只打印需要執(zhí)行的命令
-p    輸出所有宏定義和目標(biāo)文件描述
-s    靜止?fàn)顟B(tài)下運(yùn)行,不顯示任何命令行信息
-f file    通知make程序從file中讀取內(nèi)部依賴說明,缺省情況下會(huì)讀取makefile或者M(jìn)akefile文件處理,文件名-表示讀取標(biāo)準(zhǔn)輸入,在Linux中,GNU make工具在當(dāng)前工作目錄中按照GNUmakefile、makefile、Makefile的順序搜索makefile文件

注意:在源文件沒有被修改的情況下,運(yùn)行make命令會(huì)生成一條消息,說源文件的可執(zhí)行文件是最新的,不需要用make命令重新編譯和鏈接。要強(qiáng)制再創(chuàng)建可執(zhí)行代碼,需要改變?cè)次募纳洗胃聲r(shí)間,可以使用touch命令,然后再次運(yùn)行make命令。

touch [選項(xiàng)] 文件或目錄
-r 文件或目錄  把指定文件或目錄的日期時(shí)間,設(shè)成和參考文件或目錄的日期時(shí)間相同
-d 日期時(shí)間     使用指定的日期時(shí)間,而非現(xiàn)在的時(shí)間 


makefile規(guī)則

make程序基于文件之間的依賴性,需要建立的目標(biāo)文件,以及建立目標(biāo)文件時(shí)要執(zhí)行的命令,以上所有被稱為規(guī)則,存放在文件makefile中。定制規(guī)則的語法如下:
目標(biāo)列表 : 關(guān)聯(lián)性列表
<TAB>命令列表
注意:
1.可以在關(guān)聯(lián)性列表和命令列表中使用shell文件名模式匹配字符,例如?、*、[]等等。
2.如果目標(biāo)的命令列表中某個(gè)命令前面帶有@,那么當(dāng)make程序執(zhí)行時(shí),該命令是不會(huì)有反應(yīng)的,在程序運(yùn)行完畢之后,所有前面帶@的命令按照反序執(zhí)行。可以通過執(zhí)行make -n命令顯示這些命令以供查看。
3.如果目標(biāo)的命令列表中某個(gè)命令前面帶有-,說明如果該命令執(zhí)行有誤,會(huì)跳過該命令并繼續(xù)執(zhí)行。


make程序使用makefile中的規(guī)則決定程序中需要重新編譯的文件,并再次鏈接生成可執(zhí)行代碼。如果源文件上修改的時(shí)間戳比目標(biāo)文件上的時(shí)間戳更新,那么make重新編譯build中包含的源文件。例如,如果修改了一個(gè).h頭文件,make程序就會(huì)重新編譯所有包含該頭文件的源文件,前提是頭文件在這些源文件的目標(biāo)文件的關(guān)聯(lián)性列表中;再如某.c源文件被修改,那么該源文件被重新編譯,生成對(duì)應(yīng)的新的目標(biāo)文件。
myprog : foo.o bar.o
    gcc –o myprog foo.o bar.o
foo.o : foo.c foo.h bar.h
    gcc –o foo.o –c foo.c
bar.o : bar.c bar.h
    gcc –o bar.o –c bar.c
上述規(guī)則中,只要目標(biāo)文件比冒號(hào)后面的文件任何一個(gè)舊,將會(huì)執(zhí)行下一行的命令;但是在檢查foo.o和bar.o的時(shí)間之前,會(huì)往下查找那些把foo.o和bar.o作為目標(biāo)文件的規(guī)則;以此類推,并最終回到myprog規(guī)則。
如何得到每個(gè)C文件的輸出規(guī)則呢?可使用-M和-MM編譯選項(xiàng)。注意:使用-M和-MM編譯選項(xiàng)時(shí),僅在shell中輸出規(guī)則信息,不能用于產(chǎn)生可執(zhí)行文件,即不能寫成gcc -o hello -M hello.c這樣的形式。
gcc –M hello.c            //輸出hello.c和該文件中所有<>和””包含的頭文件
gcc –MM hello.c        //僅輸出hello.c和該文件中所有””包含的頭文件

后綴(隱含)規(guī)則

make -p命令顯示了所有后綴規(guī)則列表。為了建立一個(gè)目標(biāo),make使用程序會(huì)遍歷一連串的依賴關(guān)系,從而決定從何處開始建立。如果沒有找到目標(biāo)文件,make程序按照優(yōu)先順序查找源文件,首先查找.c、.f或.s后綴的文件,然后再查找SCCS(帶.c~后綴)文件,如果沒有找到任何一個(gè)源文件,make程序就會(huì)報(bào)告一個(gè)異常。
make程序知道調(diào)用gcc -c xxx.c -o xxx.o的預(yù)定義命令,而且還知道目標(biāo)文件通常和源文件是相同的,這種功能稱作標(biāo)準(zhǔn)依賴性,所以foo.o : foo.c foo.h bar.h這樣的語句可以簡寫成foo.o : foo.h bar.h。同時(shí),如果把生成foo.o和bar.o的命令從規(guī)則中刪除,make將自動(dòng)查找它的隱含規(guī)則(gcc -M/MM輸出的代碼),然后找到一個(gè)適當(dāng)?shù)拿睿钪袝?huì)使用一些變量,并且按照一定步驟設(shè)定。
因此,上述makefile的內(nèi)容可以根據(jù)后綴規(guī)則簡寫成:
myprog : foo.o bar.o
    gcc –o myprog foo.o bar.o
foo.o : foo.h bar.h
bar.o : bar.h

宏定義(變量)

makefile中的變量定義可以存儲(chǔ)文件名列表、可執(zhí)行文件名以及編譯器標(biāo)識(shí)等,主要是使用如下方法:

VAR=name     變量定義
define VAR   
    name
endef        同上
$(VAR)       使用變量,如果變量名為單字符,可以不使用圓括號(hào)或花括號(hào)
${VAR}       同上
$@           當(dāng)前目標(biāo)文件
$*           刪除了后綴的目標(biāo)名
$<           依賴列表中,比當(dāng)前目標(biāo)更新的當(dāng)前依賴名稱(即第一個(gè)依賴文件)
$^           整個(gè)依賴列表
$?           依賴列表中,比當(dāng)前目標(biāo)更新的當(dāng)前依賴列表
CFLAGS       通常默認(rèn)值是-O,但是可以被修改

未使用后綴規(guī)則的makefile文件變成如下:
OBJS=foo.o bar.o
SOURCES=foo.c bar.c
HEADERS=foo.h bar.h
CC=gcc
CFLAGS=-Wall -O -g
myprog : $(OBJS)
    $(CC) $^ -o $@
foo.o : foo.c foo.h bar.h
    $(CC) $(CFLAGS) –c $< -o $@
bar.o : bar.c bar.h
    $(CC) $(CFLAGS) –c $< -o $@
而使用了后綴規(guī)則的makefile文件變成如下:
OBJS=foo.o bar.o
SOURCES=foo.c bar.c
HEADERS=foo.h bar.h
CC=gcc
myprog : $(OBJS)
    $(CC) S^ -o $@
foo.o : foo.h bar.h
bar.o : bar.h

虛目標(biāo)

假設(shè)一個(gè)項(xiàng)目最后需要產(chǎn)生兩個(gè)可執(zhí)行文件exec1和exec2,但兩個(gè)文件是相互獨(dú)立的,此時(shí)可使用假想目的all來達(dá)到效果。
all : exec1 exec2
all文件并不存在,make總是會(huì)假設(shè)它需要被生成,因此會(huì)檢查它的依靠文件exec1和exec2是否需要更新,當(dāng)把它的依靠文件更新后,就會(huì)執(zhí)行它的規(guī)則里的命令行,但是在規(guī)則里沒有哪個(gè)命令作用于名為all的實(shí)際文件,所以該規(guī)則并不真正改變all的狀態(tài)。
注意下面的語句用法,這些語句可以添加到makefile文件后:
myprog.tar : makefile $(SOURCES) $(HEADERS)
    tar -cvf $@ S^
clean :
    rm *.o
當(dāng)make命令不帶參數(shù)執(zhí)行時(shí),最后兩個(gè)目標(biāo)myprog.tar和clean的命令不會(huì)執(zhí)行,因?yàn)檫@些文件沒有依賴文件。將這兩個(gè)目標(biāo)作為參數(shù)傳遞給make命令,可以調(diào)用與目標(biāo)關(guān)聯(lián)的命令。例如:執(zhí)行make myprog.tar命令會(huì)執(zhí)行tar -cvf myprog.tar makefile foo.c bar.c foo.h bar.h語句,而執(zhí)行make clean命令會(huì)執(zhí)行clean *.o語句。

下面給出一個(gè)較完整的makefile文件:
---------------------------------------------------------
#Updated makefile that uses some built-in macros and
#@-preceded commands
define CC
    gcc
endef
OPTIONS=-03
OBJECTS=main.o input.o compute.o
SOURCES=main.c input.c compute.c
HEADERS=main.h input.h compute.h
complete : power
    @echo "Build complete"
power : $(OBJECTS)
    $(CC) $(OPTIONS) -o $@ $^ -lm
    @echo "The executable is in the 'power' file"
main.o : main.h input.h compute.h
compute.o : compute.h
input.o : input.h
power.tar : makefile $(HEADERS) $(SOURCES)
    tar -cvf $@ $^
clean :
    rm -f *.o
-----------------------------------------------------
執(zhí)行結(jié)果為:
-----------------------------------------------------
$ make
gcc -c main.c -o main.o
gcc -c input.c -o input.o
gcc -c compute.c -o compute.o
gcc -o3 -o power main.o input.o compute.o -lm
The executable is in the 'power' file
Build complete
$ make power.tar
tar -cvf power.tar makefile main.h input.h compute.h main.c input.c compute.c
makefile
main.h
input.h
compute.h
main.c
input.c
compute.c
$ make clean
rm -f *.o
------------------------------------------------------