Makefile的學習筆記

1    Makefile

GNU make的工做分爲兩個階段。在第一階段,make讀取makefile文件、內置變量及其值、隱含規則和具體規則、構造全部目標的依賴關係以及全部目標各自的依賴等。在第二階段,make決定須要從新構造的目標並使用必要的規則進行工做。shell

make工做第一階段發生的擴展是當即擴展,直接把變量和函數擴展爲makefile文件語句的一部分。make工做第二階段發生的擴展稱爲延時擴展。數據庫

1.1         make命令的使用

make[option] [target]express

 

-Cdir   :在讀入makefile以前,把路徑切換到dir下。若是同時使用幾個‘-C’選項,則eachis interpreted relative to the previous one。app

-d          :在正常處理後打印調試信息。ide

-e          :設置環境變量的優先權高於makefile文件變量的優先權。函數

-ffile    :將file設置爲makefile文件。ui

-i           :忽略在執行重建文件命令時產生的全部錯誤。spa

-Idir    :指定搜尋makefile文件的路徑。若是同時使用幾個‘-I’選項,則按照次序搜尋這些路徑。命令行

-k          :在出現錯誤後,儘量的繼續執行。也就是說當一個目標建立失敗後,全部依靠它的目標文件將不能重建,而這些目標的其它依賴則可繼續處理。debug

-n          :打印要執行的命令,但卻不執行它們。

-ofile    :即便文件file比它的依賴舊,也不重建該文件。

-p          :打印數據庫,其中的數據來自讀入makefile文件的結果;打印以後執行。

make –qp                           打印數據庫後不執行。

make –p –f/dev/null       打印預約義的規則和變量的數據庫。

-q          :不打印也不執行命令。若是全部目標都已經更新到最新,make的退出狀態是0;若是一部分須要更新,退出狀態是1;若是make遇到錯誤,退出狀態是2。

-r          :禁止使用預約義的隱含規則,同時也清除了缺省的後綴列表和後綴規則。注意缺省的變量仍然有效。

-R         :禁止使用內建的規則變量。‘-R’自動使‘-r’生效。

-s          :不回顯執行的命令。

-S          :使‘-k’失效。除非在遞歸調用make時,經過變量MAKEFLAGS從上層make繼承‘-k’,或環境中設置了選項‘-k’,不然沒有必要使用該選項。

-t           :標記文件已經更新到最新,但實際卻沒有更新它們。

-w         :打印執行makefile文件時涉及的全部工做目錄。

-Wfile  :Pretendthat the file has been just modified。在和‘-n’一塊兒使用時,將代表更改該文件會發生什麼。若是沒有和‘-n’一塊兒使用,那麼它和在運行make以前對該文件使用 touch命令的結果幾乎同樣,但使用該選項make只是在想象中更改該文件的時間戳。

1.2         include的使用

缺省狀況下,make按順序尋找makefile文件:GNUmakefile、makefile和Makefile。若是使用非標準名字的 makefile文件,可使用‘-f’參數指定makefile文件。若是使用多個‘-f’參數,全部的makefile文件按順序發生做用。一旦使用 了‘-f’參數,將再也不自動檢查是否存在標準名字的makefile文件。

include指令告訴make暫停讀取當前的makefile文件,先讀完include指定的makefile文件後再繼續。include指令在makefile文件佔單獨一行,其格式以下:

includefilename

行首的多餘空格是容許的,注意不能以Tab開始。由於,以Tab開始的行,make認爲是命令行。多個文件名之間也以空格隔開,多餘的空格被忽略。文件名能夠包含變量及函數調用,它們在處理時由make進行擴展。

1.3         環境變量MAKEFILES

若是定義了環境變量MAKEFILES,make認爲該變量的值是一列附加的makefile文件名,文件名之間由空格隔開,而且這些 makefile文件應該首先讀取,讀取方式和include指令的方式基本相同。值得注意的是,缺省最終目標不會出如今這些makefile文件中,而 且若是一些makefile文件沒有找到也不會出現任何錯誤信息。

1.4         搜索目標/依賴文件

正常狀況下,雖然目標/依賴文件名中不含有路徑,其實這種文件名的路徑部分是空值,表明的是當前目錄(./)。若是咱們要在文件名中給出路徑,則有兩種方法:

一、絕對路徑,必須以‘/’開頭;

二、相對路徑,非‘/’開頭的全部路徑。

1.4.1       變量VPATH

變量VPATH定義了一組搜索路徑。若是一個文件在文件名給出的路徑中不存在,就在(VPATH+文件名中的路徑)的路徑中搜索該文件。

在VPATH定義中,路徑的名字由冒號或空格分開。

1.4.2       命令vpath

1.4.2.1     vpath pattern directories

vpath容許對符合某一格式的文件名指定一個搜尋路徑,路徑由冒號或空格隔開。pattern是一個包含‘%’的字符串,例如:

vpath%.h $(INCDIR)

若是有多個vpath和一個文件名匹配,則make按次序搜索這些vpath指定的路徑。

1.4.2.2     vpath pattern

清除和該格式相關聯的搜尋路徑。

1.4.2.3     vpath

清除前面全部由vapth指定的搜尋路徑。

1.4.3       變量GPATH

1.       若是目標文件是根據文件名搜索獲得的,此時$@表明文件名。

2.       若是目標文件是經過搜索其它路徑獲得的,此時$@表明文件名。可是若是搜索路徑出如今變量GPATH中,$@將表明(搜索路徑+文件名)。

3.       若是目標文件沒有被搜索到,意味着須要執行規則命令來建立該目標文件,此時$@表明文件名。

4.       若是依賴文件是根據文件名搜索獲得的,並且該依賴文件不須要被更新,此時$<表明文件名。

5.       若是依賴文件是經過搜索其它路徑獲得的,並且該依賴文件不須要被更新,此時$<表明(搜索路徑+文件名)。

6.       若是依賴文件沒有被搜索到或是須要被更新,此時$<表明的是當它做爲目標文件被建立或更新的規則命令中的$@。

2    規則的使用

makefile的基本單位是規則。每一條規則說明一個目標,除了描述該目標所依賴的文件,還要指明生成和更新該目標所需的命令。規則的格式爲:

targetsruleop [prerequisites] [;recipe]

{<tab>recipe}

 

targets  :目標文件列表。

ruleop  :分割目標文件與依賴文件的符號,常見的是‘:’也可使用其它一些符號。

‘::’用於一個目標有多個規則的情形;

‘:^’將依賴文件和目標文件已有的依賴文件合起來,成爲新的依賴文件列表;

‘:-’清除目標文件原有的依賴文件,將新的依賴文件做爲目標的依賴文件列表;

‘:!’對每一個更新過的依賴文件都執行一次命令菜單;

‘:|’只在隱含規則中使用。

prerequisites:目標所依賴的文件列表。

recipe:命令菜單,從新生成目標文件的命令,能夠在tab以後加上如下符號。

-:忽略本命令行的錯誤返回,繼續執行。不然,make會中止執行;

+:make始終執行本命令行;

@:執行本命令行時不在標準輸出上顯示。

makefile文件的一些書寫規則:

1.每個命令行的開頭都要是一個tab;

2.命令行之間能夠插入任意多個空行,這些空行也要以tab開頭;

3.第一條命令能夠直接跟在依賴文件列表後面,用‘;’隔開;

4.若是一行過長,能夠在到達右邊界以前放入一個「\」符號,並且「\」和新的一行之間不能有空白;

5.註釋以‘#’開始,以換行符結束,若是其它地方用到‘#’,要用雙引號引用;

6.makefile中涉及的文件名容許使用通配符。

 

規則不論其形式如何,都按相同的方式擴展:

immediate: immediate ; deferred

deferred

目標和依賴部分都當即擴展,命令一般都是延時擴展。

通常狀況下規則的次序可有可無,但決定缺省最終目標時倒是例外。缺省最終目標是沒有指定最終目標時make指定的最終目標。缺省最終目標是 makefile文件中的第一條規則的目標。若是第一條規則有多個目標,只有第一個目標被認爲是缺省最終目標。因此,咱們編寫makefile文件時,通 常將第一個規則的目標定爲編譯所有程序(設定一個稱爲‘all’的目標)。

有兩種例外的狀況:以‘.’開始的目標不是缺省最終目標;格式規則定義的目標不是缺省最終目標。

2.1         格式規則

格式規則是指定多個目標並可以根據每一個目標名構造對應的依賴名的規則。要使用格式規則,目標文件名必須匹配目標格式,並且符合依賴格式的文件必須存在或能夠建立。

格式規則的語法格式:

targets...: target-pattern: dep-patterns ...

commands

...

目標能夠含有通配符。target-pattern表示目標格式,dep-patterns表示依賴格式,一般都包含字符‘%’。目標格式匹配目標時,‘%’表明的字符串稱爲徑(stem)。每一個依賴名使用徑代替‘%’。在格式規則中使用的‘%’擴展是在全部變量和函數擴展之後進行的,而全部的變量和函數擴展都是在makefile文件讀入時完成的。

當目標格式中不包含‘/’,目標文件名中的路徑名部分首先被去除,而後再進行匹配,最後路徑名將會加在產生的依賴前面。

 

在依賴格式中不包含‘%’也是合法的,此時對全部目標來講,依賴是相同的。

OBJ= hello1.o hello2.o

all:$(OBJ)

$(OBJ):%.o: %.c

……

上面的規則展開後等價於下面兩條規則:

hello1.o: hello1.c

……

hello1.o: hello2.c

……

每個目標必須和目標格式匹配,若是不符則產生警告。若是您有一系列文件,其中僅有一部分和格式匹配,您可使用filter函數把不符合的文件移走。

FILE= test. c test1.o test2.o

$(filter %.o, $(FILE) ): %.o: %.c

……

若是一個目標文件符合對於多個格式規則,使用最先出現的格式規則。本身編寫的格式規則比預約義的隱含規則優先。

格式規則只能用於規則中指定的目標。而隱含規則能夠應用於任何與它匹配的目標,前提是這些目標沒有與之對應的規則,並且隱含規則中對應的依賴也能夠被搜尋到。若是有多條隱含規則適合,僅執行其中一條規則,按照隱含規則定義的順序進行選擇。

2.2         隱含規則

若是根據依賴推斷出某一個目標文件須要更新,而對應的規則卻沒有給出命令,這時make就會從隱含規則中查找符合該目標格式的隱含規則,並根據該隱 含規則生成對應的依賴文件(注意隱含規則使用的依賴文件名是隱含規則根據目標格式本身生成的,而不是使用目標文件原有的依賴文件),而後用隱含規則中的命 令來更新改目標文件。

若是一條規則的其中一個依賴文件沒有經過路徑搜索找到,則make會嘗試使用隱含規則來建立該文件,若是沒有對應的隱含規則能夠建立該文件,就會返回error。

2.2.1       預約義隱含規則

能夠在makefile文件中重載這些隱含規則。也能夠取消預約義的隱含規則,只要不在後面寫命令就能夠了。make命令的參數‘-r’也能把缺省的後綴列表清空,從而刪除全部預約義的隱含規則。

若是預約義的隱含規則的依賴出如今後綴列表中,則該預約義的隱含規則也稱爲後綴規則。若是更改了後綴列表,那些依賴的後綴沒有出如今新後綴列表中的預約義的隱含規則將被禁止。

後綴是特殊目標.SUFFIXES的依賴名。例如:

.SUFFIXES:                          # 刪除缺省的後綴列表

.SUFFIXES:.cpp .obj          #把.cpp和.obj添加到後綴列表中。

2.2.2       隱含規則鏈

有時生成一個文件須要使用多個隱含規則組成的序列,這樣的隱含規則序列稱爲隱含規則鏈。同一條隱含規則不能在隱含規則鏈中出現兩次或兩次以上。

一般狀況下,任何在makefile文件中說起的目標和依賴都不是中間文件。可是,咱們能夠特別指定一些文件爲中間文件,只需將這些文件指定爲特殊目標.INTERMEDIATE的依賴。若是.INTERMEDIATE沒有依賴文件,它將不會發生做用。

爲了阻止自動刪除中間文件,能夠將須要保留的中間文件指定爲特殊目標.SECONDARY的依賴。.SECONDARY的依賴被處理爲中間文件,但它們永遠不能自動刪除。若是.SECONDARY沒有依賴文件,則全部的目標都將被處理爲中間文件。

也能夠將一個隱含規則的目標格式做爲特殊目標.PRECIOUS的依賴,這樣就能夠保留那些由該隱含規則建立的中間文件。

2.2.3       後綴規則

後綴規則已經被格式規則代替,分爲單後綴和雙後綴規則。

雙後綴規則:                               等同於格式規則:

.c.o:                                                %.o: %.c

……                                                ……

單後綴規則:                               等同於格式規則:

.c                                                    %: %.c

……                                                ……

後綴規則不能有任何屬於它們本身的依賴,而格式規則能夠有依賴文件。例如:

%.o:%.c hello.h

……

沒有命令的後綴規則沒有意義,它們並不像沒有命令的格式規則那樣移去之前的規則。

2.3         假想目標

假設一個項目最後須要產生兩個可執行文件,並且這兩個文件是相互獨立的,可使用假想目標來達到這種效果。一個假想目標跟一個正常的目標幾乎是同樣的,只是這個目標文件是不存在的。例如在makefile的最開始輸入:

default: exec1 exec2

make把default作爲它的主要目標,每次執行時都會嘗試把default更新。可既然這個文件並不存在,make就會嘗試運用該規則建立default,就檢查它的依賴exec1,exec2是否須要更新,若是須要,就把它們更新,從而達到咱們的目的。

假想目標也能夠用來描述一組動做。例如,須要把全部make產生的文件刪除,你能夠在makefile裏設立這樣一個規則:

clean:

rm *.o

使用命令「make clean」,make就會把clean作爲它的主要目標,執行rm命令。可是若是恰巧存在一個名字爲clean的文件,由於在這個規則裏沒有任何依賴文 件,因此這個目標文件必定是最新的了,因此即便用戶使用命令「make clean」,也不會有任何事情發生。解決方法是標明全部的假想目標爲.PHONY,這就告訴make不用檢查它們是否存在,也不用查找任何隱含規則,直接假設指定的目標須要被更新。例如在makefile里加入下面這行規則:

.PHONY: clean

當假想目標A是假想目標B的依賴,則A將做爲B的子程序,並且是先執行A中的命令,再執行B中的命令。例如,這裏‘make cleanall’用來刪除全部文件:

.PHONY:cleanall cleanobj cleansrc cleanheader

cleanall:cleanobj cleansrc clearnheader

cleanobj:

rm *.o

cleansrc:

rm *.c

cleanheader:

rm *.h

2.4         沒有命令和依賴的規則

若是一個規則只有目標,沒有依賴,也沒有命令,並且該規則的目標也不存在,則make認爲只要該規則運行,其目標就已被更新。這意味着全部以這種規則的目標爲依賴的規則,它們的命令將總被執行。

cleanobj: FORCE

rm*.o

FORCE:

目標FORCR知足上面的條件,因此以其爲依賴的目標clean將總執行命令。

還有一個辦法能夠強制執行一條規則中的命令,只要該規則的目標是假想目標,並且該假想目標沒有依賴,並且該假想目標是主要目標的依賴,這樣的話該規則的命令就必定會被執行。

.PHONY:all cleanobj

all:cleanobj ……

……

cleanobj:

rm *.o

2.5         內建的特殊目標名

.DEFAULT

.DEFAULT指定一些命令,這些命令應用於那些沒能找到規則的目標。

2.6         具備多個目標的規則

具備多個目標的規則等同於多條獨立的規則,這些規則除了目標不一樣以外,其他部分徹底相同,意味着全部的目標有相同的依賴,相同的命令應用於全部目標,能夠在命令中使用‘$@’區分實際的目標名稱。

2.7         具備多條規則的目標

當一個目標出如今多條規則中時,全部的規則必須是同一類型:要麼都是雙冒號規則,要麼都是普通規則。

若是它們都是雙冒號規則,則它們之間都是相互獨立的。雙冒號規則實際就是將具備相同目標的多條規則相互分離,每一條雙冒號規則都獨立的運行,就像這些規則的目標不一樣同樣。每個雙冒號規則都應該指定命令,若是沒有指定命令,則會使用隱含規則。

若是它們都是普通規則。則在全部規則中說起的依賴將合併在一個依賴列表中。若是該目標比任何一個依賴舊,命令將被執行來重建該目標。可是若是有一條以上的規則爲該目標文件指定命令,make將使用最後給出的規則,同時打印錯誤信息。

2.8         自動生成依賴

對每個源程序文件name.c有一個名爲name.d的文件和它對應,該文件中列出了name.o所依賴的文件。根據name.c生成name.d的格式規則:

DEPEND=dependencies

depend:

      @set -e;\

      $(CC) -MM $(SRC)>$(DEPEND)

而後就可使用include命令直接將DEPEND文件讀入。

include$(DEPEND)

2.8.1       sed用法簡介

2.8.1.1     s/regularexpression/replacement/flags

用replacement string替換符合regularexpression的字符串,用法中的‘/’能夠用任何其它字符代替,在下面的例子中咱們就使用了‘,’。

flags is:

n Substitute for just the nth occurrence ofthe regular expression.

g Globally substitute.

| sed 's,.\.c[ :]*,\1.o:,g'>

能夠將輸入中以.c結尾的字符串都置換爲以.o的字符串,而後再輸出。

2.8.1.2     re

Defines a sub-expression re. A subsequent reference of ‘\n’,where n is a number in the range 1–9。若是re是一個regularsub-expression,則‘\n’擴展爲第n個re匹配的字符串。若是re不是一個regular sub-expression,則‘\n’擴展爲第n個re自己。

2.8.1.3     *

Matches the single-character regularexpression or sub-expression immediately preceding it zero or more times. If *is the first character of a regular expression or sub-expression, it matchesitself.

2.8.1.4     [char-class]

Matches any single character in char-class.To include a ] in char class, it must be the first character. A range ofcharacters may be specified by separating the begin and end characters of therange with ‘-’, for example, a-z specifies the lowercase characters.

3    規則中的命令

規則中的命令由一系列shell命令行組成,它們按順序執行。除第一條命令行能夠用分號附屬在目標-依賴行後面外,全部的命令行必須以Tab開始。空白行與註釋行可在命令行中間出現,處理時它們被忽略。可是必須注意,以Tab開始的空白行不是空白行,它是而命令。

3.1         遞歸調用

能夠在makefile文件中將make做爲一個命令使用。例如,假設有一個子目錄subdir,該目錄中有它本身的makefile文件,能夠按下述方式編寫:

subsystem:

cd subdir && $(MAKE)

或者:

subsystem:

$(MAKE) -C subdir

注意遞歸調用make的命令老是使用變量MAKE。

缺省方式下,只有環境變量或在命令行中定義的變量才能傳遞給內層make。若是要將主makefile中的變量輸出給子make,用export指令,格式以下:

exportvariable ……

要將阻止一些變量輸出給子make,用unexport指令,格式以下:

unexportvariable ……

能夠同時定義並輸出一個變量:

exportvariable = value

若是您要將全部的變量都輸出,您能夠單獨使用export。這就告訴make把export和unexport沒有說起的變量通通輸出,但 unexport說起的變量仍然不能輸出,並且名字中含有字母、數字和下劃線之外字符的變量將不能輸出,這些變量只能使用export指令才能輸出。

須要注意的是,有兩個變量,一個是SHELL,一個是MAKEFLAGS,這兩個變量無論是否export,其老是要傳遞到下層makefile中。

參數‘-C’,‘-f’,‘-o’和‘-W’即便在MAKEFLAGS中也不能向下傳遞。在命令行中使用的參數也將經過MAKEFLAGS傳遞給子make。若是你不想往下層傳遞參數,你能夠:

subsystem:

cd subdir && $(MAKE) MAKEFLAGS=

變量MAKELEVEL的值在向下層傳遞時發生變化。該變量的值是字符型,它用十進制數表示層的深度。‘0’表明頂層make,‘1’表明子make,‘2’表明子-子-make,以此類推。

若是您使用幾層make遞歸調用,使用‘-w’能夠顯示每一個make開始處理以及處理完成的目錄。當使用‘-C’選項時,‘-w’選項已經自動打開。

3.2         定義固定次序的命令(定義多行變量)

可使用define指令定義固定次序的命令。若是前綴字符(@,-,和+)在引用固定次序的命令行中使用,則該前綴字符將應用到固定次序的每一行中。固定次序實際是一個變量,所以它的名字不能和其它的變量名衝突。

definename

……

endef

使用:

$(name)

4    變量的使用

在makefile文件讀入時,除規則命令中的變量、‘=’右邊的變量、以及使用define定義的變量此時不擴展外,makefile文件其它各個部分的變量和函數都將擴展。

變量名不能含有‘:’、‘#’、‘=’;前導或結尾空格。變量名是區分大小寫的。

make將忽略環境變量SHELL的值,因此若是要使用變量SHELL,咱們必須本身定義。

4.1         變量的定義

變量名中也能夠包含變量引用和函數調用,它們在讀入時擴展。

除了空格,‘=’前面不能有tab或其它任何符號,不然都會被make看成變量名的一部分。‘=’後面的字符串能夠包含任意字符,但會去除前面的全部空格和tab。

注意:變量值包含全部的結尾空格。

變量的定義能夠出如今三個地方:

1.在makefile中定義;

2.在make命令行中定義。因爲命令行中用空格做爲不一樣參數的分隔符,所以當變量值中含有空格時,要用引號將整個變量值包含起來。

3.載入環境變量定義。

make命令行變量定義>makefile中變量定義>shell中變量定義>內定義變量定義

4.1.1       immediate = deferred / define

遞歸調用擴展型變量。適用於那些須要根據上下文才能肯定值的變量的定義。

若是該變量值包含對其它變量的引用,這些引用在變量替換時才被擴展。若是在定義中使用函數,則函數只有在完成變量替換時纔會執行。

4.1.2       immediate ?= deferred

僅在變量尚未定義的狀況下有效。

4.1.3       immediate := immediate

簡單擴展型變量,在定義的時候變量的值就肯定了。適用於那些可以在一開始就肯定值的變量的定義。

若是該變量值包含對其它變量的引用,這些引用在定義的時候就已經展開。若是在定義中使用函數,則函數在定義的時候就已經執行。

4.1.4       immediate += deferred orimmediate

變量原來的值加上一個空格,再加上後面的字符串,做爲新的變量值。

若是變量在之前沒有定義過,則‘+=’的做用和‘=’相同。若是變量在之前定義過,‘+=’的做用依賴於原始定義的特點。

4.1.5       特定目標變量的值

make中變量的值通常是全局性的,特定目標變量的值是個例外:

target... : variable-assignmen

或這樣:

target... : override variable-assignmen

variable-assignmen使用任何賦值方式都是有效的:=、:=、+=、?=。在命令行中定義的變量值優先,而使用override定義的變量值優先。

當定義一個特定目標變量時,該變量值對特定目標target ...的全部依賴有效,除非這些依賴用它們本身的特定目標變量值將該變量重載。

4.1.6       特定格式變量的值

使用特定格式變量的值,能夠爲匹配指定格式的目標定義變量。

pattern... : variable-assignment

或這樣:

pattern... : override variable-assignment

這裏的pattern是‘%’格式。

variable-assignmen使用任何賦值方式都是有效的:=、:=、+=、?=。在命令行中定義的變量值優先,而使用override定義的變量值優先。

4.2         變量的引用

$(變量名)

當變量只有單個字符時,能夠省略括號。

${SRC:oldend=newend}

若是SRC中某個字以字符串「oldend」的結尾,則將「oldend」替換爲「newend」。

例如:OBJ=${SRC:.c=.o}

${SRC:oldpattern=newpattern}

pattern中包含‘%’,將SRC中的oldpattern替換爲newpattern,‘%’表明的部分不改變。

例如:OBJ=$(SRC:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

4.3         內定義變量

4.3.1       用於規則命令

$@              目標文件名,若是目標是形如libname(member)的庫成員,$@等於庫名libname。

$%       目標文件名,若是目標是形如libname(member)的庫成員,$%等於成員名member。注意此時$%再也不遵循目錄搜索的規則,只能表明member。

$>                目標文件名,只能用於目標文件是庫成員的情形,$>等於庫名libname。

$*                若是目標名以一種內建的後綴結尾,$*爲目標名去掉其後綴的部分。若是目標名不之內建的後綴結尾,則$*在該規則中設置爲空值。

 

用於表示依賴文件的內建變量不區分庫名和成員名,仍然表明整個libname(member)。

$<                第一個依賴文件名。

$?                全部依賴文件中比目標文件新的文件列表。

$^         目標文件在本規則中的全部依賴文件,省略了重複的依賴。

$+         目標文件在本規則中的全部依賴文件,保留了重複的依賴。

$&               目標文件在全部規則中的全部依賴文件;

 

如下兩個內定義變量對以上變量通用(#能夠是@,*,%,>,&,^,+,<,?):

$(#D)    $#的路徑部分,結尾‘/’已經移走。若是$?不包含‘/’,則值是‘.’。

$(#F)    $#的文件名部分

4.3.2       用於依賴文件列表

$$@     目標文件名,若是目標是形如libname(member)的庫成員,$$@等於庫名libname。

$$%     目標文件名,若是目標是形如libname(member)的庫成員,$$%等於成員名member。

$$*       若是目標名以一種內建的後綴結尾,

*在該規則中設置爲空值。

$$>       目標文件名,只能用於目標文件是庫成員的情形,等於庫名libname。

4.4         文件名和文件路徑變量的使用

d    僅展開路徑,不包括文件名

b    展開文件名,不包括擴展名

f     展開文件名,包括擴展名

 

FILE=/user/myprog/main.c

$(FILE:d)          =     /user/myprog

$(FILE:b)          =     main

$(FILE:f)          =     main.c

$(FILE:db)              =     /user/myprog/main

$(FILE:df)        =     /user/myprog/main.c

文件名前面沒有任何目錄的文件被認爲是位於當前工做目錄下,路徑名爲‘.’。

5    通配符的使用

make中的通配符是*、?和[…]。通配符在規則中(目標、依賴和命令)能夠正常使用,但在變量定義中或在函數的參數中通配符通常不能正常使用。

objects= *.o

變量objects的值實際就是字符串「*.o」。

若是通配符前面是反斜槓‘\’,則該通配符失去通配能力。

6    makefile文件的條件語句

在指令行前面容許有多餘的空格,可是不容許有Tab。若是一行以Tab開始,那麼該行將被認爲是規則的命令行。

6.1         條件語句的語法

對於沒有else指令的條件語句的語法爲:

conditional-directive

……

endif

完整的條件語句的語法爲:

conditional-directive

……

else

……

endif

6.1.1       ifeq (arg1, arg2) /ifneq (arg1, arg2)

擴展參數arg一、arg2中的全部變量引用,而且比較它們。

6.1.2       ifdef variable/ ifndef variable

7    文本轉換函數

函數調用的格式以下:

$(functionarg1, arg2, ...)

或這樣:

${functionarg1, arg2, ...}

參數和函數名之間是用空格或Tab隔開。每個參數通過變量替換或函數調用處理,最終獲得參數的值。注意變量替換是按照變量在參數中出現的次序依次進行的。

逗號,空格和不成對出現的圓括號、大括號不能做爲文本出如今參數中,若是須要使用這些字符,首先定義變量comma和space:

comma:=,

empty:=

space:=$(empty)$(empty)

7.1         字符串替換和分析函數

7.1.1       $(subst from,to,text)

在文本text中使用to替換每一處from。

7.1.2       $(patsubstpattern,replacement,text)

尋找text中符合pattern的字,並用replacement替換它們。這裏的pattern中包含‘%’。若是replacement中也含有‘%’,就用和pattern中‘%’匹配的部分代替。

7.1.3       $(strip text)

去掉前導和結尾空格,並將字中間的多個空格壓縮爲單個空格。

7.1.4       $(findstring string,text)

在text中搜尋string,若是找到返回值是string,不然返回值爲空。

7.1.5       $(filter pattern...,text)

返回在text中由空格隔開且匹配pattern...的字,並將不符合pattern...的字移出。

7.1.6       $(filter-out pattern...,text)

filter的反函數。

7.1.7       $(sort text)

將text中的字按字母排序,並取掉重複的字。輸出的是由空格隔開的字的列表。

7.2         文件名函數

函數的參數是一組文件名,文件名之間用空格隔開(前導和結尾空格被忽略)。列表中的每個文件名都採用相同的方式轉換,並且結果用單個空格串聯在一塊兒。

7.2.1       $(dir names...)

抽取每個文件名的路徑部分,文件名的路徑部分包括從文件名的開始到最後一個斜槓(含斜槓)以前的一切字符。若是文件名中沒有斜槓,路徑部分是「./」。

7.2.2       $(notdir names...)

抽取每個文件名中除路徑部分外一切字符(文件名)。若是一個文件名僅包含路徑部分(以‘/’結束),則結果爲空。

7.2.3       $(suffix names...)

抽取每個文件名的後綴。若是文件名沒有後綴,則結果爲空。

7.2.4       $(basename names...)

抽取每個文件名中除後綴外一切字符。

7.2.5       $(addsuffix suffix,names...)

將suffix附加在每個文件名的後面。

7.2.6       $(addprefix prefix,names...)

將preffix附加在每個文件名的前面。

7.2.7       $(join list1,list2)

兩個參數的第一個字串聯起來造成結果的第一個字,兩個參數的第二個字串聯起來造成結果的第二個字,以此類推。若是一個參數比另外一個參數的字多,則多餘的字原封不動的拷貝到結果中。

7.2.8       $(word n,text)

返回text中的第n個字。n的合法值從1開始。若是n超出範圍,則返回空值。

7.2.9       $(wordlist s,e,text)

返回text中從第s個字開始到第e個字結束的字。s、e的合法值從1開始。若是s超出範圍,則返回空值;若是e超出範圍,則返回從第s個字開始到text結束的全部字;若是s比e大,不返回任何值。

7.2.10  $(words text)

返回text中字的數目。例如能夠獲得text中的最後一個字:

$(word$(words text),text)

7.2.11  $(firstword text)

返回第一個字。

7.2.12  $(wildcard pattern)

pattern是一個文件名格式,能夠包含通配符,也能夠包含有目錄。wildcard函數將返回和pattern相符合的全部文件名。例如能夠獲得全部的源文件:

SRC=$(wildcard$(SRCDIR)/*.c)

7.3         函數foreach

$(foreachvar,list,text)

把list中的字逐一取出賦給變量var,而後再執行text。每一次text會返回一個字。text所返回的每一個字會以空格分隔,當整個循環結束時,foreach函數的返回值由text所返回的全部字組成(以空格分隔)。

find_files= $(wildcard $(dir)/*)

dirs:= /src /inc

files:= $(foreach dir,$(dirs),$(find_files))

在定義變量find_file時,使用了=,所以該變量是遞歸調用型變量,這樣find_file的值中的函數將在變量替換時執行。

7.4         函數if

$(ifcondition,then-part[,else-part])

首先把condition的前導/結尾空格去掉,而後擴展。若是擴展結果非空,則condition爲真;若是擴展結果爲空,則condition爲假。

若是condition爲真,計算then-part的值,並將該值做爲整個函數的返回值。

若是condition爲假,計算else-part的值,並將該值做爲整個函數的返回值;若是else-part不存在,函數返回空值。

7.5         函數call

$(callvariable,param1,param2,...)

將每個param賦值給臨時變量$(1)、$(2)等;變量$(0)的值是變量variable。在給臨時變量賦值之前首先擴展param。

variable是一個變量名,而不是對該變量的引用。若是變量名是內建函數名,則該內建函數將被執行。

call函數的目的就是經過在變量variable中使用臨時變量$(1)、$(2)等來完成必定的功能,臨時變量$(1)、$(2)等就是經過call函數進行賦值的。例如:

reverse= $(2) $(1)

foo= $(call reverse,a,b)

7.6         函數origin

$(originvariable)

函數origin的結果以下:

undefined           :variable沒有定義。

default                :variable是缺省定義。若是從新定義了一個缺省變量,將返回下面的值。

environment       :variable是環境變量定義,選項‘-e’沒有打開。

file                      :variable在makefile中定義。

command line     :variable在命令行中定義。

override              :variable在makefile中用override指令定義。

automatic           :variable是自動變量,定義它是爲了執行每一個規則中的命令。

environment override:variable是環境變量定義,選項‘-e’打開。

7.7         shell函數

雖然在規則的命令中使用的就是shell命令,可是若是想利用shell命令的輸出結果,就須要使用shell函數。shell函數的參數就是shell命令,該函數把shell命令的輸出做爲函數的返回值:

contents:= $(shell cat hello.c)

8    makefile中庫的使用

8.1         整個庫的使用

當一個庫做爲目標時,和普通目標文件的使用相同,並可使用各類建立庫的方法來建立或更新目標庫。

當一個庫做爲依賴時,可使用

1.庫文件的文件名(能夠含有路徑)。

2.‘-lname’。make首先將‘-lname’替換爲libname.so,並在當前路徑下、VPATH指定的路徑下、與vpath匹配的 路徑下,以及/lib,/usr/lib和/usr/local/lib下進行搜索。若是沒有找到libname.so文件,接着將‘-lname’替換 爲libname.a,仍在上述目錄下進行搜索。具體替換的方式能夠經過變量.LIBPATTERNS設置,缺省值是「lib%.so lib%.a」。注意name中不能包含路徑名。

若是使用第二種方法,最好可以保證庫可以被找到並且不須要被更新,此時$<表明的是(搜索路 徑+libname.so/libname.a)。若是庫沒有被搜索到或是須要被更新,則根據目錄搜尋原則,此時$<表明的是把‘-lname’做 爲目標文件建立或更新的規則命令中的$@。可是若是‘-lname’做爲目標,‘-lname’就不會再進行上述的名字替換。這樣的話根據目錄搜索的原 則$@可能會表明:-lname或(搜索路徑+-lname)。因此原規則(庫做爲依賴的規則)中的$<將失去它的做用,對編寫規則的命令很不利。

因此若是庫做爲依賴有可能找不到或者須要被更新時,最好使用第一種形式。

8.2         庫成員的使用

庫成員也能夠用做目標或依賴。使用‘libname(member1 member2 …)’的格式。這種格式只能在目標和依賴中使用,不能出如今規則的命令中。

當庫成員做爲目標時,更新庫成員的命令必須使用ar。例如:

mylib.a(hello.o):hello.o

ar cr mylib.a hello.o

$@等於庫名libname,$@的取值遵循目錄搜索規則。而$%等於成員名member,$%的取值再也不遵循目錄搜索的規則,$%只能表明member。

當庫成員做爲依賴時,$<等於libname(member),$@的取值遵循目錄搜索規則,有一點除外:若是依賴文件沒有被搜索到或是須要 被更新,此時$<表明再也不是當它做爲目標文件被建立或更新的規則命令中的$@,而是將$@中的libname替換爲 libname(member)。

9    makefile文件慣例

SHELL=/bin/sh                # 設置shell

VPATH=                          #設置全部文件搜索的路徑(或者在下面用vpath設置)

GPATH=                          #一般和VPATH(或者vpath)設置成一致的值

.SUFFIXES:                 # 清除原有的後綴列表

.SUFFIXES:.c .o          # 設置makefile中隱含規則使用的後綴

 

CC=                                 #使用的編譯器(gcc/g++)

CFLAGS=                       #編譯器的參數(-MM)

 

# 若是是debug版本就包含該變量的定義;若是是release版本就不包含該變量的定義。

DEBUG=1

ifdef DEBUG

SUFFIX:=.dbg

CFLAGS+= -g

else

SUFFIX:=

CFLAGS+= -O2

endif

 

EXECNAME:=               # 生成的可執行文件的文件名

DEPEND:=                            # 存放關聯信息的文件

 

# 下面三項將會應用於全部的源程序文件。

# 若是某源程序文件須要其它庫或頭文件的支持,則須要對該源程序文件進行單獨處理。

LIB:=                               # 支持庫,書寫格式:-lname(對應的庫爲libname.so或libname.a)

INCDIR:=                       # 搜索頭文件的目錄

LIBDIR:=                        # 搜索庫文件的目錄

 

# 下面三個目錄是肯定的

SRCDIR:=                       #全部存放源文件的目錄

OBJDIR:=                       #全部存放目標文件的目錄(一般全部的目標文件存放在同一個目

# 錄下。要是存放在多個目錄,還須要在makefile中指定各個目標

# 文件分別存放到哪一個目錄。)

BINDIR:=                       #存放可執行文件的目錄(通常也只有一個目錄,理由同上。)

 

vpath %.c SRCDIR         # 設置源程序文件的搜索目錄

vpath %.h INCDIR         # 設置頭文件的搜索目錄

vpath %.o OBJDIR         # 設置目標文件的搜索目錄

 

#如下的程序默認OBJDIR和BINDIR都只有一個目錄

 

# SRC的值是全部源程序的文件名(包含路徑)

SRC=$(foreach DIR,$(SRCDIR),$(wildcard$(DIR)/*.c))

 

# OBJ的值是全部目標文件的文件名(包含路徑)

OBJ=$(addprefix $(OBJDIR)/,$(patsubst %.c,%.o$(SUFFIX),$(notdir$(SRC))))

 

# BIN的值是可執行文件的文件名(包含路徑)

BIN=$(BINDIR)/$(EXECNAME)$(SUFFIX)

 

.PHONY:default compile install uninstallclean depend tags

 

# 默認目標,也就是主要目標。

# 若是默認目標的依賴是一個假想目標,該假想目標的命令就必定會被執行。

defaultall: depend compile tags

 

depend:

@set -e;\

$(CC) -MM $(CFLAGS) -I$(INCDIR)$(SRC)        \

# sed的功能是在目標文件前面加上路徑名,在後面加上後綴。

| sed 's,.\.o[ :]*,$(OBJDIR)/\1.o$(SUFFIX):,g'    \

>$(DEPEND)

 

include $(DEPEND)

 

compile:$(OBJ)

# 動態連接,在install時須要拷貝動態連接庫到合適的目錄。

@$(CC) -o $(BIN) $(OBJ) -W1,-Bdynamic$(LIBDIR) $(LIB)

# 靜態連接,在install時不須要拷貝靜態態連接庫。

@$(CC) -o $(BIN) $(OBJ) -W1,-Bstatic$(LIBDIR) $(LIB)

 

# 生成目標文件的隱含規則

$(OBJDIR)/%.o$(SUFFIX): %.c

@$(CC) -c -o $@ $(CFLAGS)$(filter %$<,$(SRC))

 

install:

將可執行程序、庫文件等拷貝到預約的目錄下。

 

uninstall:

 

clean:

@rm -rf $(OBJDIR)

@rm -f $(DEPEND)

 

tags:

@ctags $(INC) $(SRC)

 

ctags

功能說明:爲源程序文件產生一個tag file。

語法:ctags[--append=yes|no][--links=yes|no][-L 列表文件][-f tag文件名][文件\目錄…]

-L                列表文件中指定的文件生成tag文件

-f                 設定生成的tag文件的名字(默認是tags)

--append      用生成的tag重寫tag文件(默認是no)或者是將生成的tag添加到tag文件中

--links          是否添加symbol links(默認是yes)

--recurse      是否遞歸處理目錄。若是[文件\目錄…]爲空,並且也沒有用-L選項指定列表文

件,則默認遞歸處理當前目錄。

 

 ===========================================================================================

SHELL=/bin/sh                # 設置shell
VPATH=                        # 設置全部文件搜索的路徑(或者在下面用vpath設置)
GPATH=                        # 一般和VPATH(或者vpath)設置成一致的值
.SUFFIXES:                # 清除原有的後綴列表
.SUFFIXES:.c .o        # 設置makefile中隱含規則使用的後綴

CC=                                # 使用的編譯器(gcc/g++)
CFLAGS=                    # 編譯器的參數(-MM)

# 若是是debug版本就包含該變量的定義;若是是release版本就不包含該變量的定義。
DEBUG=1
ifdef DEBUG
SUFFIX:= .dbg
CFLAGS+= -g
else
SUFFIX:=
CFLAGS+= -O2
endif

EXECNAME:=                # 生成的可執行文件的文件名
DEPEND:=                    # 存放關聯信息的文件

# 下面三項將會應用於全部的源程序文件。
# 若是某源程序文件須要其它庫或頭文件的支持,則須要對該源程序文件進行單獨處理。
LIB:=                            # 支持庫,書寫格式:-lname(對應的庫爲libname.so或libname.a)
INCDIR:=                        # 搜索頭文件的目錄
LIBDIR:=                        # 搜索庫文件的目錄

# 下面三個目錄是肯定的
SRCDIR:=                    # 全部存放源文件的目錄
OBJDIR:=                    # 全部存放目標文件的目錄(一般全部的目標文件存放在同一個目
                                    # 錄下。要是存放在多個目錄,還須要在makefile中指定各個目標
                                    # 文件分別存放到哪一個目錄。)
BINDIR:=                        # 存放可執行文件的目錄(通常也只有一個目錄,理由同上。)

vpath %.c $(SRCDIR)        # 設置源程序文件的搜索目錄
vpath %.h $(INCDIR)        # 設置頭文件的搜索目錄
vpath %.o $(OBJDIR)        # 設置目標文件的搜索目錄

#如下的程序默認OBJDIR和BINDIR都只有一個目錄

# SRC的值是全部源程序的文件名(包含路徑)
SRC=$(foreach DIR,$(SRCDIR),$(wildcard $(DIR)/*.c))

# OBJ的值是全部目標文件的文件名(包含路徑)
OBJ=$(addprefix $(OBJDIR)/,$(patsubst %.c,%.o$(SUFFIX),$(notdir $(SRC))))

# BIN的值是可執行文件的文件名(包含路徑)
BIN=$(BINDIR)/$(EXECNAME)$(SUFFIX)

.PHONY:default compile install uninstall clean depend tags

# 默認目標,也就是主要目標。
# 若是默認目標的依賴是一個假想目標,該假想目標的命令就必定會被執行。
default: depend compile tags

depend:
    @set -e;\
    $(CC) -MM $(CFLAGS) -I$(INCDIR) $(SRC)        \
    # sed的功能是在目標文件前面加上路徑名,在後面加上後綴。
    | sed 's,.\.o[ :]*,$(OBJDIR)/\1.o$(SUFFIX):,g'    \
    >$(DEPEND)

include $(DEPEND)

compile:$(OBJ)
    # 動態連接,在install時須要拷貝動態連接庫到合適的目錄。
    @$(CC) -o $(BIN) $(OBJ) -W1,-Bdynamic $(LIBDIR) $(LIB)
    # 靜態連接,在install時不須要拷貝靜態態連接庫。
    @$(CC) -o $(BIN) $(OBJ) -W1,-Bstatic $(LIBDIR) $(LIB)

# 生成目標文件的隱含規則
$(OBJDIR)/%.o$(SUFFIX): %.c
    @$(CC) -c -o $@ $(CFLAGS) $(filter %$<,$(SRC))

install:
    將可執行程序、庫文件等拷貝到預約的目錄下。

uninstall:

clean:
    @rm -rf $(OBJDIR)
    @rm -f $(DEPEND)

tags:
    @ctags $(INC) $(SRC)

================================Makefile的一個模板(Simple)============================================================

SHELL:=/bin/sh

.SUFFIXES:

.SUFFIXES:.cpp  .o

 

CC:=g++

CFLAGS+=

 

INCDIR:=.  ./subdir

INCPATH:=$(addprefix-I,$(INCDIR))

 

SRCDIR:=.  ./subdir

SRC:=$(foreachDIR,$(SRCDIR),$(wildcard $(DIR)/*.cpp))

OBJ:=$(patsubst %.cpp,%.o,$(SRC))

BIN:=run

 

.PHONY:build clean

 

build:$(OBJ)

      @$(CC) -o $(BIN) $(OBJ)

%.o:%.cpp

      @$(CC) $(INCPATH) -c$(CFLAGS) -o $@ $<

 

clean:

      @rm -f $(BIN)

      @rm -f $(OBJ)


================================Makefile的一個模板============================================================

SHELL:=/bin/sh

 

.SUFFIXES:

.SUFFIXES:.cpp .o

 

CC:=g++

CFLAGS+=

 

EXECNAME:=run

DEPEND:=depend

 

# external libs (libname.so or libname.a), in format:-lname

LIB:=

LIBDIR:=

 

INCDIR:=.

SRCDIR:=.

OBJDIR:=.

BINDIR:=.

 

# initialize

SRC:=$( foreach  DIR,  $(SRCDIR),  $(wildcard $(DIR)/*.cpp) )

OBJ:=$( addprefix  $(OBJDIR)/, $(patsubst %.cpp, %.o, $(notdir $(SRC) ) ) )

BIN:=$(BINDIR)/$(EXECNAME)

 

.PHONY:default build depend clean

 

default:depend build

 

depend:

       @set -e;\

       $(CC)  -MM  -I$(INCDIR)  $(SRC)\

       | sed 's,  .\.o[ :]*,  $(OBJDIR)/\1.o:,  g'\

    >$(DEPEND)

 

include $(DEPEND)

 

build:$(OBJ)

       @$(CC)  -o  $(BIN)  $(OBJ) $(LIBDIR)  $(LIB)

 

# default rules for obj files

$(OBJDIR)/%.o:%.cpp

       @$(CC)  -c  -o  $@  $(CFLAGS)  $( filter %$<, $(SRC) )

 

clean:

       @rm  -f  $(DEPEND)

       @rm -f  $(BIN)

       @rm -f  $(OBJ)

相關文章
相關標籤/搜索