【问题标题】:Dynamic targets based on the dependency in Makefile基于 Makefile 中依赖的动态目标
【发布时间】:2022-01-13 11:31:40
【问题描述】:

有几种类似的问题,但我无法将任何提议的概念适用于我的案例。

只是提供一点背景信息:我有一组 Julia 文件,它们将绘图创建为 PDF,这是创建科学论文的制作过程的一部分,例如:

plots = $(shell find $(PLOT_PATH)/*.jl | sed 's/\.jl/\.pdf/g')

$(PLOT_PATH)/%.pdf: $(PLOT_PATH)/%.jl $(JULIA_SYSIMAGE)
    $(JL) --project $< -o $(PLOT_PATH)

$(DOCUMENT_FILENAME).pdf: FORCE $(plots) $(figures)
    latexmk $(DOCUMENT_FILENAME).tex

在当前设置中,每个XYZ.jl 文件都在创建一个XYZ.pdf 文件,并且它工作得非常好。

现在我正在处理从单个 Julia 文件创建多个绘图会容易得多的情况,所以像这样的脚本:

#!/usr/bin/env julia
using PGFPlotsX
...
...
pgfsave("whatever.pdf")
pgfsave("another.pdf")
pgfsave("yetanother.pdf")

这样人们就可以通过grep pgfsave SCRIPT | awk... 找出目标。但是,我不知道如何根据依赖文件(Julia 脚本)的内容生成动态目标​​(绘图)。

我的问题的 MWE 如下:我有几个文件(依赖项)正在生成一堆目标,这些目标在这些文件中定义(并且可以通过 awk/grep/sed/whatever 访问)。现在,假设这些只是*.txt 文件,每一行都是一个目标。

文件: a.txt

foo
bar
baz

文件: b.txt

naarf
fjoord

一个非常基本的(非工作)手册Makefile 来演示目标将是这样的(它不起作用,因为它无法弄清楚如何制作foo 等,但它显示了@987654335 的模式@需要重复):

文件: Makefile

all_products := $(shell find *.txt | xargs cat)

final_product: $(all_products)
    echo $< > $@

(foo bar baz): a.txt
    touch $(shell cat $<)

(narf fjoord): b.txt
    touch $(shell cat $<)

所以原则上,我需要一些东西来“处理”依赖项 (*.txt) 以创建目标列表,例如

$(shell cat $%): %.txt
    echo $< > $@

但我无法获得对目标端依赖项的引用($% 不起作用)。

有什么想法吗?也许整个方法只是个坏主意;)

【问题讨论】:

  • 在您的情况下,尝试动态创建 makefile-fragments 可能是最简单的 (make.mad-scientist.net/constructed-include-files)?
  • 嗯,是的,我尝试过这种方法,但很难理解它。我会更加努力,也许有人同时有一个现有的解决方案;)

标签: makefile gnu-make


【解决方案1】:

GNU make foreach, eval and call functions 的组合可能是您需要的。用你的例子:

TXT := $(wildcard *.txt)

.PHONY: all

.DEFAULT_GOAL := all

define MY_MACRO
$(1)-targets := $$(shell cat $(1))

$$($(1)-targets): $(1)
    echo $$< > $$@

all: $$($(1)-targets)
endef
$(foreach t,$(TXT),$(eval $(call MY_MACRO,$(t))))

(注意宏定义中的$$,是必须的)。然后:

$ make
make
echo a.txt > foo
echo a.txt > bar
echo a.txt > baz
echo b.txt > naarf
echo b.txt > fjoord

如果您希望配方一次构建所有目标,您将需要足够新的 GNU make 版本(4.3 或更高版本)及其新的rule with grouped targets (x y z&amp;: w):

TXT := $(wildcard *.txt)

.PHONY: all

.DEFAULT_GOAL := all

define MY_MACRO
$(1)-targets := $$(shell cat $(1))

$$($(1)-targets)&: $(1)
    touch $$($(1)-targets)

all: $$($(1)-targets)
endef
$(foreach t,$(TXT),$(eval $(call MY_MACRO,$(t))))

然后:

$ make
touch foo bar baz
touch naarf fjoord

请注意,在这种情况下,我们还可以使用更简单且更少依赖 GNU make 的解决方案。只需使用空的虚拟文件作为时间戳,例如 .a.txt.tag 代表 a.txt,以及静态模式规则:

TXT := $(wildcard *.txt)
TAG := $(patsubst %,.%.tag,$(TXT))

.PHONY: all

all: $(TAG)

$(TAG): .%.tag: %
    touch `cat $<` $@

【讨论】:

  • 非常感谢!我仍在努力完全理解,但它有点工作。唯一缺少且我无法弄清楚的步骤是,在我的情况下,我只需要为每个TXT 执行一个作业,它将同时生成所有目标,而不是为每个目标单独启动它。如果其中一个目标丢失,它应该简单地再次调用它并重新创建所有目标,这就是我试图用touch 命令抽象的内容。我认为拥有$$($1)-targets) 意味着所有目标都为这项工作分组,但我会玩...
  • 不,像:x y z: w 这样的规则与 3 个独立规则相同。如果您真的想要一个具有多个目标的单一规则,并且哪个配方应在一次运行中构建所有目标,那么最好使用最新的 GNU make 版本和新的rule with grouped targets:x y z &amp;: w。我编辑了我的答案以包含它。
  • 我很困惑,因为(foo bar baz): a.txttouch $(shell cat $&lt;) 一起工作。例如,当我删除bar 时,它会再次运行touch foo bar baz,一次。但也许我误解了? (使用 3.8 版)
  • 如果您有多个foo bar baz 必须重建,而不仅仅是一个,则配方将执行多次。试一试,把它们全部删除,然后重新制作,你会看到的。这就是引入&amp;: 的原因,告诉make 一次运行就足够了。
  • 啊,当然,在 make 3.8 中,最后的目标分别是 baz&amp;fjoord&amp;
猜你喜欢
  • 2021-02-24
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
  • 1970-01-01
  • 1970-01-01
  • 2011-03-23
  • 1970-01-01
  • 2012-11-30
相关资源
最近更新 更多