转自: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. Leetcode 344.反转字符串 By Python

    请编写一个函数,其功能是将输入的字符串反转过来. 示例: 输入:s = "hello" 返回:"olleh" 思路 Python里面的切片用来解决这个问题就很快 ...

  2. [hdu3966]Aragorn's Story

    传送门 题目描述 Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One ...

  3. Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)

    Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分) Description L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之 ...

  4. BRIEF特征简介

    引言 该文是由EPFL的Calonder在ECCV2010上提出了一种可以快速计算且表达方式为二进制编码的描述子.主要思路就是在特征点附近随机选取若干点对,将这些点对的灰度值的大小,组合成一个二进制串 ...

  5. [译]Golang中的优雅重启

    原文 Graceful Restart in Golang 作者 grisha 声明:本文目的仅仅作为个人mark,所以在翻译的过程中参杂了自己的思想甚至改变了部分内容,其中有下划线的文字为译者添加. ...

  6. ISO七层模型详解

    ISO七层模型详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在我刚刚接触运维这个行业的时候,去面试时总是会做一些面试题,笔试题就是看一个运维工程师的专业技能的掌握情况,这个很 ...

  7. javascript 伪数组与标准数组

    伪数组 1. 是一个对象 2. 必须有length 属性,length 值是number 类型 3. 如果这个对象的length 不为0,那么必须要有按照下标存储的数据 标准数组 判断数据是否是标准数 ...

  8. CodeForces - 163B Lemmings

    B. Lemmings time limit per test 1 second memory limit per test 256 megabytes input standard input ou ...

  9. android studio 统一管理版本号配置

    1.在android 的根目录新建一个versions.gradle 2.在这里面声明 各个第三方库的版本,写法有两种,第一种,写ext 扩展, 引用的时候, 第二种: 然后在project级的bui ...

  10. linux的基本操作与常见命令

    linux的基本操作与常见命令: jdk的安装: 步骤:(特别注意:虚拟机安装的一般是32位的操作系统,jdk也必须使用32位的) 查看虚拟机版本:sudo uname --m i686 //表示是3 ...