基于 Makefile 中依赖关系的动态目标

Dynamic targets based on the dependency in Makefile

有几个类似的问题,但我无法将任何提出的概念应用到我的案例中。

只是为了提供一些背景信息:我有一组 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 等等,但它显示需要重复的 *.txt 的模式):

文件: 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 $< > $@

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

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

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&: 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 $<` $@