所以这是一个Makefile来安装foo.conf,基于一个名为foo.conf.master的主副本。它将它安装到当前目录而不是/ etc,仅用于测试目的:
all: foo.conf.copied
foo.conf.copied: foo.conf.master foo.conf
cp foo.conf.master foo.conf
touch $@
# Recipe to tell make that it is okay for foo.conf not to exist beforehand.
foo.conf:
然后创建foo.conf.master:
$ touch foo.conf.master
$
你已经准备好测试了:
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$
重点是,如果我(使用我的“可信”系统管理员帽子)修改foo.conf.master然后make(可能由cron调用)将推出更新:
$ touch foo.conf.master
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$
但同样重要的是:如果我(使用我的“流氓”系统管理员帽子)修改已安装的版本,那么make将退出更新:
$ touch foo.conf
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$
哇噢。
好的,现在问题是:显然foo.conf并不是我想要的唯一文件,因此我需要将静态规则更改为模式规则。好的,这很简单:在目标和依赖项中用%替换foo.conf,在命令中用$替换foo.conf,然后对最后一个配方(否则只会变成'%:')做一个小修改,这样就可以了看起来我不想取消内置模式规则。
所以清理并创建这个Makefile:
all: foo.conf.copied
%.copied: %.master %
cp $*.master $*
touch $@
# Recipe to tell make that it is okay for foo.conf not to exist beforehand.
# Nop tells make that I'm not *cancelling* a pattern rule here
# (see http://stackoverflow.com/questions/34315150/make-implicit-rules-dont-work-without-command).
%: ;
但这不起作用:
$ make
make: *** No rule to make target `foo.conf.copied', needed by `all'. Stop.
$
错误消息具有误导性;它真的是foo.conf,它不知道如何制作,这可以通过在Makefile底部添加以下内容来证明:
foo.conf:
touch $@
但那时再次是静态规则,我不想要。
还有一些我想要满足的要求,上面的例子没有说明。这些是:
下面的疯狂科学家提出了一个优雅的静态规则,但我真的需要它作为一个模式规则。原因是我需要在使用规则时挂钩额外的依赖关系:
all: <new-dependency>
而不是使用变量挂钩:
STUFF_ALL_SHOULD_DEPEND_ON += <new-dependency>
此要求的原因是为了与我的大型Makefile中处理其他(非复制的)目标的方式保持一致。
然而,根据Mad Scientist的想法,我尝试了以下,但没有奏效,但也许有人帮助我:
all: foo.conf.copied
%.copied: %.master %
$(eval FILES_FOR_WHICH_AN_EMPTY_RECIPE_ARE_NEEDED += $$*)
cp $*.master $*
touch $@
define GENERATE_STATIC_EMPTY_RULE
$(1):
endef
$(foreach X,$(FILES_FOR_WHICH_AN_EMPTY_RECIPE_ARE_NEEDED),$(eval $(call GENERATE_STATIC_EMPTY_RULE,$(X))))
答案 0 :(得分:0)
你能解释为什么你要使用这个额外的“.copied”文件吗?你为什么不用它:
%: %.master ; cp $< $@
无论如何,你正在违反make与match-anything规则相关的特殊规则(像%
那样可以构建所有内容的模式规则)。如果你改变你的模式,那么它就不匹配 - 比如%.conf: ;
,那么它就可以了。但是,您可能不希望假设所有文件都以.conf
结尾。
或者,您可以使用静态模式规则,如下所示:
FILES_TO_COPY = foo.conf bar.conf biz.baz
all: $(FILES_TO_COPY:%=%.copied)
$(FILES_TO_COPY:%=%.copied): %.copied : %.master %
cp $*.master $*
touch $@
并且您不需要额外的模式规则。
答案 1 :(得分:0)
最后,我动态生成了静态规则。以下伪代码有望使实际的Makefile更容易理解:
if flag not set # flag won't be set on first call
prepare static rules
set flag # prevent running this clause again
recurse! # make invokes make
else
include static rules
do the normal thing
endif
这是真正的Makefile:
ifeq ($(MAKELEVEL),0)
all:
for X in $(patsubst %.copied,%,$^); do \
echo "$$X.copied: $$X.master $$X"; \
echo " cp $$X.master $$X"; \
echo " touch \$$@"; \
echo "$$X: ;"; \
done > Makefile.include
$(MAKE)
# The real dependencies on all are defined below, but while creating
# Makefile.include, we don't want make to digress and start making
# the dependencies; this pattern rule will stop it from doing that.
%.copied: ;
else
include Makefile.include
endif
all: foo.conf.copied
使用.SILENT
和--no-print-directory
选项(为清晰起见,上面未显示)可以使外部品牌保持沉默。
这是输出:
$ touch foo.conf.master
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$ touch foo.conf
$ make
cp foo.conf.master foo.conf
touch foo.conf.copied
$