我有一组有时需要重新生成的文件。标准制作东西。我有一条知道如何重生的规则。
# RewriteEngine On
#
# ErrorDocument 404 /err.php?id=404
# ErrorDocument 403 /err.php?id=403
# ErrorDocument 500 /err.php?id=500
这很好用。问题是
$(foreach x, $(BIG_LIST), $(eval $(x): $(x).in))
$(BIG_LIST):
opaque_binary -o $@ $^
有时也需要重建 - 我无法表达它的依赖关系。具体来说,它是一个Go二进制文件(内部处理重建代码),Go可能需要4-5秒才能找到"这里无需做任何事情"。我不能简单地在我的食谱中重建它 - 太慢了。
目前我只是从我的opaque_binary
规则单方面运行它,这很烦人,因为大部分时间它是最新的,我不需要重新生成我的BIG_LIST。浪费时间。
更糟糕的是,我有大约六个这样的二进制文件要构建,所以这一步最终需要30秒才能有效地做任何事情。
我希望有某种方式表达"这样做一次,但前提是BIG_LIST中的任何文件需要重新生成"。
答案 0 :(得分:1)
首先,这个片段对我来说很奇怪。为什么不简单地使用静态模式规则?
$(BIG_LIST): %: %.in
opaque_binary -o $@ $^
至于手头的问题,当go终于发现它已经没有什么可做的时候,我打赌它不会更新二进制文件的时间戳。然后,它做了所有评估的事实需要以其他方式记录,例如,通过自己更新时间戳。然后我们需要重新评估之后是否有任何.in文件发生了变化。
所以这就是我写它的方式:
$(BIG_LIST): %: %.in | opaque_binary
opaque_binary -o $@ $^
opaque_binary: $(BIG_LIST:%=%.in)
have a go at $@
copy /B $@+,,
您可以在食谱中将opaque_binary
缩写为$|
。
答案 1 :(得分:0)
如果我从问题和评论中直接得到了足够的问题 (也许我还没有),然后:
$(BIG_LIST)
的成员实际上是make
目录而不是你!无论如何,对于第一次近似,你需要" opaque_binary"是: -
你当然可以断定你不能使用模式规则,如果你不能指明
图案。但是,你必须遵守$(foreach subdir,$(subdirs),$(eval ...))
。
最好利用makefile 中的事实,你可以生成一个辅助的makefile
你随后 include
。如果您有选择,生成的规则和配方,您保存到生成的makefile
优于仅由$(eval ...)
在内存中生成的那些,因为它们是
更多 scrutable 。
为了说明,假装不透明的二进制文件是copy
,这是一个微不足道的
从copy.c
构建的C程序,它只将第一个参数命名的文件复制到标准输出。
你说你不能真正表达makefile中不透明二进制文件的构成,
但是你知道一些制作它的方法,所以我们只是假设它是copy
制作的方式(这是
为了更清晰make
输出,有点非正统。
这是一个项目的makefile,其中包含现有的子目录$(dirs)
一些*.x
文件,我们假装我们只能通过查找来识别。从这个*.x
文件
我们希望使用foo.out
在同一个子目录中生成copy
,但copy
必须首先
如果比*.x
文件旧,则重建。只是为了我们目前的方便makefile
还提供从头开始制作$(dirs)
和*.x
文件。
Makefile(取1)
dirs := a b c
file := foo
for_convenience := \
$(foreach dir,$(dirs),\
$(shell mkdir -p $(dir) && ([ -f $(dir)/file.x ] || \
echo "$(dir)/file.x content" > $(dir)/file.x)))
outs := $(dirs:%=%/$(file).out)
opaque_binary := copy
void := $(file >gen_rules.mk,)
void := \
$(foreach dir,$(dirs), \
$(file >>gen_rules.mk,\
$(dir:%=%/$(file).out): $(shell ls $(dir)/*.x) | \
$(opaque_binary); ./$(opaque_binary) $$< > $$@))
void := $(foreach dir,$(dirs),\
$(file >>gen_rules.mk,\
$(opaque_binary): $(shell ls $(dir)/*.x)))
.PHONY: all clean test
all: $(outs)
$(opaque_binary): copy.c
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS)
clean:
rm -fr $(dirs) $(opaque_binary)
test:
cat $(outs)
include gen_rules.mk
当make
执行任何目标时,它将生成gen_rules.mk
,其中
看起来像是:
<强> gen_rules.mk 强>
a/foo.out: a/file.x | copy; ./copy $< > $@
b/foo.out: b/file.x | copy; ./copy $< > $@
c/foo.out: c/file.x | copy; ./copy $< > $@
copy: a/file.x
copy: b/file.x
copy: c/file.x
因为$(opaque_binary)
即copy
是$(outs)
的仅订单先决条件,
在确定是否需要重新制作$(outs)
中的任何一个时,不将被考虑,
但它将在任何$(outs)
之前完成,并且它依赖于所有*.x
如果文件比其中任何一个旧,则会触发其配方。
从头开始,make
看起来像是:
$ make
cc -o copy copy.c
./copy a/file.x > a/foo.out
./copy b/file.x > b/foo.out
./copy c/file.x > c/foo.out
您可以看到,a/foo.out
需要尽快copy
,首先制作b/foo.out
,
然后不会为c/foo.out
或$(opaque_binary)
重新制作,因为它不需要。
只是为了表明$ make test
cat a/foo.out b/foo.out c/foo.out
a/file.x content
b/file.x content
c/file.x content
已经完成了它的工作:
*.x
如果我们更新任何$(opaque_binary)
个文件,则$ touch a/file.x b/file.x
$ make
cc -o copy copy.c
./copy a/file.x > a/foo.out
./copy b/file.x > b/foo.out
会重新制作,只需重复一次:
$(outs)
那会吗?
如果makefile仅用于此目的,那么这对您来说已经足够了
。同时制作所有$(outs)
。
如果它还用于制作个人$(outs)
或$(opaque_binary)
的子集,
那么$ make
make: Nothing to be done for 'all'.
的无意义构建仍有余地
你可能想要消费。 E.g。
a/file.x
一切都好。然后,代理A更新$ touch a/file.x
:
b/foo.out
代理B制作$ make b/foo.out
cc -o copy copy.o
,是最新的:
$(opaque_binary)
由于*.x
依赖于所有 $(opaque_binary)
个文件,因此代理B已被收取1 dirs := a b c
file := foo
for_convenience := \
$(foreach dir,$(dirs),\
$(shell mkdir -p $(dir) && ([ -f $(dir)/file.x ] || \
echo "$(dir)/file.x content" > $(dir)/file.x)))
outs := $(dirs:%=%/$(file).out)
opaque_binary := copy
void := $(file >gen_rules_pass1.mk,)
void := $(foreach dir,$(dirs),\
$(file >>gen_rules_pass1.mk,\
$(dir:%=%/$(file).out): $(shell ls $(dir)/*.x) | \
$(opaque_binary); ./$(opaque_binary) $$< > $$@))
void := $(file >gen_rules_pass0.mk,)
void := $(foreach dir,$(dirs),\
$(file >>gen_rules_pass0.mk,\
$(dir:%=%/$(file).out): $(shell ls $(dir)/*.x)))
void := $(foreach dir,$(dirs),\
$(file >>gen_rules_pass0.mk,\
$(dir:%=%/$(file).out): $(dir:%=%.$(opaque_binary).d); $$(MAKE) $$@))
void := $(foreach dir,$(dirs),\
$(file >>gen_rules_pass0.mk,\
$(dir:%=%.$(opaque_binary).d: $(shell ls $(dir)/*.x)); \
rm -f $(opaque_binary); touch $$@))
deps := $(dirs:%=%.$(opaque_binary).d)
.PHONY: all clean test
ifeq ($(MAKELEVEL),1)
all: $(outs)
include gen_rules_pass1.mk
else
all : $(deps)
$(MAKE)
include gen_rules_pass0.mk
endif
$(opaque_binary): copy.c
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LDLIBS)
clean:
rm -f $(deps) $(ins) $(outs) $(opaque_binary) gen_rules_pass*.mk
test:
cat $(outs)
版本的费用
他们没有要求也没有需要。令人激动的是,无偿的指控是他们的整个账单。
Fundamental theorem of software engineering, 你可以通过引入额外的间接水平来弥补这种浪费和不公正, 如:
Makefile(拍2)
gen_rules_pass0.mk
这里我们需要一个两遍制作,所以我们生成$(MAKELEVEL)
以包含
在gen_rules_pass1.mk
== 1时包含$(MAKELEVEL)
== 0和 a/foo.out: a/file.x
b/foo.out: b/file.x
c/foo.out: c/file.x
a/foo.out: a.copy.d; $(MAKE) $@
b/foo.out: b.copy.d; $(MAKE) $@
c/foo.out: c.copy.d; $(MAKE) $@
a.copy.d: a/file.x; rm -f copy; touch $@
b.copy.d: b/file.x; rm -f copy; touch $@
c.copy.d: c/file.x; rm -f copy; touch $@
时。
<强> gen_rules_pass0.mk 强>
T
在此您可以看到,对于每个目标词干T.$(opaque_binary).d
,我们都引入了依赖项文件 T/foo.out: T/file.x
提供每 - $(opaque_binary)
- 对目标,我们可以在其上挂起决定权
是否应重建T/foo.out
。如果T/file.x
已 T/foo.out: T.copy.d; $(MAKE) $@
过时
它会触发配方:
T/foo.out
将在第二轮中重新制作T.copy.d: T/file.x; rm -f copy; touch $@
。它还需要:
T.copy.d
首先进行评估,T/file.x
同样过时$(opaque_binary)
,
然后会在此传递和T.copy.d
&#39;中删除T.$(opaque_binary).d
时间戳已更新。对于默认目标,此传递仅重新创建所有
T/foo.out
然后调用第二遍。否则
启动指定 a/foo.out: a/file.x | copy; ./copy $< > $@
b/foo.out: b/file.x | copy; ./copy $< > $@
c/foo.out: c/file.x | copy; ./copy $< > $@
目标的规则。
<强> gen_rules_pass1.mk 强>
gen_rules.mk
与旧的 T/foo.out: T/file.x | copy; ./copy $< > $@
相比,它的含义相同:
copy: T/file.x
并且没有:
$(opaque_binary)
我们不再需要让*.x
依赖于
自第一遍以来make
个文件已保留或
完全按照我们的目标要求删除它。但它仍然是一个
这些目标中只有订单的先决条件,现在就是这样
如果需要的话,可以重新制作,因为如果它需要,它就会消失。
使用这个makefile,$ make
rm -f copy; touch a.copy.d
rm -f copy; touch b.copy.d
rm -f copy; touch c.copy.d
make
make[1]: Entering directory '/home/imk/develop/scrap'
cc -o copy copy.c
./copy a/file.x > a/foo.out
./copy b/file.x > b/foo.out
./copy c/file.x > c/foo.out
make[1]: Leaving directory '/home/imk/develop/scrap'
从头开始看起来像:
*.x
更新了一些$ touch a/file.x b/file.x
$ make
rm -f copy; touch a.copy.d
rm -f copy; touch b.copy.d
make
make[1]: Entering directory '/home/imk/develop/scrap'
cc -o copy copy.c
./copy a/file.x > a/foo.out
./copy b/file.x > b/foo.out
make[1]: Leaving directory '/home/imk/develop/scrap'
个文件后:
$ make
make
make[1]: Entering directory '/home/imk/develop/scrap'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/home/imk/develop/scrap'
$ touch a/file.x # Agent A
$ make b/foo.out # Agent B
make: 'b/foo.out' is up to date. # :)
到目前为止,和以前一样。但是如果我们重播代理A,代理B. 情形:
make a/foo.out
rm -f copy; touch a.copy.d
make a/foo.out
make[1]: Entering directory '/home/imk/develop/scrap'
cc -o copy copy.c
./copy a/file.x > a/foo.out
make[1]: Leaving directory '/home/imk/develop/scrap'
和
make
比以前更有效,更公平,但通过更复杂,更不健全的方式。
您可能不喜欢使用*.d
文件混乱的.d
目录
而且,更实际的是,担心闲置的手容易
搞砸了。在那个帐户上通常是这样的依赖文件
在隐藏的.deps
或{{1}}子目录中分泌。我已经省略了
为简单起见的预防措施。
即使我没有正确理解这个问题,这里的想法可能会帮助你找到解决方案。
(GNU Make 4.1)