转自:https://blog.csdn.net/QQ1452008/article/details/52247944

版权声明:本文为博主原创文章,未经博主允许不得转载。 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

Linux Makefile 中的陷阱【转】的更多相关文章

  1. linux makefile中一些复制运算的区别

    Makefile 中  :=. ?= .+= .=的区别 = 是最基本的赋值:= 是覆盖之前的值?= 是如果没有被赋值过就赋予等号后面的值,如果已经被赋值则就用之前的赋值+= 是添加等号后面的值

  2. Linux内核中Makefile、Kconfig和.config的关系(转)

    我们在编译Linux内核时,往往在Linux内核的顶层目录会执行一些命令,这里我以RK3288举例,比如:make firefly-rk3288-linux_defconfig.make menuco ...

  3. 【总结】嵌入式linux内核中Makefile、Kconfig、.config的关系及增加开机Hello World【转】

    本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/73772109 为了弄清内核的组织结构,我们先来实现下面这个简单的例子. 一.增加 ...

  4. Vs2012在Linux开发中的应用(6):改写Makefile项目的Build过程

    MSBUILD的编译过程实际上是依据一系列的targets文件定义的.当我们在IDE运行生成.批生成.清理命令的时候.VS会查找这些命令相应的Task并运行它,以下我们逐个分析这个过程. 当运行生成操 ...

  5. 【转】Linux makefile 教程 非常详细,且易懂

    From: http://blog.csdn.net/liang13664759/article/details/1771246 最近在学习Linux下的C编程,买了一本叫<Linux环境下的C ...

  6. Linux makefile 教程 非常详细,且易懂

    最近在学习Linux下的C编程,买了一本叫<Linux环境下的C编程指南>读到makefile就越看越迷糊,可能是我的理解能不行. 于是google到了以下这篇文章.通俗易懂.然后把它贴出 ...

  7. linux Makefile obj-m obj-y

    目标定义是Kbuild Makefile的主要部分,也是核心部分.主要是定义了要编 译的文件,所有的选项,以及到哪些子目录去执行递归操作. 最简单的Kbuild makefile 只包含一行: 例子: ...

  8. 向linux内核中添加外部中断驱动模块

    本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...

  9. [软件测试]Linux环境中简单清爽的Google Test (GTest)测试环境搭建(初级使用)

    本文将介绍单元测试工具google test(GTEST)在linux操作系统中测试环境的搭建方法.本文属于google test使用的基础教程.在linux中使用google test之前,需要对如 ...

随机推荐

  1. 修复Mysql主从不同步shell

    使用第三方工具MySQL Enterprise Monitor,MySQL企业版监控工具.MONyog – MySQL Monior and Advisor,MONyog大家都不陌生,windows下 ...

  2. bzoj4504 K个串 (优先队列+主席树)

    首先如果没有出现次数的限制的话,这题就是超级钢琴 但由于有了这个限制,不能简单地用前缀和 考虑顺着做的时候每个点的贡献,如果a[i]=x,x上次出现位置是lst[x](可以用一个map来记),那它会给 ...

  3. UVALive - 6442 (思维题)

    题目链接:https://vjudge.net/contest/241341#problem/I 题目大意:给你一个有N个点等距的环,编号[0,N-1],然后有些点上有一个或多个硬币,通过移动这些硬币 ...

  4. 【codeforces contest 1119 F】Niyaz and Small Degrees

    题目 描述 \(n\) 个点的树,每条边有一个边权: 对于一个 \(X\) ,求删去一些边后使得每个点的度数 \(d_i\) 均不超过 \(X\) 的最小代价: 你需要依次输出 \(X=0 \to n ...

  5. CrossFire Round #481 div.3 978 打后感

    虚拟赛,头一次打div.3感觉好TM水啊...... 一共7道题,我A了6道,第7题有思路但是没时间了. 结果还是排在700多名,可能其他人也觉得太水了吧. 逐一解析题目: A好简单,因为不想离散化我 ...

  6. jdbc操作数据库(详细)

    JDBC是由java编程语言编写的类及接口组成,同时它为程序开发人员提供了一组用于实现对数据库访问的JDBC API,并支持SQL语言.利用JDBC可以将JAVA代码连接到oracle.DB2.SQL ...

  7. 使用WinDbg下的gflags工具导致程序无法执行

    问题:the system cannot find file **.exe. 解决方法:开始-运行-输入regedit打开注册表编辑器,找到 HKEY_LOCAL_MACHINE/SOFTWARE/M ...

  8. JVM总结(五):JVM字节码执行引擎

    JVM字节码执行引擎 运行时栈帧结构 局部变量表 操作数栈 动态连接 方法返回地址 附加信息 方法调用 解析 分派 –“重载”和“重写”的实现 静态分派 动态分派 单分派和多分派 JVM动态分派的实现 ...

  9. java序列化深拷贝【转】

    java深拷贝 序列化和反序列化合成在一起的方法CloneUtils import java.io.ByteArrayInputStream; import java.io.ByteArrayOutp ...

  10. python 面试题--你能做出多少?

    python3中__get__,getattr,__getattribute__的区别 什么是 GIL 详细博客 GIL = Global Intercept Lock 全局解释器锁,任意时刻在解释器 ...