如何使用包含%的相同目标的两个规则来制作二次扩展?

时间:2016-03-27 19:29:59

标签: makefile

我有一个如下所示的目录结构:

$ tree
.
|-- dir
|   `-- subdir
|       `-- data
`-- makefile

其中data是一个文件。我的makefile看起来像这样:

all: dir/analysis

.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
        @echo TARGET: $@ DEPS: $^
        touch $@

当我运行make时,我希望结果如下:

TARGET: dir/analysis DEPS: dir/subdir/data dir/subdir
touch dir/analysis

相反,它只是报告

make: *** No rule to make target `dir/analysis', needed by `all'.  Stop.

如果我将第一条规则更改为dir/analysis: dir/subdir,那么它会按预期工作。因此我怀疑make忽略了第一条规则,并在第一条规则为%/analysis: %/subdir时直接跳到第二条规则。当两个规则都以dir/analysis作为目标而不仅仅是第一个规则时,它也可以按预期工作。 MadScientists对另一个问题here的回答似乎适用于我的问题。我尝试添加像

这样的规则
dummy_target: dir/subdir
        mkdir -p dir/subdir

dir/subdir: 
        mkdir -p dir/subdir

makefile的末尾,试图使第一个规则的依赖关系成为一个明确的目标,但这并没有改变任何东西。我对make很陌生,所以我可能错过了一些非常愚蠢的东西,但我不能为我的生活弄清楚。如何按照他们编写的顺序执行第一条和第二条规则?如果重要的话,我使用Make 3.81版。

- 编辑 -

如果我实际在第一条规则之后添加命令,例如@echo RULE 1,则该命令执行而不是第二条命令。

1 个答案:

答案 0 :(得分:5)

我认为您了解makefile:

# Original
all: dir/analysis

.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
        @echo TARGET: $@ DEPS: $^
        touch $@
像这样:

  • 默认情况下,我们会dir/analysis
  • 我们要求二次扩展
  • 我们说 /analysis取决于 /subdir。所以 这使dir/subdir成为dir/analysis
  • 的先决条件
  • 我们也说无论 /analysis取决于我们得到的东西 后缀为/data /analysis的先决条件。 (中学 扩展$$^为我们提供了目标的先决条件)。和 因为dir/analysis的先前先决条件是dir/subdir,现在就是dir/subdir dir/subdir/data 使dir/analysis成为TARGET: dir/analysis DEPS: dir/subdir/data dir/subdir touch dir/analysis 的新先决条件。

因此配方的输出应该是:

# A
xx: xa

xx: xb

xx: xc
    touch $@

这种理解是非常错误的。您混淆并混淆了操作 模式规则与操作或普通规则的关系。

如果makefile说,例如:

# B
xx: xa

xx: xb

%x: %c
    touch $@

甚至:

xx

然后组合目标xx的所有空规则的先决条件 当# A被视为目标时,使用(一)非空规则。所以 xx: xa xb xc touch $@ 相当于:

# B

xx: xa xb %x: %c touch $@ 相当于:

xx

并且在这两种情况下xa xb xc的先决条件都是%x: %a %x: %b touch $@

然而:

%x: %a %b
    touch $@

等同于:

.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
    @echo TARGET: $@ DEPS: $^
    touch $@

如果您编写了一个空的模式规则,其效果就是删除 具有相同目标和先决条件模式的任何先前模式规则。如果你 永远写一个非空模式规则,你只需替换 具有相同目标和先决条件模式的任何先前模式规则。看到 10.5.6 Canceling Implicit Rules

所以:

.SECONDEXPANSION:
%/analysis: $(addsuffix /data, $$^)
    @echo TARGET: $@ DEPS: $^
    touch $@

相当于:

dir/analysis

然后,当考虑$^的此模​​式规则时, 有没有之前的先决条件:$$^将扩展为空字符串,并且 在二次扩展中,# Residual %/analysis: /data @echo TARGET: $@ DEPS: $^ touch $@ 也是如此。所以最后的配方相当于:

# Original

您可以通过运行# Residual和{{}}来满足自己的需求 -d makefile在调试模式下(make: *** No rule to make target 'dir/analysis', needed by 'all'. Stop. )并比较输出。

这解释了为什么两种情况下的输出都得出结论:

/data

模式规则只会被实例化以制作目标,如果这样做可以为目标提供方法。为什么还要选择它?由于先决条件make foo.o不存在,因此模式规则 是不可行的,被丢弃了。就像你用makefile尝试%.o: %c touch $@

foo.c

目录中没有.PHONY: all clean all: dir/analysis %/analysis: %/subdir %/subdir/data @echo TARGET: $@ DEPS: $^ touch $@ clean: rm -f dir/analysis

如果你真的想看到你期望的输出,那么你可以从:

获得它
%/analysis: %/subdir/data

但很难相信这是你真正想要看到的。这个makefile 使分析依赖于数据,也取决于目录所在的位置 数据驻留。有什么意义呢?

dir/subdir

可能表达了数据与分析之间的理想关系: -

  • 如果数据不存在则分析已过期且无法进行(直到您获得一些数据)。
  • 如果数据存在且比分析旧,则分析不会 需要制作。
  • 如果数据存在且比分析更新,那么分析可以并且将会是 制成。

通过使touch dir/subdir成为一个独立的先决条件,你所实现的就是引入 如果分析变得比中的目录更旧,则需要重新制作分析 数据所在的 - 无论数据如何。所以如果,例如,我跑make, 这需要make重新运行分析,即使数据没有改变。

我只能通过假设来理解你的动机 现实你还希望.PHONY: all clean all: dir/analysis .SECONDARY: %/subdir: mkdir -p $@ %/subdir/data: | %/subdir touch $@ %/analysis: %/subdir/data @echo TARGET: $@ DEPS: $^ touch $@ clean: rm -f dir/analysis dir/subdir/data 制作数据,以及它所在的目录,如果 当需要分析时,它们不存在。如果那就是那种情况 你想要一个makefile:

%/subdir/data: | %/subdir

这里,模式规则:

%/subdir

使%/subdir/data成为order-only prerequisite /dir/subdir。这意味着在确定/dir/subdir/data是否需要时,/dir/subdir/data 不被视为 要制作,但如果确定要制作/dir/subdir,则首先制作/dir/subdir。这个 方式,.SECONDARY将被创建,如果它不存在,当你需要它将数据放在那里,但没有影响 是否需要进行数据或分析。

特殊目标maketechnicality。它指示make不自动删除任何后续目标 它推断出来自链式模式规则的中间人工制品。没有它,在这个例子中dir/subdir/data 会推断出dir/subdir/dir/analysis只是制作dir/subdir/data的废品并自动删除它们 在末尾。

如果您最初运行此makefile而没有dir/subdir/而没有$ make mkdir -p dir/subdir touch dir/subdir/data TARGET: dir/analysis DEPS: dir/subdir/data touch dir/analysis ,则会获得:

$ make clean
rm -f dir/analysis dir/subdir/data 
$ make
touch dir/subdir/data
TARGET: dir/analysis DEPS: dir/subdir/data
touch dir/analysis

随后:

$ make
make: Nothing to be done for 'all'.
$ touch dir/subdir
$ make
make: Nothing to be done for 'all'.

然后:

{{1}}