The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / scons-time.py @ 40c0f755

History | View | Annotate | Download (47.5 KB)

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

    
34
from __future__ import nested_scopes
35

    
36
__revision__ = "src/script/scons-time.py 3842 2008/12/20 22:59:52 scons"
37

    
38
import getopt
39
import glob
40
import os
41
import os.path
42
import re
43
import shutil
44
import string
45
import sys
46
import tempfile
47
import time
48

    
49
try:
50
    False
51
except NameError:
52
    # Pre-2.2 Python has no False keyword.
53
    import __builtin__
54
    __builtin__.False = not 1
55

    
56
try:
57
    True
58
except NameError:
59
    # Pre-2.2 Python has no True keyword.
60
    import __builtin__
61
    __builtin__.True = not 0
62

    
63
def make_temp_file(**kw):
64
    try:
65
        result = tempfile.mktemp(**kw)
66
        try:
67
            result = os.path.realpath(result)
68
        except AttributeError:
69
            # Python 2.1 has no os.path.realpath() method.
70
            pass
71
    except TypeError:
72
        try:
73
            save_template = tempfile.template
74
            prefix = kw['prefix']
75
            del kw['prefix']
76
            tempfile.template = prefix
77
            result = tempfile.mktemp(**kw)
78
        finally:
79
            tempfile.template = save_template
80
    return result
81

    
82
class Plotter:
83
    def increment_size(self, largest):
84
        """
85
        Return the size of each horizontal increment line for a specified
86
        maximum value.  This returns a value that will provide somewhere
87
        between 5 and 9 horizontal lines on the graph, on some set of
88
        boundaries that are multiples of 10/100/1000/etc.
89
        """
90
        i = largest / 5
91
        if not i:
92
            return largest
93
        multiplier = 1
94
        while i >= 10:
95
            i = i / 10
96
            multiplier = multiplier * 10
97
        return i * multiplier
98

    
99
    def max_graph_value(self, largest):
100
        # Round up to next integer.
101
        largest = int(largest) + 1
102
        increment = self.increment_size(largest)
103
        return ((largest + increment - 1) / increment) * increment
104

    
105
class Line:
106
    def __init__(self, points, type, title, label, comment, fmt="%s %s"):
107
        self.points = points
108
        self.type = type
109
        self.title = title
110
        self.label = label
111
        self.comment = comment
112
        self.fmt = fmt
113

    
114
    def print_label(self, inx, x, y):
115
        if self.label:
116
            print 'set label %s "%s" at %s,%s right' % (inx, self.label, x, y)
117

    
118
    def plot_string(self):
119
        if self.title:
120
            title_string = 'title "%s"' % self.title
121
        else:
122
            title_string = 'notitle'
123
        return "'-' %s with lines lt %s" % (title_string, self.type)
124

    
125
    def print_points(self, fmt=None):
126
        if fmt is None:
127
            fmt = self.fmt
128
        if self.comment:
129
            print '# %s' % self.comment
130
        for x, y in self.points:
131
            # If y is None, it usually represents some kind of break
132
            # in the line's index number.  We might want to represent
133
            # this some way rather than just drawing the line straight
134
            # between the two points on either side.
135
            if not y is None:
136
                print fmt % (x, y)
137
        print 'e'
138

    
139
    def get_x_values(self):
140
        return [ p[0] for p in self.points ]
141

    
142
    def get_y_values(self):
143
        return [ p[1] for p in self.points ]
144

    
145
class Gnuplotter(Plotter):
146

    
147
    def __init__(self, title, key_location):
148
        self.lines = []
149
        self.title = title
150
        self.key_location = key_location
151

    
152
    def line(self, points, type, title=None, label=None, comment=None, fmt='%s %s'):
153
        if points:
154
            line = Line(points, type, title, label, comment, fmt)
155
            self.lines.append(line)
156

    
157
    def plot_string(self, line):
158
        return line.plot_string()
159

    
160
    def vertical_bar(self, x, type, label, comment):
161
        if self.get_min_x() <= x and x <= self.get_max_x():
162
            points = [(x, 0), (x, self.max_graph_value(self.get_max_y()))]
163
            self.line(points, type, label, comment)
164

    
165
    def get_all_x_values(self):
166
        result = []
167
        for line in self.lines:
168
            result.extend(line.get_x_values())
169
        return filter(lambda r: not r is None, result)
170

    
171
    def get_all_y_values(self):
172
        result = []
173
        for line in self.lines:
174
            result.extend(line.get_y_values())
175
        return filter(lambda r: not r is None, result)
176

    
177
    def get_min_x(self):
178
        try:
179
            return self.min_x
180
        except AttributeError:
181
            try:
182
                self.min_x = min(self.get_all_x_values())
183
            except ValueError:
184
                self.min_x = 0
185
            return self.min_x
186

    
187
    def get_max_x(self):
188
        try:
189
            return self.max_x
190
        except AttributeError:
191
            try:
192
                self.max_x = max(self.get_all_x_values())
193
            except ValueError:
194
                self.max_x = 0
195
            return self.max_x
196

    
197
    def get_min_y(self):
198
        try:
199
            return self.min_y
200
        except AttributeError:
201
            try:
202
                self.min_y = min(self.get_all_y_values())
203
            except ValueError:
204
                self.min_y = 0
205
            return self.min_y
206

    
207
    def get_max_y(self):
208
        try:
209
            return self.max_y
210
        except AttributeError:
211
            try:
212
                self.max_y = max(self.get_all_y_values())
213
            except ValueError:
214
                self.max_y = 0
215
            return self.max_y
216

    
217
    def draw(self):
218

    
219
        if not self.lines:
220
            return
221

    
222
        if self.title:
223
            print 'set title "%s"' % self.title
224
        print 'set key %s' % self.key_location
225

    
226
        min_y = self.get_min_y()
227
        max_y = self.max_graph_value(self.get_max_y())
228
        range = max_y - min_y
229
        incr = range / 10.0
230
        start = min_y + (max_y / 2.0) + (2.0 * incr)
231
        position = [ start - (i * incr) for i in xrange(5) ]
232

    
233
        inx = 1
234
        for line in self.lines:
235
            line.print_label(inx, line.points[0][0]-1,
236
                             position[(inx-1) % len(position)])
237
            inx += 1
238

    
239
        plot_strings = [ self.plot_string(l) for l in self.lines ]
240
        print 'plot ' + ', \\\n     '.join(plot_strings)
241

    
242
        for line in self.lines:
243
            line.print_points()
244

    
245

    
246

    
247
def untar(fname):
248
    import tarfile
249
    tar = tarfile.open(name=fname, mode='r')
250
    for tarinfo in tar:
251
        tar.extract(tarinfo)
252
    tar.close()
253

    
254
def unzip(fname):
255
    import zipfile
256
    zf = zipfile.ZipFile(fname, 'r')
257
    for name in zf.namelist():
258
        dir = os.path.dirname(name)
259
        try:
260
            os.makedirs(dir)
261
        except:
262
            pass
263
        open(name, 'w').write(zf.read(name))
264

    
265
def read_tree(dir):
266
    def read_files(arg, dirname, fnames):
267
        for fn in fnames:
268
            fn = os.path.join(dirname, fn)
269
            if os.path.isfile(fn):
270
                open(fn, 'rb').read()
271
    os.path.walk('.', read_files, None)
272

    
273
def redirect_to_file(command, log):
274
    return '%s > %s 2>&1' % (command, log)
275

    
276
def tee_to_file(command, log):
277
    return '%s 2>&1 | tee %s' % (command, log)
278

    
279

    
280
    
281
class SConsTimer:
282
    """
283
    Usage: scons-time SUBCOMMAND [ARGUMENTS]
284
    Type "scons-time help SUBCOMMAND" for help on a specific subcommand.
285

286
    Available subcommands:
287
        func            Extract test-run data for a function
288
        help            Provides help
289
        mem             Extract --debug=memory data from test runs
290
        obj             Extract --debug=count data from test runs
291
        time            Extract --debug=time data from test runs
292
        run             Runs a test configuration
293
    """
294

    
295
    name = 'scons-time'
296
    name_spaces = ' '*len(name)
297

    
298
    def makedict(**kw):
299
        return kw
300

    
301
    default_settings = makedict(
302
        aegis               = 'aegis',
303
        aegis_project       = None,
304
        chdir               = None,
305
        config_file         = None,
306
        initial_commands    = [],
307
        key_location        = 'bottom left',
308
        orig_cwd            = os.getcwd(),
309
        outdir              = None,
310
        prefix              = '',
311
        python              = '"%s"' % sys.executable,
312
        redirect            = redirect_to_file,
313
        scons               = None,
314
        scons_flags         = '--debug=count --debug=memory --debug=time --debug=memoizer',
315
        scons_lib_dir       = None,
316
        scons_wrapper       = None,
317
        startup_targets     = '--help',
318
        subdir              = None,
319
        subversion_url      = None,
320
        svn                 = 'svn',
321
        svn_co_flag         = '-q',
322
        tar                 = 'tar',
323
        targets             = '',
324
        targets0            = None,
325
        targets1            = None,
326
        targets2            = None,
327
        title               = None,
328
        unzip               = 'unzip',
329
        verbose             = False,
330
        vertical_bars       = [],
331

    
332
        unpack_map = {
333
            '.tar.gz'       : (untar,   '%(tar)s xzf %%s'),
334
            '.tgz'          : (untar,   '%(tar)s xzf %%s'),
335
            '.tar'          : (untar,   '%(tar)s xf %%s'),
336
            '.zip'          : (unzip,   '%(unzip)s %%s'),
337
        },
338
    )
339

    
340
    run_titles = [
341
        'Startup',
342
        'Full build',
343
        'Up-to-date build',
344
    ]
345

    
346
    run_commands = [
347
        '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof0)s %(targets0)s',
348
        '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targets1)s',
349
        '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targets2)s',
350
    ]
351

    
352
    stages = [
353
        'pre-read',
354
        'post-read',
355
        'pre-build',
356
        'post-build',
357
    ]
358

    
359
    stage_strings = {
360
        'pre-read'      : 'Memory before reading SConscript files:',
361
        'post-read'     : 'Memory after reading SConscript files:',
362
        'pre-build'     : 'Memory before building targets:',
363
        'post-build'    : 'Memory after building targets:',
364
    }
365

    
366
    memory_string_all = 'Memory '
367

    
368
    default_stage = stages[-1]
369

    
370
    time_strings = {
371
        'total'         : 'Total build time',
372
        'SConscripts'   : 'Total SConscript file execution time',
373
        'SCons'         : 'Total SCons execution time',
374
        'commands'      : 'Total command execution time',
375
    }
376
    
377
    time_string_all = 'Total .* time'
378

    
379
    #
380

    
381
    def __init__(self):
382
        self.__dict__.update(self.default_settings)
383

    
384
    # Functions for displaying and executing commands.
385

    
386
    def subst(self, x, dictionary):
387
        try:
388
            return x % dictionary
389
        except TypeError:
390
            # x isn't a string (it's probably a Python function),
391
            # so just return it.
392
            return x
393

    
394
    def subst_variables(self, command, dictionary):
395
        """
396
        Substitutes (via the format operator) the values in the specified
397
        dictionary into the specified command.
398

399
        The command can be an (action, string) tuple.  In all cases, we
400
        perform substitution on strings and don't worry if something isn't
401
        a string.  (It's probably a Python function to be executed.)
402
        """
403
        try:
404
            command + ''
405
        except TypeError:
406
            action = command[0]
407
            string = command[1]
408
            args = command[2:]
409
        else:
410
            action = command
411
            string = action
412
            args = (())
413
        action = self.subst(action, dictionary)
414
        string = self.subst(string, dictionary)
415
        return (action, string, args)
416

    
417
    def _do_not_display(self, msg, *args):
418
        pass
419

    
420
    def display(self, msg, *args):
421
        """
422
        Displays the specified message.
423

424
        Each message is prepended with a standard prefix of our name
425
        plus the time.
426
        """
427
        if callable(msg):
428
            msg = msg(*args)
429
        else:
430
            msg = msg % args
431
        if msg is None:
432
            return
433
        fmt = '%s[%s]: %s\n'
434
        sys.stdout.write(fmt % (self.name, time.strftime('%H:%M:%S'), msg))
435

    
436
    def _do_not_execute(self, action, *args):
437
        pass
438

    
439
    def execute(self, action, *args):
440
        """
441
        Executes the specified action.
442

443
        The action is called if it's a callable Python function, and
444
        otherwise passed to os.system().
445
        """
446
        if callable(action):
447
            action(*args)
448
        else:
449
            os.system(action % args)
450

    
451
    def run_command_list(self, commands, dict):
452
        """
453
        Executes a list of commands, substituting values from the
454
        specified dictionary.
455
        """
456
        commands = [ self.subst_variables(c, dict) for c in commands ]
457
        for action, string, args in commands:
458
            self.display(string, *args)
459
            sys.stdout.flush()
460
            status = self.execute(action, *args)
461
            if status:
462
                sys.exit(status)
463

    
464
    def log_display(self, command, log):
465
        command = self.subst(command, self.__dict__)
466
        if log:
467
            command = self.redirect(command, log)
468
        return command
469

    
470
    def log_execute(self, command, log):
471
        command = self.subst(command, self.__dict__)
472
        output = os.popen(command).read()
473
        if self.verbose:
474
            sys.stdout.write(output)
475
        open(log, 'wb').write(output)
476

    
477
    #
478

    
479
    def archive_splitext(self, path):
480
        """
481
        Splits an archive name into a filename base and extension.
482

483
        This is like os.path.splitext() (which it calls) except that it
484
        also looks for '.tar.gz' and treats it as an atomic extensions.
485
        """
486
        if path.endswith('.tar.gz'):
487
            return path[:-7], path[-7:]
488
        else:
489
            return os.path.splitext(path)
490

    
491
    def args_to_files(self, args, tail=None):
492
        """
493
        Takes a list of arguments, expands any glob patterns, and
494
        returns the last "tail" files from the list.
495
        """
496
        files = []
497
        for a in args:
498
            g = glob.glob(a)
499
            g.sort()
500
            files.extend(g)
501

    
502
        if tail:
503
            files = files[-tail:]
504

    
505
        return files
506

    
507
    def ascii_table(self, files, columns,
508
                    line_function, file_function=lambda x: x,
509
                    *args, **kw):
510

    
511
        header_fmt = ' '.join(['%12s'] * len(columns))
512
        line_fmt = header_fmt + '    %s'
513

    
514
        print header_fmt % columns
515

    
516
        for file in files:
517
            t = line_function(file, *args, **kw)
518
            if t is None:
519
                t = []
520
            diff = len(columns) - len(t)
521
            if diff > 0:
522
                t += [''] * diff
523
            t.append(file_function(file))
524
            print line_fmt % tuple(t)
525

    
526
    def collect_results(self, files, function, *args, **kw):
527
        results = {}
528

    
529
        for file in files:
530
            base = os.path.splitext(file)[0]
531
            run, index = string.split(base, '-')[-2:]
532

    
533
            run = int(run)
534
            index = int(index)
535

    
536
            value = function(file, *args, **kw)
537

    
538
            try:
539
                r = results[index]
540
            except KeyError:
541
                r = []
542
                results[index] = r
543
            r.append((run, value))
544

    
545
        return results
546

    
547
    def doc_to_help(self, obj):
548
        """
549
        Translates an object's __doc__ string into help text.
550

551
        This strips a consistent number of spaces from each line in the
552
        help text, essentially "outdenting" the text to the left-most
553
        column.
554
        """
555
        doc = obj.__doc__
556
        if doc is None:
557
            return ''
558
        return self.outdent(doc)
559

    
560
    def find_next_run_number(self, dir, prefix):
561
        """
562
        Returns the next run number in a directory for the specified prefix.
563

564
        Examines the contents the specified directory for files with the
565
        specified prefix, extracts the run numbers from each file name,
566
        and returns the next run number after the largest it finds.
567
        """
568
        x = re.compile(re.escape(prefix) + '-([0-9]+).*')
569
        matches = map(lambda e, x=x: x.match(e), os.listdir(dir))
570
        matches = filter(None, matches)
571
        if not matches:
572
            return 0
573
        run_numbers = map(lambda m: int(m.group(1)), matches)
574
        return int(max(run_numbers)) + 1
575

    
576
    def gnuplot_results(self, results, fmt='%s %.3f'):
577
        """
578
        Prints out a set of results in Gnuplot format.
579
        """
580
        gp = Gnuplotter(self.title, self.key_location)
581

    
582
        indices = results.keys()
583
        indices.sort()
584

    
585
        for i in indices:
586
            try:
587
                t = self.run_titles[i]
588
            except IndexError:
589
                t = '??? %s ???' % i
590
            results[i].sort()
591
            gp.line(results[i], i+1, t, None, t, fmt=fmt)
592

    
593
        for bar_tuple in self.vertical_bars:
594
            try:
595
                x, type, label, comment = bar_tuple
596
            except ValueError:
597
                x, type, label = bar_tuple
598
                comment = label
599
            gp.vertical_bar(x, type, label, comment)
600

    
601
        gp.draw()
602

    
603
    def logfile_name(self, invocation):
604
        """
605
        Returns the absolute path of a log file for the specificed
606
        invocation number.
607
        """
608
        name = self.prefix_run + '-%d.log' % invocation
609
        return os.path.join(self.outdir, name)
610

    
611
    def outdent(self, s):
612
        """
613
        Strip as many spaces from each line as are found at the beginning
614
        of the first line in the list.
615
        """
616
        lines = s.split('\n')
617
        if lines[0] == '':
618
            lines = lines[1:]
619
        spaces = re.match(' *', lines[0]).group(0)
620
        def strip_initial_spaces(l, s=spaces):
621
            if l.startswith(spaces):
622
                l = l[len(spaces):]
623
            return l
624
        return '\n'.join([ strip_initial_spaces(l) for l in lines ]) + '\n'
625

    
626
    def profile_name(self, invocation):
627
        """
628
        Returns the absolute path of a profile file for the specified
629
        invocation number.
630
        """
631
        name = self.prefix_run + '-%d.prof' % invocation
632
        return os.path.join(self.outdir, name)
633

    
634
    def set_env(self, key, value):
635
        os.environ[key] = value
636

    
637
    #
638

    
639
    def get_debug_times(self, file, time_string=None):
640
        """
641
        Fetch times from the --debug=time strings in the specified file.
642
        """
643
        if time_string is None:
644
            search_string = self.time_string_all
645
        else:
646
            search_string = time_string
647
        contents = open(file).read()
648
        if not contents:
649
            sys.stderr.write('file %s has no contents!\n' % repr(file))
650
            return None
651
        result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:]
652
        result = [ float(r) for r in result ]
653
        if not time_string is None:
654
            try:
655
                result = result[0]
656
            except IndexError:
657
                sys.stderr.write('file %s has no results!\n' % repr(file))
658
                return None
659
        return result
660

    
661
    def get_function_profile(self, file, function):
662
        """
663
        Returns the file, line number, function name, and cumulative time.
664
        """
665
        try:
666
            import pstats
667
        except ImportError, e:
668
            sys.stderr.write('%s: func: %s\n' % (self.name, e))
669
            sys.stderr.write('%s  This version of Python is missing the profiler.\n' % self.name_spaces)
670
            sys.stderr.write('%s  Cannot use the "func" subcommand.\n' % self.name_spaces)
671
            sys.exit(1)
672
        statistics = pstats.Stats(file).stats
673
        matches = [ e for e in statistics.items() if e[0][2] == function ]
674
        r = matches[0]
675
        return r[0][0], r[0][1], r[0][2], r[1][3]
676

    
677
    def get_function_time(self, file, function):
678
        """
679
        Returns just the cumulative time for the specified function.
680
        """
681
        return self.get_function_profile(file, function)[3]
682

    
683
    def get_memory(self, file, memory_string=None):
684
        """
685
        Returns a list of integers of the amount of memory used.  The
686
        default behavior is to return all the stages.
687
        """
688
        if memory_string is None:
689
            search_string = self.memory_string_all
690
        else:
691
            search_string = memory_string
692
        lines = open(file).readlines()
693
        lines = [ l for l in lines if l.startswith(search_string) ][-4:]
694
        result = [ int(l.split()[-1]) for l in lines[-4:] ]
695
        if len(result) == 1:
696
            result = result[0]
697
        return result
698

    
699
    def get_object_counts(self, file, object_name, index=None):
700
        """
701
        Returns the counts of the specified object_name.
702
        """
703
        object_string = ' ' + object_name + '\n'
704
        lines = open(file).readlines()
705
        line = [ l for l in lines if l.endswith(object_string) ][0]
706
        result = [ int(field) for field in line.split()[:4] ]
707
        if not index is None:
708
            result = result[index]
709
        return result
710

    
711
    #
712

    
713
    command_alias = {}
714

    
715
    def execute_subcommand(self, argv):
716
        """
717
        Executes the do_*() function for the specified subcommand (argv[0]).
718
        """
719
        if not argv:
720
            return
721
        cmdName = self.command_alias.get(argv[0], argv[0])
722
        try:
723
            func = getattr(self, 'do_' + cmdName)
724
        except AttributeError:
725
            return self.default(argv)
726
        try:
727
            return func(argv)
728
        except TypeError, e:
729
            sys.stderr.write("%s %s: %s\n" % (self.name, cmdName, e))
730
            import traceback
731
            traceback.print_exc(file=sys.stderr)
732
            sys.stderr.write("Try '%s help %s'\n" % (self.name, cmdName))
733

    
734
    def default(self, argv):
735
        """
736
        The default behavior for an unknown subcommand.  Prints an
737
        error message and exits.
738
        """
739
        sys.stderr.write('%s: Unknown subcommand "%s".\n' % (self.name, argv[0]))
740
        sys.stderr.write('Type "%s help" for usage.\n' % self.name)
741
        sys.exit(1)
742

    
743
    #
744

    
745
    def do_help(self, argv):
746
        """
747
        """
748
        if argv[1:]:
749
            for arg in argv[1:]:
750
                try:
751
                    func = getattr(self, 'do_' + arg)
752
                except AttributeError:
753
                    sys.stderr.write('%s: No help for "%s"\n' % (self.name, arg))
754
                else:
755
                    try:
756
                        help = getattr(self, 'help_' + arg)
757
                    except AttributeError:
758
                        sys.stdout.write(self.doc_to_help(func))
759
                        sys.stdout.flush()
760
                    else:
761
                        help()
762
        else:
763
            doc = self.doc_to_help(self.__class__)
764
            if doc:
765
                sys.stdout.write(doc)
766
            sys.stdout.flush()
767
            return None
768

    
769
    #
770

    
771
    def help_func(self):
772
        help = """\
773
        Usage: scons-time func [OPTIONS] FILE [...]
774

775
          -C DIR, --chdir=DIR           Change to DIR before looking for files
776
          -f FILE, --file=FILE          Read configuration from specified FILE
777
          --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
778
          --func=NAME, --function=NAME  Report time for function NAME
779
          -h, --help                    Print this help and exit
780
          -p STRING, --prefix=STRING    Use STRING as log file/profile prefix
781
          -t NUMBER, --tail=NUMBER      Only report the last NUMBER files
782
          --title=TITLE                 Specify the output plot TITLE
783
        """
784
        sys.stdout.write(self.outdent(help))
785
        sys.stdout.flush()
786

    
787
    def do_func(self, argv):
788
        """
789
        """
790
        format = 'ascii'
791
        function_name = '_main'
792
        tail = None
793

    
794
        short_opts = '?C:f:hp:t:'
795

    
796
        long_opts = [
797
            'chdir=',
798
            'file=',
799
            'fmt=',
800
            'format=',
801
            'func=',
802
            'function=',
803
            'help',
804
            'prefix=',
805
            'tail=',
806
            'title=',
807
        ]
808

    
809
        opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
810

    
811
        for o, a in opts:
812
            if o in ('-C', '--chdir'):
813
                self.chdir = a
814
            elif o in ('-f', '--file'):
815
                self.config_file = a
816
            elif o in ('--fmt', '--format'):
817
                format = a
818
            elif o in ('--func', '--function'):
819
                function_name = a
820
            elif o in ('-?', '-h', '--help'):
821
                self.do_help(['help', 'func'])
822
                sys.exit(0)
823
            elif o in ('--max',):
824
                max_time = int(a)
825
            elif o in ('-p', '--prefix'):
826
                self.prefix = a
827
            elif o in ('-t', '--tail'):
828
                tail = int(a)
829
            elif o in ('--title',):
830
                self.title = a
831

    
832
        if self.config_file:
833
            execfile(self.config_file, self.__dict__)
834

    
835
        if self.chdir:
836
            os.chdir(self.chdir)
837

    
838
        if not args:
839

    
840
            pattern = '%s*.prof' % self.prefix
841
            args = self.args_to_files([pattern], tail)
842

    
843
            if not args:
844
                if self.chdir:
845
                    directory = self.chdir
846
                else:
847
                    directory = os.getcwd()
848

    
849
                sys.stderr.write('%s: func: No arguments specified.\n' % self.name)
850
                sys.stderr.write('%s  No %s*.prof files found in "%s".\n' % (self.name_spaces, self.prefix, directory))
851
                sys.stderr.write('%s  Type "%s help func" for help.\n' % (self.name_spaces, self.name))
852
                sys.exit(1)
853

    
854
        else:
855

    
856
            args = self.args_to_files(args, tail)
857

    
858
        cwd_ = os.getcwd() + os.sep
859

    
860
        if format == 'ascii':
861

    
862
            def print_function_timing(file, func):
863
                try:
864
                    f, line, func, time = self.get_function_profile(file, func)
865
                except ValueError, e:
866
                    sys.stderr.write("%s: func: %s: %s\n" % (self.name, file, e))
867
                else:
868
                    if f.startswith(cwd_):
869
                        f = f[len(cwd_):]
870
                    print "%.3f %s:%d(%s)" % (time, f, line, func)
871

    
872
            for file in args:
873
                print_function_timing(file, function_name)
874

    
875
        elif format == 'gnuplot':
876

    
877
            results = self.collect_results(args, self.get_function_time,
878
                                           function_name)
879

    
880
            self.gnuplot_results(results)
881

    
882
        else:
883

    
884
            sys.stderr.write('%s: func: Unknown format "%s".\n' % (self.name, format))
885
            sys.exit(1)
886

    
887
    #
888

    
889
    def help_mem(self):
890
        help = """\
891
        Usage: scons-time mem [OPTIONS] FILE [...]
892

893
          -C DIR, --chdir=DIR           Change to DIR before looking for files
894
          -f FILE, --file=FILE          Read configuration from specified FILE
895
          --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
896
          -h, --help                    Print this help and exit
897
          -p STRING, --prefix=STRING    Use STRING as log file/profile prefix
898
          --stage=STAGE                 Plot memory at the specified stage:
899
                                          pre-read, post-read, pre-build,
900
                                          post-build (default: post-build)
901
          -t NUMBER, --tail=NUMBER      Only report the last NUMBER files
902
          --title=TITLE                 Specify the output plot TITLE
903
        """
904
        sys.stdout.write(self.outdent(help))
905
        sys.stdout.flush()
906

    
907
    def do_mem(self, argv):
908

    
909
        format = 'ascii'
910
        logfile_path = lambda x: x
911
        stage = self.default_stage
912
        tail = None
913

    
914
        short_opts = '?C:f:hp:t:'
915

    
916
        long_opts = [
917
            'chdir=',
918
            'file=',
919
            'fmt=',
920
            'format=',
921
            'help',
922
            'prefix=',
923
            'stage=',
924
            'tail=',
925
            'title=',
926
        ]
927

    
928
        opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
929

    
930
        for o, a in opts:
931
            if o in ('-C', '--chdir'):
932
                self.chdir = a
933
            elif o in ('-f', '--file'):
934
                self.config_file = a
935
            elif o in ('--fmt', '--format'):
936
                format = a
937
            elif o in ('-?', '-h', '--help'):
938
                self.do_help(['help', 'mem'])
939
                sys.exit(0)
940
            elif o in ('-p', '--prefix'):
941
                self.prefix = a
942
            elif o in ('--stage',):
943
                if not a in self.stages:
944
                    sys.stderr.write('%s: mem: Unrecognized stage "%s".\n' % (self.name, a))
945
                    sys.exit(1)
946
                stage = a
947
            elif o in ('-t', '--tail'):
948
                tail = int(a)
949
            elif o in ('--title',):
950
                self.title = a
951

    
952
        if self.config_file:
953
            execfile(self.config_file, self.__dict__)
954

    
955
        if self.chdir:
956
            os.chdir(self.chdir)
957
            logfile_path = lambda x, c=self.chdir: os.path.join(c, x)
958

    
959
        if not args:
960

    
961
            pattern = '%s*.log' % self.prefix
962
            args = self.args_to_files([pattern], tail)
963

    
964
            if not args:
965
                if self.chdir:
966
                    directory = self.chdir
967
                else:
968
                    directory = os.getcwd()
969

    
970
                sys.stderr.write('%s: mem: No arguments specified.\n' % self.name)
971
                sys.stderr.write('%s  No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory))
972
                sys.stderr.write('%s  Type "%s help mem" for help.\n' % (self.name_spaces, self.name))
973
                sys.exit(1)
974

    
975
        else:
976

    
977
            args = self.args_to_files(args, tail)
978

    
979
        cwd_ = os.getcwd() + os.sep
980

    
981
        if format == 'ascii':
982

    
983
            self.ascii_table(args, tuple(self.stages), self.get_memory, logfile_path)
984

    
985
        elif format == 'gnuplot':
986

    
987
            results = self.collect_results(args, self.get_memory,
988
                                           self.stage_strings[stage])
989

    
990
            self.gnuplot_results(results)
991

    
992
        else:
993

    
994
            sys.stderr.write('%s: mem: Unknown format "%s".\n' % (self.name, format))
995
            sys.exit(1)
996

    
997
        return 0
998

    
999
    #
1000

    
1001
    def help_obj(self):
1002
        help = """\
1003
        Usage: scons-time obj [OPTIONS] OBJECT FILE [...]
1004

1005
          -C DIR, --chdir=DIR           Change to DIR before looking for files
1006
          -f FILE, --file=FILE          Read configuration from specified FILE
1007
          --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
1008
          -h, --help                    Print this help and exit
1009
          -p STRING, --prefix=STRING    Use STRING as log file/profile prefix
1010
          --stage=STAGE                 Plot memory at the specified stage:
1011
                                          pre-read, post-read, pre-build,
1012
                                          post-build (default: post-build)
1013
          -t NUMBER, --tail=NUMBER      Only report the last NUMBER files
1014
          --title=TITLE                 Specify the output plot TITLE
1015
        """
1016
        sys.stdout.write(self.outdent(help))
1017
        sys.stdout.flush()
1018

    
1019
    def do_obj(self, argv):
1020

    
1021
        format = 'ascii'
1022
        logfile_path = lambda x: x
1023
        stage = self.default_stage
1024
        tail = None
1025

    
1026
        short_opts = '?C:f:hp:t:'
1027

    
1028
        long_opts = [
1029
            'chdir=',
1030
            'file=',
1031
            'fmt=',
1032
            'format=',
1033
            'help',
1034
            'prefix=',
1035
            'stage=',
1036
            'tail=',
1037
            'title=',
1038
        ]
1039

    
1040
        opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
1041

    
1042
        for o, a in opts:
1043
            if o in ('-C', '--chdir'):
1044
                self.chdir = a
1045
            elif o in ('-f', '--file'):
1046
                self.config_file = a
1047
            elif o in ('--fmt', '--format'):
1048
                format = a
1049
            elif o in ('-?', '-h', '--help'):
1050
                self.do_help(['help', 'obj'])
1051
                sys.exit(0)
1052
            elif o in ('-p', '--prefix'):
1053
                self.prefix = a
1054
            elif o in ('--stage',):
1055
                if not a in self.stages:
1056
                    sys.stderr.write('%s: obj: Unrecognized stage "%s".\n' % (self.name, a))
1057
                    sys.stderr.write('%s       Type "%s help obj" for help.\n' % (self.name_spaces, self.name))
1058
                    sys.exit(1)
1059
                stage = a
1060
            elif o in ('-t', '--tail'):
1061
                tail = int(a)
1062
            elif o in ('--title',):
1063
                self.title = a
1064

    
1065
        if not args:
1066
            sys.stderr.write('%s: obj: Must specify an object name.\n' % self.name)
1067
            sys.stderr.write('%s       Type "%s help obj" for help.\n' % (self.name_spaces, self.name))
1068
            sys.exit(1)
1069

    
1070
        object_name = args.pop(0)
1071

    
1072
        if self.config_file:
1073
            execfile(self.config_file, self.__dict__)
1074

    
1075
        if self.chdir:
1076
            os.chdir(self.chdir)
1077
            logfile_path = lambda x, c=self.chdir: os.path.join(c, x)
1078

    
1079
        if not args:
1080

    
1081
            pattern = '%s*.log' % self.prefix
1082
            args = self.args_to_files([pattern], tail)
1083

    
1084
            if not args:
1085
                if self.chdir:
1086
                    directory = self.chdir
1087
                else:
1088
                    directory = os.getcwd()
1089

    
1090
                sys.stderr.write('%s: obj: No arguments specified.\n' % self.name)
1091
                sys.stderr.write('%s  No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory))
1092
                sys.stderr.write('%s  Type "%s help obj" for help.\n' % (self.name_spaces, self.name))
1093
                sys.exit(1)
1094

    
1095
        else:
1096

    
1097
            args = self.args_to_files(args, tail)
1098

    
1099
        cwd_ = os.getcwd() + os.sep
1100

    
1101
        if format == 'ascii':
1102

    
1103
            self.ascii_table(args, tuple(self.stages), self.get_object_counts, logfile_path, object_name)
1104

    
1105
        elif format == 'gnuplot':
1106

    
1107
            stage_index = 0
1108
            for s in self.stages:
1109
                if stage == s:
1110
                    break
1111
                stage_index = stage_index + 1
1112

    
1113
            results = self.collect_results(args, self.get_object_counts,
1114
                                           object_name, stage_index)
1115

    
1116
            self.gnuplot_results(results)
1117

    
1118
        else:
1119

    
1120
            sys.stderr.write('%s: obj: Unknown format "%s".\n' % (self.name, format))
1121
            sys.exit(1)
1122

    
1123
        return 0
1124

    
1125
    #
1126

    
1127
    def help_run(self):
1128
        help = """\
1129
        Usage: scons-time run [OPTIONS] [FILE ...]
1130

1131
          --aegis=PROJECT               Use SCons from the Aegis PROJECT
1132
          --chdir=DIR                   Name of unpacked directory for chdir
1133
          -f FILE, --file=FILE          Read configuration from specified FILE
1134
          -h, --help                    Print this help and exit
1135
          -n, --no-exec                 No execute, just print command lines
1136
          --number=NUMBER               Put output in files for run NUMBER
1137
          --outdir=OUTDIR               Put output files in OUTDIR
1138
          -p STRING, --prefix=STRING    Use STRING as log file/profile prefix
1139
          --python=PYTHON               Time using the specified PYTHON
1140
          -q, --quiet                   Don't print command lines
1141
          --scons=SCONS                 Time using the specified SCONS
1142
          --svn=URL, --subversion=URL   Use SCons from Subversion URL
1143
          -v, --verbose                 Display output of commands
1144
        """
1145
        sys.stdout.write(self.outdent(help))
1146
        sys.stdout.flush()
1147

    
1148
    def do_run(self, argv):
1149
        """
1150
        """
1151
        run_number_list = [None]
1152

    
1153
        short_opts = '?f:hnp:qs:v'
1154

    
1155
        long_opts = [
1156
            'aegis=',
1157
            'file=',
1158
            'help',
1159
            'no-exec',
1160
            'number=',
1161
            'outdir=',
1162
            'prefix=',
1163
            'python=',
1164
            'quiet',
1165
            'scons=',
1166
            'svn=',
1167
            'subdir=',
1168
            'subversion=',
1169
            'verbose',
1170
        ]
1171

    
1172
        opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
1173

    
1174
        for o, a in opts:
1175
            if o in ('--aegis',):
1176
                self.aegis_project = a
1177
            elif o in ('-f', '--file'):
1178
                self.config_file = a
1179
            elif o in ('-?', '-h', '--help'):
1180
                self.do_help(['help', 'run'])
1181
                sys.exit(0)
1182
            elif o in ('-n', '--no-exec'):
1183
                self.execute = self._do_not_execute
1184
            elif o in ('--number',):
1185
                run_number_list = self.split_run_numbers(a)
1186
            elif o in ('--outdir',):
1187
                self.outdir = a
1188
            elif o in ('-p', '--prefix'):
1189
                self.prefix = a
1190
            elif o in ('--python',):
1191
                self.python = a
1192
            elif o in ('-q', '--quiet'):
1193
                self.display = self._do_not_display
1194
            elif o in ('-s', '--subdir'):
1195
                self.subdir = a
1196
            elif o in ('--scons',):
1197
                self.scons = a
1198
            elif o in ('--svn', '--subversion'):
1199
                self.subversion_url = a
1200
            elif o in ('-v', '--verbose'):
1201
                self.redirect = tee_to_file
1202
                self.verbose = True
1203
                self.svn_co_flag = ''
1204

    
1205
        if not args and not self.config_file:
1206
            sys.stderr.write('%s: run: No arguments or -f config file specified.\n' % self.name)
1207
            sys.stderr.write('%s  Type "%s help run" for help.\n' % (self.name_spaces, self.name))
1208
            sys.exit(1)
1209

    
1210
        if self.config_file:
1211
            execfile(self.config_file, self.__dict__)
1212

    
1213
        if args:
1214
            self.archive_list = args
1215

    
1216
        archive_file_name = os.path.split(self.archive_list[0])[1]
1217

    
1218
        if not self.subdir:
1219
            self.subdir = self.archive_splitext(archive_file_name)[0]
1220

    
1221
        if not self.prefix:
1222
            self.prefix = self.archive_splitext(archive_file_name)[0]
1223

    
1224
        prepare = None
1225
        if self.subversion_url:
1226
            prepare = self.prep_subversion_run
1227
        elif self.aegis_project:
1228
            prepare = self.prep_aegis_run
1229

    
1230
        for run_number in run_number_list:
1231
            self.individual_run(run_number, self.archive_list, prepare)
1232

    
1233
    def split_run_numbers(self, s):
1234
        result = []
1235
        for n in s.split(','):
1236
            try:
1237
                x, y = n.split('-')
1238
            except ValueError:
1239
                result.append(int(n))
1240
            else:
1241
                result.extend(range(int(x), int(y)+1))
1242
        return result
1243

    
1244
    def scons_path(self, dir):
1245
        return os.path.join(dir, 'src', 'script', 'scons.py')
1246

    
1247
    def scons_lib_dir_path(self, dir):
1248
        return os.path.join(dir, 'src', 'engine')
1249

    
1250
    def prep_aegis_run(self, commands, removals):
1251
        self.aegis_tmpdir = make_temp_file(prefix = self.name + '-aegis-')
1252
        removals.append((shutil.rmtree, 'rm -rf %%s', self.aegis_tmpdir))
1253

    
1254
        self.aegis_parent_project = os.path.splitext(self.aegis_project)[0]
1255
        self.scons = self.scons_path(self.aegis_tmpdir)
1256
        self.scons_lib_dir = self.scons_lib_dir_path(self.aegis_tmpdir)
1257

    
1258
        commands.extend([
1259
            'mkdir %(aegis_tmpdir)s',
1260
            (lambda: os.chdir(self.aegis_tmpdir), 'cd %(aegis_tmpdir)s'),
1261
            '%(aegis)s -cp -ind -p %(aegis_parent_project)s .',
1262
            '%(aegis)s -cp -ind -p %(aegis_project)s -delta %(run_number)s .',
1263
        ])
1264

    
1265
    def prep_subversion_run(self, commands, removals):
1266
        self.svn_tmpdir = make_temp_file(prefix = self.name + '-svn-')
1267
        removals.append((shutil.rmtree, 'rm -rf %%s', self.svn_tmpdir))
1268

    
1269
        self.scons = self.scons_path(self.svn_tmpdir)
1270
        self.scons_lib_dir = self.scons_lib_dir_path(self.svn_tmpdir)
1271

    
1272
        commands.extend([
1273
            'mkdir %(svn_tmpdir)s',
1274
            '%(svn)s co %(svn_co_flag)s -r %(run_number)s %(subversion_url)s %(svn_tmpdir)s',
1275
        ])
1276

    
1277
    def individual_run(self, run_number, archive_list, prepare=None):
1278
        """
1279
        Performs an individual run of the default SCons invocations.
1280
        """
1281

    
1282
        commands = []
1283
        removals = []
1284

    
1285
        if prepare:
1286
            prepare(commands, removals)
1287

    
1288
        save_scons              = self.scons
1289
        save_scons_wrapper      = self.scons_wrapper
1290
        save_scons_lib_dir      = self.scons_lib_dir
1291

    
1292
        if self.outdir is None:
1293
            self.outdir = self.orig_cwd
1294
        elif not os.path.isabs(self.outdir):
1295
            self.outdir = os.path.join(self.orig_cwd, self.outdir)
1296

    
1297
        if self.scons is None:
1298
            self.scons = self.scons_path(self.orig_cwd)
1299

    
1300
        if self.scons_lib_dir is None:
1301
            self.scons_lib_dir = self.scons_lib_dir_path(self.orig_cwd)
1302

    
1303
        if self.scons_wrapper is None:
1304
            self.scons_wrapper = self.scons
1305

    
1306
        if not run_number:
1307
            run_number = self.find_next_run_number(self.outdir, self.prefix)
1308

    
1309
        self.run_number = str(run_number)
1310

    
1311
        self.prefix_run = self.prefix + '-%03d' % run_number
1312

    
1313
        if self.targets0 is None:
1314
            self.targets0 = self.startup_targets
1315
        if self.targets1 is None:
1316
            self.targets1 = self.targets
1317
        if self.targets2 is None:
1318
            self.targets2 = self.targets
1319

    
1320
        self.tmpdir = make_temp_file(prefix = self.name + '-')
1321

    
1322
        commands.extend([
1323
            'mkdir %(tmpdir)s',
1324

    
1325
            (os.chdir, 'cd %%s', self.tmpdir),
1326
        ])
1327

    
1328
        for archive in archive_list:
1329
            if not os.path.isabs(archive):
1330
                archive = os.path.join(self.orig_cwd, archive)
1331
            if os.path.isdir(archive):
1332
                dest = os.path.split(archive)[1]
1333
                commands.append((shutil.copytree, 'cp -r %%s %%s', archive, dest))
1334
            else:
1335
                suffix = self.archive_splitext(archive)[1]
1336
                unpack_command = self.unpack_map.get(suffix)
1337
                if not unpack_command:
1338
                    dest = os.path.split(archive)[1]
1339
                    commands.append((shutil.copyfile, 'cp %%s %%s', archive, dest))
1340
                else:
1341
                    commands.append(unpack_command + (archive,))
1342

    
1343
        commands.extend([
1344
            (os.chdir, 'cd %%s', self.subdir),
1345
        ])
1346

    
1347
        commands.extend(self.initial_commands)
1348

    
1349
        commands.extend([
1350
            (lambda: read_tree('.'),
1351
            'find * -type f | xargs cat > /dev/null'),
1352

    
1353
            (self.set_env, 'export %%s=%%s',
1354
             'SCONS_LIB_DIR', self.scons_lib_dir),
1355

    
1356
            '%(python)s %(scons_wrapper)s --version',
1357
        ])
1358

    
1359
        index = 0
1360
        for run_command in self.run_commands:
1361
            setattr(self, 'prof%d' % index, self.profile_name(index))
1362
            c = (
1363
                self.log_execute,
1364
                self.log_display,
1365
                run_command,
1366
                self.logfile_name(index),
1367
            )
1368
            commands.append(c)
1369
            index = index + 1
1370

    
1371
        commands.extend([
1372
            (os.chdir, 'cd %%s', self.orig_cwd),
1373
        ])
1374

    
1375
        if not os.environ.get('PRESERVE'):
1376
            commands.extend(removals)
1377

    
1378
            commands.append((shutil.rmtree, 'rm -rf %%s', self.tmpdir))
1379

    
1380
        self.run_command_list(commands, self.__dict__)
1381

    
1382
        self.scons              = save_scons
1383
        self.scons_lib_dir      = save_scons_lib_dir
1384
        self.scons_wrapper      = save_scons_wrapper
1385

    
1386
    #
1387

    
1388
    def help_time(self):
1389
        help = """\
1390
        Usage: scons-time time [OPTIONS] FILE [...]
1391

1392
          -C DIR, --chdir=DIR           Change to DIR before looking for files
1393
          -f FILE, --file=FILE          Read configuration from specified FILE
1394
          --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT
1395
          -h, --help                    Print this help and exit
1396
          -p STRING, --prefix=STRING    Use STRING as log file/profile prefix
1397
          -t NUMBER, --tail=NUMBER      Only report the last NUMBER files
1398
          --which=TIMER                 Plot timings for TIMER:  total,
1399
                                          SConscripts, SCons, commands.
1400
        """
1401
        sys.stdout.write(self.outdent(help))
1402
        sys.stdout.flush()
1403

    
1404
    def do_time(self, argv):
1405

    
1406
        format = 'ascii'
1407
        logfile_path = lambda x: x
1408
        tail = None
1409
        which = 'total'
1410

    
1411
        short_opts = '?C:f:hp:t:'
1412

    
1413
        long_opts = [
1414
            'chdir=',
1415
            'file=',
1416
            'fmt=',
1417
            'format=',
1418
            'help',
1419
            'prefix=',
1420
            'tail=',
1421
            'title=',
1422
            'which=',
1423
        ]
1424

    
1425
        opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
1426

    
1427
        for o, a in opts:
1428
            if o in ('-C', '--chdir'):
1429
                self.chdir = a
1430
            elif o in ('-f', '--file'):
1431
                self.config_file = a
1432
            elif o in ('--fmt', '--format'):
1433
                format = a
1434
            elif o in ('-?', '-h', '--help'):
1435
                self.do_help(['help', 'time'])
1436
                sys.exit(0)
1437
            elif o in ('-p', '--prefix'):
1438
                self.prefix = a
1439
            elif o in ('-t', '--tail'):
1440
                tail = int(a)
1441
            elif o in ('--title',):
1442
                self.title = a
1443
            elif o in ('--which',):
1444
                if not a in self.time_strings.keys():
1445
                    sys.stderr.write('%s: time: Unrecognized timer "%s".\n' % (self.name, a))
1446
                    sys.stderr.write('%s  Type "%s help time" for help.\n' % (self.name_spaces, self.name))
1447
                    sys.exit(1)
1448
                which = a
1449

    
1450
        if self.config_file:
1451
            execfile(self.config_file, self.__dict__)
1452

    
1453
        if self.chdir:
1454
            os.chdir(self.chdir)
1455
            logfile_path = lambda x, c=self.chdir: os.path.join(c, x)
1456

    
1457
        if not args:
1458

    
1459
            pattern = '%s*.log' % self.prefix
1460
            args = self.args_to_files([pattern], tail)
1461

    
1462
            if not args:
1463
                if self.chdir:
1464
                    directory = self.chdir
1465
                else:
1466
                    directory = os.getcwd()
1467

    
1468
                sys.stderr.write('%s: time: No arguments specified.\n' % self.name)
1469
                sys.stderr.write('%s  No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory))
1470
                sys.stderr.write('%s  Type "%s help time" for help.\n' % (self.name_spaces, self.name))
1471
                sys.exit(1)
1472

    
1473
        else:
1474

    
1475
            args = self.args_to_files(args, tail)
1476

    
1477
        cwd_ = os.getcwd() + os.sep
1478

    
1479
        if format == 'ascii':
1480

    
1481
            columns = ("Total", "SConscripts", "SCons", "commands")
1482
            self.ascii_table(args, columns, self.get_debug_times, logfile_path)
1483

    
1484
        elif format == 'gnuplot':
1485

    
1486
            results = self.collect_results(args, self.get_debug_times,
1487
                                           self.time_strings[which])
1488

    
1489
            self.gnuplot_results(results, fmt='%s %.6f')
1490

    
1491
        else:
1492

    
1493
            sys.stderr.write('%s: time: Unknown format "%s".\n' % (self.name, format))
1494
            sys.exit(1)
1495

    
1496
if __name__ == '__main__':
1497
    opts, args = getopt.getopt(sys.argv[1:], 'h?V', ['help', 'version'])
1498

    
1499
    ST = SConsTimer()
1500

    
1501
    for o, a in opts:
1502
        if o in ('-?', '-h', '--help'):
1503
            ST.do_help(['help'])
1504
            sys.exit(0)
1505
        elif o in ('-V', '--version'):
1506
            sys.stdout.write('scons-time version\n')
1507
            sys.exit(0)
1508

    
1509
    if not args:
1510
        sys.stderr.write('Type "%s help" for usage.\n' % ST.name)
1511
        sys.exit(1)
1512

    
1513
    ST.execute_subcommand(args)