转自: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. 创建首个 Android 项目

    Android 项目包括构成你的 Android 应用的源代码的所有文件. 利用 Android SDK 工具可以简单的创建 默认项目目录和文件来开始一个新的 Android 项目. 本节课展示了如何 ...

  2. List does not exist. The page you selected contains a list that does not exist. It may have been deleted by another user

    当我在subsite里点击"Add a document",报这个错,后来一看event log: 在AAM里加上一条: 问题搞定:

  3. luogu2865 路障 (dijkstra)

    求次短路,dijkstra时同时记下到某点的最短距离和次短距离即可. #include<cstdio> #include<cstring> #include<algori ...

  4. HDU 1074 Doing Homework (动态规划,位运算)

    HDU 1074 Doing Homework (动态规划,位运算) Description Ignatius has just come back school from the 30th ACM/ ...

  5. 【洛谷P1072】Hankson 的趣味题

    题目大意:给定四个数字 a,b,c,d,求满足 \(gcd(a,x)=b,lcm(c,x)=d\) 的 x 的个数. 题解: 解法1:根据 lcm 的性质,x 一定为 d 的约数.因此,直接枚举 d ...

  6. ASP: Response 对象 错误 'ASP 0251 : 80004005' 解决办法

    Response 对象 错误 'ASP 0251 : 80004005' 超过响应缓冲区限制 这种情况一般是因为需要输出的网页内容太大了,由于asp在输入内容到客户的浏览器上之前,会把需要输出的全部内 ...

  7. MATLAB:图像选取局部区域滤波(roicolor、roipoly、roifill、fspecial、roifilt2函数)

    对于某些特殊的图像处理,我们不希望将整张图都进行图像处理.这个时候就用到了roicolor.roipoly.roifill.fspecial.roifilt2函数.代码实现过程如下: close al ...

  8. shell 备份代码

    #!/bin/sh # 备份代码 basedir=/data/backup www_src=$basedir/$(date +%F_$H) [ ! -d "$www_src" ] ...

  9. JS中的数据类型和变量内存

    1. JS中存在5种简单数据类型和1种复杂数据类型: 5种简单数据类型:Undefined, Null, Boolean, Number, String. 1种复杂数据类型:Object. 上面的5种 ...

  10. DNSLog注入笔记

    测试一些网站的时候,一些注入都是无回显的,我们可以写脚本来进行盲注,但有些网站会ban掉我们的ip,这样我们可以通过设置ip代理池解决, 但是盲注往往效率很低,所以产生了DNSlog注入.具体原理如下 ...