【问题标题】:I need help trimming down dependencies in a makefile我需要帮助减少 makefile 中的依赖项
【发布时间】:2011-12-18 09:00:00
【问题描述】:

我为生成一个简单的网页创建了一个生成文件。 makefile 背后的想法是这样的:

  • 我们正在编译一个网页 index.html
  • index.html 需要必须编译的手写笔 css main.sty
  • 页面中使用了许多示例
    • 例如one 的代码位于lib/examples/one
    • 每个示例包含三个部分
      • 标记(.jade 模板文件)
      • 一些代码(.coffee 脚本文件)
      • 描述(.md 降价文件)
  • 构建脚本必须将每个示例呈现为单个 html 文件
    • Jade、Pygments、Markdown 用于生成三个html文件
    • example.jade 模板用于将这些组合到一个示例文件中
      • example.jade 必须复制到正确的构建示例目录,因为模板语言只能进行相对导入。所以为了导入example/one/code.html,我们必须将模板复制到example/one,并让它包含code.html
    • 完成后,每个示例 x 将编译为 tbuild/examples/x.html
  • lib/index.jade 模板已移至 build(以便它可以包含示例文件)
  • 然后使用Jade将index.jade模板编译成html

这有点简化,但这样更容易理解。简化之处在于每个示例中实际上都有 两个 标记文件(left.html 和 right.html),并且代码文件都通过 pygments 运行 用作一个脚本,所以 code.html 和 code.coffee 都需要把它变成 build。

现在,makefile 看起来像这样:

LIB = lib
BUILD = build
LIBEX = $(LIB)/examples
BUILDEX = $(BUILD)/examples
EXAMPLES = $(addsuffix .html,$(addprefix $(BUILDEX)/,$(shell ls $(LIBEX) | grep -v '.jade')))

all: $(BUILD)/main.css index.html

index.html: $(BUILD)/index.jade $(EXAMPLES)
    jade < $< --path $< > $@

$(BUILD)/index.jade: $(LIB)/index.jade
    mkdir -p $(@D)
    cp $< $@

$(BUILD)/main.css: $(LIB)/main.sty
    mkdir -p $(@D)
    stylus -u nib < $< > $@

$(BUILDEX)/%.html: $(BUILDEX)/%/template.jade $(BUILDEX)/%/left.html $(BUILDEX)/%/right.html $(BUILDEX)/%/code.html $(BUILDEX)/%/code.coffee $(BUILDEX)/%/text.html
    jade < $< --path $< > $@

$(BUILDEX)/%/template.jade: $(LIBEX)/template.jade
    mkdir -p $(@D)
    cp $< $@

$(BUILDEX)/%/left.html: $(LIBEX)/%/left.jade
    jade < $< --path $< > $@

$(BUILDEX)/%/right.html: $(LIBEX)/%/right.jade
    jade < $< --path $< > $@

$(BUILDEX)/%/code.html: $(LIBEX)/%/code.coffee
    pygmentize -f html -o $@ $<

$(BUILDEX)/%/code.coffee: $(LIBEX)/%/code.coffee
    mkdir -p $(@D)
    cp $< $@

$(BUILDEX)/%/text.html: $(LIBEX)/%/text.md
    markdown < $< > $@

clean:
    rm index.html -f
    rm $(BUILD) -rf

这可行,但问题是当我触摸“lib/examples/intro/code.coffee”并重新运行 make 时,我得到以下信息:

mkdir -p build/examples/intro
cp lib/examples/template.jade build/examples/intro/template.jade
jade < lib/examples/intro/left.jade --path lib/examples/intro/left.jade > build/examples/intro/left.html
jade < lib/examples/intro/right.jade --path lib/examples/intro/right.jade > build/examples/intro/right.html
pygmentize -f html -o build/examples/intro/code.html lib/examples/intro/code.coffee
mkdir -p build/examples/intro
cp lib/examples/intro/code.coffee build/examples/intro/code.coffee
markdown < lib/examples/intro/text.md > build/examples/intro/text.html
jade < build/examples/intro/template.jade --path build/examples/intro/template.jade > build/examples/intro.html
jade < build/index.jade --path build/index.jade > index.html
rm build/examples/intro/right.html build/examples/intro/code.coffee build/examples/intro/code.html build/examples/intro/left.html build/examples/intro/text.html build/examples/intro/template.jade

您会注意到,这远远超出了重新生成示例所需的工作量。我希望的是更像这样的东西:

mkdir -p build/examples/intro
pygmentize -f html -o build/examples/intro/code.html lib/examples/intro/code.coffee
cp lib/examples/intro/code.coffee build/examples/intro/code.coffee
jade < build/examples/intro/template.jade --path build/examples/intro/template.jade > build/examples/intro.html
jade < build/index.jade --path build/index.jade > index.html

换句话说,我要问的是:

  1. 当我更改一些小的东西时,我需要做什么才能使 makefile 不会重建太多?
    • 在上面的示例中,当我触摸 code.coffee 时,left.html、right.html 和 text.html 都会重新构建。我该如何防止这种情况发生?
  2. 为什么 make 将 rm 命令放在输出的末尾?这似乎可能会导致一些不必要的重建。

感谢您一直阅读这个问题!我知道我的 make-fu 不足,所以任何关于如何清理 makefile 和减少冗余的提示都非常受欢迎!

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    这个构建系统太大太复杂,无法轻松复制——我讨厌发布我没有测试过的解决方案——但请尝试添加这一行:

    .SECONDARY:
    

    编辑:
    我无法重现您描述的行为,但我可以提供一些指示。

    .SECONDARY: 是一个规则;它可以在makefile中的任何地方。基本上,如果 Make 检测到隐式规则链 A->B->C,其中 A 是存在的文件,C 是目标,它会将 B 视为 中间文件 并将删除一旦工作完成。 .SECONDARY: 规则阻止删除。

    您可以组合具有相同命令的规则。这个:

    foo: bar
        do something $< $@
    
    baz: quartz
        do something $< $@
    
    quince: geef
        do something $< $@
    

    可以改写成这样:

    foo: bar
    
    baz: quartz
    
    quince: geef
    
    foo baz quince:
        do something $< $@
    

    这将消除您的 makefile 中的大量冗余,并可能使事情变得更清晰。

    【讨论】:

    • 我知道它很复杂,但我找不到简化它的方法,但仍然存在依赖问题。 :/ 我的 gnu make 技能欠缺。应该在哪里添加 SECONDARY 行?它有什么作用,为什么会有帮助?谢谢!
    • 谢谢!这些是我一直在寻找的技巧。
    【解决方案2】:

    就像@Beta 提到的那样,您在这里遇到了中间文件的麻烦。

    每个在 Makefile 中没有明确提及,而是通过模式规则(带有% 的规则)推断的文件都是中间文件,并在 make 运行后立即被删除。然后在下一次运行时重新制作。

    这是您“问题”的核心:left.html 和 right.html 文件立即被删除。 Beta 版已经提到将它们声明为 .SECONDARY 可以解决此问题,例如 the make manual tells you

    通过在 Makefile 中的任何地方(实际上:任何地方)声明一个没有任何先决条件的目标 .SECONDARY,您将所有目标声明为次要目标 - 因此,没有自动删除。

    【讨论】:

      【解决方案3】:

      您还可以将 PHONY 用于非文件的目标以提高性能。喜欢allclean。更多信息请看这里:What is the purpose of .PHONY in a makefile?

      【讨论】:

        猜你喜欢
        • 2021-06-11
        • 1970-01-01
        • 2013-11-09
        • 2013-08-31
        • 2020-01-19
        • 2021-06-22
        • 1970-01-01
        • 2019-05-30
        • 2013-03-25
        相关资源
        最近更新 更多