GCC+Make 自动生成 Makefile 依赖
本文内容基于 GNU MAKE。
BASIS
一些 makefile 的基础知识。
wildcard
假设当前目录下有文件 a.cpp 和 b.cpp,定义:
eg1=*.cpp
eg2=$(wildcard *.cpp)
则 rm $(eg1)
的展开为 rm *.cpp
,rm $(eg2)
的展开为 rm a.cpp b.cpp
.PHONY
.PHONY
用于表示其后的目标文件是一个伪目标文件。
在 makefile 的一般格式 targets : prerequisitions
中,targets
为目标文件,一般是实际存在的文件,如 a.o
等;但有些规则并不生成实际存在的文件或不生成文件 targets
中指定的文件,这样的目标文件称为伪目标文件。
典型例子如常用于清理中间文件的伪目标文件 clean
。通常其生成命令并不生成一个名为“clean”的文件。此时若不将其指定为伪目标文件,且项目当前目录下恰好存在一个目录或文件名为“clean”,则 make clean
时 make 会检查该目录或文件的时效性,故有可能直接提醒目标文件“clean”已经最新而不执行编写的命令。
静态模式
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
...
直接用例子说明
objects = foo.o bar.o
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
等价于
foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
常用自动变量
本节摘自:陈皓《跟我一起写 Makefile》
$@
表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。$<
依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。$^
所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。$+
这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标$*
目标模式中通配符%
之前的部分,例如目标是dir/a.foo.b
,模式为a.%.b
,则$*
的值就是dir/a.foo
此外,自动变量后可加 D 或 F 以实现取目录部分或文件部分。例如,当 $@
为 dir/foo.o
时,$(@D)
为 dir
,$(@F)
为 foo.o
。对用当前目录,取目录时值为 .
。
自动生成依赖(GCC)
-M 参数
GCC 的 -M
和 -MM
参数可以生成指定源文件的依赖项。如,在 a.cpp 中 include a.h 和 b.h,则执行以下命令
g++ -MM a.cpp
会得到输出如下
a.o : a.h b.h
-MM
生成的依赖项不包含标准库等依赖,而 -M
包含。
编写 Makefile
GNU 建议对每个源文件生成一个 .d
后缀的依赖说明文件,内容即上节编译器生成的内容。
直接 include 上一节中生成的依赖就可以通过 make 的自动推导进行编译了,但如果需要指定编译参数、在 make 过程中加入回显,可以考虑在 .d
直接加入命令。
对每个源文件建立 .d
依赖文件会使得项目文件翻倍,因此此处将所有的 .d
文件合并为一个文件。这样做带来的缺点是每次 make 依赖项都需要全部重新生成。
下面直接给出 makefile。
sources=a.cpp b.cpp c.cpp
# 替换后缀
dependencies=$(sources:.cpp=.d)
$(dependencies) : %.d : %.cpp
@set -e; \
g++ -MM $(flags) $< > $@.$$$$; \
sed 's,$(*F).o,$*.o,g' < $@.$$$$ > $@; \
echo ' @g++ -c $< -o $(@:.d=.o) $(flags)' >> $@; \
cat $@ >> dependencies.d; \
rm -f $@.$$$$; rm -f $@
cleand:
rm -f dependencies.d; \
dependencies.d : cleand $(dependencies)
include dependencies.d
Makefile 细节说明
*.d
文件是生成的临时依赖项,最终会被删除。$$$$
展开为随机的数字,用于建立临时文件。
$(depencencies)
的生成命令中,第三行用于在单个文件的依赖项中补全路径。例如,执行 g++ -MM src/a.cpp
后,GCC 给出的依赖项会是 a.o : xxx
,但项目中需要 src/a.o : xxx
的形式以避免命名冲突。sed
是 Linux 指令,此处用于进行文本替换。
生成命令的第四行向依赖文件中写入编译命令,此处也可以使用 echo 报告进度。第五行将该文件的依赖写入总的依赖文件。
最后 include 总的依赖文件。第一次执行 make 时依赖文件不存在,因此定义生成生成依赖文件的规则。生成前需要先删除旧的依赖文件,因为生成时使用的是追加方式。
注意使用这种方式编写的 makefile 在第一次 make 时由于不存在依赖会导致编译失败,第二次即可恢复正常,因为此时依赖文件 dependencies.d 就已经建立了。
其他
以下 Makefile 是对每个源文件生成 .d
依赖文件的版本。
$(dependencies) : %.d : %.cpp
@set -e; rm -f $@; \
g++ -MM $(flags) $< >> $@; \
sed 's,\($*\)\.o[ :]*,\1.o : $@ ,g' < $@ > $@.$$$$; \
sed 's,$(*F).o,$*.o,g' < $@.$$$$ > $@; \
echo ' @g++ -c $< -o $(@:.d=.o) $(flags)' >> $@; \
rm -f $@.$$$$
include $(dependencies)
最新文章
- ActiveMQ 简单搭建
- Server-Sent Events(HTML5 服务器发送事件)
- 51nod1019逆序数(归并排序/树状数组)
- C++ Primer Pluse_7_课后题
- JavaWeb开发必过关-Servlet学习(一)
- [CareerCup] 5.2 Binary Representation of Real Number 实数的二进制表示
- 【转】免费开源的FTP软件,FileZilla
- .NET和JSON
- iOS崩溃日志分析-b
- 老去的JEE,焕发生命
- IOS上传文件开发
- 结合ThreadLocal来看spring事务源码,感受下清泉般的洗涤!
- mui框架上下拉加载
- 4.11Python数据处理篇之Matplotlib系列(十一)---图例,网格,背景的设置
- Shell-find . -type f -name ";*.php"; -print0 | xargs -0 wc -l
- Confluence 6 通过 SSL 或 HTTPS 运行 - 创建或请求一个 SSL 证书
- linux每日命令(32):gzip命令
- POJ 1655 - Balancing Act - [DFS][树的重心]
- Android虚拟机与Java虚拟机 两种虚拟机的比较
- 学习BOS物流项目第十天
热门文章
- UVA - 10829 L-Gap Substrings (后缀自动机+线段树启发式合并)
- VCL界面控件DevExpress VCL Controls v19.1.3全新来袭
- GitHub入门和项目托管
- Python 正则表达式Ⅳ
- vue-upload 封装组件-上传组件
- cogs2039树的统计 x
- c++复习——一个小疑问
- 【Python】爬虫汇总
- fiddler(三)、会话框添加显示请求方法栏
- [BZOJ2286][Sdoi2011]消耗战(虚树上DP)