如何设置Makefile中仅执行一次的分组触发规则?
问题描述
假设我有一个包含一组前置条件的Makefile规则:
prereqs=a b c $(prereqs): bash configure.sh我希望a、b、c中的任意一个触发
bash configure.sh执行,但该脚本仅运行一次。我了解其他解决方法,也知晓可通过装饰器改变规则行为,请问是否有办法让make仅评估该规则一次?
答案
Make的多目标规则(如你编写的$(prereqs):结构)本质上会为每个目标生成独立的规则实例,默认情况下每个目标触发时都会执行规则体,无法直接让该规则仅被评估一次。但可以通过以下两种方式实现脚本仅执行一次的效果,同时贴合你对规则评估控制的需求:
方法1:哨兵文件触发(最常用且可靠)
通过引入一个哨兵文件作为统一触发点,让所有目标依赖这个文件,确保脚本仅在哨兵文件不存在或过期时执行一次:
prereqs=a b c # 哨兵文件,用于记录脚本是否已执行 .configure_done: bash configure.sh touch $@ # 创建哨兵文件标记执行完成 # 让a、b、c都依赖哨兵文件,同时生成自身目标文件 $(prereqs): %: .configure_done touch $@
这种方式利用Make的依赖追踪机制,只有当.configure_done需要更新时才会执行脚本,所有目标共享这个触发源,自然只会执行一次。
方法2:会话变量控制(基于Make内部状态)
如果要保留原多目标规则的结构,可以利用Make的会话变量标记脚本是否已执行,通过$(eval)动态修改变量状态:
prereqs=a b c # 初始化标记变量,0表示未执行 CONFIGURE_RUN ?= 0 # 定义规则模板,仅当标记为0时执行脚本 define CONFIGURE_TEMPLATE $(1): @if [ $(CONFIGURE_RUN) -eq 0 ]; then \ bash configure.sh; \ $(eval CONFIGURE_RUN=1); \ fi touch $(1) endef # 为每个前置目标生成规则 $(foreach p,$(prereqs),$(eval $(call CONFIGURE_TEMPLATE,$(p))))
第一次触发任意目标时,脚本会执行并将CONFIGURE_RUN设为1,后续目标触发时会跳过脚本执行。注意这种方式仅在同一个Make进程会话中有效,若分多次调用Make则不生效。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

