我如何使用GNU制造的档案提取规则制作存档内容



我想编写几个规则,以提取焦油档案的内容,以产生许多文件,然后用作其他规则的输入依赖项。我希望这可以使用并行构建。我不使用递归制作。

首先,很抱歉马拉松问题,但我认为我不能以较短的形式解释它。

想想解开源文件的集合,然后将其编译为存储在档案外的规则,以产生各种构建人工制品,然后进一步使用。我没有寻求其他导致省略此问题的安排。我有充分的理由这样做是理所当然的。:)

我将以一个人为的例子来演示我的问题。当然,我从基本的东西开始:

TAR := test.tar.bz2
CONTENTS := $(addprefix out/,$(filter-out %/,$(shell tar -tf $(TAR))))
out: $(TAR)
        rm -rf out
        mkdir out
        tar -xvf $< -C out --touch || (rm -rf out; exit 1)
$(CONTENTS): out
sums: $(CONTENTS)
        md5sum $^ > $@
.DELETE_ON_ERROR:
.DEFAULT_GOAL := all
.PHONY: all clean
all: sums
clean:
        rm -rf out sums

这里的想法是,由于$(内容)都是存档中的所有文件,并且它们都取决于out,然后运行sums目标,我们最终需要提取存档。

不幸的是,如果仅在更新test.tar.bz2时使用并行调用,则(始终)(始终)工作,因为make可以决定检查$(contents)之前之前检查$(content)的时间戳运行out规则,这意味着它认为每个来源都比sums更古老,因此无事可做:

$ make clean
rm -rf out sums
$ make -j6
rm -rf out
mkdir out
tar -xvf test.tar.bz2 -C out --touch || (rm -rf out; exit 1)
data.txt
file
weird.file.name
dir/
dir/another.c
dir/more
md5sum out/data.txt out/file out/weird.file.name out/dir/another.c out/dir/more > sums
$ touch test.tar.bz2 
$ make -j6
rm -rf out
mkdir out
tar -xvf test.tar.bz2 -C out --touch || (rm -rf out; exit 1)
data.txt
file
weird.file.name
dir/
dir/another.c
dir/more

哎呀!sums规则未运行!

因此,下一个尝试是说出一个UNTAR规则实际上确实会直接制造所有$(内容)。这似乎是更好的,因为我们正在告诉《做什么真正发生的事情》,所以它知道何时忘记目标的时间戳记,当他们通过其规则重新制定目标时。

首先,让我们看一下似乎有效的东西,然后我会解决我的问题:

TAR := test.tar.bz2
CONTENTS := $(addprefix out/,$(filter-out %/,$(shell tar -tf $(TAR))))
# Here's the change.
$(addprefix %/,$(patsubst out/%,%,$(CONTENTS))): $(TAR)
        rm -rf out
        mkdir out
        tar -xvf $< -C out --touch || (rm -rf out; exit 1)
sums: $(CONTENTS)
        md5sum $^ > $@
.DELETE_ON_ERROR:
.DEFAULT_GOAL := all
.PHONY: all clean
all: sums
clean:
        rm -rf out sums

在这种情况下,我们有效地有一条规则,上面说:

%/data.txt %/file %/weird.file.name %/dir/another.c %/dir/more: test.tar.bz2
        rm -rf out
        mkdir out
        tar -xvf $< -C out --touch || (rm -rf out; exit 1)

现在,您可以看到我将输出强制进入out目录的原因之一:给我一个使用%的地方,以便我可以使用模式规则。即使这里没有强大的模式,我也被迫使用模式规则,因为这是唯一可以告诉一个规则从单个调用中创建多个输出文件的方法。(不是吗?)

如果触摸了任何文件(对于我的用例不重要),或者即使在并行构建中触摸了test.tar.bz2文件,则有效更改所有时间戳。

例如,在先前的成功构建之后:

$ touch test.tar.bz2 
$ make -j6
rm -rf out
mkdir out
tar -xvf test.tar.bz2 -C out --touch || (rm -rf out; exit 1)
data.txt
file
weird.file.name
dir/
dir/another.c
dir/more
md5sum out/data.txt out/file out/weird.file.name out/dir/another.c out/dir/more > sums

所以,如果我有一个工作解决方案,我的问题是什么?

好吧,我有许多这些档案要提取,每个档案都有自己的$(内容)。我可以解决这个问题,但是麻烦是写了一个不错的模式规则。由于每个档案都需要定义其自己的规则,因此即使档案的内容相似(或相同)内容,每个规则的模式也必须重叠。这意味着每个存档的提取文件的输出路径必须,如:

TAR := test.tar.bz2
CONTENTS := $(addprefix out.$(TAR)/,$(filter-out %/,$(shell tar -tf $(TAR))))
$(patsubst out.$(TAR)/%,out.%/%,$(CONTENTS)): $(TAR)
        rm -rf out.$(TAR)
        mkdir out.$(TAR)
        tar -xvf $< -C out.$(TAR) --touch || (rm -rf out.$(TAR); exit 1)
sums: $(CONTENTS)
        md5sum $^ > $@
.DELETE_ON_ERROR:
.DEFAULT_GOAL := all
.PHONY: all clean
all: sums
clean:
        rm -rf out.$(TAR) sums

因此,这可以与正确的目标特定变量一起使用,但现在意味着提取点都是"丑陋的",与Makefile的构造方式非常相关:

$ make -j6
rm -rf out.test.tar.bz2
mkdir out.test.tar.bz2
tar -xvf test.tar.bz2 -C out.test.tar.bz2 --touch || (rm -rf out.test.tar.bz2; exit 1)
data.txt
file
weird.file.name
dir/
dir/another.c
dir/more
md5sum out.test.tar.bz2/data.txt out.test.tar.bz2/file out.test.tar.bz2/weird.file.name out.test.tar.bz2/dir/another.c out.test.tar.bz2/dir/more > sums

我采取的下一个自然步骤是尝试将静态图案规则与多目标 - via-pattern-rule方法相结合。这将使我非常笼统地保持模式,但将其应用限制为一组特定的目标:

TAR := test.tar.bz2
CONTENTS := $(addprefix out/,$(filter-out %/,$(shell tar -tf $(TAR))))
# Same as second attempt, except "$(CONTENTS):" static pattern prefix
$(CONTENTS): $(addprefix %/,$(patsubst out/%,%,$(CONTENTS))): $(TAR)
        rm -rf out
        mkdir out
        tar -xvf $< -C out --touch || (rm -rf out; exit 1)
sums: $(CONTENTS)
        md5sum $^ > $@
.DELETE_ON_ERROR:
.DEFAULT_GOAL := all
.PHONY: all clean
all: sums
clean:
        rm -rf out sums

太好了!除了它不起作用:

$ make
Makefile:5: *** multiple target patterns.  Stop.
$ make --version
GNU Make 4.0

那么,有没有一种方法可以使用具有静态模式规则的多个目标模式?如果没有,是否有另一种方法可以实现我在上面的最后一个工作示例中拥有的东西,但是没有对输出路径的限制来制作独特的模式?我基本上需要告诉"当您解开此存档时,此目录中的所有文件(如有必要,我愿意列举)都有新的时间戳"。我可以强迫重新启动的解决方案,并且仅当它解开档案包装也可以接受,但不太理想。

原始makefile的问题是您的名称有碰撞。您有一个名为out的目标(非副词)和一个名为out的目录。让他们认为那是同一回事,并且变得非常困惑。

(注意:我将.SUFFIXES:添加到您的第一个makefile中以减少一些噪音,但不会改变任何东西。-r-R标志禁用使内置规则和变量也用于减少噪声。)

$ make clean
....
$ make -j6
....
$ touch test.tar.bz2
$ make -rRd -j6
....
Considering target file 'all'.
 File 'all' does not exist.
  Considering target file 'sums'.
    Considering target file 'out/data.txt'.
     Looking for an implicit rule for 'out/data.txt'.
     No implicit rule found for 'out/data.txt'.
      Considering target file 'out'.
        Considering target file 'test.tar.bz2'.
         Looking for an implicit rule for 'test.tar.bz2'.
         No implicit rule found for 'test.tar.bz2'.
         Finished prerequisites of target file 'test.tar.bz2'.
        No need to remake target 'test.tar.bz2'.
       Finished prerequisites of target file 'out'.
       Prerequisite 'test.tar.bz2' is older than target 'out'.
      No need to remake target 'out'.
     Finished prerequisites of target file 'out/data.txt'.
     Prerequisite 'out' is older than target 'out/data.txt'.
    No recipe for 'out/data.txt' and no prerequisites actually changed.
    No need to remake target 'out/data.txt'.
.... # This following set of lines repeats for all the other files in the tarball.
    Considering target file 'out/file'.
     Looking for an implicit rule for 'out/file'.
     No implicit rule found for 'out/file'.
      Pruning file 'out'.
     Finished prerequisites of target file 'out/file'.
     Prerequisite 'out' is older than target 'out/file'.
    No recipe for 'out/file' and no prerequisites actually changed.
    No need to remake target 'out/file'.
....
   Finished prerequisites of target file 'sums'.
   Prerequisite 'out/data.txt' is older than target 'sums'.
   Prerequisite 'out/file' is older than target 'sums'.
   Prerequisite 'out/weird.file.name' is older than target 'sums'.
   Prerequisite 'out/dir/more' is older than target 'sums'.
   Prerequisite 'out/dir/another.c' is older than target 'sums'.
  No need to remake target 'sums'.
 Finished prerequisites of target file 'all'.
Must remake target 'all'.
Successfully remade target file 'all'.
make: Nothing to be done for 'all'.

这里的主要细节是这两行:

  1. Considering target file 'out'.
  2. Prerequisite 'out' is older than target 'out/data.txt'

out目录在这里无关紧要。我们不在乎它(无论如何,都不会很好地处理目录先决条件,因为目录上的修改时间戳并不意味着与文件上的内容相同)。甚至您不希望out/data.txt不创建CC_19,因为构建伪影目录目标已经存在(并且看起来年龄较大)。

您可以通过将out目标标记为.PHONY来"修复"此操作如果您要这样做,只需结合这两个步骤)。

说我不会那样做。我认为解决这个问题的最简单解决方案是约翰·格雷厄姆(John Graham)挑剔的"原子规则"想法,并在此处解释。

sp :=
sp +=
sentinel = .sentinel.$(subst $(sp),_,$(subst /,_,$1))
atomic = $(eval $1: $(call sentinel,$1) ; @:)$(call sentinel,$1): $2 ; touch $$@ $(foreach t,$1,$(if $(wildcard $t),,$(shell rm -f $(call sentinel,$1))))
.PHONY: all
all: a b
$(call atomic,a b,c d)
   touch a b

您可能还可以使用提取邮票文件(在TARBALL上的PREREQ)进行此操作,将Tarball提取到"影子"目录,然后将/链接复制到"最终"位置(build/$file: shadow/$file TARGET),如果您愿意,但这就是我认为会更复杂。

最新更新