5 # Copyright The SCons Foundation
7 # Permission is hereby granted, free of charge, to any person obtaining
8 # a copy of this software and associated documentation files (the
9 # "Software"), to deal in the Software without restriction, including
10 # without limitation the rights to use, copy, modify, merge, publish,
11 # distribute, sublicense, and/or sell copies of the Software, and to
12 # permit persons to whom the Software is furnished to do so, subject to
13 # the following conditions:
15 # The above copyright notice and this permission notice shall be included
16 # in all copies or substantial portions of the Software.
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
19 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
20 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 scons-time - run SCons timings and collect statistics
29 A script for running a configuration through SCons with a standard set
30 of invocations to collect timing and memory statistics and to capture
31 the results in a consistent set of output files for display and analysis.
44 def HACK_for_exec(cmd
, *args
):
46 For some reason, Python won't allow an exec() within a function
47 that also declares an internal function (including lambda functions).
48 This function is a hack that calls exec() in a function with no
56 exec(cmd
, args
[0], args
[1])
60 def increment_size(self
, largest
):
62 Return the size of each horizontal increment line for a specified
63 maximum value. This returns a value that will provide somewhere
64 between 5 and 9 horizontal lines on the graph, on some set of
65 boundaries that are multiples of 10/100/1000/etc.
73 multiplier
= multiplier
* 10
76 def max_graph_value(self
, largest
):
77 # Round up to next integer.
78 largest
= int(largest
) + 1
79 increment
= self
.increment_size(largest
)
80 return ((largest
+ increment
- 1) // increment
) * increment
84 def __init__(self
, points
, type, title
, label
, comment
, fmt
="%s %s"):
89 self
.comment
= comment
92 def print_label(self
, inx
, x
, y
):
94 print('set label %s "%s" at %0.1f,%0.1f right' % (inx
, self
.label
, x
, y
))
96 def plot_string(self
):
98 title_string
= 'title "%s"' % self
.title
100 title_string
= 'notitle'
101 return "'-' %s with lines lt %s" % (title_string
, self
.type)
103 def print_points(self
, fmt
=None):
107 print('# %s' % self
.comment
)
108 for x
, y
in self
.points
:
109 # If y is None, it usually represents some kind of break
110 # in the line's index number. We might want to represent
111 # this some way rather than just drawing the line straight
112 # between the two points on either side.
117 def get_x_values(self
):
118 return [p
[0] for p
in self
.points
]
120 def get_y_values(self
):
121 return [p
[1] for p
in self
.points
]
124 class Gnuplotter(Plotter
):
126 def __init__(self
, title
, key_location
):
129 self
.key_location
= key_location
131 def line(self
, points
, type, title
=None, label
=None, comment
=None, fmt
='%s %s'):
133 line
= Line(points
, type, title
, label
, comment
, fmt
)
134 self
.lines
.append(line
)
136 def plot_string(self
, line
):
137 return line
.plot_string()
139 def vertical_bar(self
, x
, type, label
, comment
):
140 if self
.get_min_x() <= x
<= self
.get_max_x():
141 points
= [(x
, 0), (x
, self
.max_graph_value(self
.get_max_y()))]
142 self
.line(points
, type, label
, comment
)
144 def get_all_x_values(self
):
146 for line
in self
.lines
:
147 result
.extend(line
.get_x_values())
148 return [r
for r
in result
if r
is not None]
150 def get_all_y_values(self
):
152 for line
in self
.lines
:
153 result
.extend(line
.get_y_values())
154 return [r
for r
in result
if r
is not None]
159 except AttributeError:
161 self
.min_x
= min(self
.get_all_x_values())
169 except AttributeError:
171 self
.max_x
= max(self
.get_all_x_values())
179 except AttributeError:
181 self
.min_y
= min(self
.get_all_y_values())
189 except AttributeError:
191 self
.max_y
= max(self
.get_all_y_values())
202 print('set title "%s"' % self
.title
)
203 print('set key %s' % self
.key_location
)
205 min_y
= self
.get_min_y()
206 max_y
= self
.max_graph_value(self
.get_max_y())
207 incr
= (max_y
- min_y
) / 10.0
208 start
= min_y
+ (max_y
/ 2.0) + (2.0 * incr
)
209 position
= [start
- (i
* incr
) for i
in range(5)]
212 for line
in self
.lines
:
213 line
.print_label(inx
, line
.points
[0][0] - 1,
214 position
[(inx
- 1) % len(position
)])
217 plot_strings
= [self
.plot_string(l
) for l
in self
.lines
]
218 print('plot ' + ', \\\n '.join(plot_strings
))
220 for line
in self
.lines
:
226 tar
= tarfile
.open(name
=fname
, mode
='r')
234 zf
= zipfile
.ZipFile(fname
, 'r')
235 for name
in zf
.namelist():
236 dir = os
.path
.dirname(name
)
241 with
open(name
, 'wb') as f
:
242 f
.write(zf
.read(name
))
246 for dirpath
, dirnames
, filenames
in os
.walk(dir):
248 fn
= os
.path
.join(dirpath
, fn
)
249 if os
.path
.isfile(fn
):
250 with
open(fn
, 'rb') as f
:
254 def redirect_to_file(command
, log
):
255 return '%s > %s 2>&1' % (command
, log
)
258 def tee_to_file(command
, log
):
259 return '%s 2>&1 | tee %s' % (command
, log
)
264 Usage: scons-time SUBCOMMAND [ARGUMENTS]
265 Type "scons-time help SUBCOMMAND" for help on a specific subcommand.
267 Available subcommands:
268 func Extract test-run data for a function
270 mem Extract --debug=memory data from test runs
271 obj Extract --debug=count data from test runs
272 time Extract --debug=time data from test runs
273 run Runs a test configuration
277 name_spaces
= ' ' * len(name
)
282 default_settings
= makedict(
286 key_location
='bottom left',
287 orig_cwd
=os
.getcwd(),
290 python
='"%s"' % sys
.executable
,
291 redirect
=redirect_to_file
,
293 scons_flags
='--debug=count --debug=memory --debug=time --debug=memoizer',
296 startup_targets
='--help',
312 '.tar.gz': (untar
, '%(tar)s xzf %%s'),
313 '.tgz': (untar
, '%(tar)s xzf %%s'),
314 '.tar': (untar
, '%(tar)s xf %%s'),
315 '.zip': (unzip
, '%(unzip)s %%s'),
326 '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof0)s %(targets0)s',
327 '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targets1)s',
328 '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targets2)s',
339 'pre-read': 'Memory before reading SConscript files:',
340 'post-read': 'Memory after reading SConscript files:',
341 'pre-build': 'Memory before building targets:',
342 'post-build': 'Memory after building targets:',
345 memory_string_all
= 'Memory '
347 default_stage
= stages
[-1]
350 'total': 'Total build time',
351 'SConscripts': 'Total SConscript file execution time',
352 'SCons': 'Total SCons execution time',
353 'commands': 'Total command execution time',
356 time_string_all
= 'Total .* time'
361 self
.__dict
__.update(self
.default_settings
)
363 # Functions for displaying and executing commands.
365 def subst(self
, x
, dictionary
):
367 return x
% dictionary
369 # x isn't a string (it's probably a Python function),
373 def subst_variables(self
, command
, dictionary
):
375 Substitutes (via the format operator) the values in the specified
376 dictionary into the specified command.
378 The command can be an (action, string) tuple. In all cases, we
379 perform substitution on strings and don't worry if something isn't
380 a string. (It's probably a Python function to be executed.)
392 action
= self
.subst(action
, dictionary
)
393 string
= self
.subst(string
, dictionary
)
394 return (action
, string
, args
)
396 def _do_not_display(self
, msg
, *args
):
399 def display(self
, msg
, *args
):
401 Displays the specified message.
403 Each message is prepended with a standard prefix of our name
413 sys
.stdout
.write(fmt
% (self
.name
, time
.strftime('%H:%M:%S'), msg
))
415 def _do_not_execute(self
, action
, *args
):
418 def execute(self
, action
, *args
):
420 Executes the specified action.
422 The action is called if it's a callable Python function, and
423 otherwise passed to os.system().
428 os
.system(action
% args
)
430 def run_command_list(self
, commands
, dict):
432 Executes a list of commands, substituting values from the
433 specified dictionary.
435 commands
= [self
.subst_variables(c
, dict) for c
in commands
]
436 for action
, string
, args
in commands
:
437 self
.display(string
, *args
)
439 status
= self
.execute(action
, *args
)
443 def log_display(self
, command
, log
):
444 command
= self
.subst(command
, self
.__dict
__)
446 command
= self
.redirect(command
, log
)
449 def log_execute(self
, command
, log
):
450 command
= self
.subst(command
, self
.__dict
__)
451 p
= os
.popen(command
)
454 # TODO: convert to subrocess, os.popen is obsolete. This didn't work:
455 # process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
456 # output = process.stdout.read()
457 # process.stdout.close()
460 sys
.stdout
.write(output
)
462 # Not sure we need to write binary here
463 with
open(log
, 'w') as f
:
466 def archive_splitext(self
, path
):
468 Splits an archive name into a filename base and extension.
470 This is like os.path.splitext() (which it calls) except that it
471 also looks for '.tar.gz' and treats it as an atomic extensions.
473 if path
.endswith('.tar.gz'):
474 return path
[:-7], path
[-7:]
476 return os
.path
.splitext(path
)
478 def args_to_files(self
, args
, tail
=None):
480 Takes a list of arguments, expands any glob patterns, and
481 returns the last "tail" files from the list.
485 files
.extend(sorted(glob
.glob(a
)))
488 files
= files
[-tail
:]
492 def ascii_table(self
, files
, columns
,
493 line_function
, file_function
=lambda x
: x
,
496 header_fmt
= ' '.join(['%12s'] * len(columns
))
497 line_fmt
= header_fmt
+ ' %s'
499 print(header_fmt
% columns
)
502 t
= line_function(file, *args
, **kw
)
505 diff
= len(columns
) - len(t
)
508 t
.append(file_function(file))
509 print(line_fmt
% tuple(t
))
511 def collect_results(self
, files
, function
, *args
, **kw
):
515 base
= os
.path
.splitext(file)[0]
516 run
, index
= base
.split('-')[-2:]
521 value
= function(file, *args
, **kw
)
528 r
.append((run
, value
))
532 def doc_to_help(self
, obj
):
534 Translates an object's __doc__ string into help text.
536 This strips a consistent number of spaces from each line in the
537 help text, essentially "outdenting" the text to the left-most
543 return self
.outdent(doc
)
545 def find_next_run_number(self
, dir, prefix
):
547 Returns the next run number in a directory for the specified prefix.
549 Examines the contents the specified directory for files with the
550 specified prefix, extracts the run numbers from each file name,
551 and returns the next run number after the largest it finds.
553 x
= re
.compile(re
.escape(prefix
) + '-([0-9]+).*')
554 matches
= [x
.match(e
) for e
in os
.listdir(dir)]
555 matches
= [_f
for _f
in matches
if _f
]
558 run_numbers
= [int(m
.group(1)) for m
in matches
]
559 return int(max(run_numbers
)) + 1
561 def gnuplot_results(self
, results
, fmt
='%s %.3f'):
563 Prints out a set of results in Gnuplot format.
565 gp
= Gnuplotter(self
.title
, self
.key_location
)
567 for i
in sorted(results
.keys()):
569 t
= self
.run_titles
[i
]
573 gp
.line(results
[i
], i
+ 1, t
, None, t
, fmt
=fmt
)
575 for bar_tuple
in self
.vertical_bars
:
577 x
, type, label
, comment
= bar_tuple
579 x
, type, label
= bar_tuple
581 gp
.vertical_bar(x
, type, label
, comment
)
585 def logfile_name(self
, invocation
):
587 Returns the absolute path of a log file for the specificed
590 name
= self
.prefix_run
+ '-%d.log' % invocation
591 return os
.path
.join(self
.outdir
, name
)
593 def outdent(self
, s
):
595 Strip as many spaces from each line as are found at the beginning
596 of the first line in the list.
598 lines
= s
.split('\n')
601 spaces
= re
.match(' *', lines
[0]).group(0)
603 def strip_initial_spaces(line
, s
=spaces
):
604 if line
.startswith(spaces
):
605 line
= line
[len(spaces
):]
608 return '\n'.join([strip_initial_spaces(l
) for l
in lines
]) + '\n'
610 def profile_name(self
, invocation
):
612 Returns the absolute path of a profile file for the specified
615 name
= self
.prefix_run
+ '-%d.prof' % invocation
616 return os
.path
.join(self
.outdir
, name
)
618 def set_env(self
, key
, value
):
619 os
.environ
[key
] = value
623 def get_debug_times(self
, file, time_string
=None):
625 Fetch times from the --debug=time strings in the specified file.
627 if time_string
is None:
628 search_string
= self
.time_string_all
630 search_string
= time_string
631 with
open(file) as f
:
634 sys
.stderr
.write('file %s has no contents!\n' % repr(file))
636 result
= re
.findall(r
'%s: ([\d.]*)' % search_string
, contents
)[-4:]
637 result
= [float(r
) for r
in result
]
638 if time_string
is not None:
642 sys
.stderr
.write('file %s has no results!\n' % repr(file))
646 def get_function_profile(self
, file, function
):
648 Returns the file, line number, function name, and cumulative time.
652 except ImportError as e
:
653 sys
.stderr
.write('%s: func: %s\n' % (self
.name
, e
))
654 sys
.stderr
.write('%s This version of Python is missing the profiler.\n' % self
.name_spaces
)
655 sys
.stderr
.write('%s Cannot use the "func" subcommand.\n' % self
.name_spaces
)
657 statistics
= pstats
.Stats(file).stats
658 matches
= [e
for e
in statistics
.items() if e
[0][2] == function
]
660 return r
[0][0], r
[0][1], r
[0][2], r
[1][3]
662 def get_function_time(self
, file, function
):
664 Returns just the cumulative time for the specified function.
666 return self
.get_function_profile(file, function
)[3]
668 def get_memory(self
, file, memory_string
=None):
670 Returns a list of integers of the amount of memory used. The
671 default behavior is to return all the stages.
673 if memory_string
is None:
674 search_string
= self
.memory_string_all
676 search_string
= memory_string
677 with
open(file) as f
:
678 lines
= f
.readlines()
679 lines
= [l
for l
in lines
if l
.startswith(search_string
)][-4:]
680 result
= [int(l
.split()[-1]) for l
in lines
[-4:]]
685 def get_object_counts(self
, file, object_name
, index
=None):
687 Returns the counts of the specified object_name.
689 object_string
= ' ' + object_name
+ '\n'
690 with
open(file) as f
:
691 lines
= f
.readlines()
692 line
= [l
for l
in lines
if l
.endswith(object_string
)][0]
693 result
= [int(field
) for field
in line
.split()[:4]]
694 if index
is not None:
695 result
= result
[index
]
700 def execute_subcommand(self
, argv
):
702 Executes the do_*() function for the specified subcommand (argv[0]).
706 cmdName
= self
.command_alias
.get(argv
[0], argv
[0])
708 func
= getattr(self
, 'do_' + cmdName
)
709 except AttributeError:
710 return self
.default(argv
)
713 except TypeError as e
:
714 sys
.stderr
.write("%s %s: %s\n" % (self
.name
, cmdName
, e
))
716 traceback
.print_exc(file=sys
.stderr
)
717 sys
.stderr
.write("Try '%s help %s'\n" % (self
.name
, cmdName
))
719 def default(self
, argv
):
721 The default behavior for an unknown subcommand. Prints an
722 error message and exits.
724 sys
.stderr
.write('%s: Unknown subcommand "%s".\n' % (self
.name
, argv
[0]))
725 sys
.stderr
.write('Type "%s help" for usage.\n' % self
.name
)
730 def do_help(self
, argv
):
736 func
= getattr(self
, 'do_' + arg
)
737 except AttributeError:
738 sys
.stderr
.write('%s: No help for "%s"\n' % (self
.name
, arg
))
741 help = getattr(self
, 'help_' + arg
)
742 except AttributeError:
743 sys
.stdout
.write(self
.doc_to_help(func
))
748 doc
= self
.doc_to_help(self
.__class
__)
750 sys
.stdout
.write(doc
)
758 Usage: scons-time func [OPTIONS] FILE [...]
760 -C DIR, --chdir=DIR Change to DIR before looking for files
761 -f FILE, --file=FILE Read configuration from specified FILE
762 --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
763 --func=NAME, --function=NAME Report time for function NAME
764 -h, --help Print this help and exit
765 -p STRING, --prefix=STRING Use STRING as log file/profile prefix
766 -t NUMBER, --tail=NUMBER Only report the last NUMBER files
767 --title=TITLE Specify the output plot TITLE
769 sys
.stdout
.write(self
.outdent(help))
772 def do_func(self
, argv
):
776 function_name
= '_main'
779 short_opts
= '?C:f:hp:t:'
794 opts
, args
= getopt
.getopt(argv
[1:], short_opts
, long_opts
)
797 if o
in ('-C', '--chdir'):
799 elif o
in ('-f', '--file'):
801 elif o
in ('--fmt', '--format'):
803 elif o
in ('--func', '--function'):
805 elif o
in ('-?', '-h', '--help'):
806 self
.do_help(['help', 'func'])
808 elif o
in ('--max',):
810 elif o
in ('-p', '--prefix'):
812 elif o
in ('-t', '--tail'):
814 elif o
in ('--title',):
818 with
open(self
.config_file
, 'r') as f
:
820 exec(config
, self
.__dict
__)
827 pattern
= '%s*.prof' % self
.prefix
828 args
= self
.args_to_files([pattern
], tail
)
832 directory
= self
.chdir
834 directory
= os
.getcwd()
836 sys
.stderr
.write('%s: func: No arguments specified.\n' % self
.name
)
837 sys
.stderr
.write('%s No %s*.prof files found in "%s".\n' % (self
.name_spaces
, self
.prefix
, directory
))
838 sys
.stderr
.write('%s Type "%s help func" for help.\n' % (self
.name_spaces
, self
.name
))
843 args
= self
.args_to_files(args
, tail
)
845 cwd_
= os
.getcwd() + os
.sep
847 if format
== 'ascii':
851 f
, line
, func
, time
= \
852 self
.get_function_profile(file, function_name
)
853 except ValueError as e
:
854 sys
.stderr
.write("%s: func: %s: %s\n" %
855 (self
.name
, file, e
))
857 if f
.startswith(cwd_
):
859 print("%.3f %s:%d(%s)" % (time
, f
, line
, func
))
861 elif format
== 'gnuplot':
863 results
= self
.collect_results(args
, self
.get_function_time
,
866 self
.gnuplot_results(results
)
870 sys
.stderr
.write('%s: func: Unknown format "%s".\n' % (self
.name
, format
))
877 Usage: scons-time mem [OPTIONS] FILE [...]
879 -C DIR, --chdir=DIR Change to DIR before looking for files
880 -f FILE, --file=FILE Read configuration from specified FILE
881 --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
882 -h, --help Print this help and exit
883 -p STRING, --prefix=STRING Use STRING as log file/profile prefix
884 --stage=STAGE Plot memory at the specified stage:
885 pre-read, post-read, pre-build,
886 post-build (default: post-build)
887 -t NUMBER, --tail=NUMBER Only report the last NUMBER files
888 --title=TITLE Specify the output plot TITLE
890 sys
.stdout
.write(self
.outdent(help))
893 def do_mem(self
, argv
):
896 def _logfile_path(x
):
899 logfile_path
= _logfile_path
901 stage
= self
.default_stage
904 short_opts
= '?C:f:hp:t:'
918 opts
, args
= getopt
.getopt(argv
[1:], short_opts
, long_opts
)
921 if o
in ('-C', '--chdir'):
923 elif o
in ('-f', '--file'):
925 elif o
in ('--fmt', '--format'):
927 elif o
in ('-?', '-h', '--help'):
928 self
.do_help(['help', 'mem'])
930 elif o
in ('-p', '--prefix'):
932 elif o
in ('--stage',):
933 if a
not in self
.stages
:
934 sys
.stderr
.write('%s: mem: Unrecognized stage "%s".\n' % (self
.name
, a
))
937 elif o
in ('-t', '--tail'):
939 elif o
in ('--title',):
943 with
open(self
.config_file
, 'r') as f
:
945 HACK_for_exec(config
, self
.__dict
__)
949 def _logfile_path_join(x
):
950 return os
.path
.join(self
.chdir
, x
)
952 logfile_path
= _logfile_path_join
955 pattern
= '%s*.log' % self
.prefix
956 args
= self
.args_to_files([pattern
], tail
)
960 directory
= self
.chdir
962 directory
= os
.getcwd()
964 sys
.stderr
.write('%s: mem: No arguments specified.\n' % self
.name
)
965 sys
.stderr
.write('%s No %s*.log files found in "%s".\n' % (self
.name_spaces
, self
.prefix
, directory
))
966 sys
.stderr
.write('%s Type "%s help mem" for help.\n' % (self
.name_spaces
, self
.name
))
971 args
= self
.args_to_files(args
, tail
)
973 # cwd_ = os.getcwd() + os.sep
975 if format
== 'ascii':
977 self
.ascii_table(args
, tuple(self
.stages
), self
.get_memory
, logfile_path
)
979 elif format
== 'gnuplot':
981 results
= self
.collect_results(args
, self
.get_memory
,
982 self
.stage_strings
[stage
])
984 self
.gnuplot_results(results
)
988 sys
.stderr
.write('%s: mem: Unknown format "%s".\n' % (self
.name
, format
))
997 Usage: scons-time obj [OPTIONS] OBJECT FILE [...]
999 -C DIR, --chdir=DIR Change to DIR before looking for files
1000 -f FILE, --file=FILE Read configuration from specified FILE
1001 --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
1002 -h, --help Print this help and exit
1003 -p STRING, --prefix=STRING Use STRING as log file/profile prefix
1004 --stage=STAGE Plot memory at the specified stage:
1005 pre-read, post-read, pre-build,
1006 post-build (default: post-build)
1007 -t NUMBER, --tail=NUMBER Only report the last NUMBER files
1008 --title=TITLE Specify the output plot TITLE
1010 sys
.stdout
.write(self
.outdent(help))
1013 def do_obj(self
, argv
):
1017 def _logfile_path(x
):
1020 logfile_path
= _logfile_path
1022 stage
= self
.default_stage
1025 short_opts
= '?C:f:hp:t:'
1039 opts
, args
= getopt
.getopt(argv
[1:], short_opts
, long_opts
)
1042 if o
in ('-C', '--chdir'):
1044 elif o
in ('-f', '--file'):
1045 self
.config_file
= a
1046 elif o
in ('--fmt', '--format'):
1048 elif o
in ('-?', '-h', '--help'):
1049 self
.do_help(['help', 'obj'])
1051 elif o
in ('-p', '--prefix'):
1053 elif o
in ('--stage',):
1054 if a
not in self
.stages
:
1055 sys
.stderr
.write('%s: obj: Unrecognized stage "%s".\n' % (self
.name
, a
))
1056 sys
.stderr
.write('%s Type "%s help obj" for help.\n' % (self
.name_spaces
, self
.name
))
1059 elif o
in ('-t', '--tail'):
1061 elif o
in ('--title',):
1065 sys
.stderr
.write('%s: obj: Must specify an object name.\n' % self
.name
)
1066 sys
.stderr
.write('%s Type "%s help obj" for help.\n' % (self
.name_spaces
, self
.name
))
1069 object_name
= args
.pop(0)
1071 if self
.config_file
:
1072 with
open(self
.config_file
, 'r') as f
:
1074 HACK_for_exec(config
, self
.__dict
__)
1077 os
.chdir(self
.chdir
)
1079 def _logfile_path_join(x
):
1080 return os
.path
.join(self
.chdir
, x
)
1082 logfile_path
= _logfile_path_join
1085 pattern
= '%s*.log' % self
.prefix
1086 args
= self
.args_to_files([pattern
], tail
)
1090 directory
= self
.chdir
1092 directory
= os
.getcwd()
1094 sys
.stderr
.write('%s: obj: No arguments specified.\n' % self
.name
)
1095 sys
.stderr
.write('%s No %s*.log files found in "%s".\n' % (self
.name_spaces
, self
.prefix
, directory
))
1096 sys
.stderr
.write('%s Type "%s help obj" for help.\n' % (self
.name_spaces
, self
.name
))
1101 args
= self
.args_to_files(args
, tail
)
1103 cwd_
= os
.getcwd() + os
.sep
1105 if format
== 'ascii':
1107 self
.ascii_table(args
, tuple(self
.stages
), self
.get_object_counts
, logfile_path
, object_name
)
1109 elif format
== 'gnuplot':
1112 for s
in self
.stages
:
1115 stage_index
= stage_index
+ 1
1117 results
= self
.collect_results(args
, self
.get_object_counts
,
1118 object_name
, stage_index
)
1120 self
.gnuplot_results(results
)
1124 sys
.stderr
.write('%s: obj: Unknown format "%s".\n' % (self
.name
, format
))
1133 Usage: scons-time run [OPTIONS] [FILE ...]
1135 --chdir=DIR Name of unpacked directory for chdir
1136 -f FILE, --file=FILE Read configuration from specified FILE
1137 -h, --help Print this help and exit
1138 -n, --no-exec No execute, just print command lines
1139 --number=NUMBER Put output in files for run NUMBER
1140 --outdir=OUTDIR Put output files in OUTDIR
1141 -p STRING, --prefix=STRING Use STRING as log file/profile prefix
1142 --python=PYTHON Time using the specified PYTHON
1143 -q, --quiet Don't print command lines
1144 --scons=SCONS Time using the specified SCONS
1145 --svn=URL, --subversion=URL Use SCons from Subversion URL
1146 -v, --verbose Display output of commands
1148 sys
.stdout
.write(self
.outdent(help))
1151 def do_run(self
, argv
):
1154 run_number_list
= [None]
1156 short_opts
= '?f:hnp:qs:v'
1174 opts
, args
= getopt
.getopt(argv
[1:], short_opts
, long_opts
)
1177 if o
in ('-f', '--file'):
1178 self
.config_file
= a
1179 elif o
in ('-?', '-h', '--help'):
1180 self
.do_help(['help', 'run'])
1182 elif o
in ('-n', '--no-exec'):
1183 self
.execute
= self
._do
_not
_execute
1184 elif o
in ('--number',):
1185 run_number_list
= self
.split_run_numbers(a
)
1186 elif o
in ('--outdir',):
1188 elif o
in ('-p', '--prefix'):
1190 elif o
in ('--python',):
1192 elif o
in ('-q', '--quiet'):
1193 self
.display
= self
._do
_not
_display
1194 elif o
in ('-s', '--subdir'):
1196 elif o
in ('--scons',):
1198 elif o
in ('--svn', '--subversion'):
1199 self
.subversion_url
= a
1200 elif o
in ('-v', '--verbose'):
1201 self
.redirect
= tee_to_file
1203 self
.svn_co_flag
= ''
1205 if not args
and not self
.config_file
:
1206 sys
.stderr
.write('%s: run: No arguments or -f config file specified.\n' % self
.name
)
1207 sys
.stderr
.write('%s Type "%s help run" for help.\n' % (self
.name_spaces
, self
.name
))
1210 if self
.config_file
:
1211 with
open(self
.config_file
, 'r') as f
:
1213 exec(config
, self
.__dict
__)
1216 self
.archive_list
= args
1218 archive_file_name
= os
.path
.split(self
.archive_list
[0])[1]
1221 self
.subdir
= self
.archive_splitext(archive_file_name
)[0]
1224 self
.prefix
= self
.archive_splitext(archive_file_name
)[0]
1227 if self
.subversion_url
:
1228 prepare
= self
.prep_subversion_run
1230 for run_number
in run_number_list
:
1231 self
.individual_run(run_number
, self
.archive_list
, prepare
)
1233 def split_run_numbers(self
, s
):
1235 for n
in s
.split(','):
1239 result
.append(int(n
))
1241 result
.extend(list(range(int(x
), int(y
) + 1)))
1244 def scons_path(self
, dir):
1245 return os
.path
.join(dir, 'scripts', 'scons.py')
1247 def scons_lib_dir_path(self
, dir):
1248 """build the path to the engine.
1250 this used to join src/engine, but no longer.
1254 def prep_subversion_run(self
, commands
, removals
):
1255 self
.svn_tmpdir
= tempfile
.mkdtemp(prefix
=self
.name
+ '-svn-')
1256 removals
.append((shutil
.rmtree
, 'rm -rf %%s', self
.svn_tmpdir
))
1258 self
.scons
= self
.scons_path(self
.svn_tmpdir
)
1259 self
.scons_lib_dir
= self
.scons_lib_dir_path(self
.svn_tmpdir
)
1262 '%(svn)s co %(svn_co_flag)s -r %(run_number)s %(subversion_url)s %(svn_tmpdir)s',
1265 def individual_run(self
, run_number
, archive_list
, prepare
=None):
1267 Performs an individual run of the default SCons invocations.
1274 prepare(commands
, removals
)
1276 save_scons
= self
.scons
1277 save_scons_wrapper
= self
.scons_wrapper
1278 save_scons_lib_dir
= self
.scons_lib_dir
1280 if self
.outdir
is None:
1281 self
.outdir
= self
.orig_cwd
1282 elif not os
.path
.isabs(self
.outdir
):
1283 self
.outdir
= os
.path
.join(self
.orig_cwd
, self
.outdir
)
1285 if self
.scons
is None:
1286 self
.scons
= self
.scons_path(self
.orig_cwd
)
1288 if self
.scons_lib_dir
is None:
1289 self
.scons_lib_dir
= self
.scons_lib_dir_path(self
.orig_cwd
)
1291 if self
.scons_wrapper
is None:
1292 self
.scons_wrapper
= self
.scons
1295 run_number
= self
.find_next_run_number(self
.outdir
, self
.prefix
)
1297 self
.run_number
= str(run_number
)
1299 self
.prefix_run
= self
.prefix
+ '-%03d' % run_number
1301 if self
.targets0
is None:
1302 self
.targets0
= self
.startup_targets
1303 if self
.targets1
is None:
1304 self
.targets1
= self
.targets
1305 if self
.targets2
is None:
1306 self
.targets2
= self
.targets
1308 self
.tmpdir
= tempfile
.mkdtemp(prefix
=self
.name
+ '-')
1311 (os
.chdir
, 'cd %%s', self
.tmpdir
),
1314 for archive
in archive_list
:
1315 if not os
.path
.isabs(archive
):
1316 archive
= os
.path
.join(self
.orig_cwd
, archive
)
1317 if os
.path
.isdir(archive
):
1318 dest
= os
.path
.split(archive
)[1]
1319 commands
.append((shutil
.copytree
, 'cp -r %%s %%s', archive
, dest
))
1321 suffix
= self
.archive_splitext(archive
)[1]
1322 unpack_command
= self
.unpack_map
.get(suffix
)
1323 if not unpack_command
:
1324 dest
= os
.path
.split(archive
)[1]
1325 commands
.append((shutil
.copyfile
, 'cp %%s %%s', archive
, dest
))
1327 commands
.append(unpack_command
+ (archive
,))
1330 (os
.chdir
, 'cd %%s', self
.subdir
),
1333 commands
.extend(self
.initial_commands
)
1336 (lambda: read_tree('.'),
1337 'find * -type f | xargs cat > /dev/null'),
1339 (self
.set_env
, 'export %%s=%%s',
1340 'SCONS_LIB_DIR', self
.scons_lib_dir
),
1342 '%(python)s %(scons_wrapper)s --version',
1346 for run_command
in self
.run_commands
:
1347 setattr(self
, 'prof%d' % index
, self
.profile_name(index
))
1352 self
.logfile_name(index
),
1358 (os
.chdir
, 'cd %%s', self
.orig_cwd
),
1361 if not os
.environ
.get('PRESERVE'):
1362 commands
.extend(removals
)
1363 commands
.append((shutil
.rmtree
, 'rm -rf %%s', self
.tmpdir
))
1365 self
.run_command_list(commands
, self
.__dict
__)
1367 self
.scons
= save_scons
1368 self
.scons_lib_dir
= save_scons_lib_dir
1369 self
.scons_wrapper
= save_scons_wrapper
1373 def help_time(self
):
1375 Usage: scons-time time [OPTIONS] FILE [...]
1377 -C DIR, --chdir=DIR Change to DIR before looking for files
1378 -f FILE, --file=FILE Read configuration from specified FILE
1379 --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
1380 -h, --help Print this help and exit
1381 -p STRING, --prefix=STRING Use STRING as log file/profile prefix
1382 -t NUMBER, --tail=NUMBER Only report the last NUMBER files
1383 --which=TIMER Plot timings for TIMER: total,
1384 SConscripts, SCons, commands.
1386 sys
.stdout
.write(self
.outdent(help))
1389 def do_time(self
, argv
):
1393 def _logfile_path(x
):
1396 logfile_path
= _logfile_path
1401 short_opts
= '?C:f:hp:t:'
1415 opts
, args
= getopt
.getopt(argv
[1:], short_opts
, long_opts
)
1418 if o
in ('-C', '--chdir'):
1420 elif o
in ('-f', '--file'):
1421 self
.config_file
= a
1422 elif o
in ('--fmt', '--format'):
1424 elif o
in ('-?', '-h', '--help'):
1425 self
.do_help(['help', 'time'])
1427 elif o
in ('-p', '--prefix'):
1429 elif o
in ('-t', '--tail'):
1431 elif o
in ('--title',):
1433 elif o
in ('--which',):
1434 if a
not in list(self
.time_strings
.keys()):
1435 sys
.stderr
.write('%s: time: Unrecognized timer "%s".\n' % (self
.name
, a
))
1436 sys
.stderr
.write('%s Type "%s help time" for help.\n' % (self
.name_spaces
, self
.name
))
1440 if self
.config_file
:
1441 with
open(self
.config_file
, 'r') as f
:
1443 HACK_for_exec(config
, self
.__dict
__)
1446 os
.chdir(self
.chdir
)
1448 def _logfile_path_join(x
):
1449 return os
.path
.join(self
.chdir
, x
)
1451 logfile_path
= _logfile_path_join
1454 pattern
= '%s*.log' % self
.prefix
1455 args
= self
.args_to_files([pattern
], tail
)
1459 directory
= self
.chdir
1461 directory
= os
.getcwd()
1463 sys
.stderr
.write('%s: time: No arguments specified.\n' % self
.name
)
1464 sys
.stderr
.write('%s No %s*.log files found in "%s".\n' % (self
.name_spaces
, self
.prefix
, directory
))
1465 sys
.stderr
.write('%s Type "%s help time" for help.\n' % (self
.name_spaces
, self
.name
))
1470 args
= self
.args_to_files(args
, tail
)
1472 cwd_
= os
.getcwd() + os
.sep
1474 if format
== 'ascii':
1476 columns
= ("Total", "SConscripts", "SCons", "commands")
1477 self
.ascii_table(args
, columns
, self
.get_debug_times
, logfile_path
)
1479 elif format
== 'gnuplot':
1481 results
= self
.collect_results(args
, self
.get_debug_times
,
1482 self
.time_strings
[which
])
1484 self
.gnuplot_results(results
, fmt
='%s %.6f')
1488 sys
.stderr
.write('%s: time: Unknown format "%s".\n' % (self
.name
, format
))
1492 if __name__
== '__main__':
1493 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'h?V', ['help', 'version'])
1498 if o
in ('-?', '-h', '--help'):
1499 ST
.do_help(['help'])
1501 elif o
in ('-V', '--version'):
1502 sys
.stdout
.write('scons-time version\n')
1506 sys
.stderr
.write('Type "%s help" for usage.\n' % ST
.name
)
1509 ST
.execute_subcommand(args
)
1513 # indent-tabs-mode:nil
1515 # vim: set expandtab tabstop=4 shiftwidth=4: