我需要帮助减少 makefile 中的依赖项

发布于 2024-12-12 14:09:17 字数 4178 浏览 0 评论 0原文

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

  • 我们正在编译一个网页,index.html
  • index.html 需要一个必须编译的 stylus css main.sty
  • 页面中使用了许多示例
    • 例如 one 的代码位于 lib/examples/one
    • 每个示例包含三个部分
      • 标记(.jade 模板文件)
      • 一些代码(.coffee 脚本文件)
      • 描述(.md markdown 文件)
  • 构建脚本必须将每个示例渲染到单个 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 都需要将其放入构建中。

现在,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 并减少冗余的提示!

I have created a makefile for the generation of a simple web page. The idea behind the makefile is this:

  • We're compiling one web page, index.html
  • index.html requires a stylus css main.sty that must be compiled
  • There are a number of examples used in the page
    • The code for example one lives in lib/examples/one
    • Each example contains three parts
      • The markup (a .jade template file)
      • Some code (a .coffee script file)
      • A description (a .md markdown file)
  • The build script must render each example into a single html file
    • Jade, Pygments, and Markdown are used to generate three html files
    • An example.jade template is used to combine these into one example file
      • example.jade must be copied to the correct build example directory, because the template language can only do relative imports. So in order to import example/one/code.html, we must copy the template to example/one and have it include code.html.
    • When finished, each example x will have compiled to tbuild/examples/x.html
  • The lib/index.jade template is moved to build (so that it can include the example files)
  • Jade is then used to compile the index.jade template into html

This is a wee bit of a simplification, but it's easier to understand this way. The simplification is that there are actually two markup files (left.html and right.html) in each example, and that the code file is both run through pygments and used as a script, so both code.html and code.coffee need to make it into build.

Right now, the makefile looks like this:

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 < 
lt; --path 
lt; > $@

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

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

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

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

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

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

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

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

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

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

This works, but the problem is that when I touch "lib/examples/intro/code.coffee" and re-run make, I get the following:

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

Which, as you'll notice, is way more than needs to be done to regenerate the example. What I was hoping for was something more like this:

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

In other words, what I'm asking is:

  1. What do I need to do to make the makefile not rebuild too much when I change something small?
    • In the above example, left.html, right.html, and text.html are all rebuilt when I touch code.coffee. How can I prevent this?
  2. Why does make put the rm command at the end of the output? This seems like it might be causing some re-building where unnecessary.

Thanks for reading all the way through this beast of a question! I know that my make-fu is lacking, so any tips as to how to clean up the makefile and reduce redundancy are more than welcome!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

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

.SECONDARY:

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

.SECONDARY: 是一条规则;它可以放在 makefile 中的任何位置。基本上,如果 Make 检测到一系列隐式规则 A->B->C,其中 A 是存在的文件,C 是目标,则它认为 B 是中间文件并且工作完成后将删除它。 .SECONDARY: 规则阻止删除。

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

foo: bar
    do something 
lt; $@

baz: quartz
    do something 
lt; $@

quince: geef
    do something 
lt; $@

可以重写为:

foo: bar

baz: quartz

quince: geef

foo baz quince:
    do something 
lt; $@

这将删除 makefile 中的大量冗余,也许会让事情变得更清晰。

This build system is too large and complex to reproduce easily -- and I hate to post solutions I haven't tested -- but try adding this line:

.SECONDARY:

EDIT:
I haven't been able to reproduce the behavior you describe, but I can offer some pointers.

The .SECONDARY: is a rule; it can go anywhere in the makefile. Basically, if Make detects a chain of chain of implicit rules, A->B->C, where A is a file that exists and C is the target, it considers B an intermediate file and will delete it once the job is done. The .SECONDARY: rule blocks the deletion.

You can combine rules that have the same commands. This:

foo: bar
    do something 
lt; $@

baz: quartz
    do something 
lt; $@

quince: geef
    do something 
lt; $@

can be rewritten as this:

foo: bar

baz: quartz

quince: geef

foo baz quince:
    do something 
lt; $@

That will remove a lot of redundancy in your makefile, and perhaps make things clearer.

霊感 2024-12-19 14:09:17

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

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

这就是“问题”的核心: left.html 和 right.html 文件立即被删除。 Beta 已经提到将它们声明为 .SECONDARY 可以解决此问题,例如 make 手册告诉你

通过在 Makefile 中的任何位置(实际上:任何地方)声明没有任何先决条件的目标 .SECONDARY,您可以将所有目标声明为辅助目标 - 因此,不会自动删除。

Like @Beta mentioned, you are running into troubles with intermediate files here.

Each file that is not mentioned explicitely in the Makefile, but rather inferred via a pattern rule (a rule with %), is intermediate and is deleted right after make ran. And then remade on the next run.

That's the core of your "problem": The left.html and right.html files are deleted right away. Beta already mentioned that declaring them as .SECONDARY fixes this problem, like the make manual tells you.

By declaring a target .SECONDARY without any prerequisites anywhere in the Makefile (really: anywhere), you declare all targets as secondary - so, no auto-deletes.

浪漫人生路 2024-12-19 14:09:17

您还可以对非文件目标使用 PHONY 以提高性能。就像allclean 一样。请参阅此处了解更多信息: 目的是什么makefile 中的 .PHONY?

You could also use PHONY for targets that are not files to improve the performance. Like all and clean. See here for more information: What is the purpose of .PHONY in a makefile?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文