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中文网其他相关文章!