现在我们的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.o、stack.o和maze.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-32.h \
/usr/lib/gcc/i486-linux-gnu/4.3.2/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.2/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的官方手册建议这样写:

  1. all: main
  2. main: main.o stack.o maze.o
  3. gcc $^ -o $@
  4. clean:
  5. -rm main *.o
  6. .PHONY: clean
  7. sources = main.c stack.c maze.c
  8. include $(sources:.c=.d)
  9. %.d: %.c
  10. set -e; rm -f $@; \
  11. $(CC) -MM $(CPPFLAGS) $< > $@.
     

    ; \

  12. sed 's,$∗\.o[ :]*,\1.o $@ : ,g' < $@.
     

    > $@; \

  13. rm -f $@.
     

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

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

$ make
Makefile:13: main.d: No such file or directory
Makefile:13: stack.d: No such file or directory
Makefile:13: maze.d: No such file or directory
set -e; rm -f maze.d; \
cc -MM maze.c > maze.d.

; sed′s,\(maze\)\.o[:]∗,\1.omaze.d:,g′<maze.d.

> maze.d; \
rm -f maze.d.

set−e;rm−fstack.d; cc−MMstack.c>stack.d.

; \
sed 's,stack\.o[ :]*,\1.o stack.d : ,g' < stack.d.

>stack.d; rm−fstack.d.

set -e; rm -f main.d; \
cc -MM main.c > main.d.

; sed′s,\(main\)\.o[:]∗,\1.omain.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.omaze.d:,g′<maze.d.

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

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

把原来的maze.d删掉。

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

这个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。

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

不管是Makefile本身还是被它包含的文件,只要有一个文件在make过程中被更新了,make就会重新读取整个Makefile以及被它包含的所有文件,现在main.d、stack.d和maze.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.o和main.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包含进来,于是新的依赖关系生效了。

makefile 自动处理头文件的依赖关系 (zz)的更多相关文章

  1. Makefile中头文件在依赖关系中作用

    摘于:http://bbs.csdn.net/topics/120024677 (1)在makefile的依赖关系中用不用体现.h头文件?(2)如果在依赖关系中要体现.h头文件,应该体现到什么层次?= ...

  2. Makefile目标,伪目标,头文件自动依赖

    目标 即我们最终要生成的文件,make默认生成第一个目标,注意 makefile中tab和空格不是一回事,规则使用tab缩进,编辑器不要设置诸如"将tab替换为空格之类的选项",目 ...

  3. Makefile 9——为依赖关系文件建立依赖关系

    现在我们再对complicated项目做一些更改,增加程序文件间依赖关系的复杂度. /× main.c ×/ #include"foo.h" int main(void) { fo ...

  4. (转)Ubuntu中使用dpkg安装deb文件提示依赖关系问题,仍未被配置

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5638149.html 参考网址: http://zhidao.baidu.com/link?url=b ...

  5. 一个简单的wed服务器SHTTPD(9)————main函数文件,Makefile,头文件

    主函数: #include "lcw_shttpd.h" //初始化时服务器的默认配置 extern struct conf_opts conf_para= { "/us ...

  6. Makefile一 头文件及库搜索路径

    头文件及库搜索路径 头文件的搜索路径: 头文件的搜索规则是:找到就使用,停止继续往下寻找 1: #include “mytest.h” 搜索的顺序为: (1)先搜索当前目录 (2)然后搜索编译时 -I ...

  7. [错误解决]Ubuntu中使用dpkg安装deb文件提示依赖关系问题,仍未被配置

    使用dpkg进行软件安装时,提示:dpkg:处理软件包XXX时出错:依赖关系问题,仍未被配置 使用如下命令,sudo apt-get install -f 等分析完之后,重新使用dpkg –i XXX ...

  8. 察看so文件的依赖关系

    使用arm-linux-androideabi-readelf 察看依赖动态库 /android-ndk-r8d/toolchains/arm-linux-androideabi-4.7/prebui ...

  9. linux通过文件查找依赖关系

    通过文件查找安装包安装缺少libstdc++6这个文件在ls /usr/lib/libstd*下有两个文件/usr/lib/libstdc++.so.6 /usr/lib/libstdc++.so.6 ...

随机推荐

  1. css height:100%失效

    有时做移动端页面时,需要用到height:100%来控制,但是设置完后会发现,用百分比的高是不生效的. 经过上网浏览等查阅资料得知,是否可以使用百分比是根据父级对象定义的.所以解决方法就是在body和 ...

  2. 错误:Retrieving the COM class factory for component with CLSID {000209FF-0000-0000-C000-000000000046} failed due to the following error: 80070005 拒绝访问。

    最近,把一个网站部署到另一个服务器上,网站中一个功能word转pdf报下面错误: 在此附上解决方案: 方法1:配置Web.Config文件,在每次请求时模拟本地系统的账户. 具体操作:在Web.Con ...

  3. mvc 用户控件 ascx 获取 View 页面的值

    <%Html.RenderAction("AscxSideNav", "UI", new {itemName=ViewData["ItemNam ...

  4. js EasyUI前台 全选的实现

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWcAAAEQCAIAAADj/SKjAAAgAElEQVR4nO1dz48ry1W+/5N3swaFEP ...

  5. 查看Safari和钥匙串中的密码

    Safari Safari的同步书签功能很棒,还可以看到其他设备没关掉的网页.为了省时间,一些经常进的网站,比如博客,邮箱等,我都会选择让Safari保存密码,还使用iCloud同步!因为一直很放心苹 ...

  6. Bresenham画直线,任意斜率

    function DrawLineBresenham(x1,y1,x2,y2) %sort by x,sure x1<x2. if x1>x2 tmp=x1; x1=x2; x2=tmp; ...

  7. mount的艺术

    在阅读本文之前,我假设你已经对Linux系统下的硬盘.光盘的设备命令规则有所了解,比如sda和sda1的关系,以及hda.sda.fd.cdrom等设备. === 1 我把U盘插到USB口上了,下一步 ...

  8. 跟我玩ADB——初识ADB

    ADB全称Android Debug Bridge, 是Android SDK的一个可以真实操作手机设备里面内容的工具. 一.功能介绍: 进入设备的shell进行命令行操作 使用5037端口,对设备进 ...

  9. 关于abbr 的一些用法

    <span style="margin: 20px;">位 000置:</span><abbr title='四川省绵阳市梓潼县文昌镇金龙乡一大队二组 ...

  10. apache2.4下载与安装

    step1 下载apache 百度“apache下载”,找到官网链接,如下 2. 点进去后选择 Files for microsoft windows,如下 3. 前三个任选一个,这里我们选第一个,如 ...