Makefile自动生成头文件依赖

前言

Makefile自动生成头文件依赖是很常用的功能,本文的目的是想尽量详细说明其中的原理和过程。

Makefile模板

首先给出一个本人在小项目中常用的Makefile模板,支持自动生成头文件依赖。

CC      = gcc
CFLAGS  = -Wall -O
INCLUDEFLAGS =
LDFLAGS =
OBJS    = seq.o
TARGETS = test_seq 

.PHONY:all
all : $(TARGETS)

test_seq:test_seq.o $(OBJS)
    $(CC) -o $@ $^ $(LDFLAGS)

%.o:%.c
    $(CC) -o $@ -c $< $(CFLAGS) $(INCLUDEFLAGS)

%.d:%.c
    @set -e; rm -f $@; $(CC) -MM $< $(INCLUDEFLAGS) > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

-include $(OBJS:.o=.d)

.PHONY:clean
clean:
    rm -f $(TARGETS) *.o *.d *.d.*

基础知识

在进行下一步之前,首先需要了解make的执行步骤:

  1. 读入Makefile
  2. 读入被include的其它Makefile
  3. 初始化Makefile中的变量
  4. 推导隐晦规则,并分析所有规则
  5. 为所有目标创建依赖关系链
  6. 根据依赖关系,决定哪些目标需要重新生成
  7. 执行生成命令

如何动态生成依赖关系?

从上面make的执行过程中可看出,要动态生成依赖关系,只能利用第2步读入其它Makefile的机制。那么,我们是否可以先把生成的依赖关系保存到文件,然后再把该文件的内容包含进来?
答案是Yes! 只要利用include的机制。

include关键字是用于读入其它Makefile文件。当该文件不存在时,make会寻找是否有生成它的规则,如果有,则执行其生成命令,然后再尝试读入。在include前加减号"-"可以上make忽略其产生的错误,并不输出任何错误信息。

即是说,我们需要提供生成规则文件的规则。例如,我们可以这样动态生成头文件依赖关系:

seq.d : seq.c
    @echo “seq.o seq.d : seq.c seq.h" > $@

-include seq.d

当make执行时,Makefile中的内容将是这样子(指内存上的数据):

seq.d : seq.c
    @echo “seq.o seq.d : seq.c seq.h" > $@

seq.o seq.d : seq.c seq.h

特别注意的是,由于对seq.c和seq.h的修改需要更新seq.d的内容(因为依赖关系可能已变化),因此seq.d也要在依赖关系的目标列表中。

自动生成头文件依赖

基于上面的例子,现在可以开始讨论如何自动生成头文件依赖。

自动生成依赖关系

大多数c/c++编译器提供了-M选项,可自动寻找源文件依赖的头文件,并生成依赖规则。对于gcc,需要使用-MM选项,否则它会把系统依赖的头文件也包含进来。例如执行下面一个命令:

    gcc -MM seq.c

将输出:

    seq.o : seq.c seq.h

但我们需要结果是seq.d也要包含在目标列表中,所以还需要对它进行文本处理。因此,上面的例子可改为:

seq.d : seq.c
    @set -e; \
    gcc -MM $< > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

-include seq.d

生成规则中的执行命令解释

第一个命令@set -e。@关键字告诉make不输出该行命令;set -e的作用是,当后面的命令的返回值非0时,立即退出。

那么为什么要把几个命令写在”同一行“(是对于make来说,因为\的作用就是连接行),并用分号隔开每个命令?因为在Makefile这样做才能使上一个命令作用于下一个命令。这里是想要set -e作用于后面的命令。

第二个命令gcc -MM $< > $@.$$$$, 作用是根据源文件生成依赖关系,并保存到临时文件中。内建变量$<的值为第一个依赖文件(那seq.c),$$$$为字符串"$$",由于makefile中所有的$字符都是特殊字符(即使在单引号之中!),要得到普通字符$,需要用$$来转义; 而$$是shell的特殊变量,它的值为当前进程号;使用进程号为后缀的名称创建临时文件,是shell编程常用做法,这样可保证文件唯一性。

第三个命令作用是将目标文件加入依赖关系的目录列表中,并保存到目标文件。关于正则表达式部分就不说了,唯一要注意的是内建变量$*$*的值为第一个依赖文件去掉后缀的名称(这里即是seq)。

第四个命令是将该临时文件删除。

如果把内建变量都替换成其值后,实际内容是这样子:

seq.d : seq.c
    @set -e; \
    gcc -MM seq.c > seq.d.$$$$; \
    sed 's,\(seq\)\.o[ :]*,\1.o seq.d : ,g' < seq.d.$$$$ > seq.d; \
    rm -f seq.d.$$$$

-include seq.d

Makefile的模式匹配

最后,再把Makefile的模式匹配应用上,就完成自动生成头文件依赖功能了:

%.d : %.c
    @set -e; \
    gcc -MM $@ > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

-include seq.d

参考资料

<跟我一起写Makefile> by 陈晧
GNU make官方文档 http://www.gnu.org/software/make/manual/make.html

时间: 2024-09-15 02:08:24

Makefile自动生成头文件依赖的相关文章

android ndk adt自动生成头文件问题。

问题描述 android ndk adt自动生成头文件问题. android ndk adt自动生成头文件时候,在项目src目录下打开cmd.运行javah加adt复制的方法包名,为什么提示找不到com.zhhd.simplejni.MainActivity的类文件.在bin/classes也不行.怎么办,卡在这么个地方太闹心了 解决方案 javah -classpath . -jni 这里点表示当前路径.

makefile自动生成依赖关系

手工编写依赖关系不仅工作量大而且极易出现遗漏,更新也很难及时,修改源或头文件后makefile可能忘记修改.为了解决这个问题,可以用gcc的-M选项自动生成目标文件和源文件的依赖关系.-M选项会把包含的系统头文件以及其所包含的其他系统头文件也找出来了,如果我们不需要输出系统头文件的依赖关系时,可以用-MM选项. 下面我们以一个简单的例子来说明如何自动生成依赖关系: exm/      main.c      s.c      s.h makefile文件内容如下: all:a src=$(wil

程序自动生成Dump文件

前言:通过drwtsn32.NTSD.CDB等调试工具生成Dump文件, drwtsn32存在的缺点虽然NTSD.CDB可以完全解决,但并不是所有的操作系统中都安装了NTSD.CDB等调试工具.了解了mini dump文件格式后,完全可以程序自动生成Dump文件. 本文主要讨论以下内容: 1.  运行原理 2.  程序修改 3.  注意事项 一.   运行原理 当程序遇到未处理异常(主要指非指针造成)导致程序崩溃死,如果在异常发生之前调用了SetUnhandledExceptionFilter(

根据MIDlet套件自动生成jad文件

我们经常遇到有jar文件但是却没有jad文件的情况,这时候如果你想使用模拟器来运行恐怕麻烦一些.当然你可以根据jar文件中的Manifest.mf文件,自己手写jad文件.需要注意的是自己手写的时候一定不能忽略如下两个参数:MIDlet-Jar-Size和MIDlet-Jar-URL. 互联网上也有一些工具可以根据jar文件自动生成jad文件.下面我们使用java来实现一个简单的jad文件生成器.解决这个问题的关键在于使用java.util.jar包提供的api解析jar包,然后将其Manife

Eclipse中如何自动添加头文件(add include)

在使用Eclipse写程序时, 经常遇到需要找头文件的问题, 在Eclipse中, 包含自动添加头文件的功能; 可以在Java, C++等插件中, 自动添加; 位置: Source -> Add Include (Ctrl+Shift+N): 作者:csdn博客 u012515223 更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/Java/

win7电脑打开浏览器自动生成TEMP文件夹怎么办

  1.在Win7系统上点击IE浏览器,然后在IE浏览器上点击"工具"-"Internet选项";   2.然后在internet选项窗口上,切换至"常规"选项卡中,然后点击历史记录处的"设置"按钮;   3.历史浏览记录窗口上,查看历史浏览记录和临时文件目录是否指向桌面Desktop文件夹,可以重新设置别的文件夹为系统默认文件夹; 4.点击"移动文件夹"按钮,来浏览临时文件夹,再点击确定按钮保存. 按照上

jni使用基础(四)之javah生成头文件

  javah使用:帮助我们生成头文件的工具   怎样使用javah? 1.先在命令行下运行:javah.如果能执行java而不能执行javah,就需要配置环境变量. 2.cd进入到java的目录文件下,先用javac把.java文件生成.class文件. 3.在.class文件目录下执行javah,生成头文件(注意:.class文件要在相对应的包目录结构下) 如:用eclipse的工程,在bin/classes目录下com/devchina/ndk/DevActivity,需要在classes

Wix 安装部署(一)同MSBuild 自动生成打包文件

原文:Wix 安装部署(一)同MSBuild 自动生成打包文件       因为项目需要,最近在研究Wix打包部署,园子里也有一些关于wix的博客,方方面面,讲的点各不同.我自己也在测试过程中,写下过程,以供参考.最新版本WiX Toolset v3.7,如何安装的就不说了,可以参考 http://blog.csdn.net/rryqsh/article/details/8274832         打包关心的问题有 1).Net版本检查  2)桌面和菜单栏的快捷方式 3)更换图标画面,进度条

exception-Android之ndk开发,使用ant自动创建头文件 出错

问题描述 Android之ndk开发,使用ant自动创建头文件 出错 这是ant源码 <?xml version="1.0" encoding="UTF-8"?> description <!-- ================================= target: BuildAllHeaders ================================= --> <target name="BuildA