我正在努力改善我拥有大量图书馆的项目的遗产。
对于每个库foo
,我们有几个目标(例如mrproper-foo
)。
我们构建了几个不同的产品(例如foobar
),这些产品依赖于几个库(例如foo
和bar
)。
我希望实现的目标是make
自动生成mrproper-foobar
目标,这些目标会调用相应的mrproper-foo
和mrporper-bar
。
在下面的虚拟示例中,明确给出了我的基本目标foo,bar,do-foo,do-bar
。我希望make隐式理解将要创建的任何目标:
foo:
@echo $@
bar:
@echo $@
do-foo: foo
do-bar: bar
foobar: foo bar
barfoo: bar foo
.SECONDEXPANSION:
do-%: $$(addprefix do-,$$*)
@echo "stem: "
@echo $*
@echo "prerequisites: "
@echo $^
对于foobar
和barfoo
(以及任何其他barfoobar
和其他显式创建的),我希望make将其解析为do-$(barfoo)
- > {{1} }。另外,当给出明确的目标时(比如do-bar do-foo
),我不想让make调用隐式规则...
给定的例子不起作用:
do-bar
在这种情况下,make应用隐式规则,循环依赖关系的信息似乎表明make do-bar
make: Circular do-bar <- do-bar dependency dropped.
bar
stem:
bar
prerequisites:
bar
函数有效。但是尝试调用隐式创建的目标会失败:
addprefix
任何提示?
更新了问题标题
答案 0 :(得分:1)
你可以通过条件和递归make得到你想要的东西:
foo
,bar
,do-foo
,do-bar
)包含在默认情况下评估为true的条件中(真实模式)。foobar
,barfoo
)置于任何条件之外。do-%
和%
)包含在默认情况下评估为false的条件中(错误模式)。do-%
最后一个默认目标,该目标仅在假模式下使用相同目标再次调用make。%
最后一个默认目标,没有任何先决条件,并且配方只打印“$@
”(空间目标)。do-%
规则,没有先决条件和配方:
$*
(%
模式)目标来获取所有先决条件(直接或间接),%
模式,do-
前缀添加到所有剩余元素,最后添加do-*
元素作为目标。类似的东西:
MODE := true
ifeq ($(MODE),true)
foo:
@echo $@
bar:
@echo $@
do-foo: foo
@echo $@
do-bar: bar
@echo $@
do-%:
@$(MAKE) MODE=false $@
endif
foobar: foo bar
barfoo: bar foo
ifeq ($(MODE),false)
do-%:
@targets="$$($(MAKE) --no-print-directory MODE=false $*)" && \
targets="$${targets/ $* / }" && \
targets="$${targets/% $*/}" && \
targets="$${targets/# $*/ }" && \
targets="$${targets// / do-}" && \
$(MAKE) MODE=true $$targets
%:
@printf " %s" "$@"
endif
使用非显式do-foobar
目标调用make时,make:
do-foobar
do-%
配方,该模式将在目标foobar
的错误模式下再次调用make,以获得foobar
(加foobar
本身)的先决条件,并将targets
shell变量连续设置为:
" foo bar foobar"
" foo bar"
" do-foo do-bar"
do-foo do-bar
应正常处理所有其他(显式)目标。但这真的很复杂。不确定我会自己使用它......
注意:参数扩展的序列 - 复杂do-%
配方中的替换看起来有点神秘,但bash手册部分EXPANSION,子部分参数扩展,解释了这一切。以下是stem
匹配do-%
($*
)和_
代表空格的模式的简短概述:
targets="$${targets/ $* / }"
用_stem_
_
targets="$${targets/% $*/}"
在_stem
末尾用targets
代替targets="$${targets/# $*/ }"
使用_stem
targets
开头的_
targets="$${targets// / do-}"
将所有_
替换为_do-
因此,如果targets
为foo bar foobar
,则第一次替换不执行任何操作,第二次替换将targets
更改为foo bar
,第三次替换不执行任何操作,最后一次更改{{1}进入targets
。但如果do-foo do-bar
中的成员处于不同的顺序,事情仍然没有问题。
答案 1 :(得分:0)
do-barfoo
目标,因为它不作为目标存在:它从未被定义或列为任何其他目标的先决条件。
答案 2 :(得分:0)
我使用eval
函数找到了部分解决方案:
PROGRAMS = foobar barfoo
foobar_dep = foo bar
barfoo_dep = bar foo
foobar: $(foobar_dep)
barfoo: $(barfoo_dep)
LIBRARIES = foo bar
.PHONY: all
all: $(PROGRAMS) $(LIBRARIES)
foo:
@echo $@
bar:
@echo $@
do-foo: foo
do-bar: bar
define do-PROGRAM_template
do-$(1): $(addprefix do-,$($(1)_dep))
endef
# Uncomment the next line if you want to see what targets are generated
#$(foreach prog,$(PROGRAMS),$(info $(call do-PROGRAM_template,$(prog))))
$(foreach prog,$(PROGRAMS),$(eval $(call do-PROGRAM_template,$(prog))))
这将为do-<target>
中列出的每个目标自动生成PROGRAMS
。
然而,这仍然不完美:
foobar
的先决条件,因此我定义foobar_dep
并将其用作创建目标的输入(我可以创建类似的eval定义<target>
的函数,但我的用户必须将其目标定义为target_dep =
(也许target =
就足够了)但这将是一种奇怪的方式来定义目标,因此容易出错... PROGRAM
中的所有目标,不必添加此步骤就太酷了,这样我的用户只需添加一行foobar: foo bar
而不是预先声明PROGRAMS
{1}}。有什么提示可以改善这个答案吗?