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

Ben*_*say 0 makefile

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

$ tree
.
|-- dir
|   `-- subdir
|       `-- data
`-- makefile
Run Code Online (Sandbox Code Playgroud)

哪里data是文件.我makefile看起来像这样:

all: dir/analysis

.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
        @echo TARGET: $@ DEPS: $^
        touch $@
Run Code Online (Sandbox Code Playgroud)

当我跑步时make,我希望结果看起来像这样:

TARGET: dir/analysis DEPS: dir/subdir/data dir/subdir
touch dir/analysis
Run Code Online (Sandbox Code Playgroud)

相反,它只是报道

make: *** No rule to make target `dir/analysis', needed by `all'.  Stop.
Run Code Online (Sandbox Code Playgroud)

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

dummy_target: dir/subdir
        mkdir -p dir/subdir
Run Code Online (Sandbox Code Playgroud)

dir/subdir: 
        mkdir -p dir/subdir
Run Code Online (Sandbox Code Playgroud)

到最后,makefile试图使第一个规则的依赖关系成为一个明确的目标,但这并没有改变任何东西.我很新make,所以我可能会错过一些非常愚蠢的东西,但我不能为我的生活弄清楚.如何按照编写顺序执行第一条和第二条规则?我正在使用Make版本3.81以防万一.

- 编辑 -

如果我实际上在第一个规则之后添加命令,就像@echo RULE 1那个命令执行而不是第二个命令.

Mik*_*han 5

我认为您了解makefile:

# Original
all: dir/analysis

.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
        @echo TARGET: $@ DEPS: $^
        touch $@
Run Code Online (Sandbox Code Playgroud)

像这样:

  • 我们默认制作 dir/analysis
  • 我们要求二次扩展
  • 我们说无论/analysis取决于什么/subdir.所以这dir/subdir是先决条件dir/analysis
  • 我们还说,无论/analysis取决于我们通过后缀/data到先前先决条件而获得的东西/analysis.(二次扩展$$^为我们提供了目标的先决条件).而且由于先前的先决条件dir/analysisdir/subdir,现在已成为dir/subdir dir/subdir/data新的先决条件dir/analysis.

因此,配方的输出应该是:

TARGET: dir/analysis DEPS: dir/subdir/data dir/subdir
touch dir/analysis
Run Code Online (Sandbox Code Playgroud)

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

如果makefile说,例如:

# A
xx: xa

xx: xb

xx: xc
    touch $@
Run Code Online (Sandbox Code Playgroud)

甚至:

# B
xx: xa

xx: xb

%x: %c
    touch $@
Run Code Online (Sandbox Code Playgroud)

然后,xxxx被视为目标时,目标的所有空规则的先决条件与(一个)非空规则的先决条件组合.所以 # A相当于:

xx: xa xb xc
    touch $@
Run Code Online (Sandbox Code Playgroud)

# B相当于:

xx: xa xb

%x: %c
    touch $@
Run Code Online (Sandbox Code Playgroud)

在这两种情况下的先决条件xxxa xb xc.

然而:

%x: %a

%x: %b
    touch $@
Run Code Online (Sandbox Code Playgroud)

不是等同于:

%x: %a %b
    touch $@
Run Code Online (Sandbox Code Playgroud)

如果您编写了空模式规则,其效果就是删除具有相同目标和先决条件模式的任何先前模式规则.如果您编写非空模式规则,则只需 使用相同的目标和先决条件模式替换任何先前的模式规则.请参见 10.5.6取消隐式规则

所以:

.SECONDEXPANSION:
%/analysis: %/subdir
%/analysis: $(addsuffix /data, $$^)
    @echo TARGET: $@ DEPS: $^
    touch $@
Run Code Online (Sandbox Code Playgroud)

相当于:

.SECONDEXPANSION:
%/analysis: $(addsuffix /data, $$^)
    @echo TARGET: $@ DEPS: $^
    touch $@
Run Code Online (Sandbox Code Playgroud)

然后,当考虑此模式规则时dir/analysis,没有先前的先决条件:$^将扩展为空字符串,并且在二次扩展中,也将如此$$^.所以最后的配方相当于:

# Residual
%/analysis: /data
    @echo TARGET: $@ DEPS: $^
    touch $@
Run Code Online (Sandbox Code Playgroud)

您可以通过在调试模式()中运行两个# Original# Residualmakefile -d并比较输出来满足您的需求.

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

make: *** No rule to make target 'dir/analysis', needed by 'all'. Stop.
Run Code Online (Sandbox Code Playgroud)

如果这样做提供了制作目标的方法,则模式规则将仅被实例化以制作目标.为什么选择呢?由于先决条件/data不存在,因此模式规则不可行并被丢弃.就像你尝试make foo.o使用makefile一样:

%.o: %c
    touch $@
Run Code Online (Sandbox Code Playgroud)

foo.c目录中没有时.

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

.PHONY: all clean

all: dir/analysis

%/analysis: %/subdir %/subdir/data
    @echo TARGET: $@ DEPS: $^
    touch $@

clean:
    rm -f dir/analysis
Run Code Online (Sandbox Code Playgroud)

但很难相信这是你真正想要看到的.此makefile使分析依赖于数据以及数据所在的目录.有什么意义呢?

%/analysis: %/subdir/data
Run Code Online (Sandbox Code Playgroud)

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

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

通过创建dir/subdir一个独立的先决条件,您实现的所有目的是引入一个重新分析的要求,如果它变得比数据所在的目录更旧- 无论数据如何.因此,如果我运行touch dir/subdir,那将需要make重新运行分析,即使数据没有改变.

我只能假设在现实中你也想要让你的动机感make,以使数据,及其所在,如果他们当需要分析恰巧不存在的目录.如果是这种情况,那么你需要一个makefile:

.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 
Run Code Online (Sandbox Code Playgroud)

这里,模式规则:

%/subdir/data: | %/subdir
Run Code Online (Sandbox Code Playgroud)

%/subdir一个订单唯一的先决条件%/subdir/data.这意味着,/dir/subdir不考虑确定是否时/dir/subdir/data要做出的需求,但如果确定/dir/subdir/data要进行,那么/dir/subdir将首先提出.这样/dir/subdir,如果不存在,将在需要时将数据放入其中,但不会影响您是否需要进行数据或分析.

特殊目标.SECONDARY技术性的.它指示make不自动删除它推断为链接模式规则中出现的中间假象的任何后续目标.如果没有它,在这个例子中make 会推断出它dir/subdir/data,dir/subdir/并且只是最后制作dir/analysis和自动删除它们的废品.

如果你最初运行这个makefile没有dir/subdir/data,dir/subdir/你得到:

$ make
mkdir -p dir/subdir
touch dir/subdir/data
TARGET: dir/analysis DEPS: dir/subdir/data
touch dir/analysis
Run Code Online (Sandbox Code Playgroud)

随后:

$ make clean
rm -f dir/analysis dir/subdir/data 
$ make
touch dir/subdir/data
TARGET: dir/analysis DEPS: dir/subdir/data
touch dir/analysis
Run Code Online (Sandbox Code Playgroud)

然后:

$ make
make: Nothing to be done for 'all'.
$ touch dir/subdir
$ make
make: Nothing to be done for 'all'.
Run Code Online (Sandbox Code Playgroud)