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 40c0f755 Ryan
#!/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)