GNU make:定时构建,是否可以在所有目标完成后有一个目标,其配方执行

时间:2011-12-13 01:07:43

标签: gnu-make

我的问题类似于this onethis one

从本质上讲,以下是我正在寻找的东西 - 或者是一个相当干净的黑客将它拉下来:

GET_TIMESTAMP  = $(shell perl -e 'print time()')
START_TIME    := ${GET_TIMESTAMP}

all: T1 T2 ... TN

T1:
T2:
...:
TN:

#...

timer:
  @perl -e 'printf( "Loaded makefiles in %ds, build completed in %ds\n", $ARGV[1] - $ARGV[0], $ARGV[2] - $ARGV[1] );' ${START_TIME} ${LOAD_COMPLETE} ${GET_TIMESTAMP}

.ATEND: timer

LOAD_COMPLETE := ${GET_TIMESTAMP}

......可以通过多种方式启动:

~ gmake all
(...)
  Loaded makefiles in 8s, build completed in 93s
~ gmake T2 T3 T4
(...)
  Loaded makefiles in 8s, build completed in 13s

其核心是.ATEND特殊目标的这一想法会导致在所有CMDGOALSDEFAULTGOALS完成后发生某些事情。

6 个答案:

答案 0 :(得分:3)

如果您只想为构建计时,并且您在UNIXy平台上运行,为什么不只使用time本身?

如:

pax$ time sleep 1
    real    0m1.004s
    user    0m0.000s
    sys     0m0.000s

(虽然,在你的情况下,它当然是time gmake all)。

这似乎是一个更优雅的解决方案,而不是尝试在make中编写代码并使用正确的工具来完成工作。


或者,您可以将规则本身修改为:

all: T1 T2 ... TN
    @perl -e blah blah blah

这将确保Perl在所有目标完成后执行 - 你必须摆弄makefile所以它不是一个自动解决方案(你最好将自己局限于某些高级目标),但我不认为这是一个巨大的负担,因为一旦正确设置,makefile往往相对无变化。

答案 1 :(得分:1)

好吧,我一直想知道同样的事,并尝试了一些事情。 我猜一个递归的make可能会这样做,但我到目前为止还没有。 遗憾的是,确实没有。确实!

注意:显然你只会显示重新制作的食谱需要多长时间,除非是干净的。

时间

因为paxdiablo建议unix时间命令是最简单的(以及我过去所做的)。

% time make
基于

配方

为每个食谱添加一个计时器会给你一个累积的运行时间,但不知道每个食谱花了多长时间(不是它在问题中)。 但是对于特定的配方,您可以在配方中再次使用时间,甚至将输出传送到日志文件(在bash中使用pipefail)。例如

# SHELL = /bin/bash -o pipefail
# @bash -o pipefail -c ' ( time /home/matt/bin/example_stdouterr.bash ) |& tee z.log'
# @bash -o pipefail -c ' ( time /home/matt/bin/example_stdouterr.bash ) 2>&1 | tee z.log '

摘要

所以我认为将它添加到大多数食谱中是最好的。 那你不需要特别的电话。 请参阅下面的示例,该示例应显示与以下内容的区别:

time -p make 
time -p make -j 2

实施例

给出累积时间和时间戳。

TIME_START := $(shell date +%s)
define TIME-END
@time_end=`date +%s` ; time_exec=`awk -v "TS=${TIME_START}" -v "TE=$$time_end" 'BEGIN{TD=TE-TS;printf "%02dd:%02dh:%02dm:%02ds\n",TD/(60*60*24),TD/(60*60)%24,TD/(60)%60,TD%60}'` ; echo "##DATE end   `date '+%Y-%m-%d %H:%M:%S %Z'` cumulative $${time_exec} $@"
endef

PHONY_GOALS := all
all: toprule1
    $(TIME-END)

PHONY_GOALS += toprule1
toprule1: subrule1 subrule2
    @echo toprule 1 start
    @sleep 1
    @echo toprule 1 done
    $(TIME-END)

PHONY_GOALS += subrule1
subrule1: botrule1
    @echo subrule 1 start
    @sleep 1
    @echo subrule 1 done
    $(TIME-END)

PHONY_GOALS += subrule2
subrule2: botrule1
    @echo subrule 2 start
    @time -p sleep 2
    @echo subrule 2 done
    $(TIME-END)

PHONY_GOALS += botrule1
botrule1:
    @echo botrule 1 start
    @sleep 1
    @echo "botrule 1 done"
    $(TIME-END)

PHONY_GOALS += help
help:
    @echo "$(info All the goals are: ${PHONY_GOALS})"

########### end bit
.PHONY :${PHONY_GOALS}
OTHER_GOALS := ${filter-out ${PHONY_GOALS}, ${MAKECMDGOALS}}
${OTHER_GOALS}:

答案 2 :(得分:0)

我刚刚想到了一个解决方案:

GET_TIMESTAMP  = $(shell perl -e 'print time()')
START_TIME    := ${GET_TIMESTAMP}

all: T1 T2 ... TN

T1:
T2:
...:
TN:

timer_target : ${TIMER_DEPENDENCY}
  @echo do whatever to print out timing info

这将在命令行中用作:

gmake timer_target TIMER_DEPENDENCY='T3 T4 T5'

......或类似的东西。

它不能无缝地对每个目标起作用,但它可以使用。

答案 3 :(得分:0)

好的,只是想了一下,然后编码了。 您可以拥有一个计时器目标,该目标具有任何其他命令目标的先决条件,或者全部为空。那就是:

make -j 2 subrule1 timer

下面的示例(哦,只要它在相同的配方行上,它也会经过计时器。)

# gnu make file

TIME_START := $(shell date +%s)
define TIME-END
@time_end=`date +%s` ; \
 time_exec=`awk -v "TS=${TIME_START}" -v "TE=$$time_end" 'BEGIN{TD=TE-TS;printf "%02dd:%02dh:%02dm:%02ds\n",TD/(60*60*24),TD/(60*60)%24,TD/(60)%60,TD%60}'` ; \
 echo "##DATE end   `date '+%Y-%m-%d %H:%M:%S %Z'` cumulative $${time_exec} $@"
endef

define ELAPSED-START
@elapsed_start=`date +%s`
endef
define ELAPSED-END
elapsed_end=`date +%s` ; \
 elapsed_exec=`awk -v "TS=$$elapsed_start" -v "TE=$$elapsed_end" 'BEGIN{TD=TE-TS;printf "%02dd:%02dh:%02dm:%02ds\n",TD/(60*60*24),TD/(60*60)%24,TD/(60)%60,TD%60}'` ; \
 echo "##DATE end   `date '+%Y-%m-%d %H:%M:%S %Z'` elapsed $${elapsed_exec} $@"
endef

PHONY_GOALS := all
all: subrule1 subrule2
    @$(TIME-END)

PHONY_GOALS += subrule1
subrule1: 
    $(ELAPSED-START) ; \
    echo subrule 1 start ; \
    sleep 1 ; \
    echo subrule 1 done ; \
    $(ELAPSED-END)
    @sleep 1
    @$(TIME-END)

PHONY_GOALS += subrule2
subrule2: 
    @echo subrule 2 start
    @$(ELAPSED-START) ; time -p sleep 2 ; $(ELAPSED-END)
    @echo subrule 2 done
    @$(TIME-END)

# create a prereq for timer to any CMDGOAL if empty add all
TIMERPREREQ := ${filter-out timer, ${MAKECMDGOALS}}
ifeq ($(strip $(TIMERPREREQ)),)
TIMERPREREQ := all
endif
#$(info TIMERPREREQ := ${TIMERPREREQ})

PHONY_GOALS += timer
timer: ${TIMERPREREQ}
    @$(TIME-END)

PHONY_GOALS += help
help:
    @echo "$(info All the goals are: ${PHONY_GOALS})"

########### end bit
.PHONY :${PHONY_GOALS}

结果是:

subrule 1 start
subrule 1 done
##DATE end   2016-03-18 13:41:56 GMT elapsed 00d:00h:00m:01s subrule1
##DATE end   2016-03-18 13:41:57 GMT cumulative 00d:00h:00m:02s subrule1
##DATE end   2016-03-18 13:41:58 GMT cumulative 00d:00h:00m:02s timer

答案 4 :(得分:0)

我希望你喜欢这个Makefile性能测量工具。 此脚本适用于make的所有版本和品牌。

为了我自己的使用,我编写了这个Python帮助程序脚本,以便在外部维护Makefile过去的时间。它运作良好,并且有很好的文档记录。

使用此脚本从'make'实用程序卸载复杂而微妙的任务,并使Makefile编写器能够对make性能的报告进行细粒度控制。

#!/usr/bin/env python

"""
MakeTime.py(support)
NAME
    MakeTime.py - Maintain storage for Makefile rule elapsed times.
SYNOPSIS
    MakeTime.py [OPTION]
    MakeTime.py [label] [title text]
DESCRIPTION
    Hold timing data in the external file 'MakeTime.json'.
OPTIONS
    -c, --clean, --clear, --initialize
        Delete the external file.

    -s, --summary, --summarize, --report
        Generate summary of stored timing data.

    -h, --help, (anything else beginning with '-')
        Show this help

EXAMPLES
    ./MakeTime.py
    ./MakeTime.py -h
    ./MakeTime.py --help
        Show help

    ./MakeTime.py -c
    ./MakeTime.py --clean
    ./MakeTime.py --clear
    ./MakeTime.py --initialize
        Delete JSON file 'MakeTime.json' to prepare for new timings.

    ./MakeTime.py START How much time does the make execution take?
        Associate title and t0 for START rule

    ./MakeTime.py START
        Associate t1 to JSON for START rule

    ./MakeTime.py -r
    ./MakeTime.py -s
    ./MakeTime.py --report
    ./MakeTime.py --summary
    ./MakeTime.py --summarize
        Perform calculations for execution times then
        summarize collected timings with labels to help.


EXAMPLE MAKEFILE
###############################################################################
all:    START rule1 rule2 END

.PHONY:
START:
    @./MakeTime.py --clear
    @./MakeTime.py $@ Elapsed during the entire make process.

.PHONY:
rule1:
    @./MakeTime.py $@ Elapsed during rule1.
    sleep 3
    @./MakeTime.py $@

.PHONY:
rule2:
    @./MakeTime.py $@ Elapsed during rule2.
    sleep 5
    @./MakeTime.py $@

.PHONY:
END:
    @./MakeTime.py START
    @./MakeTime.py --summary
###############################################################################

COPYRIGHT
    Copyright(c)2016 Jonathan D. Lettvin, All Rights Reserved
"""

from time import time
from sys import argv
from pprint import pprint
from os import remove
from os.path import exists

store = "MakeTime.json"
result = {}
marked = time()


if len(argv) == 1:
    "When no args are given, display the help text."
    print __doc__

elif len(argv) == 2:
    arg1 = argv[1]

    if arg1 in ["-c", "--clean", "--clear", "--initialize"]:
        "Clear the backstore."
        if exists(store):
            remove(store)

    elif arg1 in ["-r", "-s", "--summary", "--summarize", "--report"]:
        "Calculate elapsed times and print summary"
        with open(store, "r") as source:
            names = []
            seen = set()
            for n, line in enumerate(source):
                data = eval(line)
                for k,v in data.iteritems():
                    result[k] = result.get(k, {})
                    result[k].update(v)
                    if not k in seen:
                        seen.add(k)
                        names.append(k)
        for k in names:
            v = result[k]
            timing = "unknown MakeTime"
            if v.has_key('t1') and v.has_key('t0'):
                timing = "%4.3f seconds" % (float(v['t1']) - float(v['t0']))
            print("\t\t%s [%s] %s" % (timing, k, v['title']))

    elif arg1[0] == '-':  # -, -h, --help, or anything beginning with '-'
        print __doc__

    else:
        "Per rule delta"
        result = {arg1: {'t1': marked}}
        with open(store, "a") as target:
            print>>target, str(result)

else:
    "Per rule t0"
    result = {argv[1]: {'t0': marked, 'title': " ".join(argv[2:])}}
    with open(store, "a") as target:
        print>>target, str(result)

答案 5 :(得分:0)

我在一个无法修改的制作系统中处理这个微小的解决方案(Makefiles只读)。

将此文件另存为" timebash"并为其设置执行权限:

var PRODUCTS = []

fetch('https://api.myjson.com/bins/10scuq')
  .then(res => res.json())
  .then(data => { PRODUCTS = data; renderMe() })

const container = document.getElementById('root')

const renderMe = () => {
  render(
    <FilterableProductTable products={PRODUCTS} />,
    document.getElementById('root')
  );
}

renderMe();

然后你可以在不改变任何Makefile的情况下调用make这样的

#!/bin/bash
# shell replacement to time make recipes, invoke with:
#    make SHELL='/path/to/timebash "$(@)"' . . .
# timing data (start, elapsed) appears in the make output, along
# with a "make stack" tracked in the MKSTACK variable like
#    "target[0].subtarg[1].subtarg2[2]..."
target="$1"
shift
export MKSTACK
MKSTACK="${MKSTACK}${MKSTACK:+.}${target}[$MAKELEVEL]"

# compose your timing data format here
# on multiple lines here for readability
now=$(date "+%Y%m%d_%H%M%S")
fmt="timebash:$$"                ## each line starts with 'timebash'+pid
fmt="${fmt}:mkstack=${MKSTACK}"  ## add the MKSTACK
fmt="${fmt}:start=${now}"        ## wall clock at start
fmt="${fmt}:elapsed=%e"          ## time() fills this in
fmt="${fmt}:cmd='%C'"            ## time() fills this too

# now invoke time and the original make command
/usr/bin/time -f "$fmt" -- /bin/bash "${@}"

我编写了一个python脚本,用于在ascii甘特图中渲染时序数据,并能够非常清晰地显示制作时间。