GNU Make 中变量的减法和回显差异

Subtract and Echo Difference of Variables in GNU Make

我在回显 运行 任务之前的时间和任务结束的时间: test: @echo $(shell date) @JUNIT_REPORT_PATH=test/report.xml ./node_modules/.bin/mocha test/integration @echo $(shell date) 有没有办法将日期存储在 2 个变量中,然后显示它们之间经过的时间? 我假设它看起来像这样:

test:
    $begin = $(shell date)
    <do stuff>
    $end = $(shell date)
    @echo $end - $begin

任何指向有关 Make 的良好文档的指针也将不胜感激。

谢谢!

简答:你想要的都有可能:

test:
    @begin=$$(date +%s); \
    <do stuff in same shell: do not forget the ";" and the "\">; \
    end=$$(date +%s); \
    echo $$((end - begin))

冗长的回答:很抱歉解释冗长,我认为您的问题是值得的,因为您的示例解决方案有很多问题需要解决。

我假设您使用 bash,您的 make 调用 bash 来执行您的食谱,并且您的 date 命令来自 GNU coreutils。否则,接下来的操作可能无法按预期工作。如果你在 Mac OS X 下问另一个关于如何使用最新版本的 bash 和 GNU coreutils 而不是默认的问题。

回到你的问题。让我们研究一下您的示例解决方案:

test:
    $begin = $(shell date)
    <do stuff>
    $end = $(shell date)
    @echo $end - $begin

并逐步解决需要解决的问题。有几点需要考虑和理解:

1) 如何使用make变量:make变量赋值name = value,扩展$(name)${name}。请注意,如果您有一个单字母变量名称 A,您也可以使用 $A 扩展它。因此,当 make 扩展示例配方的第一行时,$begin = $(shell date) 结果取决于 make 变量 b 是否存在以及是否具有非空值。因此,make 会将行扩展为 fooegin = <value>egin = <value>。让我们通过删除第一个 $ 来解决这个问题:begin = $(shell date).

2) make 变量可以赋值的地方: 不在食谱中。因此,在您编写的配方中,beginend 是 shell 变量。 shell 变量被分配 name=value,而不是 name = value。没有 spaces。在您的示例中,当 make 将 begin = <value> 传递给 shell 时,您将收到 begin: command not found 错误。让我们通过删除 spaces 来解决这个问题:begin=$(shell date).

3)日期的使用方法:默认格式不适合算术。使用 date +%s 到 return 当前日期 - 时间作为时间戳(自纪元以来的秒数)。让我们用 begin=$(shell date +%s).

来解决这个问题

4) 如何使用bash算术展开$((end - begin))计算shell变量endbegin。让我们用 @echo $((end - begin)) 替换食谱的最后一行来解决这个问题。可是等等!这是行不通的,因为 make 会在将它传递给 shell 之前尝试扩展它,你猜怎么着,它会将 (end - begin 视为 make 变量名并将其扩展为空,因为没有这样的 make多变的。所以make会通过

echo )

到shell,你会得到一个语法错误。要解决这个问题,我们必须加倍 $ 符号:@echo $$((end - begin))。请注意,make 有时会扩展不止一次。在这些情况下,您需要更多 $ 符号:@echo $$$$((end - begin)) 用于双重扩展。但这不是你的情况。

5) 当 make 扩展它的变量和函数时: make 在它需要它们的值时扩展它的变量和函数,而不是当你想的时候。这是在执行食谱之前。因此,在您的示例中,beginend 将具有相同的值,无论 <do stuff> 花费什么时间,它将是 make expanded [=52= 的瞬间日期和时间].尝试:

test:
    @echo $(shell date +%s)
    @sleep 2
    @echo $(shell date +%s)

看到两个时间戳是一样的。让我们通过不使用 $(shell) 来解决这个问题:begin=$$(date +%s)(注意 $$)。 make 会将其扩展为 begin=$(date +%s) 并将其传递给 shell。 shell 将按照您的想法进行操作,并且 begin shell 变量将在 shell 调用时分配当前时间戳。

6) make 如何处理食谱:每行在扩展后传递给不同的shell。是的,在您的示例中,您将至少有 4 个不同的 shell 调用。更多 if <do stuff> 隐藏多行。这里的问题是,我们的 beginend shell 变量对于将执行最后一行的 shell 不存在,您将得到另一个 shell错误。让我们通过将所有 shell 命令放在同一行来解决此问题,并用 ; shell 分隔符分隔:

test:
    @begin=$$(date +%s); sleep 2; end=$$(date +%s); echo $$((end - begin))

宾果!它开始工作。请注意,初始 @ 禁用整个 shell 命令列表的回显。

但是,对于很长的食谱,这不是很方便。别担心,你可以换行但是 last 字符必须是 \ 来告诉 make 忽略换行符:

target:
    @<do this>; <do that>; baz=1; \
    <and also this>; \
    if <condition>; then \
      <do this>; \
    else \
      <do that>; \
    fi; \
    <and this>; \
    echo $$baz

前面的配方在单个 shell 调用中执行。 shell变量baz从头到尾都是同一个变量。初始 @ 仍然禁用整个 shell 命令列表的回显。尝试:

test:
    @begin=$$(date +%s); \
    sleep 2; \
    end=$$(date +%s); \
    echo $$((end - begin))

并看到它像单行版本一样工作。 警告\ 必须真的只是 在换行符之前。在 \ 之后添加一两个 space 非常容易。如果这样做,您将收到 shell 错误。最糟糕的是,几乎无法理解和调试。因此,如果您对此类食谱有奇怪的错误,请搜索行尾的 space。如果您知道如何执行此操作,请告诉您的编辑器突出显示 makefile 中行尾的空白。

我们快完成了。总而言之,

test:
    @begin=$$(date +%s); \
    <do stuff in same shell: do not forget the ";" and the "\">; \
    end=$$(date +%s); \
    echo $$((end - begin))

应该做你想做的。 make first notes the initial @ and disables the echoing, 然后它将配方扩展为一个命令列表:

begin=$(date +%s); <do stuff>; end=$(date +%s); echo $((end - begin))

并在唯一调用中将其传递给 shell。

最后但重要的评论:因为只有一个 shell 调用,所以它也快得多。如果你有复杂的 makefile 来做复杂的事情,它会产生很大的不同。如果可以,请始终使用它,并加快处理速度。

如果你只是想知道经过的时间,我建议为此使用 /usr/bin/time(而不是 time 的 shell 版本),例如

/usr/bin/time -f "%E real,%U user,%S sys" g++ something

可悲的是,在一个计时器下做多件事变得丑陋:

/usr/bin/time -f "%E real,%U user,%S sys" sh -c 'g++ a; g++ b'

我只想在这里分享 /usr/bin/time。应该适用于简单的食谱。

可以在makefile中进行如下算术运算。

NUMBER1 := 10
NUMBER2 := 5

#Addition
ADD := $(shell echo ${NUMBER1}+${NUMBER2} | bc)

#Subtraction
SUBTRACT := $(shell echo ${NUMBER1}-${NUMBER2} | bc)

#Multiplication
MULTIPLY := $(shell echo ${NUMBER1}*${NUMBER2} | bc)

#Division
DIVIDE := $(shell echo ${NUMBER1}/${NUMBER2} | bc)

#Division (Floating Point)
DIVIDEF := $(shell echo "scale=3; ${NUMBER2}/${NUMBER1}" | bc)

#Modulo
MODULO := $(shell echo ${NUMBER1}%${NUMBER2} | bc)

#Comparison Greater Than
COMPARISON1 := $(shell echo ${NUMBER1}\>=${NUMBER2} | bc)

#Comparison Smaller Than
COMPARISON2 := $(shell echo ${NUMBER2}\<=${NUMBER2} | bc)

all:
  @echo Addition ${ADD}
  @echo Subtraction ${SUBTRACT}
  @echo Multiplication ${MULTIPLY}
  @echo Division ${DIVIDE}
  @echo Division  - Floating Point ${DIVIDEF}
  @echo Modulo ${MODULO}
  @echo Comparison Greater Than ${COMPARISON1}
  @echo Comparison Smaller Than ${COMPARISON2}

礼貌:Makefile Tricks: Arithmetic – Addition, Subtraction, Multiplication, Division, Modulo, Comparison