轉載自 陳皓《跟我一起寫 Makefile》《GNU Make項目管理》
GNU make 提供了若干可以協助調試的內置函數以及命令行選項。
1、warning函數
$(warning string)函數可以放在makefile 中的任何地方,執行到該函數時,會將string輸出,方便定位make執行到哪個位置。warning函數可以放在makefile 中的任何地方:開始的位置、工作目標或必要條件列表中以及命令腳本中。這讓你能夠在最方便查看變量的地方輸出變量的值。例如:
$(warning A top-level warning)
FOO := $(warning Right-hand side of a simple variable)bar BAZ = $(warning Right-hand side of a recursive variable)boo
$(warning A target)target: $(warning In a prerequisite list)makefile $(BAZ) $(warning In a command script) ls $(BAZ): |
這會產生如下的輸出:
$ make makefile:1: A top-level warning makefile:2: Right-hand side of a simple variable makefile:5: A target makefile:5: In a prerequisite list makefile:5: Right-hand side of a recursive variable makefile:8: Right-hand side of a recursive variable makefile:6: In a command script ls makefile |
注意,warning函數的求值方式是按照make標准的立即和延后求值算法。雖然對BAZ的賦值動作中包含了一個warning函數,但是直到BAZ在必要條件列表中被求值后,這個信息才會被輸出來。
2.命令行選項
有時候,我們不想讓我們的makefile中的規則執行起來,我們只想檢查一下我們的命令,或是執行的序列。於是我們可以使用make命令的下述參數:
“-n” “--just-print” “--dry-run” “--recon” 不執行參數,這些參數只是打印命令,不管目標是否更新,把規則和連帶規則下的命令打印出來,但不執行,這些參數對於我們調試makefile很有用處。
“-t” “--touch” 這個參數的意思就是把目標文件的時間更新,但不更改目標文件。也就是說,make假裝編譯目標,但不是真正的編譯目標,只是把目標變成已編譯過的狀態。
“-q” “--question” 這個參數的行為是找目標的意思,也就是說,如果目標存在,那么其什么也不會輸出,當然也不會執行編譯,如果目標不存在,其會打印出一條出錯信息。
“-W <file>;” “--what-if=<file>;” “--assume-new=<file>;” “--new-file=<file>;” 這個參數需要指定一個文件。一般是是源文件(或依賴文件),Make會根據規則推導來運行依賴於這個文件的命令,一般來說,可以和“-n”參數一同使用,來查看這個依賴文件所發生的規則命令。
三個最適合用來調試的命令行選項:
--just-print(-n)
--print-database(-p)
--warn-undefined-variables
2.1 --just-print
在一個新的makefile 工作目標上,我所做的第一個測試就是以--just-print(-n)選項來調用make。這會使得make讀進makefile並且輸出它更新工作目標時將會執行的命令,但是不會真的執行它們。GNU make有一個方便的功能,就是允許你為將被輸出的命令標上安靜模式修飾符(@)。
這個選項被假設可以抑制所有命令的執行動作,然而這只在特定的狀況下為真。實際上,你必須小心以對。盡管make不會運行命令腳本,但是在立即的語境之中,它會對shell函數
調用進行求值動作。例如:
![]() |
正如我們之前所見,_MKDIRS 簡單變量的目的是觸發必要目錄的創建動作。如果這個Makefile 是以--just-print 選項的方式運行的,那么當make 讀進Makefile 時,shell命令將會一如往常般被執行。然后,make 將會輸出(但不會執行)更新$(objects)文件列表所需要進行的每個編譯命令。
2.2 --print-data-base
--print-data-base(-p)是另一個你常會用到的選項。它會運行Makefile,顯示GNU版權信息以及make 所運行的命令,然后輸出它的內部數據庫。數據庫里的數據將會依種類划分成以下幾個組:variables、directories、implicit rules、pattern-specific variables、files(explicit rules)以及vpath earch path。如下所示:
# GNU Make 3.80 # Copyright (C) 2002 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. # There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. 正常的命令將會在此處執行
# Make data base, printed on Thu Apr 29 20:58:13 2004 # Variables ... # Directories ... # Implicit Rules ... # Pattern-specific variable values ... # Files ... # VPATH Search Paths |
讓我們更詳細地查看以上這幾個區段。
變量區段(variable)將會列出每個變量以及具描述性的注釋:

自動變量不會被顯示出來,但是通過它們可以方便變量的獲得,像$(<D)。注釋所指出的是origin 函數所返回的變量類型(參見“較不重要的雜項函數”一節)。如果變量被定義在一個文件中,則會在注釋中指出其文件名以及該定義所在的行號。簡單變量和遞歸變量的差別在於賦值運算符。簡單變量的值將會被顯示成右邊部分被求值的形式。
下一個區段標示為Directories,它對make 開發人員比對make 用戶有用。它列出了將會被make 檢查的目錄,包括可能會存在的SCCS 和RCS 子目錄,但它們通常不存在。對每個目錄來說,make 會顯示實現細節,比如設備編號、inode 以及文件名模式匹配的統計數據。
接着是Implicit Rules 區段。這個區段包含了make 數據庫中所有的內置的和用戶自定義的模式規則。此外,對於那些定義在文件中的規則,它們的注釋將會指出文件名以及行號:
%.c %.h: %.y # commands to execute (from `../mp3_player/makefile', line 73): $(YACC.y) --defines $< $(MV) y.tab.c $*.c $(MV) y.tab.h $*.h
%: %.c # commands to execute (built-in): $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
%.o: %.c # commands to execute (built-in): $(COMPILE.c) $(OUTPUT_OPTION) $< |
查看這個區段,是讓你能夠熟悉make 內置規則的變化和結構的最佳方法。當然,並非所有的內置規則都會被實現成模式規則。如果你沒有找到你想要的規則,可以查看Files區段,舊式后綴規則就列在該處。
下一個區段被標示為Pattern-specific variables,此處所列出的是定義在makefile 里的模式專屬變量。所謂模式專屬變量,就是變量定義的有效范圍被限定在相關的模式規則執行的時候。例如,模式變量YYLEXFLAG 被定義成:
%.c %.h: YYLEXFLAG := -d %.c %.h: %.y $(YACC.y) --defines $< $(MV) y.tab.c $*.c $(MV) y.tab.h $*.h |
將會被顯示成:
# Pattern-specific variable values %.c : # makefile (from `Makefile', line 1) # YYLEXFLAG := -d # variable set hash-table stats: # Load=1/16=6%, Rehash=0, Collisions=0/1=0% %.h : # makefile (from `Makefile', line 1) # YYLEXFLAG := -d # variable set hash-table stats: # Load=1/16=6%, Rehash=0, Collisions=0/1=0% # 2 pattern-specific variable values |
接着是Files 區段,此處所列出的都是與特定文件有關的自定義和后綴規則:
# Not a target: .p.o: # Implicit rule search has not been done. # Modification time never checked. # File has not been updated. # commands to execute (built-in): $(COMPILE.p) $(OUTPUT_OPTION) $<
lib/ui/libui.a: lib/ui/ui.o # Implicit rule search has not been done. # Last modified 2004-04-01 22:04:09.515625 # File has been updated. # Successfully updated. # commands to execute (from `../mp3_player/lib/ui/module.mk', line 3): ar rv $@ $^
lib/codec/codec.o: ../mp3_player/lib/codec/codec.c ../mp3_player/lib/codec/codec.c ../mp3_player/include/codec/codec.h # Implicit rule search has been done. # Implicit/static pattern stem: `lib/codec/codec' # Last modified 2004-04-01 22:04:08.40625 # File has been updated. # Successfully updated. # commands to execute (built-in): $(COMPILE.c) $(OUTPUT_OPTION) $< |
中間文件與后綴規則會被標示為Not a target,其余是工作目標。每個文件將會包含注釋,用以指出make 是如何處理此規則的。被找到的文件在被顯示的時候將會通過標准的vpath 搜索來找出其路徑。
最后一個區段被標示為VPATH Search Paths,列出了VPATH 的值以及所有的vpath模式。
對於大規模使用eval 以及用戶自定義函數來建立復雜的變量和規則的makefile 來說,查看它們的輸出結果通常是確認宏是否已被擴展成預期值的唯一方法。
2.3 --warn-undefined-variables
這個選項會使得make 在未定義的變量被擴展時顯示警告信息。因為未定義的變量會被擴展成空字符串,這常見於變量名稱打錯而且很長一段時間未被發現到。這個選項有個問題,這也是為什么我很少使用這個選項的原因,那就是許多內置規則都會包含未定義的變量以作為用戶自定義值的掛鈎。所以使用這個選項來運行make必然會產生許多不是錯誤的警告信息,而且對用戶的makefile 沒有什么用處。例如:
$ make --warn-undefined-variables -n makefile:35: warning: undefined variable MAKECMDGOALS makefile:45: warning: undefined variable CFLAGS makefile:45: warning: undefined variable TARGET_ARCH ... makefile:35: warning: undefined variable MAKECMDGOALS make: warning: undefined variable CFLAGS make: warning: undefined variable TARGET_ARCH make: warning: undefined variable CFLAGS make: warning: undefined variable TARGET_ARCH ... make: warning: undefined variable LDFLAGS make: warning: undefined variable TARGET_ARCH make: warning: undefined variable LOADLIBES make: warning: undefined variable LDLIBS |
不過,此命令在需要捕獲此類錯誤的某些場合上可能非常有用。
3.--debug 選項
當你需要知道make 如何分析你的依存圖時,可以使用--debug 選項。除了運行調試器,這個選項是讓你獲得最詳細信息的另一個方法。你有五個調試選項以及一個修飾符可用,分別是:basic、verbose、implicit、jobs、all 以及makefile。
如果調試選項被指定成--debug,就是在進行basic 調試;如果調試選項被指定成-d,就是在進行all調試;如果要使用選項的其他組合,則可以使用--debug=option1,option2 這個以逗號為分隔符的列表,此處的選項可以是下面任何一個單詞(實際上,make 只會查看第一個字母):
3.1 basic
這是所提供的信息最不詳細的基本調試功能。啟用時,make會輸出被發現尚未更新的工作目標並更新動作的狀態。它的輸出會像下面這樣:
File all does not exist. File app/player/play_mp3 does not exist. File app/player/play_mp3.o does not exist. Must remake target app/player/play_mp3.o. gcc ... ../mp3_player/app/player/play_mp3.c Successfully remade target file app/player/play_mp3.o. |
3.2 verbose
這個選項會設定basic 選項,以及提供關於“哪些文件被分析、哪些必要條件不需要重建等”的額外信息:
File all does not exist. Considering target file app/player/play_mp3. File app/player/play_mp3 does not exist. Considering target file app/player/play_mp3.o. File app/player/play_mp3.o does not exist. Pruning file ../mp3_player/app/player/play_mp3.c. Pruning file ../mp3_player/app/player/play_mp3.c. Pruning file ../mp3_player/include/player/play_mp3.h. Finished prerequisites of target file app/player/play_mp3.o. Must remake target app/player/play_mp3.o. gcc ... ../mp3_player/app/player/play_mp3.c Successfully remade target file app/player/play_mp3.o. Pruning file app/player/play_mp3.o. |
3.3 implicit
這個選項會設定basic 選項,以及提供關於“為每個工作目標搜索隱含規則”的額外信息:
File all does not exist. File app/player/play_mp3 does not exist. Looking for an implicit rule for app/player/play_mp3. Trying pattern rule with stem play_mp3. Trying implicit prerequisite app/player/play_mp3.o. Found an implicit rule for app/player/play_mp3. File app/player/play_mp3.o does not exist. Looking for an implicit rule for app/player/play_mp3.o. Trying pattern rule with stem play_mp3. Trying implicit prerequisite app/player/play_mp3.c. Found prerequisite app/player/play_mp3.c as VPATH ../mp3_player/app/ player/play_mp3.c Found an implicit rule for app/player/play_mp3.o. Must remake target app/player/play_mp3.o. gcc ... ../mp3_player/app/player/play_mp3.c Successfully remade target file app/player/play_mp3.o. |
3.4 jobs
這個選項會輸出被make 調用的子進程的細節,它不會啟用basic 選項的功能
Got a SIGCHLD; 1 unreaped children. gcc ... ../mp3_player/app/player/play_mp3.c Putting child 0x10033800 (app/player/play_mp3.o) PID 576 on the chain. Live child 0x10033800 (app/player/play_mp3.o) PID 576 Got a SIGCHLD; 1 unreaped children. Reaping winning child 0x10033800 PID 576 Removing child 0x10033800 PID 576 from chain. |
3.5 all
這會啟用前面的所有選項,當你使用-d 選項時,默認會啟用此功能。
3.6 makefile
它不會啟用調試信息,直到Makefile 被更新—— 這包括更新任何的引入文件。如果使用此修飾符,make 會在重編譯makefile 以及引入文件的時候,輸出被選擇的信息。這個選項會啟用basic 選項,all 選項也會啟用此選項。
如何調試Makefile變量
轉載自:http://coolshell.cn/articles/3790.html
六、七年前寫過一篇《跟我一起寫Makefile》,直到今天,還有一些朋友問我一些Makefile的問題,老實說,我有一段時間沒有用Makefile了,生疏了。回顧,這幾年來大家問題我的問題,其實很多時候是makefile的調試問題。所以,就像我在之前的那篇關於GDB的技巧的文章中做的一樣,在這里向大家介紹一個小小的調試變量的技巧。相信一定對你有用。
對於Makefile中的各種變量,可能是我們比較頭痛的事了。我們要查看他們並不是很方便,需要修改makefile加入echo命令。這有時候很不方便。其實我們可以制作下面一個專門用來輸出變量的makefile(假設名字叫:vars.mk)
vars.mk
%: @echo '$*=$($*)'
d-%: @echo '$*=$($*)' @echo ' origin = $(origin $*)' @echo ' value = $(value $*)' @echo ' flavor = $(flavor $*)' |
這樣一來,我們可以使用make命令的-f參數來查看makefile中的相關變量(包括make的內建變量,比如:COMPILE.c或MAKE_VERSION之類的)。注意:第二個以“d-”為前綴的目標可以用來打印關於這個變量更為詳細的東西(后面有詳細說明)
假設我們的makefile是這個樣子(test.mk)
test.mk
OBJDIR := objdir OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
foo = $(bar)bar = $(ugh)ugh = Huh?
CFLAGS = $(include_dirs) -O include_dirs = -Ifoo -Ibar CFLAGS := $(CFLAGS) -Wall
MYOBJ := a.o b.o c.o MYSRC := $(MYOBJ:.o=.c) |
那么,我們可以這樣進行調試:
演示
[hchen@RHELSVR5]$ make -f test.mk -f var.mk OBJS OBJS=objdir/foo.o objdir/bar.o objdir/baz.o
[hchen@RHELSVR5]$ make -f test.mk -f var.mk d-foo foo=Huh? origin = file value = $(bar) flavor = recursive
[hchen@RHELSVR5]$ make -f test.mk -f var.mk d-CFLAGS CFLAGS=-Ifoo -Ibar -O -O origin = file value = -Ifoo -Ibar -O -O flavor = simple
[hchen@RHELSVR5]$ make -f test.mk -f var.mk d-COMPILE.c COMPILE.c=cc -Ifoo -Ibar -O -Wall -c origin = default flavor = recursive value = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
|
我們可以看到:
- make的第一個-f后是要測試的makefile,第二個是我們的debug makefile。
- 后面直接跟變量名,如果在變量名前加”d-“,則輸出更為詳細的東西。
說一說”d-” 前綴(其意為details),其中調用了下面三個參數。
- $(origin):告訴你這個變量是來自哪兒,file表示文件,environment表示環境變量,還有environment override,command line,override,automatic等。
- $(value):打出這個變量沒有被展開的樣子。比如上述示例中的 foo 變量。
- $(flavor):有兩個值,simple表示是一般展開的變量,recursive表示遞歸展開的變量。
make調試工具:remake
http://bashdb.sourceforge.net/remake/