OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 3 # scons-time - run SCons timings and collect statistics |
| 4 # |
| 5 # A script for running a configuration through SCons with a standard |
| 6 # set of invocations to collect timing and memory statistics and to |
| 7 # capture the results in a consistent set of output files for display |
| 8 # and analysis. |
| 9 # |
| 10 |
| 11 # |
| 12 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S
Cons Foundation |
| 13 # |
| 14 # Permission is hereby granted, free of charge, to any person obtaining |
| 15 # a copy of this software and associated documentation files (the |
| 16 # "Software"), to deal in the Software without restriction, including |
| 17 # without limitation the rights to use, copy, modify, merge, publish, |
| 18 # distribute, sublicense, and/or sell copies of the Software, and to |
| 19 # permit persons to whom the Software is furnished to do so, subject to |
| 20 # the following conditions: |
| 21 # |
| 22 # The above copyright notice and this permission notice shall be included |
| 23 # in all copies or substantial portions of the Software. |
| 24 # |
| 25 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
| 26 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
| 27 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 28 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 29 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 30 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 31 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 32 from __future__ import division |
| 33 from __future__ import nested_scopes |
| 34 |
| 35 __revision__ = "src/script/scons-time.py 5134 2010/08/16 23:02:40 bdeegan" |
| 36 |
| 37 import getopt |
| 38 import glob |
| 39 import os |
| 40 import re |
| 41 import shutil |
| 42 import sys |
| 43 import tempfile |
| 44 import time |
| 45 |
| 46 try: |
| 47 sorted |
| 48 except NameError: |
| 49 # Pre-2.4 Python has no sorted() function. |
| 50 # |
| 51 # The pre-2.4 Python list.sort() method does not support |
| 52 # list.sort(key=) nor list.sort(reverse=) keyword arguments, so |
| 53 # we must implement the functionality of those keyword arguments |
| 54 # by hand instead of passing them to list.sort(). |
| 55 def sorted(iterable, cmp=None, key=None, reverse=False): |
| 56 if key is not None: |
| 57 result = [(key(x), x) for x in iterable] |
| 58 else: |
| 59 result = iterable[:] |
| 60 if cmp is None: |
| 61 # Pre-2.3 Python does not support list.sort(None). |
| 62 result.sort() |
| 63 else: |
| 64 result.sort(cmp) |
| 65 if key is not None: |
| 66 result = [t1 for t0,t1 in result] |
| 67 if reverse: |
| 68 result.reverse() |
| 69 return result |
| 70 |
| 71 if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is not None: |
| 72 # We can't apply the 'callable' fixer until the floor is 2.6, but the |
| 73 # '-3' option to Python 2.6 and 2.7 generates almost ten thousand |
| 74 # warnings. This hack allows us to run regression tests with the '-3' |
| 75 # option by replacing the callable() built-in function with a hack |
| 76 # that performs the same function but doesn't generate the warning. |
| 77 # Note that this hack is ONLY intended to be used for regression |
| 78 # testing, and should NEVER be used for real runs. |
| 79 from types import ClassType |
| 80 def callable(obj): |
| 81 if hasattr(obj, '__call__'): return True |
| 82 if isinstance(obj, (ClassType, type)): return True |
| 83 return False |
| 84 |
| 85 def make_temp_file(**kw): |
| 86 try: |
| 87 result = tempfile.mktemp(**kw) |
| 88 try: |
| 89 result = os.path.realpath(result) |
| 90 except AttributeError: |
| 91 # Python 2.1 has no os.path.realpath() method. |
| 92 pass |
| 93 except TypeError: |
| 94 try: |
| 95 save_template = tempfile.template |
| 96 prefix = kw['prefix'] |
| 97 del kw['prefix'] |
| 98 tempfile.template = prefix |
| 99 result = tempfile.mktemp(**kw) |
| 100 finally: |
| 101 tempfile.template = save_template |
| 102 return result |
| 103 |
| 104 def HACK_for_exec(cmd, *args): |
| 105 ''' |
| 106 For some reason, Python won't allow an exec() within a function |
| 107 that also declares an internal function (including lambda functions). |
| 108 This function is a hack that calls exec() in a function with no |
| 109 internal functions. |
| 110 ''' |
| 111 if not args: exec(cmd) |
| 112 elif len(args) == 1: exec cmd in args[0] |
| 113 else: exec cmd in args[0], args[1] |
| 114 |
| 115 class Plotter(object): |
| 116 def increment_size(self, largest): |
| 117 """ |
| 118 Return the size of each horizontal increment line for a specified |
| 119 maximum value. This returns a value that will provide somewhere |
| 120 between 5 and 9 horizontal lines on the graph, on some set of |
| 121 boundaries that are multiples of 10/100/1000/etc. |
| 122 """ |
| 123 i = largest // 5 |
| 124 if not i: |
| 125 return largest |
| 126 multiplier = 1 |
| 127 while i >= 10: |
| 128 i = i // 10 |
| 129 multiplier = multiplier * 10 |
| 130 return i * multiplier |
| 131 |
| 132 def max_graph_value(self, largest): |
| 133 # Round up to next integer. |
| 134 largest = int(largest) + 1 |
| 135 increment = self.increment_size(largest) |
| 136 return ((largest + increment - 1) // increment) * increment |
| 137 |
| 138 class Line(object): |
| 139 def __init__(self, points, type, title, label, comment, fmt="%s %s"): |
| 140 self.points = points |
| 141 self.type = type |
| 142 self.title = title |
| 143 self.label = label |
| 144 self.comment = comment |
| 145 self.fmt = fmt |
| 146 |
| 147 def print_label(self, inx, x, y): |
| 148 if self.label: |
| 149 print 'set label %s "%s" at %s,%s right' % (inx, self.label, x, y) |
| 150 |
| 151 def plot_string(self): |
| 152 if self.title: |
| 153 title_string = 'title "%s"' % self.title |
| 154 else: |
| 155 title_string = 'notitle' |
| 156 return "'-' %s with lines lt %s" % (title_string, self.type) |
| 157 |
| 158 def print_points(self, fmt=None): |
| 159 if fmt is None: |
| 160 fmt = self.fmt |
| 161 if self.comment: |
| 162 print '# %s' % self.comment |
| 163 for x, y in self.points: |
| 164 # If y is None, it usually represents some kind of break |
| 165 # in the line's index number. We might want to represent |
| 166 # this some way rather than just drawing the line straight |
| 167 # between the two points on either side. |
| 168 if not y is None: |
| 169 print fmt % (x, y) |
| 170 print 'e' |
| 171 |
| 172 def get_x_values(self): |
| 173 return [ p[0] for p in self.points ] |
| 174 |
| 175 def get_y_values(self): |
| 176 return [ p[1] for p in self.points ] |
| 177 |
| 178 class Gnuplotter(Plotter): |
| 179 |
| 180 def __init__(self, title, key_location): |
| 181 self.lines = [] |
| 182 self.title = title |
| 183 self.key_location = key_location |
| 184 |
| 185 def line(self, points, type, title=None, label=None, comment=None, fmt='%s %
s'): |
| 186 if points: |
| 187 line = Line(points, type, title, label, comment, fmt) |
| 188 self.lines.append(line) |
| 189 |
| 190 def plot_string(self, line): |
| 191 return line.plot_string() |
| 192 |
| 193 def vertical_bar(self, x, type, label, comment): |
| 194 if self.get_min_x() <= x and x <= self.get_max_x(): |
| 195 points = [(x, 0), (x, self.max_graph_value(self.get_max_y()))] |
| 196 self.line(points, type, label, comment) |
| 197 |
| 198 def get_all_x_values(self): |
| 199 result = [] |
| 200 for line in self.lines: |
| 201 result.extend(line.get_x_values()) |
| 202 return [r for r in result if not r is None] |
| 203 |
| 204 def get_all_y_values(self): |
| 205 result = [] |
| 206 for line in self.lines: |
| 207 result.extend(line.get_y_values()) |
| 208 return [r for r in result if not r is None] |
| 209 |
| 210 def get_min_x(self): |
| 211 try: |
| 212 return self.min_x |
| 213 except AttributeError: |
| 214 try: |
| 215 self.min_x = min(self.get_all_x_values()) |
| 216 except ValueError: |
| 217 self.min_x = 0 |
| 218 return self.min_x |
| 219 |
| 220 def get_max_x(self): |
| 221 try: |
| 222 return self.max_x |
| 223 except AttributeError: |
| 224 try: |
| 225 self.max_x = max(self.get_all_x_values()) |
| 226 except ValueError: |
| 227 self.max_x = 0 |
| 228 return self.max_x |
| 229 |
| 230 def get_min_y(self): |
| 231 try: |
| 232 return self.min_y |
| 233 except AttributeError: |
| 234 try: |
| 235 self.min_y = min(self.get_all_y_values()) |
| 236 except ValueError: |
| 237 self.min_y = 0 |
| 238 return self.min_y |
| 239 |
| 240 def get_max_y(self): |
| 241 try: |
| 242 return self.max_y |
| 243 except AttributeError: |
| 244 try: |
| 245 self.max_y = max(self.get_all_y_values()) |
| 246 except ValueError: |
| 247 self.max_y = 0 |
| 248 return self.max_y |
| 249 |
| 250 def draw(self): |
| 251 |
| 252 if not self.lines: |
| 253 return |
| 254 |
| 255 if self.title: |
| 256 print 'set title "%s"' % self.title |
| 257 print 'set key %s' % self.key_location |
| 258 |
| 259 min_y = self.get_min_y() |
| 260 max_y = self.max_graph_value(self.get_max_y()) |
| 261 incr = (max_y - min_y) / 10.0 |
| 262 start = min_y + (max_y / 2.0) + (2.0 * incr) |
| 263 position = [ start - (i * incr) for i in range(5) ] |
| 264 |
| 265 inx = 1 |
| 266 for line in self.lines: |
| 267 line.print_label(inx, line.points[0][0]-1, |
| 268 position[(inx-1) % len(position)]) |
| 269 inx += 1 |
| 270 |
| 271 plot_strings = [ self.plot_string(l) for l in self.lines ] |
| 272 print 'plot ' + ', \\\n '.join(plot_strings) |
| 273 |
| 274 for line in self.lines: |
| 275 line.print_points() |
| 276 |
| 277 |
| 278 |
| 279 def untar(fname): |
| 280 import tarfile |
| 281 tar = tarfile.open(name=fname, mode='r') |
| 282 for tarinfo in tar: |
| 283 tar.extract(tarinfo) |
| 284 tar.close() |
| 285 |
| 286 def unzip(fname): |
| 287 import zipfile |
| 288 zf = zipfile.ZipFile(fname, 'r') |
| 289 for name in zf.namelist(): |
| 290 dir = os.path.dirname(name) |
| 291 try: |
| 292 os.makedirs(dir) |
| 293 except: |
| 294 pass |
| 295 open(name, 'w').write(zf.read(name)) |
| 296 |
| 297 def read_tree(dir): |
| 298 for dirpath, dirnames, filenames in os.walk(dir): |
| 299 for fn in filenames: |
| 300 fn = os.path.join(dirpath, fn) |
| 301 if os.path.isfile(fn): |
| 302 open(fn, 'rb').read() |
| 303 |
| 304 def redirect_to_file(command, log): |
| 305 return '%s > %s 2>&1' % (command, log) |
| 306 |
| 307 def tee_to_file(command, log): |
| 308 return '%s 2>&1 | tee %s' % (command, log) |
| 309 |
| 310 |
| 311 |
| 312 class SConsTimer(object): |
| 313 """ |
| 314 Usage: scons-time SUBCOMMAND [ARGUMENTS] |
| 315 Type "scons-time help SUBCOMMAND" for help on a specific subcommand. |
| 316 |
| 317 Available subcommands: |
| 318 func Extract test-run data for a function |
| 319 help Provides help |
| 320 mem Extract --debug=memory data from test runs |
| 321 obj Extract --debug=count data from test runs |
| 322 time Extract --debug=time data from test runs |
| 323 run Runs a test configuration |
| 324 """ |
| 325 |
| 326 name = 'scons-time' |
| 327 name_spaces = ' '*len(name) |
| 328 |
| 329 def makedict(**kw): |
| 330 return kw |
| 331 |
| 332 default_settings = makedict( |
| 333 aegis = 'aegis', |
| 334 aegis_project = None, |
| 335 chdir = None, |
| 336 config_file = None, |
| 337 initial_commands = [], |
| 338 key_location = 'bottom left', |
| 339 orig_cwd = os.getcwd(), |
| 340 outdir = None, |
| 341 prefix = '', |
| 342 python = '"%s"' % sys.executable, |
| 343 redirect = redirect_to_file, |
| 344 scons = None, |
| 345 scons_flags = '--debug=count --debug=memory --debug=time --debug
=memoizer', |
| 346 scons_lib_dir = None, |
| 347 scons_wrapper = None, |
| 348 startup_targets = '--help', |
| 349 subdir = None, |
| 350 subversion_url = None, |
| 351 svn = 'svn', |
| 352 svn_co_flag = '-q', |
| 353 tar = 'tar', |
| 354 targets = '', |
| 355 targets0 = None, |
| 356 targets1 = None, |
| 357 targets2 = None, |
| 358 title = None, |
| 359 unzip = 'unzip', |
| 360 verbose = False, |
| 361 vertical_bars = [], |
| 362 |
| 363 unpack_map = { |
| 364 '.tar.gz' : (untar, '%(tar)s xzf %%s'), |
| 365 '.tgz' : (untar, '%(tar)s xzf %%s'), |
| 366 '.tar' : (untar, '%(tar)s xf %%s'), |
| 367 '.zip' : (unzip, '%(unzip)s %%s'), |
| 368 }, |
| 369 ) |
| 370 |
| 371 run_titles = [ |
| 372 'Startup', |
| 373 'Full build', |
| 374 'Up-to-date build', |
| 375 ] |
| 376 |
| 377 run_commands = [ |
| 378 '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof0)s %(targ
ets0)s', |
| 379 '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targ
ets1)s', |
| 380 '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targ
ets2)s', |
| 381 ] |
| 382 |
| 383 stages = [ |
| 384 'pre-read', |
| 385 'post-read', |
| 386 'pre-build', |
| 387 'post-build', |
| 388 ] |
| 389 |
| 390 stage_strings = { |
| 391 'pre-read' : 'Memory before reading SConscript files:', |
| 392 'post-read' : 'Memory after reading SConscript files:', |
| 393 'pre-build' : 'Memory before building targets:', |
| 394 'post-build' : 'Memory after building targets:', |
| 395 } |
| 396 |
| 397 memory_string_all = 'Memory ' |
| 398 |
| 399 default_stage = stages[-1] |
| 400 |
| 401 time_strings = { |
| 402 'total' : 'Total build time', |
| 403 'SConscripts' : 'Total SConscript file execution time', |
| 404 'SCons' : 'Total SCons execution time', |
| 405 'commands' : 'Total command execution time', |
| 406 } |
| 407 |
| 408 time_string_all = 'Total .* time' |
| 409 |
| 410 # |
| 411 |
| 412 def __init__(self): |
| 413 self.__dict__.update(self.default_settings) |
| 414 |
| 415 # Functions for displaying and executing commands. |
| 416 |
| 417 def subst(self, x, dictionary): |
| 418 try: |
| 419 return x % dictionary |
| 420 except TypeError: |
| 421 # x isn't a string (it's probably a Python function), |
| 422 # so just return it. |
| 423 return x |
| 424 |
| 425 def subst_variables(self, command, dictionary): |
| 426 """ |
| 427 Substitutes (via the format operator) the values in the specified |
| 428 dictionary into the specified command. |
| 429 |
| 430 The command can be an (action, string) tuple. In all cases, we |
| 431 perform substitution on strings and don't worry if something isn't |
| 432 a string. (It's probably a Python function to be executed.) |
| 433 """ |
| 434 try: |
| 435 command + '' |
| 436 except TypeError: |
| 437 action = command[0] |
| 438 string = command[1] |
| 439 args = command[2:] |
| 440 else: |
| 441 action = command |
| 442 string = action |
| 443 args = (()) |
| 444 action = self.subst(action, dictionary) |
| 445 string = self.subst(string, dictionary) |
| 446 return (action, string, args) |
| 447 |
| 448 def _do_not_display(self, msg, *args): |
| 449 pass |
| 450 |
| 451 def display(self, msg, *args): |
| 452 """ |
| 453 Displays the specified message. |
| 454 |
| 455 Each message is prepended with a standard prefix of our name |
| 456 plus the time. |
| 457 """ |
| 458 if callable(msg): |
| 459 msg = msg(*args) |
| 460 else: |
| 461 msg = msg % args |
| 462 if msg is None: |
| 463 return |
| 464 fmt = '%s[%s]: %s\n' |
| 465 sys.stdout.write(fmt % (self.name, time.strftime('%H:%M:%S'), msg)) |
| 466 |
| 467 def _do_not_execute(self, action, *args): |
| 468 pass |
| 469 |
| 470 def execute(self, action, *args): |
| 471 """ |
| 472 Executes the specified action. |
| 473 |
| 474 The action is called if it's a callable Python function, and |
| 475 otherwise passed to os.system(). |
| 476 """ |
| 477 if callable(action): |
| 478 action(*args) |
| 479 else: |
| 480 os.system(action % args) |
| 481 |
| 482 def run_command_list(self, commands, dict): |
| 483 """ |
| 484 Executes a list of commands, substituting values from the |
| 485 specified dictionary. |
| 486 """ |
| 487 commands = [ self.subst_variables(c, dict) for c in commands ] |
| 488 for action, string, args in commands: |
| 489 self.display(string, *args) |
| 490 sys.stdout.flush() |
| 491 status = self.execute(action, *args) |
| 492 if status: |
| 493 sys.exit(status) |
| 494 |
| 495 def log_display(self, command, log): |
| 496 command = self.subst(command, self.__dict__) |
| 497 if log: |
| 498 command = self.redirect(command, log) |
| 499 return command |
| 500 |
| 501 def log_execute(self, command, log): |
| 502 command = self.subst(command, self.__dict__) |
| 503 output = os.popen(command).read() |
| 504 if self.verbose: |
| 505 sys.stdout.write(output) |
| 506 open(log, 'wb').write(output) |
| 507 |
| 508 # |
| 509 |
| 510 def archive_splitext(self, path): |
| 511 """ |
| 512 Splits an archive name into a filename base and extension. |
| 513 |
| 514 This is like os.path.splitext() (which it calls) except that it |
| 515 also looks for '.tar.gz' and treats it as an atomic extensions. |
| 516 """ |
| 517 if path.endswith('.tar.gz'): |
| 518 return path[:-7], path[-7:] |
| 519 else: |
| 520 return os.path.splitext(path) |
| 521 |
| 522 def args_to_files(self, args, tail=None): |
| 523 """ |
| 524 Takes a list of arguments, expands any glob patterns, and |
| 525 returns the last "tail" files from the list. |
| 526 """ |
| 527 files = [] |
| 528 for a in args: |
| 529 files.extend(sorted(glob.glob(a))) |
| 530 |
| 531 if tail: |
| 532 files = files[-tail:] |
| 533 |
| 534 return files |
| 535 |
| 536 def ascii_table(self, files, columns, |
| 537 line_function, file_function=lambda x: x, |
| 538 *args, **kw): |
| 539 |
| 540 header_fmt = ' '.join(['%12s'] * len(columns)) |
| 541 line_fmt = header_fmt + ' %s' |
| 542 |
| 543 print header_fmt % columns |
| 544 |
| 545 for file in files: |
| 546 t = line_function(file, *args, **kw) |
| 547 if t is None: |
| 548 t = [] |
| 549 diff = len(columns) - len(t) |
| 550 if diff > 0: |
| 551 t += [''] * diff |
| 552 t.append(file_function(file)) |
| 553 print line_fmt % tuple(t) |
| 554 |
| 555 def collect_results(self, files, function, *args, **kw): |
| 556 results = {} |
| 557 |
| 558 for file in files: |
| 559 base = os.path.splitext(file)[0] |
| 560 run, index = base.split('-')[-2:] |
| 561 |
| 562 run = int(run) |
| 563 index = int(index) |
| 564 |
| 565 value = function(file, *args, **kw) |
| 566 |
| 567 try: |
| 568 r = results[index] |
| 569 except KeyError: |
| 570 r = [] |
| 571 results[index] = r |
| 572 r.append((run, value)) |
| 573 |
| 574 return results |
| 575 |
| 576 def doc_to_help(self, obj): |
| 577 """ |
| 578 Translates an object's __doc__ string into help text. |
| 579 |
| 580 This strips a consistent number of spaces from each line in the |
| 581 help text, essentially "outdenting" the text to the left-most |
| 582 column. |
| 583 """ |
| 584 doc = obj.__doc__ |
| 585 if doc is None: |
| 586 return '' |
| 587 return self.outdent(doc) |
| 588 |
| 589 def find_next_run_number(self, dir, prefix): |
| 590 """ |
| 591 Returns the next run number in a directory for the specified prefix. |
| 592 |
| 593 Examines the contents the specified directory for files with the |
| 594 specified prefix, extracts the run numbers from each file name, |
| 595 and returns the next run number after the largest it finds. |
| 596 """ |
| 597 x = re.compile(re.escape(prefix) + '-([0-9]+).*') |
| 598 matches = [x.match(e) for e in os.listdir(dir)] |
| 599 matches = [_f for _f in matches if _f] |
| 600 if not matches: |
| 601 return 0 |
| 602 run_numbers = [int(m.group(1)) for m in matches] |
| 603 return int(max(run_numbers)) + 1 |
| 604 |
| 605 def gnuplot_results(self, results, fmt='%s %.3f'): |
| 606 """ |
| 607 Prints out a set of results in Gnuplot format. |
| 608 """ |
| 609 gp = Gnuplotter(self.title, self.key_location) |
| 610 |
| 611 for i in sorted(results.keys()): |
| 612 try: |
| 613 t = self.run_titles[i] |
| 614 except IndexError: |
| 615 t = '??? %s ???' % i |
| 616 results[i].sort() |
| 617 gp.line(results[i], i+1, t, None, t, fmt=fmt) |
| 618 |
| 619 for bar_tuple in self.vertical_bars: |
| 620 try: |
| 621 x, type, label, comment = bar_tuple |
| 622 except ValueError: |
| 623 x, type, label = bar_tuple |
| 624 comment = label |
| 625 gp.vertical_bar(x, type, label, comment) |
| 626 |
| 627 gp.draw() |
| 628 |
| 629 def logfile_name(self, invocation): |
| 630 """ |
| 631 Returns the absolute path of a log file for the specificed |
| 632 invocation number. |
| 633 """ |
| 634 name = self.prefix_run + '-%d.log' % invocation |
| 635 return os.path.join(self.outdir, name) |
| 636 |
| 637 def outdent(self, s): |
| 638 """ |
| 639 Strip as many spaces from each line as are found at the beginning |
| 640 of the first line in the list. |
| 641 """ |
| 642 lines = s.split('\n') |
| 643 if lines[0] == '': |
| 644 lines = lines[1:] |
| 645 spaces = re.match(' *', lines[0]).group(0) |
| 646 def strip_initial_spaces(l, s=spaces): |
| 647 if l.startswith(spaces): |
| 648 l = l[len(spaces):] |
| 649 return l |
| 650 return '\n'.join([ strip_initial_spaces(l) for l in lines ]) + '\n' |
| 651 |
| 652 def profile_name(self, invocation): |
| 653 """ |
| 654 Returns the absolute path of a profile file for the specified |
| 655 invocation number. |
| 656 """ |
| 657 name = self.prefix_run + '-%d.prof' % invocation |
| 658 return os.path.join(self.outdir, name) |
| 659 |
| 660 def set_env(self, key, value): |
| 661 os.environ[key] = value |
| 662 |
| 663 # |
| 664 |
| 665 def get_debug_times(self, file, time_string=None): |
| 666 """ |
| 667 Fetch times from the --debug=time strings in the specified file. |
| 668 """ |
| 669 if time_string is None: |
| 670 search_string = self.time_string_all |
| 671 else: |
| 672 search_string = time_string |
| 673 contents = open(file).read() |
| 674 if not contents: |
| 675 sys.stderr.write('file %s has no contents!\n' % repr(file)) |
| 676 return None |
| 677 result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:] |
| 678 result = [ float(r) for r in result ] |
| 679 if not time_string is None: |
| 680 try: |
| 681 result = result[0] |
| 682 except IndexError: |
| 683 sys.stderr.write('file %s has no results!\n' % repr(file)) |
| 684 return None |
| 685 return result |
| 686 |
| 687 def get_function_profile(self, file, function): |
| 688 """ |
| 689 Returns the file, line number, function name, and cumulative time. |
| 690 """ |
| 691 try: |
| 692 import pstats |
| 693 except ImportError, e: |
| 694 sys.stderr.write('%s: func: %s\n' % (self.name, e)) |
| 695 sys.stderr.write('%s This version of Python is missing the profiler
.\n' % self.name_spaces) |
| 696 sys.stderr.write('%s Cannot use the "func" subcommand.\n' % self.na
me_spaces) |
| 697 sys.exit(1) |
| 698 statistics = pstats.Stats(file).stats |
| 699 matches = [ e for e in statistics.items() if e[0][2] == function ] |
| 700 r = matches[0] |
| 701 return r[0][0], r[0][1], r[0][2], r[1][3] |
| 702 |
| 703 def get_function_time(self, file, function): |
| 704 """ |
| 705 Returns just the cumulative time for the specified function. |
| 706 """ |
| 707 return self.get_function_profile(file, function)[3] |
| 708 |
| 709 def get_memory(self, file, memory_string=None): |
| 710 """ |
| 711 Returns a list of integers of the amount of memory used. The |
| 712 default behavior is to return all the stages. |
| 713 """ |
| 714 if memory_string is None: |
| 715 search_string = self.memory_string_all |
| 716 else: |
| 717 search_string = memory_string |
| 718 lines = open(file).readlines() |
| 719 lines = [ l for l in lines if l.startswith(search_string) ][-4:] |
| 720 result = [ int(l.split()[-1]) for l in lines[-4:] ] |
| 721 if len(result) == 1: |
| 722 result = result[0] |
| 723 return result |
| 724 |
| 725 def get_object_counts(self, file, object_name, index=None): |
| 726 """ |
| 727 Returns the counts of the specified object_name. |
| 728 """ |
| 729 object_string = ' ' + object_name + '\n' |
| 730 lines = open(file).readlines() |
| 731 line = [ l for l in lines if l.endswith(object_string) ][0] |
| 732 result = [ int(field) for field in line.split()[:4] ] |
| 733 if index is not None: |
| 734 result = result[index] |
| 735 return result |
| 736 |
| 737 # |
| 738 |
| 739 command_alias = {} |
| 740 |
| 741 def execute_subcommand(self, argv): |
| 742 """ |
| 743 Executes the do_*() function for the specified subcommand (argv[0]). |
| 744 """ |
| 745 if not argv: |
| 746 return |
| 747 cmdName = self.command_alias.get(argv[0], argv[0]) |
| 748 try: |
| 749 func = getattr(self, 'do_' + cmdName) |
| 750 except AttributeError: |
| 751 return self.default(argv) |
| 752 try: |
| 753 return func(argv) |
| 754 except TypeError, e: |
| 755 sys.stderr.write("%s %s: %s\n" % (self.name, cmdName, e)) |
| 756 import traceback |
| 757 traceback.print_exc(file=sys.stderr) |
| 758 sys.stderr.write("Try '%s help %s'\n" % (self.name, cmdName)) |
| 759 |
| 760 def default(self, argv): |
| 761 """ |
| 762 The default behavior for an unknown subcommand. Prints an |
| 763 error message and exits. |
| 764 """ |
| 765 sys.stderr.write('%s: Unknown subcommand "%s".\n' % (self.name, argv[0])
) |
| 766 sys.stderr.write('Type "%s help" for usage.\n' % self.name) |
| 767 sys.exit(1) |
| 768 |
| 769 # |
| 770 |
| 771 def do_help(self, argv): |
| 772 """ |
| 773 """ |
| 774 if argv[1:]: |
| 775 for arg in argv[1:]: |
| 776 try: |
| 777 func = getattr(self, 'do_' + arg) |
| 778 except AttributeError: |
| 779 sys.stderr.write('%s: No help for "%s"\n' % (self.name, arg)
) |
| 780 else: |
| 781 try: |
| 782 help = getattr(self, 'help_' + arg) |
| 783 except AttributeError: |
| 784 sys.stdout.write(self.doc_to_help(func)) |
| 785 sys.stdout.flush() |
| 786 else: |
| 787 help() |
| 788 else: |
| 789 doc = self.doc_to_help(self.__class__) |
| 790 if doc: |
| 791 sys.stdout.write(doc) |
| 792 sys.stdout.flush() |
| 793 return None |
| 794 |
| 795 # |
| 796 |
| 797 def help_func(self): |
| 798 help = """\ |
| 799 Usage: scons-time func [OPTIONS] FILE [...] |
| 800 |
| 801 -C DIR, --chdir=DIR Change to DIR before looking for files |
| 802 -f FILE, --file=FILE Read configuration from specified FILE |
| 803 --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT |
| 804 --func=NAME, --function=NAME Report time for function NAME |
| 805 -h, --help Print this help and exit |
| 806 -p STRING, --prefix=STRING Use STRING as log file/profile prefix |
| 807 -t NUMBER, --tail=NUMBER Only report the last NUMBER files |
| 808 --title=TITLE Specify the output plot TITLE |
| 809 """ |
| 810 sys.stdout.write(self.outdent(help)) |
| 811 sys.stdout.flush() |
| 812 |
| 813 def do_func(self, argv): |
| 814 """ |
| 815 """ |
| 816 format = 'ascii' |
| 817 function_name = '_main' |
| 818 tail = None |
| 819 |
| 820 short_opts = '?C:f:hp:t:' |
| 821 |
| 822 long_opts = [ |
| 823 'chdir=', |
| 824 'file=', |
| 825 'fmt=', |
| 826 'format=', |
| 827 'func=', |
| 828 'function=', |
| 829 'help', |
| 830 'prefix=', |
| 831 'tail=', |
| 832 'title=', |
| 833 ] |
| 834 |
| 835 opts, args = getopt.getopt(argv[1:], short_opts, long_opts) |
| 836 |
| 837 for o, a in opts: |
| 838 if o in ('-C', '--chdir'): |
| 839 self.chdir = a |
| 840 elif o in ('-f', '--file'): |
| 841 self.config_file = a |
| 842 elif o in ('--fmt', '--format'): |
| 843 format = a |
| 844 elif o in ('--func', '--function'): |
| 845 function_name = a |
| 846 elif o in ('-?', '-h', '--help'): |
| 847 self.do_help(['help', 'func']) |
| 848 sys.exit(0) |
| 849 elif o in ('--max',): |
| 850 max_time = int(a) |
| 851 elif o in ('-p', '--prefix'): |
| 852 self.prefix = a |
| 853 elif o in ('-t', '--tail'): |
| 854 tail = int(a) |
| 855 elif o in ('--title',): |
| 856 self.title = a |
| 857 |
| 858 if self.config_file: |
| 859 exec open(self.config_file, 'rU').read() in self.__dict__ |
| 860 |
| 861 if self.chdir: |
| 862 os.chdir(self.chdir) |
| 863 |
| 864 if not args: |
| 865 |
| 866 pattern = '%s*.prof' % self.prefix |
| 867 args = self.args_to_files([pattern], tail) |
| 868 |
| 869 if not args: |
| 870 if self.chdir: |
| 871 directory = self.chdir |
| 872 else: |
| 873 directory = os.getcwd() |
| 874 |
| 875 sys.stderr.write('%s: func: No arguments specified.\n' % self.na
me) |
| 876 sys.stderr.write('%s No %s*.prof files found in "%s".\n' % (sel
f.name_spaces, self.prefix, directory)) |
| 877 sys.stderr.write('%s Type "%s help func" for help.\n' % (self.n
ame_spaces, self.name)) |
| 878 sys.exit(1) |
| 879 |
| 880 else: |
| 881 |
| 882 args = self.args_to_files(args, tail) |
| 883 |
| 884 cwd_ = os.getcwd() + os.sep |
| 885 |
| 886 if format == 'ascii': |
| 887 |
| 888 for file in args: |
| 889 try: |
| 890 f, line, func, time = \ |
| 891 self.get_function_profile(file, function_name) |
| 892 except ValueError, e: |
| 893 sys.stderr.write("%s: func: %s: %s\n" % |
| 894 (self.name, file, e)) |
| 895 else: |
| 896 if f.startswith(cwd_): |
| 897 f = f[len(cwd_):] |
| 898 print "%.3f %s:%d(%s)" % (time, f, line, func) |
| 899 |
| 900 elif format == 'gnuplot': |
| 901 |
| 902 results = self.collect_results(args, self.get_function_time, |
| 903 function_name) |
| 904 |
| 905 self.gnuplot_results(results) |
| 906 |
| 907 else: |
| 908 |
| 909 sys.stderr.write('%s: func: Unknown format "%s".\n' % (self.name, fo
rmat)) |
| 910 sys.exit(1) |
| 911 |
| 912 # |
| 913 |
| 914 def help_mem(self): |
| 915 help = """\ |
| 916 Usage: scons-time mem [OPTIONS] FILE [...] |
| 917 |
| 918 -C DIR, --chdir=DIR Change to DIR before looking for files |
| 919 -f FILE, --file=FILE Read configuration from specified FILE |
| 920 --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT |
| 921 -h, --help Print this help and exit |
| 922 -p STRING, --prefix=STRING Use STRING as log file/profile prefix |
| 923 --stage=STAGE Plot memory at the specified stage: |
| 924 pre-read, post-read, pre-build, |
| 925 post-build (default: post-build) |
| 926 -t NUMBER, --tail=NUMBER Only report the last NUMBER files |
| 927 --title=TITLE Specify the output plot TITLE |
| 928 """ |
| 929 sys.stdout.write(self.outdent(help)) |
| 930 sys.stdout.flush() |
| 931 |
| 932 def do_mem(self, argv): |
| 933 |
| 934 format = 'ascii' |
| 935 logfile_path = lambda x: x |
| 936 stage = self.default_stage |
| 937 tail = None |
| 938 |
| 939 short_opts = '?C:f:hp:t:' |
| 940 |
| 941 long_opts = [ |
| 942 'chdir=', |
| 943 'file=', |
| 944 'fmt=', |
| 945 'format=', |
| 946 'help', |
| 947 'prefix=', |
| 948 'stage=', |
| 949 'tail=', |
| 950 'title=', |
| 951 ] |
| 952 |
| 953 opts, args = getopt.getopt(argv[1:], short_opts, long_opts) |
| 954 |
| 955 for o, a in opts: |
| 956 if o in ('-C', '--chdir'): |
| 957 self.chdir = a |
| 958 elif o in ('-f', '--file'): |
| 959 self.config_file = a |
| 960 elif o in ('--fmt', '--format'): |
| 961 format = a |
| 962 elif o in ('-?', '-h', '--help'): |
| 963 self.do_help(['help', 'mem']) |
| 964 sys.exit(0) |
| 965 elif o in ('-p', '--prefix'): |
| 966 self.prefix = a |
| 967 elif o in ('--stage',): |
| 968 if not a in self.stages: |
| 969 sys.stderr.write('%s: mem: Unrecognized stage "%s".\n' % (se
lf.name, a)) |
| 970 sys.exit(1) |
| 971 stage = a |
| 972 elif o in ('-t', '--tail'): |
| 973 tail = int(a) |
| 974 elif o in ('--title',): |
| 975 self.title = a |
| 976 |
| 977 if self.config_file: |
| 978 HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__) |
| 979 |
| 980 if self.chdir: |
| 981 os.chdir(self.chdir) |
| 982 logfile_path = lambda x: os.path.join(self.chdir, x) |
| 983 |
| 984 if not args: |
| 985 |
| 986 pattern = '%s*.log' % self.prefix |
| 987 args = self.args_to_files([pattern], tail) |
| 988 |
| 989 if not args: |
| 990 if self.chdir: |
| 991 directory = self.chdir |
| 992 else: |
| 993 directory = os.getcwd() |
| 994 |
| 995 sys.stderr.write('%s: mem: No arguments specified.\n' % self.nam
e) |
| 996 sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self
.name_spaces, self.prefix, directory)) |
| 997 sys.stderr.write('%s Type "%s help mem" for help.\n' % (self.na
me_spaces, self.name)) |
| 998 sys.exit(1) |
| 999 |
| 1000 else: |
| 1001 |
| 1002 args = self.args_to_files(args, tail) |
| 1003 |
| 1004 cwd_ = os.getcwd() + os.sep |
| 1005 |
| 1006 if format == 'ascii': |
| 1007 |
| 1008 self.ascii_table(args, tuple(self.stages), self.get_memory, logfile_
path) |
| 1009 |
| 1010 elif format == 'gnuplot': |
| 1011 |
| 1012 results = self.collect_results(args, self.get_memory, |
| 1013 self.stage_strings[stage]) |
| 1014 |
| 1015 self.gnuplot_results(results) |
| 1016 |
| 1017 else: |
| 1018 |
| 1019 sys.stderr.write('%s: mem: Unknown format "%s".\n' % (self.name, for
mat)) |
| 1020 sys.exit(1) |
| 1021 |
| 1022 return 0 |
| 1023 |
| 1024 # |
| 1025 |
| 1026 def help_obj(self): |
| 1027 help = """\ |
| 1028 Usage: scons-time obj [OPTIONS] OBJECT FILE [...] |
| 1029 |
| 1030 -C DIR, --chdir=DIR Change to DIR before looking for files |
| 1031 -f FILE, --file=FILE Read configuration from specified FILE |
| 1032 --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT |
| 1033 -h, --help Print this help and exit |
| 1034 -p STRING, --prefix=STRING Use STRING as log file/profile prefix |
| 1035 --stage=STAGE Plot memory at the specified stage: |
| 1036 pre-read, post-read, pre-build, |
| 1037 post-build (default: post-build) |
| 1038 -t NUMBER, --tail=NUMBER Only report the last NUMBER files |
| 1039 --title=TITLE Specify the output plot TITLE |
| 1040 """ |
| 1041 sys.stdout.write(self.outdent(help)) |
| 1042 sys.stdout.flush() |
| 1043 |
| 1044 def do_obj(self, argv): |
| 1045 |
| 1046 format = 'ascii' |
| 1047 logfile_path = lambda x: x |
| 1048 stage = self.default_stage |
| 1049 tail = None |
| 1050 |
| 1051 short_opts = '?C:f:hp:t:' |
| 1052 |
| 1053 long_opts = [ |
| 1054 'chdir=', |
| 1055 'file=', |
| 1056 'fmt=', |
| 1057 'format=', |
| 1058 'help', |
| 1059 'prefix=', |
| 1060 'stage=', |
| 1061 'tail=', |
| 1062 'title=', |
| 1063 ] |
| 1064 |
| 1065 opts, args = getopt.getopt(argv[1:], short_opts, long_opts) |
| 1066 |
| 1067 for o, a in opts: |
| 1068 if o in ('-C', '--chdir'): |
| 1069 self.chdir = a |
| 1070 elif o in ('-f', '--file'): |
| 1071 self.config_file = a |
| 1072 elif o in ('--fmt', '--format'): |
| 1073 format = a |
| 1074 elif o in ('-?', '-h', '--help'): |
| 1075 self.do_help(['help', 'obj']) |
| 1076 sys.exit(0) |
| 1077 elif o in ('-p', '--prefix'): |
| 1078 self.prefix = a |
| 1079 elif o in ('--stage',): |
| 1080 if not a in self.stages: |
| 1081 sys.stderr.write('%s: obj: Unrecognized stage "%s".\n' % (se
lf.name, a)) |
| 1082 sys.stderr.write('%s Type "%s help obj" for help.\n' %
(self.name_spaces, self.name)) |
| 1083 sys.exit(1) |
| 1084 stage = a |
| 1085 elif o in ('-t', '--tail'): |
| 1086 tail = int(a) |
| 1087 elif o in ('--title',): |
| 1088 self.title = a |
| 1089 |
| 1090 if not args: |
| 1091 sys.stderr.write('%s: obj: Must specify an object name.\n' % self.na
me) |
| 1092 sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.n
ame_spaces, self.name)) |
| 1093 sys.exit(1) |
| 1094 |
| 1095 object_name = args.pop(0) |
| 1096 |
| 1097 if self.config_file: |
| 1098 HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__) |
| 1099 |
| 1100 if self.chdir: |
| 1101 os.chdir(self.chdir) |
| 1102 logfile_path = lambda x: os.path.join(self.chdir, x) |
| 1103 |
| 1104 if not args: |
| 1105 |
| 1106 pattern = '%s*.log' % self.prefix |
| 1107 args = self.args_to_files([pattern], tail) |
| 1108 |
| 1109 if not args: |
| 1110 if self.chdir: |
| 1111 directory = self.chdir |
| 1112 else: |
| 1113 directory = os.getcwd() |
| 1114 |
| 1115 sys.stderr.write('%s: obj: No arguments specified.\n' % self.nam
e) |
| 1116 sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self
.name_spaces, self.prefix, directory)) |
| 1117 sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.na
me_spaces, self.name)) |
| 1118 sys.exit(1) |
| 1119 |
| 1120 else: |
| 1121 |
| 1122 args = self.args_to_files(args, tail) |
| 1123 |
| 1124 cwd_ = os.getcwd() + os.sep |
| 1125 |
| 1126 if format == 'ascii': |
| 1127 |
| 1128 self.ascii_table(args, tuple(self.stages), self.get_object_counts, l
ogfile_path, object_name) |
| 1129 |
| 1130 elif format == 'gnuplot': |
| 1131 |
| 1132 stage_index = 0 |
| 1133 for s in self.stages: |
| 1134 if stage == s: |
| 1135 break |
| 1136 stage_index = stage_index + 1 |
| 1137 |
| 1138 results = self.collect_results(args, self.get_object_counts, |
| 1139 object_name, stage_index) |
| 1140 |
| 1141 self.gnuplot_results(results) |
| 1142 |
| 1143 else: |
| 1144 |
| 1145 sys.stderr.write('%s: obj: Unknown format "%s".\n' % (self.name, for
mat)) |
| 1146 sys.exit(1) |
| 1147 |
| 1148 return 0 |
| 1149 |
| 1150 # |
| 1151 |
| 1152 def help_run(self): |
| 1153 help = """\ |
| 1154 Usage: scons-time run [OPTIONS] [FILE ...] |
| 1155 |
| 1156 --aegis=PROJECT Use SCons from the Aegis PROJECT |
| 1157 --chdir=DIR Name of unpacked directory for chdir |
| 1158 -f FILE, --file=FILE Read configuration from specified FILE |
| 1159 -h, --help Print this help and exit |
| 1160 -n, --no-exec No execute, just print command lines |
| 1161 --number=NUMBER Put output in files for run NUMBER |
| 1162 --outdir=OUTDIR Put output files in OUTDIR |
| 1163 -p STRING, --prefix=STRING Use STRING as log file/profile prefix |
| 1164 --python=PYTHON Time using the specified PYTHON |
| 1165 -q, --quiet Don't print command lines |
| 1166 --scons=SCONS Time using the specified SCONS |
| 1167 --svn=URL, --subversion=URL Use SCons from Subversion URL |
| 1168 -v, --verbose Display output of commands |
| 1169 """ |
| 1170 sys.stdout.write(self.outdent(help)) |
| 1171 sys.stdout.flush() |
| 1172 |
| 1173 def do_run(self, argv): |
| 1174 """ |
| 1175 """ |
| 1176 run_number_list = [None] |
| 1177 |
| 1178 short_opts = '?f:hnp:qs:v' |
| 1179 |
| 1180 long_opts = [ |
| 1181 'aegis=', |
| 1182 'file=', |
| 1183 'help', |
| 1184 'no-exec', |
| 1185 'number=', |
| 1186 'outdir=', |
| 1187 'prefix=', |
| 1188 'python=', |
| 1189 'quiet', |
| 1190 'scons=', |
| 1191 'svn=', |
| 1192 'subdir=', |
| 1193 'subversion=', |
| 1194 'verbose', |
| 1195 ] |
| 1196 |
| 1197 opts, args = getopt.getopt(argv[1:], short_opts, long_opts) |
| 1198 |
| 1199 for o, a in opts: |
| 1200 if o in ('--aegis',): |
| 1201 self.aegis_project = a |
| 1202 elif o in ('-f', '--file'): |
| 1203 self.config_file = a |
| 1204 elif o in ('-?', '-h', '--help'): |
| 1205 self.do_help(['help', 'run']) |
| 1206 sys.exit(0) |
| 1207 elif o in ('-n', '--no-exec'): |
| 1208 self.execute = self._do_not_execute |
| 1209 elif o in ('--number',): |
| 1210 run_number_list = self.split_run_numbers(a) |
| 1211 elif o in ('--outdir',): |
| 1212 self.outdir = a |
| 1213 elif o in ('-p', '--prefix'): |
| 1214 self.prefix = a |
| 1215 elif o in ('--python',): |
| 1216 self.python = a |
| 1217 elif o in ('-q', '--quiet'): |
| 1218 self.display = self._do_not_display |
| 1219 elif o in ('-s', '--subdir'): |
| 1220 self.subdir = a |
| 1221 elif o in ('--scons',): |
| 1222 self.scons = a |
| 1223 elif o in ('--svn', '--subversion'): |
| 1224 self.subversion_url = a |
| 1225 elif o in ('-v', '--verbose'): |
| 1226 self.redirect = tee_to_file |
| 1227 self.verbose = True |
| 1228 self.svn_co_flag = '' |
| 1229 |
| 1230 if not args and not self.config_file: |
| 1231 sys.stderr.write('%s: run: No arguments or -f config file specified.
\n' % self.name) |
| 1232 sys.stderr.write('%s Type "%s help run" for help.\n' % (self.name_s
paces, self.name)) |
| 1233 sys.exit(1) |
| 1234 |
| 1235 if self.config_file: |
| 1236 exec open(self.config_file, 'rU').read() in self.__dict__ |
| 1237 |
| 1238 if args: |
| 1239 self.archive_list = args |
| 1240 |
| 1241 archive_file_name = os.path.split(self.archive_list[0])[1] |
| 1242 |
| 1243 if not self.subdir: |
| 1244 self.subdir = self.archive_splitext(archive_file_name)[0] |
| 1245 |
| 1246 if not self.prefix: |
| 1247 self.prefix = self.archive_splitext(archive_file_name)[0] |
| 1248 |
| 1249 prepare = None |
| 1250 if self.subversion_url: |
| 1251 prepare = self.prep_subversion_run |
| 1252 elif self.aegis_project: |
| 1253 prepare = self.prep_aegis_run |
| 1254 |
| 1255 for run_number in run_number_list: |
| 1256 self.individual_run(run_number, self.archive_list, prepare) |
| 1257 |
| 1258 def split_run_numbers(self, s): |
| 1259 result = [] |
| 1260 for n in s.split(','): |
| 1261 try: |
| 1262 x, y = n.split('-') |
| 1263 except ValueError: |
| 1264 result.append(int(n)) |
| 1265 else: |
| 1266 result.extend(list(range(int(x), int(y)+1))) |
| 1267 return result |
| 1268 |
| 1269 def scons_path(self, dir): |
| 1270 return os.path.join(dir, 'src', 'script', 'scons.py') |
| 1271 |
| 1272 def scons_lib_dir_path(self, dir): |
| 1273 return os.path.join(dir, 'src', 'engine') |
| 1274 |
| 1275 def prep_aegis_run(self, commands, removals): |
| 1276 self.aegis_tmpdir = make_temp_file(prefix = self.name + '-aegis-') |
| 1277 removals.append((shutil.rmtree, 'rm -rf %%s', self.aegis_tmpdir)) |
| 1278 |
| 1279 self.aegis_parent_project = os.path.splitext(self.aegis_project)[0] |
| 1280 self.scons = self.scons_path(self.aegis_tmpdir) |
| 1281 self.scons_lib_dir = self.scons_lib_dir_path(self.aegis_tmpdir) |
| 1282 |
| 1283 commands.extend([ |
| 1284 'mkdir %(aegis_tmpdir)s', |
| 1285 (lambda: os.chdir(self.aegis_tmpdir), 'cd %(aegis_tmpdir)s'), |
| 1286 '%(aegis)s -cp -ind -p %(aegis_parent_project)s .', |
| 1287 '%(aegis)s -cp -ind -p %(aegis_project)s -delta %(run_number)s .', |
| 1288 ]) |
| 1289 |
| 1290 def prep_subversion_run(self, commands, removals): |
| 1291 self.svn_tmpdir = make_temp_file(prefix = self.name + '-svn-') |
| 1292 removals.append((shutil.rmtree, 'rm -rf %%s', self.svn_tmpdir)) |
| 1293 |
| 1294 self.scons = self.scons_path(self.svn_tmpdir) |
| 1295 self.scons_lib_dir = self.scons_lib_dir_path(self.svn_tmpdir) |
| 1296 |
| 1297 commands.extend([ |
| 1298 'mkdir %(svn_tmpdir)s', |
| 1299 '%(svn)s co %(svn_co_flag)s -r %(run_number)s %(subversion_url)s %(s
vn_tmpdir)s', |
| 1300 ]) |
| 1301 |
| 1302 def individual_run(self, run_number, archive_list, prepare=None): |
| 1303 """ |
| 1304 Performs an individual run of the default SCons invocations. |
| 1305 """ |
| 1306 |
| 1307 commands = [] |
| 1308 removals = [] |
| 1309 |
| 1310 if prepare: |
| 1311 prepare(commands, removals) |
| 1312 |
| 1313 save_scons = self.scons |
| 1314 save_scons_wrapper = self.scons_wrapper |
| 1315 save_scons_lib_dir = self.scons_lib_dir |
| 1316 |
| 1317 if self.outdir is None: |
| 1318 self.outdir = self.orig_cwd |
| 1319 elif not os.path.isabs(self.outdir): |
| 1320 self.outdir = os.path.join(self.orig_cwd, self.outdir) |
| 1321 |
| 1322 if self.scons is None: |
| 1323 self.scons = self.scons_path(self.orig_cwd) |
| 1324 |
| 1325 if self.scons_lib_dir is None: |
| 1326 self.scons_lib_dir = self.scons_lib_dir_path(self.orig_cwd) |
| 1327 |
| 1328 if self.scons_wrapper is None: |
| 1329 self.scons_wrapper = self.scons |
| 1330 |
| 1331 if not run_number: |
| 1332 run_number = self.find_next_run_number(self.outdir, self.prefix) |
| 1333 |
| 1334 self.run_number = str(run_number) |
| 1335 |
| 1336 self.prefix_run = self.prefix + '-%03d' % run_number |
| 1337 |
| 1338 if self.targets0 is None: |
| 1339 self.targets0 = self.startup_targets |
| 1340 if self.targets1 is None: |
| 1341 self.targets1 = self.targets |
| 1342 if self.targets2 is None: |
| 1343 self.targets2 = self.targets |
| 1344 |
| 1345 self.tmpdir = make_temp_file(prefix = self.name + '-') |
| 1346 |
| 1347 commands.extend([ |
| 1348 'mkdir %(tmpdir)s', |
| 1349 |
| 1350 (os.chdir, 'cd %%s', self.tmpdir), |
| 1351 ]) |
| 1352 |
| 1353 for archive in archive_list: |
| 1354 if not os.path.isabs(archive): |
| 1355 archive = os.path.join(self.orig_cwd, archive) |
| 1356 if os.path.isdir(archive): |
| 1357 dest = os.path.split(archive)[1] |
| 1358 commands.append((shutil.copytree, 'cp -r %%s %%s', archive, dest
)) |
| 1359 else: |
| 1360 suffix = self.archive_splitext(archive)[1] |
| 1361 unpack_command = self.unpack_map.get(suffix) |
| 1362 if not unpack_command: |
| 1363 dest = os.path.split(archive)[1] |
| 1364 commands.append((shutil.copyfile, 'cp %%s %%s', archive, des
t)) |
| 1365 else: |
| 1366 commands.append(unpack_command + (archive,)) |
| 1367 |
| 1368 commands.extend([ |
| 1369 (os.chdir, 'cd %%s', self.subdir), |
| 1370 ]) |
| 1371 |
| 1372 commands.extend(self.initial_commands) |
| 1373 |
| 1374 commands.extend([ |
| 1375 (lambda: read_tree('.'), |
| 1376 'find * -type f | xargs cat > /dev/null'), |
| 1377 |
| 1378 (self.set_env, 'export %%s=%%s', |
| 1379 'SCONS_LIB_DIR', self.scons_lib_dir), |
| 1380 |
| 1381 '%(python)s %(scons_wrapper)s --version', |
| 1382 ]) |
| 1383 |
| 1384 index = 0 |
| 1385 for run_command in self.run_commands: |
| 1386 setattr(self, 'prof%d' % index, self.profile_name(index)) |
| 1387 c = ( |
| 1388 self.log_execute, |
| 1389 self.log_display, |
| 1390 run_command, |
| 1391 self.logfile_name(index), |
| 1392 ) |
| 1393 commands.append(c) |
| 1394 index = index + 1 |
| 1395 |
| 1396 commands.extend([ |
| 1397 (os.chdir, 'cd %%s', self.orig_cwd), |
| 1398 ]) |
| 1399 |
| 1400 if not os.environ.get('PRESERVE'): |
| 1401 commands.extend(removals) |
| 1402 |
| 1403 commands.append((shutil.rmtree, 'rm -rf %%s', self.tmpdir)) |
| 1404 |
| 1405 self.run_command_list(commands, self.__dict__) |
| 1406 |
| 1407 self.scons = save_scons |
| 1408 self.scons_lib_dir = save_scons_lib_dir |
| 1409 self.scons_wrapper = save_scons_wrapper |
| 1410 |
| 1411 # |
| 1412 |
| 1413 def help_time(self): |
| 1414 help = """\ |
| 1415 Usage: scons-time time [OPTIONS] FILE [...] |
| 1416 |
| 1417 -C DIR, --chdir=DIR Change to DIR before looking for files |
| 1418 -f FILE, --file=FILE Read configuration from specified FILE |
| 1419 --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT |
| 1420 -h, --help Print this help and exit |
| 1421 -p STRING, --prefix=STRING Use STRING as log file/profile prefix |
| 1422 -t NUMBER, --tail=NUMBER Only report the last NUMBER files |
| 1423 --which=TIMER Plot timings for TIMER: total, |
| 1424 SConscripts, SCons, commands. |
| 1425 """ |
| 1426 sys.stdout.write(self.outdent(help)) |
| 1427 sys.stdout.flush() |
| 1428 |
| 1429 def do_time(self, argv): |
| 1430 |
| 1431 format = 'ascii' |
| 1432 logfile_path = lambda x: x |
| 1433 tail = None |
| 1434 which = 'total' |
| 1435 |
| 1436 short_opts = '?C:f:hp:t:' |
| 1437 |
| 1438 long_opts = [ |
| 1439 'chdir=', |
| 1440 'file=', |
| 1441 'fmt=', |
| 1442 'format=', |
| 1443 'help', |
| 1444 'prefix=', |
| 1445 'tail=', |
| 1446 'title=', |
| 1447 'which=', |
| 1448 ] |
| 1449 |
| 1450 opts, args = getopt.getopt(argv[1:], short_opts, long_opts) |
| 1451 |
| 1452 for o, a in opts: |
| 1453 if o in ('-C', '--chdir'): |
| 1454 self.chdir = a |
| 1455 elif o in ('-f', '--file'): |
| 1456 self.config_file = a |
| 1457 elif o in ('--fmt', '--format'): |
| 1458 format = a |
| 1459 elif o in ('-?', '-h', '--help'): |
| 1460 self.do_help(['help', 'time']) |
| 1461 sys.exit(0) |
| 1462 elif o in ('-p', '--prefix'): |
| 1463 self.prefix = a |
| 1464 elif o in ('-t', '--tail'): |
| 1465 tail = int(a) |
| 1466 elif o in ('--title',): |
| 1467 self.title = a |
| 1468 elif o in ('--which',): |
| 1469 if not a in self.time_strings.keys(): |
| 1470 sys.stderr.write('%s: time: Unrecognized timer "%s".\n' % (s
elf.name, a)) |
| 1471 sys.stderr.write('%s Type "%s help time" for help.\n' % (se
lf.name_spaces, self.name)) |
| 1472 sys.exit(1) |
| 1473 which = a |
| 1474 |
| 1475 if self.config_file: |
| 1476 HACK_for_exec(open(self.config_file, 'rU').read(), self.__dict__) |
| 1477 |
| 1478 if self.chdir: |
| 1479 os.chdir(self.chdir) |
| 1480 logfile_path = lambda x: os.path.join(self.chdir, x) |
| 1481 |
| 1482 if not args: |
| 1483 |
| 1484 pattern = '%s*.log' % self.prefix |
| 1485 args = self.args_to_files([pattern], tail) |
| 1486 |
| 1487 if not args: |
| 1488 if self.chdir: |
| 1489 directory = self.chdir |
| 1490 else: |
| 1491 directory = os.getcwd() |
| 1492 |
| 1493 sys.stderr.write('%s: time: No arguments specified.\n' % self.na
me) |
| 1494 sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self
.name_spaces, self.prefix, directory)) |
| 1495 sys.stderr.write('%s Type "%s help time" for help.\n' % (self.n
ame_spaces, self.name)) |
| 1496 sys.exit(1) |
| 1497 |
| 1498 else: |
| 1499 |
| 1500 args = self.args_to_files(args, tail) |
| 1501 |
| 1502 cwd_ = os.getcwd() + os.sep |
| 1503 |
| 1504 if format == 'ascii': |
| 1505 |
| 1506 columns = ("Total", "SConscripts", "SCons", "commands") |
| 1507 self.ascii_table(args, columns, self.get_debug_times, logfile_path) |
| 1508 |
| 1509 elif format == 'gnuplot': |
| 1510 |
| 1511 results = self.collect_results(args, self.get_debug_times, |
| 1512 self.time_strings[which]) |
| 1513 |
| 1514 self.gnuplot_results(results, fmt='%s %.6f') |
| 1515 |
| 1516 else: |
| 1517 |
| 1518 sys.stderr.write('%s: time: Unknown format "%s".\n' % (self.name, fo
rmat)) |
| 1519 sys.exit(1) |
| 1520 |
| 1521 if __name__ == '__main__': |
| 1522 opts, args = getopt.getopt(sys.argv[1:], 'h?V', ['help', 'version']) |
| 1523 |
| 1524 ST = SConsTimer() |
| 1525 |
| 1526 for o, a in opts: |
| 1527 if o in ('-?', '-h', '--help'): |
| 1528 ST.do_help(['help']) |
| 1529 sys.exit(0) |
| 1530 elif o in ('-V', '--version'): |
| 1531 sys.stdout.write('scons-time version\n') |
| 1532 sys.exit(0) |
| 1533 |
| 1534 if not args: |
| 1535 sys.stderr.write('Type "%s help" for usage.\n' % ST.name) |
| 1536 sys.exit(1) |
| 1537 |
| 1538 ST.execute_subcommand(args) |
| 1539 |
| 1540 # Local Variables: |
| 1541 # tab-width:4 |
| 1542 # indent-tabs-mode:nil |
| 1543 # End: |
| 1544 # vim: set expandtab tabstop=4 shiftwidth=4: |
OLD | NEW |