现在的位置: 首页 > 综合 > 正文

makefile自动生成依赖性

2013年09月06日 ⁄ 综合 ⁄ 共 2877字 ⁄ 字号 评论关闭

八、自动生成依赖性
在Makefile 中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的main.c
中有一句“#include "defs.h"”,那么我们的依赖关系应该是:
main.o : main.c defs.h
但是,如果是一个比较大型的工程,你必需清楚哪些C 文件包含了哪些头文件,并且,你
在加入或删除头文件时,也需要小心地修改Makefile,这是一个很没有维护性的工作。为了
避免这种繁重而又容易出错的事情,我们可以使用C/C++编译的一个功能。大多数的C/C++
编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。
例如,如果我们执行下面的命令:
cc -M main.c
其输出是:
main.o : main.c defs.h
于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,
而由编译器自动生成了。需要提醒一句的是,如果你使用GNU 的C/C++编译器,你得用
“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。
gcc -M main.c 的输出是:
main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \
/usr/include/bits/sched.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/include/bits/wchar.h /usr/include/gconv.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h
gcc -MM main.c 的输出则是:
main.o: main.c defs.h
那么,编译器的这个功能如何与我们的Makefile 联系在一起呢。因为这样一来,我们的
Makefile 也要根据这些源文件重新生成,让Makefile 自已依赖于源文件?这个功能并不现
实,不过我们可以有其它手段来迂回地实现这一功能。GNU 组织建议把编译器为每一个源
文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”
的Makefile 文件,[.d]文件中就存放对应[.c]文件的依赖关系。
于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让make 自动更新或自成[.d]文件,并
把其包含在我们的主Makefile 中,这样,我们就可以自动化地生成每个文件的依赖关系了。
这里,我们给出了一个模式规则来产生[.d]文件:
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' < $@.$$$$ > $@; \
rm -f $@.$$$$
这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除所有的目标,
也就是[.d]文件,第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”
表示模式“%.d”文件,如果有一个C 文件是name.c,那么“%”就是“name”,“$$$$”意为一个
随机编号,第二行生成的文件有可能是“name.d.12345”,第三行使用sed 命令做了一个替换,
关于sed 命令的用法请参看相关的使用文档。第四行就是删除临时文件。

附解释:

sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' < $@.$$$$ > $@; 

这行是模式替换。sed 's/part-of-find/part-of-replace/gw' <inputfile>outputfile

\($*\)\.o[ :]*是在字符串匹配的模式,\($*\)匹配并且被处理的模式。\.o[ :]*代表一个. :和任意字符。

\1.o $@ :是匹配后,被替换的模式。\1.o代表\($*\)被匹配到得内容。

g代表查询全文件匹配。


总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即把依
赖关系:
main.o : main.c defs.h
转成:
main.o main.d : main.c defs.h
于是,我们的[.d]文件也会自动更新了,并会自动生成了,当然,你还可以在这个[.d]文件中
加入的不只是依赖关系,包括生成的命令也可一并加入,让每个[.d]文件都包含一个完赖的
规则。一旦我们完成这个工作,接下来,我们就要把这些自动生成的规则放进我们的主
Makefile 中。我们可以使用Makefile 的“sinclude”命令,来引入别的Makefile 文件(前面讲过),
例如:
sources = foo.c bar.c
include $(sources:.c=.d)
上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]
的字串都替换成[.d],关于这个“替换”的内容,在后面我会有更为详细的讲述。当然,你得
注意次序,因为include 是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标。

Makefile 示例

 

#file start

SOURCES = $(wildcard *.c)

OBJS := $(patsubst %.c,%.o, $(SOURCES))

 

all: main

 

%.d: %.c

        @set
-e; rm -f $@; \

        $(CC)
-MM  $< > $@.$$$$; \

        sed
's/\($*\)\.o[ :]*/\1.o $@ : /g' < $@.$$$$ > $@; \

        rm
-f $@.$$$$

 

sinclude $(SOURCES:.c=.d)

 

main:$(OBJS)

        $(CC)
-o main $(OBJS)

 

clean:

        rm
-f *.o test *.d

#file end


 

说明:

SOURCES
= $(wildcard *.c)是展开所有的*.c文件。wildcard是展开的意思。属于make函数。

(1)       OBJS
:= $(patsubst %.c,%.o, $(SOURCES))

     $(SRC)中的.c文件列表替换成对应的.o文件,在这里不能用“*.c”“*.o”来代替,试想在.o文件还不存在的情况下,规则语句“main:
$(OBJS)
”就会被解析成“main:*.o”,main依赖于“*.o”,“*.o”对应的规则又去哪找呢

抱歉!评论已关闭.