轉自:https://blog.csdn.net/QQ1452008/article/details/52247944
前言
每個編寫過Makefile的程序員都可能遇見過Makefile中內含的陷阱,本博文旨在展現陷阱,提醒自己,也供大家一起學習。
本博文會隨所遇見的Makefile陷阱有關的問題而進行后續的更新。
陷阱一:在定義變量的語句后面空格之后使用了‘#’注釋符
結果:導致變量的值并不是你所賦值的,而是把值與注釋符之間的空格一起賦值給了變量,使得執行違背自己的意愿,而不容易察覺。
實例說明如下(Makefile版本:GNU MAKE 3.81):
TmpDir = /Source #此處隨意定義了一個目錄,#為了驗證此陷阱,特意在賦值語句后空幾格并進行注釋,ifeq ($(TmpDir), /Source)
Result = They are equal
else
Result = They are not equal endif all: @echo $(TmpDir)||||||| @echo $(Result)
?
make之后其結果為 :
/Source ||||||| (注意:/Source與|之間的空格,其實是屬于TmpDir變量的)
They are not equal若把
ifeq ($(TmpDir), /Source)
改為
ifeq ($(TmpDir), /Source )
說明:/Source后面的空格需要跟定義TmpDir與注釋符之間的空格數相等如此一來,再次make,結果為:They are equal
擴展一:其實驗證的過程中也引申出了另一個陷阱,ifeq()語句中的陷阱,見陷阱二
擴展二 : 變量賦值語句存在這個陷阱,那宏定義語句呢?及類似于如下語句
CFLAGS += -DTMP=1 #注釋語句
INCFLAGS += -I$(APP_COMMON_SRC_DIR)/Include #注釋語句 main:mian.o gcc $< $(CFLAGS) $(INCFLAGS) -o $@
?
其實經過實測表明,這樣并不會影響宏定義“TMP”在源文件中的值, 以及“INCFLAGS ”所在的路徑值。
心得: 通過以上求證,注釋符會影響到Makefile文件內部定義使用的變量的值,而不會影響到諸如 -D , -I 后面的值。所以建議Makefile中注釋都不要寫在語句后面,而是語句的前一行,來避免類似的問題出現。
陷阱二:ifeq語句的括號里面,不要隨意使用空格
結果:makefile會吧參數后面的空格也當作參數的一部分來進行比較,導致結果違背自己的意愿。
實例說明如下(Makefile版本:GNU MAKE 3.81):
TmpDir = /Source#下方的/Source后面空了幾格
ifeq ($(TmpDir), /Source )
Result = They are equal
else
Result = They are not equal endif all: @echo $(Result)
make之后其結果為 :
They are not equal若把
ifeq ($(TmpDir), /Source )
改為
ifeq ($(TmpDir), /Source) 如此一來,再次make,結果為:They are equal
經過實測表明,$(TmpDir)后面空幾格沒有影響,唯獨/Source后面空格就會有影響了
心得 : 在Makefile中,最好保證參數的一致性,是否空格等,不像C語言等語言編程一樣,那么寬松。
陷阱三:在mingw環境下使用路徑時的陷阱
詳情:在正確使用并能生成.d依賴文件,理論上使得修改任一 .h 或者 .c 文件都能自動進行編譯的情況下,其結果偏偏就是在修改了.h文件而不能編譯與之相關的.c文件,即沒有檢查到有文件更新,從而沒有進行編譯。待仔細查看Makefile的內容,也不能輕易看出端倪。其實這背后存在一個不易察覺的陷阱。
例子大概如下:
TARGET = Temp
# abspath 函數:獲取其參數中的文件或者目錄的絕對路徑
APP_BASE = $(abspath ../..) DEV_BLD_DIR = $(APP_BASE)/$(TARGET)/Build TEMP = $(APPSRC:.c=.o) APPOBJS_TMP = $(TEMP:.S=.o) # addprefix 函數:把 APPOBJS_TMP 中的文件一一添加前綴 $(DEV_BLD_DIR)/ APPOBJS := $(addprefix $(DEV_BLD_DIR)/,$(APPOBJS_TMP)) APPDEPS_TMP = $(APPOBJS_TMP:.o=.d) APPDEPS := $(addprefix $(DEV_BLD_DIR)/,$(APPDEPS_TMP)) all: Tmp.bin -include $(APPDEPS) ...... #省略了若干內容 ...... # subst 函數:把$@中的 Source 替換成 Build # 該編譯的命令,在編譯源文件的同時,也生成了.d 依賴文件 $(DEV_BLD_DIR)/%.o: %.c $(info Compiling $< ...) $(CC) -c -o $(subst Source,Build,$@) $(CFLAGS) $(INCFLAGS) $< -MD -MF $(DEV_BLD_DIR)/$*.d -MP
?
請點擊進入 .d依賴文件 相關內容介紹
其實從結果上便能大致推測是.d依賴文件部分出現了問題,因為改寫任一文件都要能重新編譯,本身就是.d依賴文件所要賦予的功能。
陷阱:目標路徑的問題,即同一文件目標的引用時要保持路徑一致。mingw環境下,windows路徑(e.g. c:\agc.o) 和 mingw路徑(/c/agc.o)都能夠識別,對于make而言, c:\abc.o 和 /c/abc.o 是兩個不同的目標。若要是不知道這一知識要點,很難發現 .d 文件開頭 c:\ 和 /c/ 的區別。(個人疑點:同一環境,不同工程,有些生成的.d依賴文件中.o目標路徑和make中引用的路徑是一樣的,目前也不知是什么原因,總之這個陷阱還是存在的。)
實例陷阱說明:
#以下行將導入所有的.d依賴文件的內容,即以 /c/...開頭的內容
-include $(APPDEPS)
#而以下目標依賴關系中,指明目標的路徑則是以 c:\...開頭的路徑
$(DEV_BLD_DIR)/%.o: %.c
#其結果就是導致了因路徑表示的不同,而認為不是同一目標的情況出現
#使得make不能找到.o目標文件依賴的所有依賴源文件,其中包括.h頭文件
#自然而然,也就不能因為.h文件的更新,而重新編譯對應的.c文件來生成.o文件
?
解決方法:
既然知道了陷阱所在,就可以利用如下命令來解決該問題:
#通過增加sed命令,把生成的.d依賴文件中的.o目標路徑改寫就可以了。
$(DEV_BLD_DIR)/%.o: %.c$(info Compiling $< ...) $(CC) -c -o $(subst Source,Build,$@) $(CFLAGS) $(INCFLAGS) $< -MD -MF $(DEV_BLD_DIR)/$*.d.tmp -MP sed 's,.*\.o[ :]*,$@:,g' < $(DEV_BLD_DIR)/$*.d.tmp > $(DEV_BLD_DIR)/$*.d;\ rm -f $(DEV_BLD_DIR)/$*.d.tmp @echo
?
心得:以后出現類似該情況,即表面上 makefile 中沒有什么問題,但在使用了依賴文件,并修改.h 文件后,不重新編譯的情況,這個時候要考慮路徑問題。不同路徑的表示方法,所表示的目標文件在make中會認為不是同一文件。
--------------------- 本文來自 Jerry_yl_ 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/QQ1452008/article/details/52247944?utm_source=copy?