zoukankan      html  css  js  c++  java
  • Linux Makefile 中的陷阱【转】

    转自: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 

  • 相关阅读:
    TensorRT 开始
    Vim 常用操作
    Android AsyncTask 的实现及 cancel 方式
    让 Andriod TextView 中的文本链接可点击的方法—— Linkify 及其他
    Android通过百度地图API用Service和Alarm在后台定时获取地理位置信息
    【转】Mac OS X 快捷键合集
    iframe导致的IE6下https页面安全提示
    [转载]IE的版本识别
    [转]iframe异步加载
    IE的html条件注释
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/9735196.html
Copyright © 2011-2022 走看看