GNU Make中条件更新生成文件,避免不必要重复构建
解决GNU Make中条件生成头文件的重复构建问题
我在构建流程里用工具生成C++头文件,只有当生成内容和磁盘现有文件不一致时才写入,目的是避免触发依赖该头文件的目标文件重编译。但用GNU Make实现时,临时生成文件$(TEMP_GENERATED_FILE)几乎总是比目标文件$(GENERATED_FILE)新,导致对应的规则每次构建都会执行,我希望内容无变更时make不执行任何操作。
之前尝试的方案:
$(TEMP_GENERATED_FILE): $(SCRIPT_INPUT_FILES) $(SCRIPT) $< -o $@ # 试图让生成临时文件后重新读取Makefile,让下方条件生效 .PHONY: run_script run_script: $(TEMP_GENERATED_FILE) -include run_script ifeq ($(shell cmp -s "$(TEMP_GENERATED_FILE)" "$(GENERATED_FILE)" && echo identical || echo different), different) $(GENERATED_FILE): $(TMP_GENERATED_FILE) cp -f "$<" "$@" endif
但遇到了GNU Make文档中提到的问题:标记为PHONY的目标不会触发Makefile重新生成,导致逻辑失效。
可行解决方案
直接把内容对比和条件写入的逻辑放到规则命令里,同时处理文件时间避免重复构建,不需要依赖Makefile重新读取:
$(GENERATED_FILE): $(SCRIPT_INPUT_FILES) # 先生成临时文件 $(SCRIPT) $< -o $(TEMP_GENERATED_FILE) # 对比临时文件和目标文件内容 @if cmp -s $(TEMP_GENERATED_FILE) $(GENERATED_FILE); then \ # 内容相同,删除临时文件,不更新目标 rm -f $(TEMP_GENERATED_FILE); \ echo "$@ 内容无变更,无需更新"; \ else \ # 内容不同,覆盖目标文件后删除临时文件 cp -f $(TEMP_GENERATED_FILE) $(GENERATED_FILE); \ rm -f $(TEMP_GENERATED_FILE); \ echo "$@ 已更新"; \ fi
或者拆分两步规则,逻辑更清晰:
# 第一步:生成临时文件(仅当输入文件变化时执行) $(TEMP_GENERATED_FILE): $(SCRIPT_INPUT_FILES) $(SCRIPT) $< -o $@ # 第二步:对比并更新目标文件 $(GENERATED_FILE): $(TEMP_GENERATED_FILE) @if ! cmp -s $< $@; then \ cp -f $< $@; \ echo "更新了 $@"; \ else \ echo "$@ 已是最新版本"; \ # 同步目标文件时间,确保它比临时文件新,避免下次重复触发 touch $@; \ fi # 不管是否更新,都删除临时文件 rm -f $(TEMP_GENERATED_FILE)
这样实现的核心逻辑:
- 只有当输入文件变化时,才会重新生成临时文件
- 临时文件生成后,仅在内容与目标文件不同时才覆盖目标,避免不必要的重编译
- 内容相同时,通过
touch更新目标文件的修改时间,确保它比临时文件(已删除)“更新”,下次构建时不会重复执行规则
内容的提问来源于stack exchange,提问作者ryan
相关产品推荐
相关产品推荐

