强制Makefile在构建目标之前执行脚本

30
我正在使用Makefiles。
然而,我想在执行任何目标之前执行一个命令(zsh脚本)。
我应该如何做?
谢谢!
4个回答

32

有几种技术可以在目标构建之前执行代码。你应该选择哪一种,取决于你想要做什么,以及为什么要这样做。(zsh脚本是做什么的?为什么必须执行它?)

你可以像@John建议的那样,将zsh脚本放置在第一个依赖项中。然后,除非它实际上生成名为zsh的文件,否则应将 zsh 目标标记为.PHONY

另一种解决方案(至少在GNU make中)是作为变量赋值的一部分调用$(shell ...)函数:

ZSH_RESULT:=$(shell zsh myscript.zsh)

当解析makefile时,这将立即执行脚本,并在执行任何目标之前执行。如果您递归调用makefile,则也将执行该脚本。


4
请注意,即使使用make的-n、--just-print、--dry-run或--recon选项, $(shell ...)也会运行。 - thejoshwolfe
很好的回答。有没有办法在退出make时运行命令?在这里发布了一个问题:https://dev59.com/7HrZa4cB1Zd3GeqP0D-4 - PonyEars
2
我喜欢这种方法。但是,myscript.zsh的输出没有被打印出来。我处于类似的情况,脚本会提示用户输入密码,因为sudo被调用。 - 170730350
@Saichovsky 如果你只想要输出被打印出来,你可以将 stdout 重定向到 stderr >&2。不确定它是否会使 sudo 生效,因为你需要 stdin。 - HectorJ

10

只需将其作为其他目标的依赖项即可。

foo.obj : zsh foo.c 
   rule for compileing foo.c

zsh: 
   rule for running zsh script.

或者,作为替代方案,让你的第一个目标依赖于它。

goal: zsh foo.exe

6
这里第一个解决方案的问题是,每次即使foo.c没有更新也会强制重新构建foo.obj。第二个解决方案的问题是,现在您无法使用多个目标调用您的makefile。 - Nate C-K

7

使用MAKECMDGOALS和双冒号规则在makefiles中实现预处理和后处理的解决方案。

MAKECMDGOALS是命令行上列出的目标。

第一步是从命令行获取第一个和最后一个目标,或者如果没有列出任何目标,则使用默认目标。

ifneq ($(MAKECMDGOALS),)
FIRST_GOAL := $(word 1, $(MAKECMDGOALS))
LAST_GOAL := $(word $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
else
FIRST_GOAL := all
LAST_GOAL := all
endif

双冒号规则允许按顺序执行同一目标的多个配方。您需要将所有命令行目标更改为双冒号规则。

#Dummy rule to set the default
.PHONY: all
all ::

#Preprocessing
$(FIRST_GOAL) ::
    echo "Starting make..."

all :: normal_prerequistes
    normal_recipe

other_stuff

#Postprocessing
$(LAST_GOAL) ::
    echo "All done..."

4

如果不修改现有的Makefile,也有解决方案(与被接受的答案不同之处在于这一点)。只需创建一个包含以下内容的makefile

.PHONY: all

all:
    pre-script
    @$(MAKE) -f Makefile --no-print-directory $(MAKECMDGOALS) MAKE='$(MAKE) -f Makefile'
    post-script

$(MAKECMDGOALS): all ;

唯一的缺点是即使没有其他任务需要执行,预处理和后处理脚本也会始终运行。但如果您使用其中一个 --dry-run 选项调用make,它们将不会被运行(与已接受答案的区别)。

我添加了 .PHONY: allall: version,其中“version”打印库版本。它不会在其他目标(如help)上执行。 - WestCoastProjects

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