【问题标题】:Variables expansion in MakeMake中的变量扩展
【发布时间】:2026-01-15 05:10:02
【问题描述】:

假设我有以下 Makefile

N := 1
.PHONY : target_$(N)
target_$(N) :
    @echo $(N)

N := 2
.PHONY : target_$(N)
target_$(N) :
    @echo $(N)

结果是:

$ make target_1
2
$ make target_2
2

有什么方法可以实现我想要的吗?我的想法是使用变量作为参数复制粘贴配方。该变量必须在目标名称和该目标的配方中展开,也许它也可能出现在依赖项中,但目前还不行。问题是配方扩展似乎发生在配方执行时,但我期望并且需要它与目标扩展同时发生。

【问题讨论】:

    标签: variables makefile gnu-make


    【解决方案1】:

    另一种选择是使用target-specific variables

    N := 1
    .PHONY : target_$(N)
    target_$(N) :
            @echo $(N)
    target_$(N) : N := $(N)
    

    【讨论】:

      【解决方案2】:

      我会说保持简单,而不是所有丑陋的嵌套函数调用,并使用命令行变量作为单个目标的参数。

      target:
          @echo ${N}
          @if test ${N} -eq 1; then DO THIS; else DO THAT; fi
      

      并调用你的 Makefile

      make target N=1
      

      这样您就拥有了 shell 控制结构的所有功能。并且您的 Makefile 仍然完全可移植到非 GNU make。

      【讨论】:

      • 好吧,假设规则实际上构建了有问题的目标,因此如果您再次运行make,它不会重新运行配方。在这种情况下,这个建议不起作用。一般来说,它不是很像。如果这就是你想要的,为什么不直接使用 shell 脚本呢?
      【解决方案3】:

      我可以通过automatic variables 和模式规则达到您想要的结果:

      target_%:
          @echo $*
      

      输出:

      $ make target_1
      1
      $ make target_2
      2
      

      要使该值在先决条件列表中可用,您需要 .SECONDEXPANSION 特殊目标:

      .SECONDEXPANSION:
      target_%: prerequisite_$$*
          @echo $<
      
      prerequisite_%:
          @echo "do something here to build prerequisite nb.$*"
      

      输出:

      $ make target_1
      do something here to build prerequisite nb.1
      prerequisite_1
      

      【讨论】:

        【解决方案4】:

        make 变量的扩展在必要时发生。在目标行中,例如target_$(N):,在读取行时发生。在命令(配方)行中,它在执行操作时发生。因此,您可以在读取文件时更改N 的值,但最终值是执行命令时将使用的值,因此值2 出现两次。 (这样做会导致混乱;不要在生产 makefile 中使用它。)

        AFAIK,没有办法改变这种行为。您不能在读取命令行的位置冻结宏集。有些宏可能直到在命令行中使用后才被定义,您需要能够更改默认规则使用的宏的值等。

        【讨论】:

          【解决方案5】:

          我发现做我想做的最好的方法是从目标名称本身中删除参数:

          TARGETS := target_1 target_2
          .PHONY : $(TARGETS)
          $(TARGETS) :
              $(eval $@_N := $(subst target_,,$@))
              @echo $($@_N)
          

          如果目标名称变得过于复杂,可以从一开始的参数集以编程方式创建它们。

          【讨论】: