如何在Makefile中当目标由其他规则生成时跳过其对应规则
问题分析与修复方案
先看你遇到的情况:你的Makefile在默认执行时符合预期,但启用USE_CACHE参数后,build目标并没有被缓存规则跳过,反而还是执行了原本希望跳过的命令——这确实是个典型的Makefile依赖逻辑陷阱。
先贴出你的原始Makefile方便对照:
all: build build: ifdef USE_CACHE @printf ":sigh: failure :(\nThis should have been skipped by cache's recipe.\n" endif @printf "Building ...\n" touch $@ ifdef USE_CACHE build: cache cache: @printf "Installing cache ...\n" touch $@ @printf "Trying to skip build because cache satisfied it ... \n" touch build endif
当执行rm build cache; make USE_CACHE=1时,你会看到不想看到的输出:
Installing cache ... touch cache Trying to skip build because cache satisfied it ... touch build :sigh: failure :( This should have been skipped by cache's recipe. Building ... touch build
问题根源
Make的执行逻辑是先判断目标是否需要构建,再处理依赖:
- 一开始
build不存在,Make已经决定要执行build的命令 - 发现
build依赖cache,于是先构建cache,在这个过程中创建了build - 但Make已经进入了
build的构建流程,哪怕此时build已经存在,还是会执行预先定义好的build命令
另外你在Makefile中对build目标的规则是分两段定义的,启用USE_CACHE时会合并build的命令和依赖,这让问题更隐蔽——build既有依赖cache,又有自己的执行命令,导致Make不会因为cache规则创建了build就跳过后续操作。
修复后的Makefile
我们可以用条件编译把两种场景的build规则完全分开,让启用缓存时build只依赖cache、自身无执行命令:
all: build ifdef USE_CACHE # 启用缓存时:build仅依赖cache,无自身命令 build: cache cache: @printf "Installing cache ...\n" touch $@ @printf "Trying to skip build because cache satisfied it ... \n" touch build else # 未启用缓存时:正常执行build构建逻辑 build: @printf "Building ...\n" touch $@ endif
测试验证
- 首次启用缓存执行:
输出符合预期,没有执行rm build cache; make USE_CACHE=1build的命令:Installing cache ... touch cache Trying to skip build because cache satisfied it ... touch build - 缓存已存在时执行
make USE_CACHE=1,输出为空(所有目标已是最新状态) - 未启用缓存时执行,和原来的正常输出一致
内容的提问来源于stack exchange,提问作者Trevor
相关产品推荐
相关产品推荐

