【问题标题】:Makefile: declare PHONY targets as recipe prerequisteMakefile:将 PHONY 目标声明为配方先决条件
【发布时间】:2018-05-31 19:57:50
【问题描述】:

我的 Makefile 正在捆绑配方以并行处理大量文件(make <recipe> -j8 等)。但在处理文件之前,需要使用find 找到它们。这是一个耗时的操作,所以我只想为用户调用的确切配方运行它。我可以这样做:

test: FILES=$(shell find "$(SEARCHDIR)/" -mindepth 3 -maxdepth 3 -type f ! -regex $(SOMEREGEX))
test: $(FILES)
$(FILES):
    echo "do thing with file $@ here"

问题在于,由于文件本身已经存在,因此需要将它们声明为 .PHONY 才能运行配方,如下所示:

.PHONY: $(FILES)

但为了使其工作,FILES 变量需要存在并被填充,这需要我运行find 命令。这违背了我在调用test 之前不执行搜索以查找FILES 的目标。我需要的是这样的:

test: FILES=$(shell find "$(SEARCHDIR)/" -mindepth 3 -maxdepth 3 -type f ! -regex $(SOMEREGEX))
test: .PHONY: $(FILES)
test: $(FILES)
$(FILES):
    echo "do thing with file $@ here"

但是test: .PHONY: $(FILES) 是无效的语法并且不起作用。

【问题讨论】:

  • 明确一点,您的$(FILES) 配方实际上并没有生成特定于其目标的文件,对吗?
  • @Beta 正确。它实际上删除了在FILES 中找到的文件,在其他情况下将符号链接转换为文件等。

标签: makefile


【解决方案1】:

即使没有.PHONY 的东西,你的makefile 已经不能工作了;只是:

test: FILES=$(shell find "$(SEARCHDIR)/" -mindepth 3 -maxdepth 3 -type f ! -regex $(SOMEREGEX))

$(FILES):
        echo "do thing with file $@ here"

失败是因为make 在开始运行test 目标之前解析makefile 时,规则目标中的$(FILES) 扩展为空字符串。

我建议你在这里使用递归make;像这样写你的makefile:

FIND_FILES :=
FILES :=
ifneq ($(FIND_FILES),)
FILES := $(shell find "$(SEARCHDIR)/" -mindepth 3 -maxdepth 3 -type f ! -regex $(SOMEREGEX))
endif

test:
        $(MAKE) test-recurse FIND_FILES=1

test-recurse: $(FILES)
$(FILES):
        echo "do thing with file $@ here"
.PHONY: test test-recurse $(FILES)

【讨论】:

  • FWIW,我在递归 make 的配方中没有 find 命令的原因,如 $(MAKE) test-recursive FILES='$(shell ...)',是因为你说有很多文件,如果有有很多文件可能会耗尽环境空间,并且递归 make 命令不起作用。如果没有足够的问题来解决这个问题,那么这样做并避免FIND_FILESifneq 肯定更简单、更清晰。
  • 我一直在寻找递归解决方案,但是当我需要将额外的参数传递给原始命令时,比如make test -j4 ARG1=foo ARG2=bar ... 等,那么我必须硬编码将所有参数传递给递归$(MAKE) 调用?
  • 看起来像这里的答案:gnu.org/software/make/manual/html_node/…
  • 您无需执行任何操作。如果您使用$(MAKE) 变量(或${MAKE})来调用它,所有命令行选项和覆盖都会自动提供给子制作。
  • 我应该很清楚:虽然所有命令行选项和变量覆盖都会自动提供给子制作,但命令行上给出的 targets 不会传递给子制作(即没有意义)。但既然在这里你只想运行一个特定的目标,那应该没问题。