GNU make: есть ли время для сборки, возможно ли иметь цель, рецепт которой выполняется после завершения всех целей - PullRequest
3 голосов
/ 13 декабря 2011

Мой вопрос похож на этот и этот .

В сущности, что-то вроде следующего - это то, что я ищу - или довольно чистый взлом для его проверки:

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, которая заставляет что-то происходить, когда все CMDGOALS или DEFAULTGOALS завершены.

Ответы [ 6 ]

3 голосов
/ 13 декабря 2011

Если вы просто хотите рассчитать время сборки и работаете на платформе 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, так что это не автоматическое решение (вам лучше ограничиться определенными целями высокого уровня), но я не думаю, что это огромная нагрузка, поскольку make-файлы имеют тенденцию быть относительно неизменными после правильной настройки.

1 голос
/ 17 марта 2016

Ну, я некоторое время удивлялся тому же и пробовал несколько вещей.Я полагаю, что рекурсивное создание может сделать это, но я пока избегаю этого.Жаль, что действительно нет .ATEND!

Примечание: Очевидно, что вы покажете, сколько времени займет переработка рецептов, если только не из чистого.

время

Итаккак предложил paxdiablo, команда unix time command является самой простой (и то, что я делал в прошлом).

% time make

на основе рецептов

Добавление таймера к каждому рецепту даст вам совокупное время выполнения, но не представление о том, сколько времени занял каждый рецепт (не то, что это было в вопросе).Однако для определенных рецептов вы можете снова использовать время внутри рецепта и даже передавать результаты в файл журнала (используя pipefail в bash).например,

# 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}:
0 голосов
/ 25 мая 2018

Я работал над этим крошечным решением в системе make, которую нельзя было изменить (файлы Makefile были доступны только для чтения).

Сохраните этот файл как «timebash» и установите для него разрешения на выполнение:

#!/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 "${@}"

Затем вы можете вызывать make таким образом, не меняя никаких Makefiles

make SHELL='/path/to/timebash "$(@)"' . . .

Я написал скрипт на Python для визуализации данных синхронизации в диаграмме Асии Ганта и смог довольно четко визуализировать время создания.

0 голосов
/ 19 декабря 2016

Надеюсь, вам понравился этот инструмент измерения производительности Makefile. Этот скрипт работает со ВСЕМИ версиями и брендами марки.

Для собственного использования я написал этот вспомогательный скрипт 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)
0 голосов
/ 18 марта 2016

Хорошо, просто подумал и закодировал.У вас может быть цель таймера, которая имеет предпосылки для любых других командных целей или все, если они пусты.Тогда это будет просто:

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
0 голосов
/ 13 декабря 2011

Мне пришло в голову решение для этого:

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'

... или что-то подобное.

Он не будет работать для каждой цели без проблем, но его можно использовать.

...