Makefile 学习

来源:互联网 发布:淘宝男模特 编辑:程序博客网 时间:2024/06/05 19:40

最近重新温习了陈皓写的《跟我一去写Makefile》,这本书浅显易懂,容易入门。当然书中有几处对于新手来讲不容易理解,我对其中一些不好理解的地方单独拿出来,加以解释,以备后用。
其中有一段提到”自动声称的依赖性”,其中有个例子不好理解。文中描述如下:
“大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。
例如,如果我们执行下面的命令:

cc -M main.c

其输出是: main.o : main.c defs.h

需要提醒一句的是,如果你使用 GNU 的 C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。

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 $@.$$$$

解释如下:
第一行不用多说;
第二行:@set -e; rm -f $@; /
1. @set -e;
// @表示makefile执行这条命令时不显示出来,
// set -e; 表示此脚本在执行时返回非零的值,此脚本将终止运行,查找帮助文件如下:

ricky@ubuntu:~$ help set-e  Exit immediately if a command exits with a non-zero status.(Using + rather than - causes these flags to be turned off.//)

2.rm -f $@;
删除生成的目标文件(即 *.d)

第三行: $(CC) -M $(CPPFLAGS) $< > $@.$$$$; /
1. $(CC) -M $(CPPFLAGS) $<
$< 依赖的目标集(即 *.c) ,
-M: 表示生成文件依赖关系,
2. > $@.$$$$
> 重定向输出,
$@:表示生成的目标文件(即 *.d),
$$: 表示本身的Process ID
总的意思就是: 将a.c 文件生成的依赖关系报存在a.d.1212 文件中.(假设 a.c 为文件名, 12为Process ID).
备注: 编译依赖文件,并把它的名字根据进程号命名成一个临时文件,用进程号命名是一个技巧,这样重名的可能性很小.

第四行: sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.$$$$ > $@; /
这一行用到了sed编辑器, sd是stream editor的缩写,流编辑器的意思.它可以对来自文件或者标准输入的输入流进行变换,
它常被用作管道中的过滤器.详细可以查看帮助文档.

sed命令的结构是s,match,replace,g, 其中s后面的,号就是常见的/,一般写成s/pattern/pattern/的形式,但也可以把/替换成其他符号,比如这里是逗号。g是全局的意思,具体你查查sed。match和replace都是正则表达式。match部分,要先解析makefile的元字符,$*$@,这个你查查 makefile。/(/)是为了后面的引用,后面的/1就是引用这里/(和/)之间括起的部分模式。[ :]也很好理解,你查查正则表达式的文档。s,match,replace,g 对应 s,/($*/)/.o[ :]*,/1.o $@ : ,g

分别对应如下:

s = smatch = /($*/)/.o[ :]*            // 需要熟悉正则表达式replace = /1.o $@ :           // 需要熟悉正则表达式     g = g

即第四行可以写为: sed sed_cmd < $@.$$$$ > $@;

< $@.$$$$ 表示sed 处理的数据为$@.$$$$文件内容,就是上面生成的临时文件(a.d.1212), > $@ 表示将sed处理后的内容重定向到$@(a.12)文件中.

总得意思就是: 将a.d.1212文件中的内容经过sed命令的处理,输出到a.d文件中
备注:用 < 来改变读进的数据信道(stdin),使之从指定的档案读进;
用 > 来改变送出的数据信道(stdout, stderr),使之输出到指定的档案;
整句话的意思是把临时文件做一个修改然后写入目标文件。这个修改是这样的,临时文件的第一行是
a.o: a.c …
修改后是
a.o a.d : a.c …

第5行 rm -f $@.$$$$
删除临时文件(a.d.1212)

综上所述: (假设针对的是文件a.c)

%.d: %.c

// 所有的.d文件依赖于.c文件

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

// 删除前期生成的目标文件(a.d)

$(CC) -M $(CPPFLAGS) $< > $@.$$$$; /

// 将编译器针对源文件(a.o)生成的依赖关系 保存到临时文件(a.1212)中
// 备注:12为当前进程号

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

// 对临时文件中的内容进行过滤修改 保存到目标文件(a.d)中

rm -f $@.$$$$

// 删除临时文件(a.1212)

原创粉丝点击