【问题标题】:Makefile expanding variables inside conditionals depends on order of definitionMakefile 在条件内扩展变量取决于定义的顺序
【发布时间】:2019-08-07 21:47:30
【问题描述】:

我想根据 makefile 中的另一个变量值来不同地定义一个变量。我认为使用条件可以解决问题,就像在 makefile 中这样:

ifeq ($(BOOT_FLAG),installed)
        BOOT_TEST=$(BOOT_FLAG)
else
        BOOT_TEST=no
endif

BOOT_DEFINE=$(BOOT_FLAG)

BOOT_FLAG=installed

.PHONY: all
all:
        @echo $(BOOT_TEST)
        @echo $(BOOT_DEFINE)

我预计输出是:

installed
installed

但我得到了这个:

no
installed

显然 ifeq 没有​​将 BOOT_FLAG 扩展为 installed
但是 BOOT_DEFINE 变量的设置可以正确扩展它。

我在手册中读到:

“make 在读取 makefile 时评估条件。因此,您不能在条件测试中使用自动变量,因为它们在命令运行之前没有定义”

但 BOOT_FLAG 不是自动变量。此外,如果我将 BOOT_FLAG 的定义移到 ifeq 之前,那么它可以按我的意愿工作。但是,我想保持定义的当前顺序(而且我不明白为什么 make 在使用条件时会对定义的顺序无关性产生异常)

【问题讨论】:

    标签: makefile conditional-statements


    【解决方案1】:

    答案就在你引用的陈述中:

    make 在读取 makefile 时评估条件。

    由于make在读取makefile中的该行时已经评估了条件,并且在读取该行时尚未定义变量,因此变量设置条件之后无法生效.

    仅仅因为文档列出了此行为的一个后果(大多数人对此感到困惑)并不意味着这是此行为的唯一后果.

    但是,我想保持定义的当前顺序

    你不能。

    (而且我不明白为什么 make 在使用条件时会对定义的顺序独立性产生异常)

    这几乎是不可能的,即使可以做到,除非在最微不足道的情况下,否则所产生的行为几乎无法辨认。如果你不相信我,试着写下一个算法来描述它是如何工作的。记住要考虑简单的变量赋值、嵌套条件、目标和先决条件列表中使用的变量、在 makefile 的不同部分中有意重置的变量等。

    ETA 你可以这样做,将ifeq 放入define 变量中,然后在设置BOOT_FLAG 之后使用eval 来扩展它。对我来说似乎很恶心,但是......

    【讨论】:

    • 后来我意识到了这一点。 “预处理器”在实际运行 make 文件之前评估条件。我只是假设(希望?) make 不是这样工作的。我得想办法解决这个问题。
    • 明确地说,它的工作原理是这样的:make 解析 makefile 并将结构内化为表示目标及其先决条件的有向图。作为此解析的一部分,它将设置变量并处理条件、包含等。解析完成后,make 遍历它构建的图形,寻找“要做的工作”(通过比较修改时间)。我知道这种简单的行为不是您想要的,但是如果您尝试编写一种算法,它会按照您希望的方式运行,我相信您会发现它确实不可行。
    【解决方案2】:

    这是因为 makefile 在解析文件时正在评估 ifeq。

    所以当它到达ifeq... 时,BOOT_FLAG 尚未设置,所以 BOOT_TEST = no

    然后你设置 BOOT_FLAG。

    然后,一旦所有变量都被解析,makefile 将通过并运行您的规则 - 因此在这种情况下,BOOT_DEFINE 被评估为 $(BOOT_FLAG) 最终值 installed

    试试这个:

    $(info start - BOOT_FLAG=$(BOOT_FLAG))
    ifeq ($(BOOT_FLAG),installed)
            BOOT_TEST=$(BOOT_FLAG)
    else
            BOOT_TEST=no
    endif
    
    $(info after if - BOOT_FLAG=$(BOOT_FLAG))
    
    BOOT_DEFINE=$(BOOT_FLAG)
    
    BOOT_FLAG=installed
    
    $(info after assignment - BOOT_FLAG=$(BOOT_FLAG))
    
    .PHONY: all
    all:
            @echo $(BOOT_TEST)
            @echo $(BOOT_DEFINE)
    

    在 makefile 解析期间,您将看到在不同时间打印的各种值。在第一次通过时,它会评估变量(和 if),然后在第二次通过时,它可以执行目标规则。

    【讨论】:

      【解决方案3】:

      正如其他人指出的那样,问题在于 ifeq 已就地扩展和评估。

      如果您想将评估推迟到某个较晚的时刻,您必须将整个表达式保留在递归变量中。然后条件可以通过$(if ...) 函数来实现,而不是ifeq(好吧,$(eval ifeq...) 也应该是可行的,但是......好吧,很糟糕)。

      当然,对于这种简单的情况来说,这是一个相当大的开销,但仍然可以这样做:

      BOOT_TEST=$(if $(subst _installed,,_$(BOOT_FLAG)),no,installed)
      
      BOOT_DEFINE=$(BOOT_FLAG)
      
      BOOT_FLAG=installed
      
      .PHONY: all
      all:
          @echo $(BOOT_TEST)
          @echo $(BOOT_DEFINE)
      

      【讨论】:

      • 这解决了我所说的问题。不幸的是,真正的问题(我简化为简化版本)更加棘手,因为 BOOT_FLAG 是配方的目标。我将尝试在另一个 stackoverflow 问题中再次减少问题。
      • @PerViberg 我猜你说的是like this。但这肯定是一种误用。
      • 我解决了我原来的问题! (忽略我写的关于 BOOT_FLAG 是目标的内容。那是我在 makefile 上的新手)。 $(if ..) 函数是关键!我设法在 $(if ,,) 而不是 ifeq .. else .. 感谢一百万为我指出正确的方向!。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多