Makefile元编程:基于其他目标先决条件扩展的模板目标

时间:2017-10-17 13:13:17

标签: makefile

我正在努力改善我拥有大量图书馆的项目的遗产。

对于每个库foo,我们有几个目标(例如mrproper-foo)。

我们构建了几个不同的产品(例如foobar),这些产品依赖于几个库(例如foobar)。

我希望实现的目标是make自动生成mrproper-foobar目标,这些目标会调用相应的mrproper-foomrporper-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 $^

对于foobarbarfoo(以及任何其他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

任何提示?

修改

更新了问题标题

3 个答案:

答案 0 :(得分:1)

你可以通过条件和递归make得到你想要的东西:

  1. 将您的显式根目标(foobardo-foodo-bar)包含在默认情况下评估为true的条件中(真实模式)。
  2. 将复合目标(foobarbarfoo)置于任何条件之外。
  3. 将所有其他目标(do-%%)包含在默认情况下评估为false的条件中(错误模式)。
  4. 在真实条件中添加do-%最后一个默认目标,该目标仅在假模式下使用相同目标再次调用make。
  5. 在假条件中添加%最后一个默认目标,没有任何先决条件,并且配方只打印“$@”(空间目标)。
  6. 在假条件中添加do-%规则,没有先决条件和配方:
    • 以错误模式再次调用make,使用$*%模式)目标来获取所有先决条件(直接或间接),
    • 处理它以删除%模式,
    • do-前缀添加到所有剩余元素,最后添加
    • 以真实模式再次调用make,并将生成的do-*元素作为目标。
  7. 类似的东西:

    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:

    1. 以真实模式启动并以目标do-foobar
    2. 的错误模式再次调用自身
    3. 运行错误模式的do-%配方,该模式将在目标foobar的错误模式下再次调用make,以获得foobar(加foobar本身)的先决条件,并将targets shell变量连续设置为:
      • " foo bar foobar"
      • " foo bar"
      • " do-foo do-bar"
    4. 调用最后一次以目标do-foo do-bar
    5. 的真实模式

      应正常处理所有其他(显式)目标。但这真的很复杂。不确定我会自己使用它......

      注意:参数扩展的序列 - 复杂do-%配方中的替换看起来有点神秘,但bash手册部分EXPANSION,子部分参数扩展,解释了这一切。以下是stem匹配do-%$*)和_代表空格的模式的简短概述:

      • targets="$${targets/ $* / }"_stem_
      • 替换_
      • targets="$${targets/% $*/}"_stem末尾用targets代替
      • targets="$${targets/# $*/ }"使用_stem
      • 替换targets开头的_
      • targets="$${targets// / do-}"将所有_替换为_do-

      因此,如果targetsfoo 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}}。

有什么提示可以改善这个答案吗?