增量构建¶
antel build 的目标只有一个:不重编没有变化的东西,也绝不留下一份过期的目标。
数据从哪来¶
一次编译会同时产生两个文件:
依赖文件记的是这次编译真正读过的输入,包括源文件本身和它 include 的所有头文件(系统头文件除外):
这份依赖清单是增量判断的关键,它解决了一个靠源码列表无法解决的问题:source 里只有 .c 文件,改一个头文件并不会改变任何源文件的 hash,但受影响的编译单元必须重编。
判断哪些要重编¶
build 分三步:
- 算 hash 变化:把配置里的源文件、
include_directories下的文件、以及上次构建记录的依赖文件里出现的全部路径,逐个算 md5,与基线log/hashes比较,得到本次变化的文件集合 - 挑出受影响的编译单元:遍历配置里的每个源文件,只要满足下面任意一条,就需要重新编译
- 目标文件不存在(被手工删掉、或构建中断)
- 依赖文件不存在(例如没有走过一次完整编译)
- 源文件本身在变化集合里
- 该编译单元的依赖文件里,有文件在变化集合里
- 按需编译后再统一链接:只编译第 2 步挑出来的源文件,然后重新链接,最后用这次构建的全部输入刷新 hash 基线。编译阶段默认并行(
jobs,见配置参考),并发度只影响速度,不影响判定结果
变化清单与重编清单都会落盘,便于核对:
一个例子¶
三个源文件共用一个头文件:
只改 inc/common.h,然后 antel build:
只有 a.c、b.c 被重编,main.c 不动。再只改 main.c,需重新编译文件:1。
hash 基线¶
log/hashes 记录的是上次成功构建的全部输入文件与它们的 md5。基线在构建成功之后才刷新,因此一次失败的构建不会被当成「已经编好了」。
基线文件不存在、或读到不认识的内容(例如从旧版本升级上来的旧格式基线),会提示一次并按全量构建处理,跑一次 rebuild 即可回到增量状态。
什么时候不会增量¶
| 情况 | 行为 |
|---|---|
基线不存在(首次构建、clean 之后) |
全部源文件视为变化,做一次全量构建 |
| 基线格式无法识别 | 同上,并打印一次提示 |
obj/ 或 log/ 被删 |
目标文件缺失,逐个补编 |
| 某个目标文件被手工删除 | 该目标文件补编,其余不动 |
执行 antel rebuild |
清空 obj/ 与 log/,无条件全部重编 |
常见问题¶
build 说「项目没有改动」但我明明改了东西?
先看 log/hashes_diff:如果它是空的,说明变化的文件不在跟踪范围内。只有 source、include_directories 下的文件,以及上次编译记录过的依赖才参与判断。比如一个从未被任何源文件 include 的头文件,改了它不会触发重编——这是对的。若确认改的文件在跟踪范围内,用 antel rebuild 重建基线。
为什么改了头文件却只重编了一部分源文件?
这正是依赖文件的作用:只有 include 了该头文件的编译单元需要重编。可以在 log/<输出目录>/obj/*.d 里看到每个编译单元的真实依赖。
构建失败了,基线会不会被改坏?
不会。编译或链接返回非 0 时立即终止,基线只在构建成功之后刷新,下次 build 仍会重编失败的部分。