如何使用带有反向链接的Makefile构建HTML?

时间:2018-12-16 01:08:18

标签: makefile

我正在尝试静态构建HTML文件,该文件需要markdown文件和一个名为“ whatlinkshere”的元文件,以便HTML文件展示其back links

我认为可以通过Makefile来完成,方法是首先生成所有“ whatlinkshere”文件。我不认为这可以并行完成,因为生成这些文件的程序需要追加到whatlinkshere文件中,并且可能存在我不太确定如何解决的竞争条件。

一旦生成了“ whatlinkshere”文件,则如果编辑了降价文件,则说 foo.mdwn指向bar.mdwn ,仅需要再次对foo.mdwn进行“ whatlinkshere”分析。变化。最后,仅需重新构建foo.html和bar.html。

我在backlinks project中努力做到这一点。

INFILES = $(shell find . -name "*.mdwn")
OUTFILES = $(INFILES:.mdwn=.html)
LINKFILES = $(INFILES:.mdwn=.whatlinkshere)

all: $(OUTFILES)

# These need to be all made before the HTML is processed
$(LINKFILES): $(INFILES)
    @echo Creating backlinks $@
    @touch $@
    @go run backlinks.go $<

%.html: %.mdwn %.whatlinkshere
    @echo Deps $^
    @cmark $^ > $@

当前的问题是* .whatlinkshere **不在首次运行时生成。我的解决方法是for i in *.mdwn; do go run backlinks.go $i; done。此外,如上所述,在编辑文件后没有进行所需的重建。发生严重错误。我想念什么?

1 个答案:

答案 0 :(得分:2)

我想我终于了解了您的问题。如果我了解得很好:

  1. 您有一堆*.mdwn源文件。
  2. 您可以使用*.whatlinkshere实用程序从*.mdwn源文件生成backlinks.go文件。但是,该实用程序不会从foo.whatlinkshere产生foo.mdwn。它分析foo.mdwn,在其中搜索到其他页面的链接,并为找到的每个指向bar的链接附加 [foo](foo.html) }}。
  3. 从每个bar.whatlinkshere源文件中,您要使用以下命令构建相应的foo.mdwn文件:

    foo.html

您的规则:

$ cmark foo.mdwn foo.whatlinkshere

包含一个错误,并具有多个缺点。错误是在配方中使用了$(LINKFILES): $(INFILES) @echo Creating backlinks $@ @touch $@ @go run backlinks.go $< 自动变量。它作为第一个先决条件扩展,在您的情况下,可能总是$<。不是你想要的。 pageA.mdwn会扩展为所有先决条件,但这不是正确的解决方案,因为:

  1. 您的go实用程序仅使用一个源文件名,即使它接受多个源文件名...
  2. ... make将多次运行该配方,每个链接文件一次,这很浪费,并且...
  3. ...当go实用程序追加到链接文件时,这甚至比浪费还糟:每个反向链接都会被计数几次,并且...
  4. ...如果make在并行模式下运行(请注意,您可以使用$^或通过向您的Makefile添加make -j1特殊规则来防止这种情况,但这很可惜)种族条件。

重要:以下内容仅适用于平面组织,其中所有源文件和HTML文件都与Makefile位于同一目录中。当然也可以使用其他组织,但是需要进行一些修改。

使用多目标模式规则的第一个选项

一种可能性是使用make模式规则的特殊属性:当它们具有多个目标时,make认为一次执行配方会产生所有目标。例如:

.NOTPARALLEL:

告诉人们pageA.w%e pageB.w%e pageC.w%e: pageA.mdwn pageB.mdwn pageC.mdwn for m in $^; do go run backlinks.go $$m; done pageA.whatlinksherepageB.whatlinkshere都是由以下一次执行生成的:

pageC.whatlinkshere

(make将for m in pageA.mdwn pageB.mdwn pageC.mdwn; do go run backlinks.go $m; done 扩展为所有先决条件,将$^扩展为$$m)。当然,我们希望自动化$m模式目标列表的计算。这应该做到:

pageA.w%e pageB.w%e pageC.w%e

注意:

  • 我将INFILES := $(shell find . -name "*.mdwn") OUTFILES := $(INFILES:.mdwn=.html) LINKFILES := $(INFILES:.mdwn=.whatlinkshere) LINKPATTERN := $(INFILES:.mdwn=.w%e) .PHONY: all clean .PRECIOUS: $(LINKFILES) all: $(OUTFILES) # These need to be all made before the HTML is processed $(LINKPATTERN): $(INFILES) @echo Creating backlinks @rm -f $(LINKFILES) @touch $(LINKFILES) @for m in $^; do go run backlinks.go $$m; done %.html: %.mdwn %.whatlinkshere @echo Deps $^ @cmark $^ > $@ clean: rm -f $(LINKFILES) $(OUTFILES) all声明为phony,因为...这就是它们。
  • 我将clean文件声明为precious是因为(其中一些文件)被make视为中间体,如果没有此声明,make会在构建HTML文件后将其删除。
  • 在添加了whatlinkshere的{​​{1}}文件的食谱中,如果执行了食谱,我们将从干净状态重新开始,而不是将新内容连接到旧的(可能是过时的)引用。 / li>
  • whatlinkshere中的模式词干可以是任何字符,但必须至少匹配一个字符。我使用了rm -f $(LINKFILES),但是$(LINKPATTERN)也可以使用。使用适合您情况的足够具体的东西。如果您有w%e文件,则最好使用whatlin%sherepageB.where

此解决方案有一个缺点,但这是由于您的特定设置造成的:每次更改单个whatlin%shere文件时,都必须重新分析(正常情况),但是必须重新分析任何what%here文件可能会受到影响。这是无法预料的,​​它取决于此源文件中已修改的链接。但是更成问题的是,该分析的结果被附加到了受影响的mdwn文件中。它们没有被“ 编辑”,相对于该源文件的旧内容被新文件替换。因此,如果仅更改源文件中的注释,则其所有链接将再次附加到各自的whatlinkshere文件中(当它们已经存在时)。这可能不是您想要的。

这就是为什么上述解决方案在每次更改一个源文件时都删除所有whatlinkshere文件并重新分析所有源文件的原因。另一个负面结果是,所有HTML文件也必须重新生成,因为所有whatlinkshere文件都已更改(即使它们的内容并没有真正更改,但make不知道这一点)。如果分析速度非常快,并且您的whatlinkshere文件数量很少,则应该可以。否则,它不是最佳选择,但由于您的特定设置而难以解决。

使用递归构造的第二个选项,将反向链接文件和标记文件分开

但是有一种可能性,其中包括:

  1. 在每个对/一对之间用一个whatlinkshere文件分隔所有反向链接引用:mdwn包含对在whatlinkshere中找到的foo.backlinks/bar.whatlinkshere的所有引用,
  2. 在第一次调用时(未设置bar make变量时)使用递归make来更新所有需要的递归foo.mdwn文件,第二次调用(STEP设置为2)生成所需的HTML文件,
  3. 使用空的虚拟文件来标记已经分析了whatlinkshere个文件:STEP
  4. 使用secondary expansion能够在其先决条件列表中引用模式规则的茎(并使用foo.mdwn逃避第一拳的扩张)。

但是它可能很难理解(和维护)。

foo.backlinks/.done

即使看起来更复杂,此版本也有一些优点:

  1. 仅重建过期的目标,并且每个目标仅重建一次,
  2. 在更新所有HTML文件(如果需要)之前更新(如果需要)所有$$文件,
  3. INFILES := $(shell find . -name "*.mdwn") OUTFILES := $(INFILES:.mdwn=.html) DONEFILES := $(patsubst %.mdwn,%.backlinks/.done,$(INFILES)) .PHONY: all clean ifeq ($(STEP),) all $(OUTFILES): $(DONEFILES) $(MAKE) STEP=2 $@ %.backlinks/.done: %.mdwn rm -rf $(dir $@) mkdir -p $(dir $@) cp $< $(dir $@) cd $(dir $@); go run ../backlinks.go $<; rm $< touch $@ else all: $(OUTFILES) .SECONDEXPANSION: %.html: %.mdwn $$(wildcard *.backlinks/$$*.whatlinkshere) @echo Deps $^ @cmark $^ > $@ endif clean: rm -rf *.backlinks $(OUTFILES) 文件可以并行构建,
  4. HTML文件可以并行构建。

仅使用递归品牌和标记文件的第三种选择

如果您不关心结果不准确的情况,即反向链接从源文件中消失后仍然保留在结果中,或者反向链接被无用复制的情况,我们可以重用先前解决方案中的想法,但将各个字段之间的分离whatlinkshere个文件。

whatlinkshere

注意:

  1. 因为这仅适用于单位组织,所以我用内置的whatlinkshere替换了INFILES := $(wildcard *.mdwn) OUTFILES := $(patsubst %.mdwn,%.html,$(INFILES)) LINKFILES := $(patsubst %.mdwn,%.whatlinkshere,$(INFILES)) DONEFILES := $(patsubst %.mdwn,.%.done,$(INFILES)) .PHONY: all clean .PRECIOUS: $(LINKFILES) ifeq ($(STEP),) .NOTPARALLEL: all $(OUTFILES): $(DONEFILES) $(MAKE) STEP=2 $@ .%.done: %.mdwn go run backlinks.go $< touch $@ else all: $(OUTFILES) %.html: %.mdwn %.whatlinkshere @echo Deps $^ @cmark $^ > $@ %.whatlinkshere: touch $@ endif clean: rm -f $(OUTFILES) $(LINKFILES) $(DONEFILES)
  2. 我使用了$(shell find...)而不是旧的语​​法,但这只是一个问题。
  3. $(wildcard ...)规则是用于创建丢失的空patsubst文件的默认规则。
  4. %.whatlinkshere:特殊目标可防止在构建whatlinkshere文件时并行执行。