在构建任何目标之后(即退出之前),强制Makefile执行脚本

5
我希望在make退出时执行一个shell命令,无论它构建了什么目标。似乎make没有直接实现这一功能的方法。但是,以下是一个例子,在启动时让make执行一个shell命令,而不管目标是什么: 强制Makefile在构建目标之前执行脚本 是否有类似的方法可以让make在退出之前执行一次shell命令?我可以将该命令放在每个目标的底部,但是a)它会被执行多次,b)这很丑陋且难以管理。

你的使用场景是什么? - JesperE
1
当退出 make 进程时,我需要将共享资源标记为已释放(我使用问题中链接中所示的方法在 makefile 开始时将其标记为繁忙)。 - PonyEars
4个回答

4

正如Janos所说,make没有内置的功能来完成此操作。您可以使用他建议的包装器。您还可以通过递归将其推入您的makefile中,如果您愿意接受某些程度的“bleah”。它可能看起来像这样:

ifdef RECURSING

    ... normal makefile goes here ...

else

.DEFAULT all:
        @$(MAKE) RECURSING=true $@ ; r=$$? ; \
         <extra shell command here>; \
         exit $$r

endif

谢谢,我最喜欢这种方法,因为我只需键入make而无需记住执行脚本。 - PonyEars
一个问题:我注意到我被迫使用“make all”而不只是能够键入“make”。有没有办法解决这个问题? - PonyEars
太棒了,谢谢!GNU make文档中的.DEFAULT部分有点难以理解,但我现在明白了。 - PonyEars

3
您可以使用两个makefile,其中一个调用另一个。例如:
wrapper.mk:
foo bar:
        make -f other.mk $@
        @echo finished
.PHONY: foo bar

other.mk:

foo bar:
        @echo Building $@

然后运行"make -f wrapper.mk foo",wrapper.mk运行other.mk来构建foo。.PHONY目标意味着无论文件是否存在,这些目标都将每次传递给other.mk进行考虑。

这需要在wrapper.mk中列出所有目标-可能有使用默认规则或匹配任何规则的方法可以避免这种情况。


我想补充说,Shell脚本包装器解决方案更简单,更不容易出错。 - Gavin Smith

2
你可以创建一个包装脚本,它只是将所有参数传递给 make,然后运行你想要的自定义操作:
#!/bin/sh
make "$@" && ./custom_script.sh

1
可能可以使用包装Makefile而不是包装Shell脚本。这类似于MadScientist的答案,只是有两个Makefile而不是一个。 - Gavin Smith
谢谢,这是我一直在使用的黑客技巧,但缺点是,人们必须记得调用包装器。 - PonyEars

1

@Gavin Smith@MadScientist的答案与以下其他问题相结合:

  1. 如何从命令行向Makefile传递参数?
  2. Makefile匹配任何目标/任务
  3. 如何获取makefile调用目标?
  4. 禁止“all”无事可做
  5. Makefile:声明所有目标为PHONY
  6. 在目标主体中设置全局变量的Makefile

我建立了这个:

Makefile

ECHOCMD:=/bin/echo -e
SHELL := /bin/bash

define DEFAULTTARGET :=
    printf 'Calling makerules.mk "%s"\n\n' "${MAKECMDGOALS}"
    make -f makerules.mk ${MAKECMDGOALS}

    printf '\n'
    printf 'Running something after all rules finished\n'
endef

%:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"

    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval IS_MAKEFILE_RUNNING_TARGETS=1)

all:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"

    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval IS_MAKEFILE_RUNNING_TARGETS=1)

makerules.mk

all:
    printf 'Calling my all rule\n'

foo:
    printf 'Calling my foo rule\n'

bar:
    printf 'Calling my bar rule\n'

xyzzy:
    printf 'Calling my xyzzy rule\n'

使用示例:

  1. make

    printf '调用 makerules.mk "%s"\n\n' ""
    调用 makerules.mk ""
    
    make -f makerules.mk
    make[1]: 进入目录 '/cygdrive/d/User/Downloads'
    printf '调用我的所有规则\n'
    调用我的所有规则
    make[1]: 离开目录 '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf '在所有规则完成后运行一些东西\n'
    在所有规则完成后运行一些东西
    
  2. make all

    printf '调用 makerules.mk "%s"\n\n' "all"
    调用 makerules.mk "all"
    
    make -f makerules.mk all
    make[1]: 进入目录 '/cygdrive/d/User/Downloads'
    printf '调用我的所有规则\n'
    调用我的所有规则
    make[1]: 离开目录 '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf '在所有规则完成后运行一些东西\n'
    在所有规则完成后运行一些东西
    
  3. make all foo

    printf '调用 makerules.mk "%s"\n\n' "all foo"
    调用 makerules.mk "all foo"
    
    make -f makerules.mk all foo
    make[1]: 进入目录 '/cygdrive/d/User/Downloads'
    printf '调用我的所有规则\n'
    调用我的所有规则
    printf '调用我的 foo 规则\n'
    调用我的 foo 规则
    make[1]: 离开目录 '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf '在所有规则完成后运行一些东西\n'
    在所有规则完成后运行一些东西
    
  4. make all foo bar

    printf '调用 makerules.mk "%s"\n\n' "all foo bar"
    调用 makerules.mk "all foo bar"
    
    make -f makerules.mk all foo bar
    make[1]: 进入目录 '/cygdrive/d/User/Downloads'
    printf '调用我的所有规则\n'
    调用我的所有规则
    printf '调用我的 foo 规则\n'
    调用我的 foo 规则
    printf '调用我的 bar 规则\n'
    调用我的 bar 规则
    make[1]: 离开目录 '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf '在所有规则完成后运行一些东西\n'
    在所有规则完成后运行一些东西
    
  5. make all foo bar xyzzy

    printf '调用 makerules.mk "%s"\n\n' "all foo bar xyzzy"
    调用 makerules.mk "all foo bar xyzzy"
    
    make -f makerules.mk all foo bar xyzzy
    make[1]: 进入目录 '/cygdrive/d/User/Downloads'
    printf '调用我的所有规则\n'
    调用我的所有规则
    printf '调用我的 foo 规则\n'
    调用我的 foo 规则
    printf '调用我的 bar 规则\n'
    调用我的 bar 规则
    printf '调用我的 xyzzy 规则\n'
    调用我的 xyzzy 规则
    make[1]: 离开目录 '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf '在所有规则完成后运行一些东西\n'
    在所有规则完成后运行一些东西
    

相关问题:

  1. 停止执行Makefile
  2. 在不引发错误的情况下强制退出Makefile目标
  3. 抑制Make规则错误输出
  4. Make:如何在命令失败后继续执行?
  5. https://unix.stackexchange.com/questions/460606/make-how-to-suppress-make-error-messages-without-suppressing-other-output
  6. Makefile中使用在目标中定义的变量
  7. 如何使makefile函数停止当前目标?
  8. 我可以在规则之外使makefile中止吗?
  9. 从MAKECMDGOALS中删除目标?
  10. 如何检测是否设置了makefile **`--silent`** / **`--quiet`** 命令行选项?

更新

这与上面的内容相同,但现在您可以在单个Makefile中拥有所有内容:

  1. 从makefile获取makefile的名称

Makefile

ECHOCMD:=/bin/echo -e
SHELL := /bin/bash


ifeq (${IS_MAKEFILE_RUNNING_TARGETS},)

MAKEFILE_JUSTNAME := $(firstword ${MAKEFILE_LIST})

define DEFAULTTARGET :=
    printf 'Calling "%s" "%s"\n\n' "${MAKEFILE_JUSTNAME}" "${MAKECMDGOALS}"
    make -f ${MAKEFILE_JUSTNAME} ${MAKECMDGOALS}

    printf '\n'
    printf 'Running something after all rules finished\n'
endef

%:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"

    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval export IS_MAKEFILE_RUNNING_TARGETS=1)

all:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"

    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval export IS_MAKEFILE_RUNNING_TARGETS=1)



else

all:
    printf 'Calling my all rule\n'

foo:
    printf 'Calling my foo rule\n'

bar:
    printf 'Calling my bar rule\n'

xyzzy:
    printf 'Calling my xyzzy rule\n'


endif

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接