快速編寫“專家級”makefile(2.創建基本編譯環境)

一、運用規則:
先來創建 simple 項目的兩個源程序:
foo.c
  1. #include <stdio.h>
  2. void foo()
  3. {
  4. printf("This is foo ()!\n");
  5. }
main.c
  1. #include <stdio.h>
  2. extern void foo();
  3. int main()
  4. {
  5. foo();
  6. return 0;
  7. }
    對於項目,在編寫 Makefile 時,應該首先在腦子裏勾勒出 Makefile 的依賴關係。
    對於 simple 可執行程序來說,上圖就是它的 “依賴樹” 。有了它,Makefile 的編寫就會變得輕鬆許多。
 
    上圖展示了依賴關係與規則間的映射。
  1. all : main.c foo.o
  2. gcc -o simple main.c foo.o
  3. main.o : main.c
  4. gcc -o main.c -c main.c
  5. foo.o : foo.c
  6. gcc -o foo.o -c foo.c
  7. clean :
  8. rm simple main.o foo.o
    運行結果(終端):
$ make
gcc -c main.c -o main.o    
gcc -c foo.c -o foo.o 
gcc -o simple main.o foo.o
$ make clean
rm simple main.o foo.o
    在不修改源程序文件的情況下再執行一次 make ,出現以下結果:
$ make
gcc -o simple main.o foo.o
    第二次編譯並沒有構建目標文件的動作,但有構建 simple 的動作。
  1. make 在第二次執行時,首先查找是否存在 all 目標,若有就不執行其後面的命令,若沒有,則執行。
  2. make 是通過文件的時間戳來判斷哪些文件需要重新編譯。 make 在分析一個規則以創建目標時,如果發現先決條件中文件的時間戳大於目標文件的時間戳,即目標文件比先決條件中的文件更新,就需要運行規則中命令重新構建目標。

假目標:
  1. .PHONY : clean
  2. simple : main.o foo.o
  3. gcc -o simple main.o foo.o
  4. main.o : main.c
  5. gcc -o main.o -c main.c
  6. foo.o : foo.c
  7. gcc -o foo.o -c foo.c
  8. clean:
  9. rm -rf simple main.o foo.o
    運行結果:
$ make clean
rm -rf simple main.o foo.o
    採用 .PHONY 關鍵字聲明一個目標後,make 並不會將其當作一個文件來處理。由於假目標不與一個文件關聯,故無論 clean 文件是否存在,它都執行;

運用 “變量”
  1. .PHONY : clean
  2. CC = gcc
  3. RM = rm
  4. EXE = simple.exe
  5. OBJS = main.o foo.o
  6. $(EXE) : $(OBJS)
  7. $(CC) -o $(EXE) $(OBJS)
  8. main.o : main.c
  9. $(CC) -o main.o -c main.c
  10. foo.o : foo.c
  11. $(CC) -o foo.o -c foo.c
  12. clean :
  13. $(RM) -rf $(EXE) $(OBJS)
    引用變量需要採用 “$(變量名)” 或 “${變量名}” 的形式。

1.自動變量:
  • $@ : 用於表示一個規則中的目標。當一個規則中有多個目標時,$@ 所指的是其中任何造成規則命令被運行的目標
  • $^:表示規則中所有的先決條件
  • $<:表示規則中第一個先決條件
  1. .PHONY : all
  2. all : first second third
  3. @echo "\$$@ = $@"
  4. @echo "$$^ = $^"
  5. @echo "$$< = $<"
  6. first second third :
    運行結果:
$make
$@ = all
$^ =  first second third
$< = first
        注意:
  1. 在 Makefile 中 “$” 具有特殊含義,如果採用 echo 輸出 “$” ,則必須用兩個連着的 “$”;
  2. “$@” 對於 Bash Shell 也有特殊含義,需要在 “$$@” 之前再加一個脫字符 “\” (引號不包括在內)
    1. .PHONY : clean
    2. CC = gcc
    3. RM = rm
    4. EXE = simple
    5. OBJS = main.o foo.o
    6. &(EXE) : $(OBJS)
    7. $(CC) -o $@ $^
    8. main.o : main.c
    9. $(CC) -o $@ -c $^
    10. foo.o : foo.c
    11. $(CC) -o $@ -c $^
    12. clean :
    13. $(RM) -rf $(EXE) $(OBJS)
2.特殊變量:
  • MAKE
  • MAKECMDGOALS
  1. .PHONY : all
  2. all :
  3. @echo "MAKE = $(MAKE)"
    運行結果:
$make
MAKE = make
    $(MAKE) 的值就是 “make” ,當在一個 Makefile 中運行另一個 Makefile 時,需要用到這個變量。
  1. .PHONY : all clean
  2. all clean :
  3. $echo "\$$@ = $@"
  4. $echo "MAKECMDGOALS = $MAKECMDGOALS"
    運行結果:
$make
$@ = all
MAKECMDGOALS =
$make all
$@ = all
MAKECMDGOALS = all
$make clean
$@ = clean
MAKECMDGOALS = clean
$make all clean
$@ = all
MAKECMDGOALS = all
$@ = cleanl
MAKECMDGOALS = clean
    從運行結果:
  1. MAKECMDGOALS 變量指的是用戶輸入的目標,當 make 不帶參數時,MAKECMDGOALS 爲空,即使前面我們所提到 make 不帶參數時 即生成默認目標。
  2. make 後可帶多個參數,表示構建多個目標,其將從左到右逐個構建目標。

3.變量的類別與賦值:      
     =      
  1. .PHONY : all
  2. foo = $(bar)
  3. bar = $(ugh)
  4. ugh = Huh?
  5. all :
  6. @echo $(foo)
    運行結果
$make
Huh?
    只用 “=” 符號定義的變量稱爲遞歸擴展變量,它的引用時遞歸的,容易出現死循環。

    :=
  1. .PHONY : all
  2. x = foo
  3. y = $(x) b
  4. x = later
  5. xx := foo
  6. yy := &(xx) b
  7. xx := later
  8. all :
  9. @echo "x = $(y), xx = $(yy)"
    運行結果
$make
x = later  b, xx = foo  b
    用 “:=” 定義的變量稱爲簡單擴展變量,make 只對其展開一次。
實例:
  1. .PHONY : all
  2. obj = main.o foo.o bar.o
  3. obj := $(obj) another.o
  4. all :
  5. @echo $(obj)
    運行結果
$make
main.o foo.o bar.o another.o
 
    ?=
  1. .PHONY : all
  2. foo = x
  3. foo ?= y
  4. bar ?= y
  5. all :
  6. @echo "foo = $(foo), bar = $(bar)"
    運行結果
$make
foo = x, bar = y
    當用到 “?= ” 若變量被定義了,不改變其原值,若變量沒有被定義,則將右邊的值賦值給它。

    +=
  1. .PHONY : all
  2. obj = main.o foo.o bar.o
  3. obj += another.o
  4. all :
  5. @echo $(obj)
    運行結果
$make
main.o foo.o bar.o another.o
    通過 “+=” 實現追加賦值,其效果與 “:=” 完全一樣。

4.變量的來源:
    除了在 Makefile 中直接定義變量外,還可以有其他幾種方式是 make 獲得變量值
  1. 對於自動變量,它的值是每一條規則根據上下文自動獲取。
  2. 在運行 make 時, 可以使用 “make bar = x” 的形式對變量 bar 進行賦值。
  3. 變量也可以來自於 Shell 環境, 採用 export 命令定義變量,如(?=):
$make
foo = x, bar = y
$export bar = x
$make
foo = x, bar = x

5.高級變量的引用:
  1. .PHONY : all
  2. foo = a.c b.c c.c
  3. bar := $(foo : .c = .o)
  4. all :
  5. @echo "bar = $(bar)"
    運行結果
$make
bar = a.o b.o c.o
    從運行結果:
  1. bar 變量中的文件名從 .c 後綴變成了 .o
  2. 這種功能可用後面介紹的 patsubst 函數

6.避免變量被覆蓋:
  1. .PHONY : all
  2. override foo = x
  3. all :
  4. @echo "foo = $(foo)"
    運行結果
$make foo = ha
foo = x
    從運行結果:
        根據上面的 “4.變量的來源” 所提, 變量的值可以有 make 來獲取,若我們在設計 Makefile 時不希望它的值自定義後再被修改,可在變量前加上 “override“ 來進行保護。

二、採用精簡規則
  1. .PHONY : clean
  2. CC = gcc
  3. RM = rm
  4. EXE = simple.exe
  5. OBJS = main.o foo.o
  6. $(EXE) : $(OBJS)
  7. $(CC) -o $@ $^
  8. %.o : %.c
  9. $(CC) -o $@ -c $^
  10. clean :
  11. $(RM) -rf $(EXE) $(OBJS)
參考文獻:《專業嵌入式軟件開發》李雲·著                                                                    2016年7月2日,星期六
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章