工程中編寫自己的makefile---6 makefile學習

1        Makefile

1.1             Makefile運行順序

1、讀入所有的 Makefile。

2、讀入被 include 的其它 Makefile。

3、初始化文件中的變量。

4、推導隱晦規則,並分析所有規則。

5、爲所有的目標文件創建依賴關係鏈。

6、根據依賴關係,決定哪些目標要重新生成。

7、執行生成命令。

 

1-5步爲第一個階段,6-7 爲第二個階段。第一個階段中,如果定義

的變量被使用了,那麼,make 會把其展開在使用的位置。但 make

並不會完全馬上展開,make 使用的是拖延戰術,如果變量出現在依

賴關係的規則中,那麼僅當這條依賴被決定要使用了,變量纔會在其

內部展開。

1.2             Makefile顯式規則

target ... : prerequisites ...

command

...

...

例如

target:目標文件1  目標文件2  目標文件3

    <tab>   gcc –o 欲建立的執行文件目標文件1  目標文件2  目標文件3

 

target:        是一個目標文件;可以是 Object File,也可以是執行文件;還可以是一個標籤(Label);

prerequisites: 要生成那個target 所需要的文件或是目標

command:       也就是 make 需要執行的命令(任意的Shell 命令)

1.3             引用其他的makefile

使用 include 關鍵字可以把別的 Makefile 包含進來,這很像C語言的#include,被包含的文件會原模原樣的放在當前文件的包含位置。

include 的語法是:

include <filename>

filename 可以是當前操作系統 Shell 的文件模式(可以保含路徑和通配符)

注意!!!在 include 前面可以有一些空字符, 但是絕不能是[Tab]鍵開始。 include和<filename>可以用一個或多個空格隔開

1.4             一些注意的符號

注意!!!

1,-    :命令前面加了一個小減號,標記爲不管命令出不出錯都認爲是成功的

2,@       :用“@”字符在命令行前,這個命令將不被 make 顯示出來

3,$<     :自動化變量,表示所有的依賴目標集

4,$@     :自動化變量,表示目標集

5,$$$$  :意爲一個隨機編號

6,$(CFLAGS) 編譯參數。如果沒有定義就是空的。CFLAGS主要是指makefile中的隱式規則裏會用到的常見預定義變量,是C編譯器的選項

1.5             Make的參數

1,-n:只顯示命令,但不執行命令,利於調試Makefile,看看命令執行起來是什麼順序

2,-s:全面禁止命令的顯示

3,-f:運行指定的 Makefile,例如:make -f make.linux

4,-i:Makefile 中所有命令都會忽略錯誤

5,-k:如果某規則中的命令出錯了,那麼就終目該規則的執行,但繼續執行其它規則

1.6             文件搜尋目錄

在一些大的工程中,有大量的源文件,我們通常的做法是把這許多的源文件分類,並存放在不同的目錄中。所以,當 make 需要去找尋文件的依賴關係時,你可以在文件前加上路徑,但最好的方法是把一個路徑告訴 make,讓 make 在自動去找。

         Makefile文件中的特殊變量“VPATH”就是完成這個功能的,如果沒有指明這個變量,make只會在當前的目錄中去找尋依賴文件和目標文件。如果定義了這個變量,那麼,make就會在噹噹前目錄找不到的情況下,到所指定的目錄中去找尋文件了。

VPATH = src:../headers

上面的的定義指定兩個目錄,“src”“../headers”make會按照這個順序進行搜索。目錄由冒號分隔。(當然,當前目錄永遠是最高優先搜索的地方)

另一個設置文件搜索路徑的方法是使用makevpath關鍵字(注意,它是全小寫的),這不是變量,這是一個make的關鍵字,它的使用方法有三種

         vpath <pattern> <directories>

爲符合模式<pattern>的文件指定搜索目錄<directories>

vpath <pattern>

清除符合模式<pattern>的文件的搜索目錄。

vpath

清除所有已被設置好了的文件搜索目錄。

vapth使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”結尾的文件。<pattern>指定了要搜索的文件集,而<directories>則指定了<pattern>的文件集的搜索的目錄。例如:

vpath  %.h  ../headers                  該語句表示,要求make“../headers”目錄下搜索所有以“.h”結尾的文件。(如果某文件在當前目錄沒有找到的話)

vpath  %.h  ../headers:work          該語句表示,要求make“../headers”work 兩個目錄下搜索所有以“.h”結尾的文件。(如果某文件在當前目錄沒有找到的話)

我們可以連續地使用 vpath 語句,以指定不同搜索策略。如果連續的vpath 語句中出現了相同的<pattern>,或是被重複了的<pattern>,那麼,make 會按照 vpath 語句的先後順序來執行搜索

1.7             僞目標

         僞目標並不是一個文件,只是一個標籤,由於僞目標不是文件,所以make無法生成它的依賴關係和決定它是否要執行;爲了避免和文件重名的這種情況,我們可以使用一個特殊的標記.PHONY來顯示地指明一個目標是“僞目標”

.PHONY:clean

clean :

rm edit $(OBJECTS)

 

       僞目標作爲依賴;目的:不同的指令清除不同的目標;可以輸入“make  cleanall”和

make cleanobj”和“make cleandiff”命令來達到清除不同種類文件的目的

      .PHONY:cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff

rm program

cleanobj :

rm *.o

cleandiff :

rm *.diff

注意!!!

可以使用 依賴一個僞目標,這樣每次編譯時因爲ALWAYSMAKE不存在,都會執行依賴後的命令

1.8             靜態模式

需要重點看下!!!

         靜態模式可以更加容易地定義多目標的規則,可以讓我們的規則變得更加的有彈性和靈活。

<targets ...>: <target-pattern>:<prereq-patterns ...>

<commands>

..

targets定義了一系列的目標文件,可以有通配符。是目標的一個集合(目標集)

target-parrtern是指明瞭targets的模式,也就是的目標集模式(目標模式)

prereq-parrterns是目標的依賴模式(依賴模式),它對target-parrtern形成的模式再進行一次依賴目標的定義。

上面的模式爲生成一個新集合(依賴目標集或者依賴集)

 

還是舉個例子來說明一下吧。如果我們的<target-parrtern>定義成“%.o”,意思是我們的<target>集合中都是以“.o”結尾的,而如果我們的<prereq-parrterns>定義成“%.c”,意思是對<target-parrtern>所形成的目標集進行二次定義,其計算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]這個結尾),併爲其加上[.c]這個結尾,形成的新集合(依賴目標集)

 

objects = foo.o bar.o

all: $(objects)

 

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

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

等價於

      foo.o:foo.c

           $(CC) –c  $(CFLAGS) foo.c  –o  foo.o

      bar.o:bar.c

           $(CC) –c  $(CFLAGS) bar.c  –o  bar.o

 

      files= foo.elc bar.o lose.o

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

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

$(filter %.elc,$(files)): %.elc: %.el

emacs -f batch-byte-compile $<

 

$(filter %.o,$(files))表示調用Makefile filter 函數,過濾“$filter”集,

上面的例子中,指明瞭我們的目標從$object中獲取,“%.o”表明要所有以“.o”結尾的目標,也就是“foo.o bar.o”,也就是變量$object集合的模式,而依賴模式“%.c”則取模式“%.o”“%”,也就是“foo bar”,併爲其加下“.c”的後綴,於是,我們的依賴目標就是“foo.c bar.c”。而命令中的“$<”“$@”則是自動化變量,“$<”表示所有的依賴目標集(也就是“foo.c bar.c”),“$@”表示目標集(也就是“foo.o bar.o”)。

1.8.1.1   嵌套執行 make

      在一些大的工程中,我們會把我們不同模塊或是不同功能的源文件放在不同的目錄中,我們可以在每個目錄中都書寫一個該目錄的Makefile,這有利於讓我們的Makefile變得更加地簡潔,而不至於把所有的東西全部寫在一個Makefile中,這樣會很難維護我們的Makefile,這個技術對於我們模塊編譯和分段編譯有着非常大的好處。

      我們有一個子目錄叫subdir,這個目錄下有個Makefile文件,來指明瞭這個目錄下文件的編譯規則。那麼我們總控的Makefile可以這樣書寫:

subsystem:

cd subdir && $(MAKE)

其等價於:

subsystem:

$(MAKE) -C subdir

定義$(MAKE)宏變量的意思是,也許我們的make需要一些參數,所以定義成一個變量比較利於維護。這兩個例子的意思都是先進入“subdir”目錄,然後執行make命令。

 

如果你要傳遞變量到下級Makefile中,那麼你可以使用這樣的聲明:

export <variable ...>

否則

unexport <variable ...>

 

export variable = value

其等價於:

variable = value

export variable

其等價於:

export variable := value

其等價於:

variable := value

export variable

如果你要傳遞所有的變量,那麼,只要一個export就行了。後面什麼也不用跟,表示傳遞所有的變量。

 

“-w”           輸出當前的工作目錄;

如果我們的下級make目錄是“/home/hchen/gnu/make”,如果我們使用“make -w”來執行,那麼當進入該目錄時,我們會看到:

make: Entering directory `/home/hchen/gnu/make'.

而在完成下層make後離開目錄時,我們會看到:

make: Leaving directory `/home/hchen/gnu/make'

當你使用-C參數來指定make下層Makefile時,“-w”會被自動打開的。如果參數中有“-s”“--slient”)或是“--no-print-directory”,那麼,“-w”總是失效的。

1.9             Makefile變量

1.9.1        定義變量

變量的命名字可以包含字符、數字,下劃線(可以是數字開頭),但不應該含有“:”“#”“=”或是空字符(空格、回車等)。使用寫法(MAKEFLAGS)

變量使用:

$ MAKEFLAGS

$(MAKEFLAGS)同一使用此種模式,包括在shell

${ MAKEFLAGS }

如果要使用真實的“$”字符,那麼需要用“$$”來表示。

1.9.2        定義命令包

這種命令序列的語法以“define”開始,以“endef”結束

1.9.3        變量中的變量

1,”=”

MyName=$(Name)

Name=wzt

變量是可以使用後面的變量來定義的(遞歸調用容易出問題)

2,”:=”

    Name:=wzt

MyName:=$(Name)

前面的變量不能使用後面的變量,只能使用前面已定義好了的變量

推薦使用此種方法

3,”?=”

如果 FOO 沒有被定義過,那麼變量 FOO 的值就是“bar”

如果 FOO 先前被定義過,那麼這條語將什麼也不做

4,”+=”

追加變量值

objects =main.o foo.o bar.o utils.o

objects +=another.o

於是,我們的$(objects)值變成:“main.o foo.o bar.o utils.o another.o

another.o 被追加進去了)

如果變量之前沒有定義過,那麼,“+=”會自動變成“=”,如果前面有變量定義,那麼“+=”會繼承於前次操作的賦值符。如果前一次的是“:=”,那麼“+=”會以“:=”作爲其賦值符,

1.9.4        多行變量

define 指示符後面跟的是變量的名字,而重起一行定義變量的值,定義是以 endef 關鍵字結束。其工作方式和“=”操作符一樣。變量的值可以包含函數、命令、文字,或是其它變量。因爲命令需要以[Tab]鍵開頭,所以如果你用 define 定義的命令變量中沒有以[Tab]鍵開頭,那麼 make 就不會把其認爲是命令

definetwo-lines

echo foo

echo $(bar)

endef

1.9.5        變量高級用法

1,變量值的替換(類似靜態模式)

$(var:a=b)

把變量“var”中所有以“a”字串“結尾”的“a”替換成“b”字串。這裏的“結尾”意思是“空格”或是“結束符”

 

2,把變量的值再當成變量

x = y

y = z

a := $($(x))

    有$(a)=z

1.9.6        目標變量

    可以爲某個目標設置局部變量,這種變量被稱爲“Target-specific Variable”,它可以和“全局變量”同名,因爲它的作用範圍只在這條規則以及連帶規則中,所以其值也只在作用範圍內有效。而不會影響規則鏈以外的全局變量的值。

其語法是:

<target...> : <variable-assignment>

<target...> : overide <variable-assignment>

 

<variable-assignment>可以是前面講過的各種賦值表達式,如“=”、

“:=”、 “+=”或是“?=”。第二個語法是針對於 make 命令行帶入的變量,

或是系統環境變量。

這個特性非常的有用,當我們設置了這樣一個變量,這個變量會作用

到由這個目標所引發的所有的規則中去。如:

prog: CFLAGS = -g

prog: prog.o foo.o bar.o

$(CC)$(CFLAGS) prog.o foo.o bar.o

prog.o: prog.c

$(CC)$(CFLAGS) prog.c

foo.o: foo.c

$(CC)$(CFLAGS) foo.c

bar.o: bar.c

$(CC)$(CFLAGS) bar.c

在這個示例中,不管全局的$(CFLAGS)的值是什麼,在 prog 目標,以及其所引發的所有規則中(prog.o foo.o bar.o 的規則) ,$(CFLAGS) 的值都是“-g”

1.9.7        模式變量

我們知道,make 的“模式”一般是至少含有一個“%”的,所以,我們可以以如下方式給所有以[.o]結尾的目標定義目標變量:

%.o: CFLAGS = -O

同樣,模式變量的語法和“目標變量”一樣:

<pattern...> : <variable-assignment>

<pattern...> : override <variable-assignment>

override同樣是針對於系統環境傳入的變量,或是make 命令行指定的變量

1.10       條件判斷

<conditional-directive>

<text-if-true>

else

<text-if-false>

endif

 

ifeq(<arg1>, <arg2> )

ifeq'<arg1>' '<arg2>'

ifeq"<arg1>" "<arg2>"

ifeq"<arg1>" '<arg2>'

ifeq'<arg1>' "<arg2>"

比較參數“arg1”和“arg2”的值是否相同

 

<conditional-directive>表示條件關鍵字,如“ifeq”。這個關鍵字有四個。

1,ifeq  “arg1”和“arg2”的值相同爲真

2,ifneq “arg1”和“arg2”的值不同爲真

3,ifdef

    語法:

ifdef <variable-name>

如果變量<variable-name>的值非空,那到表達式爲真。否則,表達式爲假,

ifdef 只是測試一個變量是否有值,其並不會把變量擴展到當前位置。

4,ifndef

 

在<conditional-directive>這一行上,多餘的空格是被允許的,但是不能以[Tab]鍵做爲開始(不然就被認爲是命令) 。而註釋符“#”同樣也是安全的。“else”和“endif”也一樣,只要不是以[Tab]鍵開始就行了。

特別注意的是,make 是在讀取 Makefile 時就計算條件表達式的值,並根據條件表達式的值來選擇語句,所以,你最好不要把自動化變量(如“$@”等)放入條件表達式中,因爲自動化變量是在運行時纔有的。

 

 

1.11       常用函數

函數調用,很像變量的使用,也是以“$”來標識的,其語法爲:$( )或${ }。

1.11.1    wildcard

原型:$(wildcard PATTERN)

功能:獲取匹配模式的文件名

說明:這個函數的功能是查找當前目錄下所有符合模式 PATTERN 的文件名,其返回值是以空格分割的,當前目錄下的所有符合模式 PATTERN 的文件名列表。

 

例如:

如下模式返回當前目錄下所有擴展名位 .c 的文件列表。

$(wildcard *.c)

 

1.11.2    patsubst

原型:$(patsubst pattern, replacement, text)

功能:模式替換函數

說明:函數功能是查找字符串 text 中按照空格分開的單詞,將符合模式 pattern 的字符串替換成 replacement。 Pattern 中的模式可以使用通配符,當 pattern 和 replacement 中都有 % 時,符合條件的字符將被 replacement 中的替換。函數的返回值是替換後的新字符串。

例如

需要將 C 文件替換爲 .o 的目標文件可以使用如下模式:

$(patsubst%.c, %.o, add.c)

上面的模式將 add.c 字符串作爲輸入,當擴展名爲 .c 時符合模式 %.c ,其中 % 在這裏代表 add,替換爲 add.o,並作爲輸出字符串。

$(patsubst%.c, %.o, $(wildcard *.c))

輸出的字符串將當前擴展名爲 .c 的文件替換成 .o 的文件列表。

1.11.3    foreach

原型:$(foreach VAR, LIST, TEXT)

功能:循環函數

說明: foreach 將 LIST 字符串中一個空格分割的單詞,先傳給臨時變量 VAR ,然後執行 TEXT 表達式,TEXT 表達式處理結束後輸出。其返回值是空格分割表達式 TEXT 的計算結果。

例如:

對於存在 add 和 sub 的兩個目錄,設置 DIRS 爲 "add sub ./" 包含目錄 add、sub 和當前目錄。表達式$(wildcard $(dir)/*.c) ,可以取出目錄 add 和 sub 及當前目錄中的所有擴展名爲 .c 的C語言源文件:

DIRS = subadd ./

FILES =$(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))

1.11.4    filter

原型:$(filter PATTERN…,TEXT)

功能:過濾函數

說明:過濾掉字串“TEXT”中所有不符合模式“PATTERN”的單詞,保留所有符合此模式的單詞。可以使用多個模式。模式中一般需要包含模式字符“%”。存在多個模式時,模式表達式之間使用空格分割。一般用來去除一個變量中的某些字符串

返回值:空格分割的“TEXT”字串中所有符合模式“PATTERN”的字串。

        例如:

sources := foo.c bar.c baz.s ugh.h

foo: $(sources)

cc $(filter %.c %.s,$(sources)) -o foo

        使用“$(filter %.c%.s,$(sources))”的返回值給 cc 來編譯生成目標“foo”,函數返回

值爲“foo.c bar.c baz.s”

1.11.5    subst

原型:$(subst FROM, TO, TEXT),

功能:替換函數

說明:即將字符串TEXT中的子串FROM變爲TO

 

 

 

發佈了51 篇原創文章 · 獲贊 106 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章