Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GNU make: Timing a build, is it possible to have a target whose recipe executes after all targets complete

Tags:

gnu-make

My question is similar to this one and this one.

In essence, something like the following is what I'm looking for -- or a fairly clean hack for pulling it off:

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}

... which could be kicked off in a number of ways:

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

At the heart of it is this idea of an .ATEND special target that causes something to happen when all CMDGOALS or DEFAULTGOALS are finished.

like image 276
Brian Vandenberg Avatar asked Dec 13 '11 01:12

Brian Vandenberg


2 Answers

If you just want to time your build, and you're running on a UNIXy platform, why not just use time itself?

As in:

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

(although, in your case, it would of course be time gmake all).

This seems a much more elegant solution than trying to code something up in make and uses the right tools for the job.


Alternatively, you could modify the rule itself to be something like:

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

This will ensure the Perl is executed once all targets are complete - you'll have to fiddle with the makefile so it's not an automatic solution (you'll be best off limiting yourself to certain high-level targets), but I don't think that's a huge burden since makefiles tend to be relatively changeless once set up correctly.

like image 162
paxdiablo Avatar answered Jan 01 '23 12:01

paxdiablo


Well I have been wondering the same for a while, and tried a few things. I guess a recursive make might do it, but I avoid those so far. Pity there isn't a .ATEND indeed!

Note: Obviously you will only show how long re-made recipes take, unless from a clean.

time

So as paxdiablo suggested unix time commmand is the easiest (and what I have done in the past).

% time make

recipe based

Adding a timer to each recipe will give you a cumulative run time, but not an idea of how long each recipe took (not that it was in the question). However for specific recipes you could make use of time again within the recipe and even tee the output to a logfile (using pipefail in bash). eg

# 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 '

summary

So I think just adding it to most of the recipes would be the best. You don't need a special call then. See below for an example, which should show differences with:

time -p make 
time -p make -j 2

Example

Gives cumulative time, as well as timestamp.

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}:
like image 22
M Hutson Avatar answered Jan 01 '23 13:01

M Hutson