核心命令:gcc -M *.h、*.cpp

转:

自动处理头文件的依赖关系

http://blog.csdn.net/su_ocean16/article/details/5374696

现在我们的Makefile写成这样:

all: main

main: main.o stack.o maze.o
gcc $^ -o $@ main.o: main.h stack.h maze.h
stack.o: stack.h main.h
maze.o: maze.h main.h clean:
-rm main *.o .PHONY: clean

按照惯例,用all做缺省目标。现在还有一点比较麻烦,在写main.ostack.omaze.o这三个目标的规则时要查看源代码,找出它们依赖于哪些头文件,这很容易出错,一是因为有的头文件包含在另一个头文件中,在写规则时很容易遗漏,二是如果以后修改源代码改变了依赖关系,很可能忘记修改Makefile的规则。为了解决这个问题,可以用gcc-M选项自动生成目标文件和源文件的依赖关系:

$ gcc -M main.c
main.o: main.c /usr/include/stdio.h /usr/include/features.h /
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h /
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-.h /
/usr/lib/gcc/i486-linux-gnu/4.3./include/stddef.h /
/usr/include/bits/types.h /usr/include/bits/typesizes.h /
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h /
/usr/lib/gcc/i486-linux-gnu/4.3./include/stdarg.h /
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h main.h /
stack.h maze.h

-M选项把stdio.h以及它所包含的系统头文件也找出来了,如果我们不需要输出系统头文件的依赖关系,可以用-MM选项:

$ gcc -MM *.c
main.o: main.c main.h stack.h maze.h
maze.o: maze.c maze.h main.h
stack.o: stack.c stack.h main.h

接下来的问题是怎么把这些规则包含到Makefile中,GNU make的官方手册建议这样写:

all: main

main: main.o stack.o maze.o
gcc $^ -o $@ clean:
-rm main *.o .PHONY: clean sources = main.c stack.c maze.c include $(sources:.c=.d) %.d: %.c
set -e; rm -f $@; /
$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; /
sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.$$$$ > $@; /
rm -f $@.$$$$

sources变量包含我们要编译的所有.c文件,$(sources:.c=.d)是一个变量替换语法,把sources变量中每一项的.c替换成.d,所以include这一句相当于:

include main.d stack.d maze.d

类似于C语言的#include指示,这里的include表示包含三个文件main.dstack.dmaze.d,这三个文件也应该符合Makefile的语法。如果现在你的工作目录是干净的,只有.c文件、.h文件和Makefile,运行make的结果是:

$ make
Makefile:: main.d: No such file or directory
Makefile:: stack.d: No such file or directory
Makefile:: maze.d: No such file or directory
set -e; rm -f maze.d; /
cc -MM maze.c > maze.d.$$; /
sed 's,/(maze/)/.o[ :]*,/1.o maze.d : ,g' < maze.d.$$ > maze.d; /
rm -f maze.d.$$
set -e; rm -f stack.d; /
cc -MM stack.c > stack.d.$$; /
sed 's,/(stack/)/.o[ :]*,/1.o stack.d : ,g' < stack.d.$$ > stack.d; /
rm -f stack.d.$$
set -e; rm -f main.d; /
cc -MM main.c > main.d.$$; /
sed 's,/(main/)/.o[ :]*,/1.o main.d : ,g' < main.d.$$ > main.d; /
rm -f main.d.$$
cc -c -o main.o main.c
cc -c -o stack.o stack.c
cc -c -o maze.o maze.c
gcc main.o stack.o maze.o -o main

一开始找不到.d文件,所以make会报警告。但是make会把include的文件名也当作目标来尝试更新,而这些目标适用模式规则%.d: %c,所以执行它的命令列表,比如生成maze.d的命令:

set -e; rm -f maze.d; /
cc -MM maze.c > maze.d.$$; /
sed 's,/(maze/)/.o[ :]*,/1.o maze.d : ,g' < maze.d.$$ > maze.d; /
rm -f maze.d.$$

注意,虽然在Makefile中这个命令写了四行,但其实是一条命令,make只创建一个Shell进程执行这条命令,这条命令分为5个子命令,用;号隔开,并且为了美观,用续行符/拆成四行来写。执行步骤为:

  1. set -e命令设置当前Shell进程为这样的状态:如果它执行的任何一条命令的退出状态非零则立刻终止,不再执行后续命令。

  2. 把原来的maze.d删掉。

  3. 重新生成maze.c的依赖关系,保存成文件maze.d.1234(假设当前Shell进程的id是1234)。注意,在Makefile中$有特殊含义,如果要表示它的字面意思则需要写两个$,所以Makefile中的四个$传给Shell变成两个$,两个$在Shell中表示当前进程的id,一般用它给临时文件起名,以保证文件名唯一。

  4. 这个sed命令比较复杂,就不细讲了,主要作用是查找替换。maze.d.1234的内容应该是maze.o: maze.c maze.h main.h,经过sed处理之后存为maze.d,其内容是maze.o maze.d: maze.c maze.h main.h

  5. 最后把临时文件maze.d.1234删掉。

不管是Makefile本身还是被它包含的文件,只要有一个文件在make过程中被更新了,make就会重新读取整个Makefile以及被它包含的所有文件,现在main.dstack.dmaze.d都生成了,就可以正常包含进来了(假如这时还没有生成,make就要报错而不是报警告了),相当于在Makefile中添了三条规则:

main.o main.d: main.c main.h stack.h maze.h
maze.o maze.d: maze.c maze.h main.h
stack.o stack.d: stack.c stack.h main.h

如果我在main.c中加了一行#include "foo.h",那么:

1、main.c的修改日期变了,根据规则main.o main.d: main.c main.h stack.h maze.h要重新生成main.omain.d。生成main.o的规则有两条:

main.o: main.c main.h stack.h maze.h
%.o: %.c
# commands to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<

第一条是把规则main.o main.d: main.c main.h stack.h maze.h拆开写得到的,第二条是隐含规则,因此执行cc命令重新编译main.o。生成main.d的规则也有两条:

main.d: main.c main.h stack.h maze.h
%.d: %.c
set -e; rm -f $@; /
$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; /
sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.$$$$ > $@; /
rm -f $@.$$$$

因此main.d的内容被更新为main.o main.d: main.c main.h stack.h maze.h foo.h

2、由于main.d被Makefile包含,main.d被更新又导致make重新读取整个Makefile,把新的main.d包含进来,于是新的依赖关系生效了。

------------------------

问题来了,Visual Studio的C++ 的获得头文件依赖的命令是很什么呢?【即 cl.exe 系列工具 参数】

CPLUSPLUS 获得 一个源文件的头文件依赖。即该文件所需要的所有头文件的更多相关文章

  1. Makefile自动生成头文件依赖

    前言 Makefile自动生成头文件依赖是很常用的功能,本文的目的是想尽量详细说明其中的原理和过程. Makefile模板 首先给出一个本人在小项目中常用的Makefile模板,支持自动生成头文件依赖 ...

  2. Makefile中自动生成头文件依赖

    为什么需要自动生成头文件依赖? 编译单个源文件时,需要获取文件中包含的头文件的信息,但是一般的Makefile不会在规则中明确写明文件依赖的头文件,所以单独修改头文件后,不会导致包含头文件的源文件重新 ...

  3. C语言学习_C如何在一个文件里调用另一个源文件中的函数

    问题 C如何在一个文件里调用另一个源文件中的函数,如题. 解决办法 当程序大了代码多了之后,想模块化开发,不同文件中存一点,是很好的解决办法,那我们如何做才能让各个文件中的代码协同工作呢?我们知道,m ...

  4. [转] 关于c++的头文件依赖

    http://www.cnblogs.com/yvesliao/p/3938730.html PS: 使用单向依赖 正在看google c++编程规范,里面对头文件依赖是这么说的: 1 2 3 4 5 ...

  5. 未能加载文件或程序集“Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”或它的某一个依赖项。系统找不到指定的文件

    发布的打包项目在本机测试好使,部署到客户服务器上报错 分析器错误消息: 未能加载文件或程序集“Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Cu ...

  6. 关于c++的头文件依赖

    正在看google c++编程规范,里面对头文件依赖是这么说的: 使用前置声明(forward declarations)尽量减少.h文件中#include的数量. 当一个头文件被包含的同时也引入了一 ...

  7. 机房重构所遇问题&quot;未能载入文件或程序集“DAL”或它的某一个依赖项。系统找不到指定的文件&quot;的解决的方法集锦

    敲七层登录的时候.忽然间认为敲三层搞清的思路瞬间又凌乱了.花了一天的时间边敲边梳理,最终整完了,执行的时候弹出了这种错误:未能载入文件或程序集"DAL"或它的某一个依赖项. 系统找 ...

  8. Error-ASP.NET:未能加载文件或程序集“CMSCalendar”或它的某一个依赖项。系统找不到指定的文件。

    ylbtech-Error-ASP.NET:未能加载文件或程序集“CMSCalendar”或它的某一个依赖项.系统找不到指定的文件. 1.返回顶部 1. “/”应用程序中的服务器错误. 分析器错误 说 ...

  9. 未能加载文件或程序集“Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5”或它的某一个依赖项。系统找不到指定的文件。

    在创建ASP.NET MVC项目过程中发生了这个异常 未能加载文件或程序集"Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0 ...

随机推荐

  1. Java中public、protected、default和private的区别

    public: 具有最大的访问权限,可以访问任何一个在classpath下的类.接口.异常等.它往往用于对外的情况,也就是对象或类对外的一种接口的形式. protected: 主要的作用就是用来保护子 ...

  2. 002.DNS-BIND简介

    一 Linux-BIND服务器简介 Bind是Berkeley Internet Name Domain Service的简写,它是一款实现DNS服务器的开放源码软件.已经成为世界上使用最为广泛的DN ...

  3. 利用在线HTTP接口测试工具ApiPost,模拟移动端请求API接口

    我们做开发一般都是在PC端,然而我们很多时候需要模拟移动端进行接口请求. 比如,当一个API需要根据用户不同的终端来返回不同的数据时,就需要一个工具来模拟不同的终端了. 利用接口测试工具ApiPost ...

  4. iOS 11开发教程(十五)iOS11应用视图的位置和大小

    iOS 11开发教程(十五)iOS11应用视图的位置和大小 当一个视图使用拖动的方式添加到主视图后,它的位置和大小可以使用拖动的方式进行设置,也可以使用尺寸检查器面板中的内容进行设置,如图1.52所示 ...

  5. javascript相关基础

    1.javascript:void(0) javascript:void(0)表示不做任何动作 Javascript中void是一个操作符,该操作符指定要计算一个表达式但是不返回值. <a hr ...

  6. 【BZOJ-2142】礼物 拓展Lucas定理

    2142: 礼物 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1313  Solved: 541[Submit][Status][Discuss] ...

  7. scrum vs devops vs sre

    DevOps&SRE 超越传统运维之道[北京站] IT大咖说 - 大咖干货,不再错过 http://www.itdks.com/eventlist/detail/908

  8. oracle 两个逗号分割的字符串 如何判断是否其中有相同值

    比如字段A: 'ab,cd,ef,gh'字段B: 'aa,bb,cc,dd' 没有相同值 字段A: 'ab,cd,ef,gh'字段B: 'aa,bb,cd,dd' 有相同值cd 1.CREATE OR ...

  9. Code Fragment-UI加载策略之-可视者优先加载

    通常情况 通常程序的UI不太复杂,我们会直接加载这些UI信息 复杂的UI 加载的元素就相对多一些. 加载的数据相对多. 因为UI元素和数据元素都比较多,加载的时间相对多. 可视者优先加载 不是默认的加 ...

  10. hdu1242 Rescue(BFS +优先队列 or BFS )

    http://acm.hdu.edu.cn/showproblem.php?pid=1242 题意:     Angel被传说中神秘的邪恶的Moligpy人抓住了!他被关在一个迷宫中.迷宫的长.宽不超 ...