代码之家  ›  专栏  ›  技术社区  ›  hendry

如何用带反向链接的makefile构建HTML?

  •  0
  • hendry  · 技术社区  · 5 年前

    我试图静态地构建HTML文件,需要一个标记文件和一个名为“whatLinkShere”的元文件来演示HTML文件 back links .

    我相信通过生成一个makefile,首先生成所有的“whatlinksher”文件,可以有效地完成这个任务。我不认为这可以并行完成,因为生成这些文件的程序需要附加到whatlinksher文件中,并且可能存在我不太确定如何解决的争用条件。

    一旦生成了“whatlinksher”文件,那么如果编辑了降价文件,比如 foo.mdwn指向bar.mdwn 只有foo.mdwn需要再次分析“whatlinksher”的变化。最后,只需要重建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 $^ > $@
    

    目前的问题是,首次运行时没有生成*.whatlinksher**。我的解决方法是 for i in *.mdwn; do go run backlinks.go $i; done . 此外,在按照前面的描述编辑文件之后,没有按照我想要的方式进行重建。出了大问题。我错过了什么?

    1 回复  |  直到 5 年前
        1
  •  2
  •   Renaud Pacalet    5 年前

    我想我终于明白你的问题了。如果我理解得很好:

    1. 你有很多 *.mdwn 源文件。
    2. 你创造了 *.whatlinkshere 来自您的文件 * 源文件使用 backlinks.go 实用工具。但是这个实用程序不能产生 foo.whatlinkshere foo.mdwn . 分析 美国食品药品监督管理局 ,搜索指向其中其他页的链接,并针对每个链接 bar 它发现,它 追加 [foo](foo.html) 参考 bar.whatlinkshere .
    3. 从每个 美国食品药品监督管理局 要生成相应的源文件 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 -j1 或者通过添加 .NOTPARALLEL: 对你的makefile有特别的规定,但遗憾的是)有种族条件的风险。

    重要的 :以下仅适用于平面组织,其中所有源文件和HTML文件与生成文件位于同一目录中。当然,其他组织是可能的,但它们需要一些修改。

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

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

    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.whatlinkshere , pageB.whatlinkshere pageC.whatlinkshere 都是通过执行以下操作生成的:

    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 clean 作为 phony 因为…他们就是这样。
    • 我宣布 whatlinkshere 文件作为 precious 因为(其中一些)被make视为中间人,如果没有这个声明,make将在构建HTML文件后删除它们。
    • 在配方中 干洗机 我添加的文件 rm -f $(LINKFILES) 这样,如果配方被执行,我们就从一个干净的状态重新启动,而不是将新的东西连接到旧的(可能过时的)引用。
    • 图案干在 $(LINKPATTERN) 可以是任何内容,但必须至少匹配一个字符。我用过 w%e 但是 whatlin%shere 也可以。在您的案例中使用足够具体的内容。如果你有 pageB.where 文件首选项 什么东西? what%here .

    这个解决方案有一个缺点,但这是由于您的特定设置:每次一个 mdwn 文件更改必须重新分析(正常),但 沃特林克希尔 文件可能会受到影响。这是不可预测的,它取决于在此源文件中修改的链接。但问题更大的是,这种分析的结果是 附加 对受影响者 沃特林克希尔 文件夹。它们不是“ 编辑 “将与此源文件相关的旧内容替换为新内容。因此,如果只更改源文件中的注释,则其所有链接都将再次附加到相应的 沃特林克希尔 文件(当它们已经存在时)。这可能不是你想要的。

    这就是上面的解决方案删除所有 沃特林克希尔 每次更改一个源文件时,文件和重新分析所有源文件。另一个负面后果是所有HTML文件也必须重新生成,因为 沃特林克希尔 文件已更改(即使其内容没有真正更改,但make不知道这一点)。如果分析速度非常快,并且您有少量 双绞线 文件,应该没问题。否则它是次优的,但不容易解决,因为你的特殊设置。

    第二个选项使用递归生成、分离的后链接文件和标记文件

    然而,有一种可能性,包括:

    1. 用一个引用分隔所有后链接引用 沃特林克希尔 从/到对的文件: foo.backlinks/bar.whatlinkshere 包含对的所有引用 酒吧 发现于 美国食品药品监督管理局 ,
    2. 在第一次调用中使用递归make(当 STEP make variable is unset)更新所有 沃特林克希尔 需要的文件和第二次调用( 步骤 设置为2)以生成需要的HTML文件,
    3. 使用空的虚拟文件标记 FOO.M镇 已分析文件: foo.backlinks/.done ,
    4. 使用 secondary expansion 能够在其先决条件列表中引用模式规则的词干(并使用 $$ 为了逃避拳头的扩张)。

    但要理解(和维护)可能会有点困难。

    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)
    

    即使看起来更复杂,这个版本也有一些优势:

    1. 只重建过时的目标,每个目标只重建一次,
    2. 全部的 沃特林克希尔 在更新任何HTML文件(如果需要)之前更新文件(如果需要),
    3. 这个 沃特林克希尔 文件可以并行生成,
    4. HTML文件可以并行构建。

    第三个选项只使用递归make和标记文件

    如果您不关心返回链接从源文件中消失后仍存在于结果中的不准确结果,或者返回链接被无用地复制,那么我们可以重用以前解决方案中的想法,但减少单独的“从/到”分离 沃特林克希尔 文件夹。

    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)
    

    笔记:

    1. 因为这只适用于一个扁平的组织,所以我替换了 $(shell find...) 由内置的 $(wildcard ...) .
    2. 我用过 patsubst 而不是旧的语法,但这只是一个品味问题。
    3. 这个 %.whatlinkshere: 规则是创建缺少的空值的默认规则 干洗机 文件夹。
    4. 这个 NOTPARALLEL: 特殊目标在构建 沃特林克希尔 文件夹。