Makefile 是 Linux 系統中常用的檔案格式,它可以用來描述程式的編譯過程和依賴關係,讓使用者可以方便地使用 make 指令來建置和管理程式。 Makefile 的語法和規則相對簡單,但也有一些細節和特殊情況需要注意,否則可能會導致編譯失敗或出現意想不到的結果。本文將為你介紹 Linux Makefile 中的一些常見的錯誤和陷阱,以及如何避免和解決它們,讓你在 Linux 下更好地使用和編寫 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 $$@
其實經過實測表明,這樣並不會影響巨集定義「TMP」在來源檔案中的值, 以及「INCFLAGS 」所在的路徑值。
心得: 透過以上求證,註解符會影響到Makefile檔案內部定義使用的變數的值,而不會影響到諸如 -D , -I 後面的值。所以建議Makefile中註解都不要寫在語句後面,而是語句的前一行,以避免類似的問題出現。
#結果: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語言等語言程式設計一樣,那麼寬鬆。
詳情:在正確使用並能產生.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 $$@) $(CFLAGS) $(INCFLAGS) $
請點選進入 .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 $$@) $(CFLAGS) $(INCFLAGS) $'s,.*\.o[ :]*,$@:,g' $(DEV_BLD_DIR)/$*.d;\ rm -f $(DEV_BLD_DIR)/$*.d.tmp @echo
通过本文,你应该对 Linux Makefile 中的一些注意事项有了一个基本的了解,知道了如何避免和解决一些常见的错误和陷阱,如变量赋值、条件判断、目标文件、伪目标、自动变量等。你也应该明白了 Makefile 的作用和优点,以及如何在 Linux 下正确地使用和编写 Makefile。我们建议你在使用 Makefile 时要遵循一些规范和习惯,如使用缩进、注释、通配符等,以提高 Makefile 的可读性和可维护性。同时,我们也提醒你在使用 Makefile 时要注意一些潜在的问题和挑战,如跨平台、并行编译、递归调用等。希望本文能够帮助你更好地使用 Linux 系统,让你在 Linux 下编写出高效和优雅的 Makefile。
以上是Linux Makefile 的注意事項:如何避免常見的錯誤和陷阱的詳細內容。更多資訊請關注PHP中文網其他相關文章!