【问题标题】:How do I perform arithmetic in a makefile?如何在生成文件中执行算术?
【发布时间】:2010-12-27 22:02:09
【问题描述】:

是否可以对 makefile 中的变量执行一些操作? 例如,定义

JPI=4
JPJ=2

是否可以在同一个makefile中定义一个变量JPIJ等于$(JPI)*$(JPJ)的扩展值?

【问题讨论】:

    标签: makefile


    【解决方案1】:

    使用Bash arithmetic expansion:

    SHELL=/bin/bash
    JPI=4
    JPJ=2
    all:
        echo $$(( $(JPI) * $(JPJ) ))
    

    第一行是choose the Bash shell instead of the default (sh)。通常, sh 不支持算术扩展。但是在 Ubuntu 中,/bin/sh 由 Dash, which supports this feature 提供。因此可以跳过该行。

    双美元符号是因为我们想要the expansion to be done by the shell。注意:JPI 和 JPJ 变量先通过 make 展开,然后表达式传递给 bash,如下所示:

    $(( 4 * 2 ))
    

    【讨论】:

    • POSIX sh 确实支持 arithmetic expansion
    • +1 因为这是最好的解决方案,因为它只依赖于 POSIX 兼容的 shell。感谢您指出在 make 中进行 shell 算术时我们需要一个双美元符号。
    【解决方案2】:

    如果您使用的是 GNU make 并在您的系统上安装了 bc,您可以使用这样的东西:

    JPI=4
    JPJ=2
    FOO=$(shell echo $(JPI)\*$(JPJ) | bc)
    all:
      echo $(FOO)
    

    【讨论】:

    • expr 比 bc 更常见:$(shell expr $(JPI) \* $(JPJ))
    • @DanielAlder:有趣,我总是使用bc,但我可以看到expr 如果您正在做简单的以 10 为底的整数运算可能更方便(这个问题就是这种情况) .
    【解决方案3】:

    @mrkj 的回答很好,但正如@Daniel 提到的,并非所有系统都有bc(例如,我在 MSys 上没有)。

    我找到了以下两种方法,都使用了shell:$$(( ... ))expr ...

    JPI=4
    JPJ=2
    
    #With Double-dollar
    JPIJ_1 = $(shell echo $$(( $(JPI) + $(JPJ) )))
    
    #With 'expr'
    JPIJ_2 = $(shell expr $(JPI) + $(JPJ) )
    
    $(info Sum with Double-$$: $(JPIJ_1))
    $(info Sum with 'expr': $(JPIJ_2))
    

    请注意,当使用expr 时,您+ 周围放置空格,否则它将返回4+2。使用 $$ 时不需要这样做。

    .

    当您有可用的bc 时,您肯定会选择它。我发现以下页面非常有趣:http://www.humbug.in/2010/makefile-tricks-arithmetic-addition-subtraction-multiplication-division-modulo-comparison/

    【讨论】:

    • 请注意 expr ... 由您的 shell 执行,因此会受到名称扩展的影响,例如expr 2 * 3 将导致错误而不是预期的输出,因为 * 被 shell 扩展为当前目录中的所有文件名。您需要转义运算符 expr 2 \* 3 或将整个表达式放在单引号 expr '2 * 3' 中。
    【解决方案4】:

    它很笨拙(或者很聪明,取决于你的观点),但你可以直接在 GNU make 中进行算术运算。见Learning GNU Make Functions with Arithmetic。请注意,尽管这种方法不能很好地扩展。正如您在问题中显示的那样,它对于小数字非常有效,但是当您处理大数字(大于 10,000,000)时效果不佳。

    【讨论】:

    • hmmm.... 令人惊讶的是,makefile 可以进行数学运算。这不应该是一个容易添加的功能吗?
    • @Kamath 查看gmtt 以找到大数字的实现(约 63 个位置,但如果需要,您可以拥有更多位置)。代码仍然笨拙、令人抓狂、尴尬或可怕,这取决于你的观点,虽然
    【解决方案5】:

    在支持 Guile 的 GNU Make 中(即从 4.0 版开始),很容易使用调用 Scheme 语言进行算术或其他计算。它是在不创建任何子shell或子进程的情况下完成的。

    一个例子

    JP-I := 4
    JP-J := 2
    JP-IJ := $(guile (* $(JP-I) $(JP-J) ))
    
    $(info JP-IJ = $(JP-IJ) )
    # prints: JP-IJ = 8
    

    另请参阅 Guile Arithmetic Functions 的手册。

    Guile 的可能检查:

    ifeq (,$(filter guile,$(.FEATURES)))
      $(error Your Make version $(MAKE_VERSION) is not built with support for Guile)
    endif
    

    【讨论】:

      【解决方案6】:

      GNU Make Standard Library 提供整数运算功能。

      include gmsl
      
      JPI = 4
      JPJ = 2
      
      JPIJ = $(call plus,$(JPI),$(JPJ))
      

      【讨论】:

      • 哦,伙计...我看到了这个,觉得很棒,没有黑客就可以构建它。然后我注意到它是一个 SourceForge 项目,该项目只是劫持了名称并添加了一些炒作。
      • @MatthewSimoneau 'GNU'
      • @YoYoYonnY,它是 GNU Make 的“标准库”。我可以看到“标准库”有点大胆,但“GNU Make”部分只是描述性的。
      【解决方案7】:

      使用makepp 会容易得多。您可以直接访问底层 Perl 解释器。在这种情况下,makeperl 函数在评估为 Perl 之前会进行变量扩展,perl 函数 OTOH 只会评估:

      JPI=4
      JPJ=2
      JPIJ = $(makeperl $(JPI)*$(JPJ))
      &echo result: $(JPIJ)
      

      您可以在规则之外使用内置的 &echo 命令作为语句。

      【讨论】:

        【解决方案8】:

        要向池中添加迟到的答案:The GNUmake table toolkit 具有许多算术函数。您可以加、减、乘、除、取以 8,10 和 16 为底的模数。还有常用的二进制运算andorxornot。数字可以是 60 位左右,但如果您需要更多,您可以调整它。代码是纯 GNUmake 语法,因此可以在 Windows 和 Unix 之间移植,这与 shell 脚本相反——如果你想计算数字,可能会有更好的解决方案;)当然。

        这是一个例子:

        include gmtt/gmtt.mk
        
        NUMBER_A := -12392834798732429827442389
        NUMBER_B := 984398723982791273498234
        $(info $(call add,$(NUMBER_A),$(NUMBER_B)))
        $(info $(call sub,$(NUMBER_A),$(NUMBER_B)))
        $(info $(call mul,$(NUMBER_A),$(NUMBER_B)))
        $(info $(call div,$(NUMBER_A),$(NUMBER_B)))
        $(info $(call mod,$(NUMBER_A),$(NUMBER_B)))
        

        输出:

        $ make
        -11408436074749638553944155
        -13377233522715221100940623
        -12199490762401735834920873237276176262117128241026
        -12
        -580050110938934545463581
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-07-07
          • 1970-01-01
          • 2013-04-16
          • 2020-01-23
          • 2017-04-15
          • 1970-01-01
          • 2014-10-20
          • 1970-01-01
          相关资源
          最近更新 更多