1. 程式人生 > >Makefile常用除錯方法

Makefile常用除錯方法

本文轉自: https://www.cnblogs.com/AP0904225/p/5936465.html

轉載自 陳皓《跟我一起寫 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函式

呼叫進行求值動作。例如:

 

image

正如我們之前所見,_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)將會列出每個變數以及具描述性的註釋:

image

自動變數不會被顯示出來,但是通過它們可以方便變數的獲得,像$(<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 [email protected]

 

%.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(origin*)'

        @echo '   value = $(value  $*)'

        @echo '  flavor = (flavor(flavor*)'

 

這樣一來,我們可以使用make命令的-f引數來檢視makefile中的相關變數(包括make的內建變數,比如:COMPILE.c或MAKE_VERSION之類的)。注意:第二個以“d-”為字首的目標可以用來列印關於這個變數更為詳細的東西(後面有詳細說明)
 

假設我們的makefile是這個樣子(test.mk)

test.mk

OBJDIR := objdir

OBJS := (addprefix(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)

 

那麼,我們可以這樣進行除錯:

演示

[[email protected]]$ make -f test.mk -f var.mk OBJS

OBJS=objdir/foo.o objdir/bar.o objdir/baz.o

 

[[email protected]]$ make -f test.mk -f var.mk d-foo

foo=Huh?

  origin = file

  value = $(bar)

  flavor = recursive

 

[[email protected]]$ make -f test.mk -f var.mk d-CFLAGS

CFLAGS=-Ifoo -Ibar -O -O

  origin = file

  value = -Ifoo -Ibar -O -O

  flavor = simple

 

[[email protected]]$  make -f test.mk -f var.mk d-COMPILE.c

COMPILE.c=cc -Ifoo -Ibar -O -Wall   -c

  origin = default

  flavor = recursive

   value = (CC)(CC)(CFLAGS) (CPPFLAGS)(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/

相關部落格:https://blog.csdn.net/qq1452008/article/details/50855810