首頁  >  文章  >  系統教程  >  Linux Makefile 的注意事項:如何避免常見的錯誤和陷阱

Linux Makefile 的注意事項:如何避免常見的錯誤和陷阱

WBOY
WBOY轉載
2024-02-11 12:12:12808瀏覽

Makefile 是 Linux 系統中常用的檔案格式,它可以用來描述程式的編譯過程和依賴關係,讓使用者可以方便地使用 make 指令來建置和管理程式。 Makefile 的語法和規則相對簡單,但也有一些細節和特殊情況需要注意,否則可能會導致編譯失敗或出現意想不到的結果。本文將為你介紹 Linux 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中註解都不要寫在語句後面,而是語句的前一行,以避免類似的問題出現


陷阱二: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 $$@) $(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中文網其他相關文章!

陳述:
本文轉載於:lxlinux.net。如有侵權,請聯絡admin@php.cn刪除