在Makefile

时间:2018-03-15 16:50:23

标签: makefile gnu-make

tl; dr:我想从同一规则构建base.extbase_variant.ext

我经常有一个Makefile可以构建一些目标的变种,默认版本开始类似base.ext和变体例如base_variant.ext。这些是从相同(一组)来源构建的,只有一些小差异。这些规则可能非常复杂,我真的不想重复它们。

随着岁月的流逝,我逐渐变得更加烦恼,因为我不能使用下面的Makefile之类的东西:

options = ...
%_variant.ext: options += ....

base%.ext: sources
    command $^ $(options) > $@

对于Makefile的不同用途,这是一个真正回归的模式。它可以用于构建可执行文件,这可能类似于:

SRC=$(wildcard *.c)
%_instr.o:%.c
     $(CC) -some-instrumentation-flags $^ -o $@

# Ideally I would like this rule to build foo from implicit-rules binaries,
# and foo_instr from the objects generated by the rule above
foo%:$(SRC:.c=%.o)
     $(CC) $^ -o $@

在提供MCVE的过程中,这里有一个带有beamer演示的示例,其中beamer.pdf是基线版本,beamer_notes.pdf在第二个屏幕上有#34;注释"已启用,beamer_handout.pdf是一个更密集的版本,页面更少:

% beamer.tex
\documentclass[ignorenonframetext,pdfusetitle,17pt]{beamer}
\usepackage{pgfpages}
\mode<handout:0>{\setbeameroption{show notes on second screen=right}}
\mode<handout:1>{\setbeameroption{hide notes}}
\title{some talk} \author{some author}
\begin{document}
\begin{frame}{Hello world}
\maketitle
\end{frame}
\end{document}
# Makefile
SRC=beamer.tex

options:=
default:$(SRC:.tex=.pdf)

handout:$(SRC:.tex=_handout.pdf)
handout:options:=handout

notes:$(SRC:.tex=_notes.pdf)
notes:options:=notes

$(SRC:.tex=%.pdf):$(SRC)
    pdflatex -jobname="$(@:.pdf=)" '\PassOptionsToClass{$(options)}{beamer}\input{$<}'
    # here a bunch more rules potentially to deal with bibliographies etc

.PHONY:default handout notes

现在这个Makefile并不像我想的那样工作:

$ make
make: *** No rule to make target 'beamer.pdf', needed by 'default'.  Stop.

make handoutmake notes按预期工作。是否有任何方式使这项工作?

到目前为止,我发现了3种不完美的方式:

  • 将最终规则更改为%: $(SRC),但只要在Makefile中有多个规则就会变得一团糟

  • 拥有默认目标版本beamer_.pdf(通常会开始生活在一个所有默认目标都有一个带有尾随下划线的愚蠢base_.ext名称的世界中)

  • 添加构建beamer_.pdf的规则并将其移至beamer.pdf
    beamer_.pdf: beamer.pdf mv $< $@
    但是现在永远不会满足此规则,并且beamer_.pdf始终会重建,即使beamer.pdf比其来源更新。我已尝试制作beamer_.pdf PHONY,但无济于事。

是否有其他人遇到此问题并找到了令人满意的解决方案?

1 个答案:

答案 0 :(得分:1)

对于3.82之前的make版本,有一个相当简单的解决方案:

# Makefile
SRC=beamer.tex

default: $(SRC:.tex=.pdf)
handout: $(SRC:.tex=_handout.pdf)
notes: $(SRC:.tex=_notes.pdf)

$(SRC:.tex=.pdf) $(SRC:.tex=_%.pdf) : $(SRC)
    pdflatex -jobname="$(@:.pdf=)" '\PassOptionsToClass{$*}{beamer}\input{$<}

.PHONY:default handout notes

也就是说,您可以为两个目标规范使用相同的源和规则,非模式规范(显式规则)和模式规则(隐式规则)。它曾经工作过。不幸的是,在3.82版本中不推荐使用此语法。

然而,在版本4.1中,它再次被接受,尽管有警告。 来自资源中的文件NEWS

  
      
  • 更改混合显式和隐式规则的致命错误,即   在GNU中引入3.82,给出了一个非致命的错误。但是,这种语法   仍然被弃用,并可能在未来的版本中返回非法   GNU make。应修复依赖此语法的Makefile。
  •   

事实上,linux内核源代码中的许多makefile仍在使用它,而且可能是。 (阅读用于混合显式和隐式规则的宽松约束 here以获取更多信息。)

截至当前版本4.2,它仍然有效,并带有警告。但是,它不是一个永久的解决方案,因为它可能会再次消失。

评论中已经提到的罐头食谱是要走的路。请注意,$*的处理方式不同:当在隐式规则的配方中使用时,它会扩展到词干的值(匹配%的字符串),而在明确规则的配方中,它是空的或,如果目标以“已知”后缀结束,则会扩展到没有后缀的目标。

# Makefile
SRC=beamer.tex

define my_rules
pdflatex -jobname="$(@:.pdf=)" '\PassOptionsToClass{$*}{beamer}\input{$<}
# Other complicated rules...
endef

default: $(SRC:.tex=.pdf)
handout: $(SRC:.tex=_handout.pdf)
notes: $(SRC:.tex=_notes.pdf)

$(SRC:.tex=.pdf) : $(SRC)
    $(my_rules) 

$(SRC:.tex=_%.pdf) : $(SRC)
    $(my_rules)

.PHONY:default handout notes