~mwinter4/maus/ckov_0_9_3

« back to all changes in this revision

Viewing changes to third_party/scons-2.0.1/bin/scons-time

  • Committer: tunnell
  • Date: 2010-09-30 13:56:05 UTC
  • Revision ID: tunnell@itchy-20100930135605-wxbkfgy75p0sndk3
add third party

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 SCons 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 %(targets0)s',
 
379
        '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targets1)s',
 
380
        '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targets2)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.name_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.name)
 
876
                sys.stderr.write('%s  No %s*.prof files found in "%s".\n' % (self.name_spaces, self.prefix, directory))
 
877
                sys.stderr.write('%s  Type "%s help func" for help.\n' % (self.name_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, format))
 
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' % (self.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.name)
 
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.name_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, format))
 
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' % (self.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.name)
 
1092
            sys.stderr.write('%s       Type "%s help obj" for help.\n' % (self.name_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.name)
 
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.name_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, logfile_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, format))
 
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_spaces, 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 %(svn_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, dest))
 
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' % (self.name, a))
 
1471
                    sys.stderr.write('%s  Type "%s help time" for help.\n' % (self.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.name)
 
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.name_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, format))
 
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: