【问题标题】:Make defer prerequisite expansion until after target expansion将先决条件扩展推迟到目标扩展之后
【发布时间】:2019-09-27 12:48:07
【问题描述】:

编辑我扩展了 Makefile。

获取以下makefile:

VERILATOR:=/usr/sbin/verilator
COMMONPREPARAMS:=-Wall
COMMONPOSTPARAMS:=--trace

# Multishot timer
MULTISHOT_TIMER_OBJDIR:=multishot_timer_dir
MULITSHOT_TIMER_VERILOG_FILES:=common/multishot_timer_tb.v common/multishot_timer.v
MULITSHOT_TIMER_TOPLEVEL:=multishot_timer_top.cpp
MULTISHOT_TIMER_TARGET:=$(MULTISHOT_TIMER_OBJDIR)/multishot

#incrementer
INCREMENTER_OBJDIR:=incrementer_dir
INCREMENTER_VERILOG_FILES:=common/incrementer.v
INCREMENTER_TOPLEVEL:=src/incrementer_top.cpp
INCREMENTER_TARGET:=$(INCREMENTER_OBJDIR)/incrementer

ALLTARGETS := $(MULTISHOT_TIMER_TARGET)
ALLOBJDIRS := $(MULTISHOT_TIMER_OBJDIR)

.PHONY: all multishot_timer incrementer

all: multishot_timer incrementer

multishot_timer: OBJDIR:=$(MULTISHOT_TIMER_OBJDIR)
multishot_timer: VERILOG_FILES:=$(MULITSHOT_TIMER_VERILOG_FILES)
multishot_timer: TOPLEVEL:=$(MULITSHOT_TIMER_TOPLEVEL)
multishot_timer: $(MULTISHOT_TIMER_TARGET)

incrementer: OBJDIR:=$(INCREMENTER_OBJDIR)
incrementer: VERILOG_FILES:=$(INCREMENTER_VERILOG_FILES)
incrementer: TOPLEVEL:=$(INCREMENTER_TOPLEVEL)
incrementer: $(INCREMENTER_TARGET)

$(ALLTARGETS): $(TOPLEVEL) $(VERILOG_FILES)
    $(VERILATOR) $(COMMONPREPARAMS) -cc $(VERILOG_FILES) --exe $(TOPLEVEL) -o $@

我的实际问题是 $(ALLTARGETS) 的先决条件评估得太早,因此该规则永远不会有任何先决条件。

我想将 ALLTARGETS 规则的先决条件的评估推迟到目标先决条件评估之后,因为那是声明 $(TOPLEVEL) 等变量的地方。此外,我希望能够键入 all 然后启动多个不同的目标,每个目标都有自己的变量,例如 $(TOPLEVEL)。

这样做的一种方法是为 $(ALLTARGETS) 中的每个目标创建一个新规则,但由于每个目标的工作完全相同,我想只使用一个目标。有可能吗?

【问题讨论】:

    标签: makefile gnu-make


    【解决方案1】:

    你似乎很困惑。我很困惑。

    multishot_timer: TOPLEVEL := $(MULITSHOT_TIMER_TOPLEVEL)
    

    这是一个特定于目标的变量。 ${TOPLEVEL} 仅在 make 扩展 multishot_timer 的配方或 其任何先决条件 的配方时才具有值 multishot_timer_top.cpp。当 make 扩展这一行时,情况并非如此:

    $(ALLTARGETS): $(TOPLEVEL) $(VERILOG_FILES)
    

    修复?只需将TOPLEVEL 设为一个普通的全局变量即可。

    TOPLEVEL := $(MULITSHOT_TIMER_TOPLEVEL)
    

    我认为你有一个更大的模式,你没有告诉我们。

    编辑

    好的。有两件事要管理。

    依赖关系

    一个目标可以有许多依赖行,但其中只有一个可以有一个配方。

    multishot_timer: common/multishot_timer_tb.v common/multishot_timer.v
    incrementer: common/incrementer.v
    
    multishot_timer incrementer:
        ${VERILATOR} ${COMMONPREPARAMS} -cc $^ --exe ${TOPLEVEL} -o $@
    

    根据目标更改配方

    这里有很多选择。以下是针对当前问题的建议:

    基本上${TOPLEVEL} 的不同取决于$@multishot_timer 还是incrementer

    TOPLEVEL<multishot_timer> := multishot_timer_top.cpp
    TOPLEVEL<incrementer> := src/incrementer_top.cpp
    TOPLEVEL = ${TOPLEVEL<$@>}
    

    很好。可能不值得定义TOPLEVEL(注意=,它是一个惰性变量很重要),直接在配方中使用${TOPLEVEL&lt;$@&gt;}

    加分项:目标列表

    获取TOPLEVEL&lt;%&gt;形式的所有变量名,去掉TOPLEVEL&lt;前缀和&gt;后缀。

    ALLTARGETS := $(patsubst TOPLEVEL<%>,%,$(filter TOPLEVEL<%>,${.VARIABLES}))
    

    (注意我如何为变量保留${…},为函数保留$(…)。我发现它有帮助。YMMV。)

    【讨论】:

    • 我将 OP 中的示例放大以进一步解释我的意思,但基本上:我有许多不同的可能设置,每个设置都有不同的重要变量值,但它们都具有相同的一般性创建可执行文件的规则。
    • 我尝试实现这一点,但我似乎做错了什么或被误解了。我无法获得正确扩展变量的最终规则。你能举一个更详细的例子吗?
    • 两个提示:使用 --warn-undefined-variables 运行 make 并研究输出。另请注意,您必须正确使用=:=。它们相同。
    【解决方案2】:

    如果您使用 GNU make,则可以通过 secondary expansion 执行此操作。

    在您的示例中,您将使用:

    .SECONDEXPANSION:
    $(ALLTARGETS): $$(TOPLEVEL) $$(VERILOG_FILES)
    

    注意先决条件列表中额外的$ 转义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-10
      • 2021-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-03
      • 2021-09-20
      相关资源
      最近更新 更多