Makefile僞目標、多目標、靜態模式與變量基礎用法

1、make的工做方式

  1. 讀入全部的 Makefile。shell

  2. 讀入被 include 的其它 Makefile。bash

  3. 初始化文件中的變量。函數

  4. 推導隱晦規則,並分析全部規則。ui

  5. 爲全部的目標文件建立依賴關係鏈。spa

  6. 根據依賴關係,決定哪些目標要從新生成。.net

  7. 執行生成命令。code

    1-5爲第一階段;
    6-7爲第二階段;
    複製代碼

第一階段中,若是定義的變量被使用了,那麼,make 會把其展開在使用的位置。但 make 並不會徹底立刻展開,make 使用的是拖延戰術,若是變量出如今依賴關係的規則中,那麼僅當這條依賴被決定要使用了,變量纔會在其內部展開。遞歸

makefile只有一個最終目標,第一條規則的目標爲最終目標。字符串

2、僞目標

僞目標 PHONY英文釋義:假的,欺騙的。 就是告訴make這個不是真正意義上的target,make不須要自動生成依賴關係和推導規則。get

.PHONY: clean
clean:
    -rm run *.o
複製代碼

關鍵字.PHONY告訴make,無論當前目錄是否有「clean」這個文件,clean都是僞目標。 若是不加.PHONY,剛好存在名爲clean文件時,執行make clean就會出現。

root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make clean
make: `clean' is up to date. 複製代碼

3、多目標

須要生成多個可執行文件的作法。 使用僞目標的方法,給僞目標指定所依賴的文件。

all: main hello test
.PHONY: all
main:
    gcc -o main main.c
hello:
    gcc -o hello hello.c
test:
    gcc -o test test.c
.PHONY: clean
clean:
    rm main hello test
複製代碼

運行結果:

root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make all
gcc -o main main.c
gcc -o hello hello.c
gcc -o test test.c
複製代碼

生成3個可執行文件,只須要一個make all命令。

同時也可使用靜態模式來解決多目標的問題。

4、靜態模式

語法:

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

....

targets 定義了一系列的目標文件,能夠有通配符。是目標的一個集合。

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

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

專業術語看得也是懵懵的。

「$<」和「$@」則是自動化變量,「$<」爲依賴目標集和「$@」則是目標集。

target = main.o hello.o test.o
all : $(target)
$(target):%.o: %.c
    gcc -c $< -o $@ 
複製代碼

運行結果:

root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make all
gcc -c main.c -o main.o	
gcc -c hello.c -o hello.o	
gcc -c test.c -o test.o	
複製代碼

解讀下,all這個目標依賴於target這個變量。如今就是打算把依賴的文件修改了,單純懶得從新寫依賴關係。直接在原來依賴關係的基礎上修改下。%.o表示當前目錄下全部.o文件,相似shell中*.o表示全部文件同樣。%.o: %.c無非表示全部.o文件文件名後綴修改成.c罷了。

最終顯示出來的結果,

target = main.o hello.o test.o
all : $(target)
$(target) : main.c hello.c test.c
    gcc -c $< -o $@
複製代碼

$< 從那些依賴文件中讀取(main.c hello.c test.c這些文件),$@從target這個變量賦值的多目標中讀取。

再配合函數filter使用,就更加靈活了。filter英文釋義爲過濾器。

filter過濾函數用法

$(filter <pattern...>, )

名稱:過濾函數——filter。

功能:以 模式過濾 字符串中的單詞,保留符合模式 的單詞。能夠有多個模式。

返回:返回符合模式 的字串。

target = main.o hello.o test.o other.a some.so
all : 
    @echo $(filter %.o %.a, $(target))
複製代碼

運行結果:

root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make all
main.o hello.o test.o other.a
複製代碼

filter函數將target變量中包含的全部.o .a文件保留其餘文件過濾。

5、變量的使用

引用變量:(變量名)
使用\$字符:$

變量中的變量 普通定義

VAR1 = $(VAR2)
VAR2 = 666
test:
    @echo $(VAR1)
複製代碼

輸出VAR1的值爲666,可是這種方式也有缺點,在使用遞歸定義時出現循環引用。還有就是若是在變量中使用函數,那麼,這種方式會讓咱們的 make 運行時很是慢,更糟糕的是,他會使用得兩個 make 的函數「wildcard」和「shell」發生不可預知的錯誤。由於你不會知道這兩個函數會被調用多少次。

遞歸定義

VAR1 = $(VAR2)
VAR2 = $(VAR1)
test:
    @echo $(VAR1)
複製代碼

運行結果:

root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make test
Makefile:1: *** Recursive variable `VAR1' references itself (eventually). Stop. 複製代碼

使用 :=操做符進行賦值,就不會報上述錯誤。

VAR1 := $(VAR2)
VAR2 := $(VAR1)
test:
    @echo $(VAR1)
複製代碼

「:=」操做符做用?與「=」有啥區別?

使用「=」操做符

VAR1 = 666
VAR2 = $(VAR1) 233
VAR1 = 555
test:
    @echo $(VAR1)
    @echo $(VAR2)
複製代碼

運行結果:

root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make test
555
555 233
複製代碼

再次聲明VAR1變量,原先的值會被覆蓋。而且VAR2中引用VAR1的變量是makefile展開後的最終值。

使用「:=」操做符

VAR1 := 666
VAR2 := $(VAR1) 233
VAR1 := 555
test:
    @echo $(VAR1)
    @echo $(VAR2)
複製代碼

運行結果:

root@chenwr-pc:/home/workspace/my_workspace/study/makefile# make test
555
666 233
複製代碼

值得一提的是,這種方法,前面的變量不能使用後面的變量,只能使用前面已定義好了的變量。所以VAR2中的引用的VAR1只能使用以前定義賦值的值。

如何定義一個值爲空格的變量?

null :=
space := $(null) # 
test:
    @echo hello$(space)world
複製代碼

運行結果:

hello world
複製代碼

$(null) #注意中間有個空格

「?=」操做符的做用 變量以前定義後,則不執行這條語句,若是變量沒有被定義,則進行賦值。

VAR1 := 666
VAR1 ?= 777
test:
    @echo $(VAR1)
複製代碼

輸出:666

VAR1 :=
VAR1 ?= 777
test:
    @echo $(VAR1)
複製代碼

輸出:空

VAR1 ?= 777
test:
    @echo $(VAR1)

輸出:777
複製代碼

大體概括一下

  • = 是最基本的賦值
  • := 是覆蓋以前的值 (「:=」表示變量的值決定於它在makefile中的位置,而不是整個makefile展開後的最終值。)
  • ?= 是若是沒有被賦值過就賦予等號後面的值
  • += 是添加等號後面的值
相關文章
相關標籤/搜索