From 323886c70c0754b96e076ce89c3e80c61507b303 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Thu, 7 Feb 2008 10:24:55 +0000 Subject: [PATCH] added missing scons files --- scons/scons-time.py | 1469 +++++++++++++++++++++++++++++++++++++++++++ scons/scons.py | 161 +++++ scons/sconsign.py | 497 +++++++++++++++ 3 files changed, 2127 insertions(+) create mode 100755 scons/scons-time.py create mode 100755 scons/scons.py create mode 100755 scons/sconsign.py diff --git a/scons/scons-time.py b/scons/scons-time.py new file mode 100755 index 000000000..eb6fef175 --- /dev/null +++ b/scons/scons-time.py @@ -0,0 +1,1469 @@ +#!/usr/bin/env python +# +# scons-time - run SCons timings and collect statistics +# +# A script for running a configuration through SCons with a standard +# set of invocations to collect timing and memory statistics and to +# capture the results in a consistent set of output files for display +# and analysis. +# + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +from __future__ import nested_scopes + +__revision__ = "src/script/scons-time.py 2523 2007/12/12 09:37:41 knight" + +import getopt +import glob +import os +import re +import shutil +import string +import sys +import tempfile +import time + +try: + False +except NameError: + # Pre-2.2 Python has no False keyword. + import __builtin__ + __builtin__.False = not 1 + +try: + True +except NameError: + # Pre-2.2 Python has no True keyword. + import __builtin__ + __builtin__.True = not 0 + +def make_temp_file(**kw): + try: + result = tempfile.mktemp(**kw) + except TypeError: + try: + save_template = tempfile.template + prefix = kw['prefix'] + del kw['prefix'] + tempfile.template = prefix + result = tempfile.mktemp(**kw) + finally: + tempfile.template = save_template + return result + +class Plotter: + def increment_size(self, largest): + """ + Return the size of each horizontal increment line for a specified + maximum value. This returns a value that will provide somewhere + between 5 and 9 horizontal lines on the graph, on some set of + boundaries that are multiples of 10/100/1000/etc. + """ + i = largest / 5 + if not i: + return largest + multiplier = 1 + while i >= 10: + i = i / 10 + multiplier = multiplier * 10 + return i * multiplier + + def max_graph_value(self, largest): + # Round up to next integer. + largest = int(largest) + 1 + increment = self.increment_size(largest) + return ((largest + increment - 1) / increment) * increment + +class Line: + def __init__(self, points, type, title, label, comment, fmt="%s %s"): + self.points = points + self.type = type + self.title = title + self.label = label + self.comment = comment + self.fmt = fmt + + def print_label(self, inx, x, y): + if self.label: + print 'set label %s "%s" at %s,%s right' % (inx, self.label, x, y) + + def plot_string(self): + if self.title: + title_string = 'title "%s"' % self.title + else: + title_string = 'notitle' + return "'-' %s with lines lt %s" % (title_string, self.type) + + def print_points(self, fmt=None): + if fmt is None: + fmt = self.fmt + if self.comment: + print '# %s' % self.comment + for x, y in self.points: + print fmt % (x, y) + print 'e' + + def get_x_values(self): + return [ p[0] for p in self.points ] + + def get_y_values(self): + return [ p[1] for p in self.points ] + +class Gnuplotter(Plotter): + + def __init__(self, title, key_location): + self.lines = [] + self.title = title + self.key_location = key_location + + def line(self, points, type, title=None, label=None, comment=None, fmt='%s %s'): + if points: + line = Line(points, type, title, label, comment, fmt) + self.lines.append(line) + + def plot_string(self, line): + return line.plot_string() + + def vertical_bar(self, x, type, label, comment): + if self.get_min_x() <= x and x <= self.get_max_x(): + points = [(x, 0), (x, self.get_max_x())] + self.line(points, type, label, comment) + + def get_all_x_values(self): + result = [] + for line in self.lines: + result.extend(line.get_x_values()) + return result + + def get_all_y_values(self): + result = [] + for line in self.lines: + result.extend(line.get_y_values()) + return result + + def get_min_x(self): + try: + return self.min_x + except AttributeError: + self.min_x = min(self.get_all_x_values()) + return self.min_x + + def get_max_x(self): + try: + return self.max_x + except AttributeError: + self.max_x = max(self.get_all_x_values()) + return self.max_x + + def get_min_y(self): + try: + return self.min_y + except AttributeError: + self.min_y = min(self.get_all_y_values()) + return self.min_y + + def get_max_y(self): + try: + return self.max_y + except AttributeError: + self.max_y = max(self.get_all_y_values()) + return self.max_y + + def draw(self): + + if not self.lines: + return + + if self.title: + print 'set title "%s"' % self.title + print 'set key %s' % self.key_location + + inx = 1 + max_y = self.max_graph_value(self.get_max_y())/2 + for line in self.lines: + line.print_label(inx, line.points[0][0]-1, max_y) + inx += 1 + + plot_strings = [ self.plot_string(l) for l in self.lines ] + print 'plot ' + ', \\\n '.join(plot_strings) + + for line in self.lines: + line.print_points() + + + +def untar(fname): + import tarfile + tar = tarfile.open(name=fname, mode='r') + for tarinfo in tar: + tar.extract(tarinfo) + tar.close() + +def unzip(fname): + import zipfile + zf = zipfile.ZipFile(fname, 'r') + for name in zf.namelist(): + dir = os.path.dirname(name) + try: + os.makedirs(dir) + except: + pass + open(name, 'w').write(zf.read(name)) + +def read_tree(dir): + def read_files(arg, dirname, fnames): + for fn in fnames: + fn = os.path.join(dirname, fn) + if os.path.isfile(fn): + open(fn, 'rb').read() + os.path.walk('.', read_files, None) + +def redirect_to_file(command, log): + return '%s > %s 2>&1' % (command, log) + +def tee_to_file(command, log): + return '%s 2>&1 | tee %s' % (command, log) + + + +class SConsTimer: + """ + Usage: scons-time SUBCOMMAND [ARGUMENTS] + Type "scons-time help SUBCOMMAND" for help on a specific subcommand. + + Available subcommands: + func Extract test-run data for a function + help Provides help + mem Extract --debug=memory data from test runs + obj Extract --debug=count data from test runs + time Extract --debug=time data from test runs + run Runs a test configuration + """ + + name = 'scons-time' + name_spaces = ' '*len(name) + + def makedict(**kw): + return kw + + default_settings = makedict( + aegis = 'aegis', + aegis_project = None, + chdir = None, + config_file = None, + initial_commands = [], + key_location = 'bottom left', + orig_cwd = os.getcwd(), + outdir = None, + prefix = '', + python = '"%s"' % sys.executable, + redirect = redirect_to_file, + scons = None, + scons_flags = '--debug=count --debug=memory --debug=time --debug=memoizer', + scons_lib_dir = None, + scons_wrapper = None, + startup_targets = '--help', + subdir = None, + subversion_url = None, + svn = 'svn', + svn_co_flag = '-q', + tar = 'tar', + targets = '', + targets0 = None, + targets1 = None, + targets2 = None, + title = None, + unzip = 'unzip', + verbose = False, + vertical_bars = [], + + unpack_map = { + '.tar.gz' : (untar, '%(tar)s xzf %%s'), + '.tgz' : (untar, '%(tar)s xzf %%s'), + '.tar' : (untar, '%(tar)s xf %%s'), + '.zip' : (unzip, '%(unzip)s %%s'), + }, + ) + + run_titles = [ + 'Startup', + 'Full build', + 'Up-to-date build', + ] + + run_commands = [ + '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof0)s %(targets0)s', + '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targets1)s', + '%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targets2)s', + ] + + stages = [ + 'pre-read', + 'post-read', + 'pre-build', + 'post-build', + ] + + stage_strings = { + 'pre-read' : 'Memory before reading SConscript files:', + 'post-read' : 'Memory after reading SConscript files:', + 'pre-build' : 'Memory before building targets:', + 'post-build' : 'Memory after building targets:', + } + + memory_string_all = 'Memory ' + + default_stage = stages[-1] + + time_strings = { + 'total' : 'Total build time', + 'SConscripts' : 'Total SConscript file execution time', + 'SCons' : 'Total SCons execution time', + 'commands' : 'Total command execution time', + } + + time_string_all = 'Total .* time' + + # + + def __init__(self): + self.__dict__.update(self.default_settings) + + # Functions for displaying and executing commands. + + def subst(self, x, dictionary): + try: + return x % dictionary + except TypeError: + # x isn't a string (it's probably a Python function), + # so just return it. + return x + + def subst_variables(self, command, dictionary): + """ + Substitutes (via the format operator) the values in the specified + dictionary into the specified command. + + The command can be an (action, string) tuple. In all cases, we + perform substitution on strings and don't worry if something isn't + a string. (It's probably a Python function to be executed.) + """ + try: + command + '' + except TypeError: + action = command[0] + string = command[1] + args = command[2:] + else: + action = command + string = action + args = (()) + action = self.subst(action, dictionary) + string = self.subst(string, dictionary) + return (action, string, args) + + def _do_not_display(self, msg, *args): + pass + + def display(self, msg, *args): + """ + Displays the specified message. + + Each message is prepended with a standard prefix of our name + plus the time. + """ + if callable(msg): + msg = msg(*args) + else: + msg = msg % args + if msg is None: + return + fmt = '%s[%s]: %s\n' + sys.stdout.write(fmt % (self.name, time.strftime('%H:%M:%S'), msg)) + + def _do_not_execute(self, action, *args): + pass + + def execute(self, action, *args): + """ + Executes the specified action. + + The action is called if it's a callable Python function, and + otherwise passed to os.system(). + """ + if callable(action): + action(*args) + else: + os.system(action % args) + + def run_command_list(self, commands, dict): + """ + Executes a list of commands, substituting values from the + specified dictionary. + """ + commands = [ self.subst_variables(c, dict) for c in commands ] + for action, string, args in commands: + self.display(string, *args) + sys.stdout.flush() + status = self.execute(action, *args) + if status: + sys.exit(status) + + def log_display(self, command, log): + command = self.subst(command, self.__dict__) + if log: + command = self.redirect(command, log) + return command + + def log_execute(self, command, log): + command = self.subst(command, self.__dict__) + output = os.popen(command).read() + if self.verbose: + sys.stdout.write(output) + open(log, 'wb').write(output) + + # + + def archive_splitext(self, path): + """ + Splits an archive name into a filename base and extension. + + This is like os.path.splitext() (which it calls) except that it + also looks for '.tar.gz' and treats it as an atomic extensions. + """ + if path.endswith('.tar.gz'): + return path[:-7], path[-7:] + else: + return os.path.splitext(path) + + def args_to_files(self, args, tail=None): + """ + Takes a list of arguments, expands any glob patterns, and + returns the last "tail" files from the list. + """ + files = [] + for a in args: + g = glob.glob(a) + g.sort() + files.extend(g) + + if tail: + files = files[-tail:] + + return files + + def ascii_table(self, files, columns, + line_function, file_function=lambda x: x, + *args, **kw): + + header_fmt = ' '.join(['%12s'] * len(columns)) + line_fmt = header_fmt + ' %s' + + print header_fmt % columns + + for file in files: + t = line_function(file, *args, **kw) + diff = len(columns) - len(t) + if diff > 0: + t += [''] * diff + t.append(file_function(file)) + print line_fmt % tuple(t) + + def collect_results(self, files, function, *args, **kw): + results = {} + + for file in files: + base = os.path.splitext(file)[0] + run, index = string.split(base, '-')[-2:] + + run = int(run) + index = int(index) + + value = function(file, *args, **kw) + + try: + r = results[index] + except KeyError: + r = [] + results[index] = r + r.append((run, value)) + + return results + + def doc_to_help(self, obj): + """ + Translates an object's __doc__ string into help text. + + This strips a consistent number of spaces from each line in the + help text, essentially "outdenting" the text to the left-most + column. + """ + doc = obj.__doc__ + if doc is None: + return '' + return self.outdent(doc) + + def find_next_run_number(self, dir, prefix): + """ + Returns the next run number in a directory for the specified prefix. + + Examines the contents the specified directory for files with the + specified prefix, extracts the run numbers from each file name, + and returns the next run number after the largest it finds. + """ + x = re.compile(re.escape(prefix) + '-([0-9]+).*') + matches = map(lambda e, x=x: x.match(e), os.listdir(dir)) + matches = filter(None, matches) + if not matches: + return 0 + run_numbers = map(lambda m: int(m.group(1)), matches) + return int(max(run_numbers)) + 1 + + def gnuplot_results(self, results, fmt='%s %.3f'): + """ + Prints out a set of results in Gnuplot format. + """ + gp = Gnuplotter(self.title, self.key_location) + + indices = results.keys() + indices.sort() + + for i in indices: + try: + t = self.run_titles[i] + except IndexError: + t = '??? %s ???' % i + results[i].sort() + gp.line(results[i], i+1, t, None, t, fmt=fmt) + + for bar_tuple in self.vertical_bars: + try: + x, type, label, comment = bar_tuple + except ValueError: + x, type, label = bar_tuple + comment = label + gp.vertical_bar(x, type, None, label, comment) + + gp.draw() + + def logfile_name(self, invocation): + """ + Returns the absolute path of a log file for the specificed + invocation number. + """ + name = self.prefix_run + '-%d.log' % invocation + return os.path.join(self.outdir, name) + + def outdent(self, s): + """ + Strip as many spaces from each line as are found at the beginning + of the first line in the list. + """ + lines = s.split('\n') + if lines[0] == '': + lines = lines[1:] + spaces = re.match(' *', lines[0]).group(0) + def strip_initial_spaces(l, s=spaces): + if l.startswith(spaces): + l = l[len(spaces):] + return l + return '\n'.join([ strip_initial_spaces(l) for l in lines ]) + '\n' + + def profile_name(self, invocation): + """ + Returns the absolute path of a profile file for the specified + invocation number. + """ + name = self.prefix_run + '-%d.prof' % invocation + return os.path.join(self.outdir, name) + + def set_env(self, key, value): + os.environ[key] = value + + # + + def get_debug_times(self, file, time_string=None): + """ + Fetch times from the --debug=time strings in the specified file. + """ + if time_string is None: + search_string = self.time_string_all + else: + search_string = time_string + contents = open(file).read() + result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:] + result = [ float(r) for r in result ] + if not time_string is None: + result = result[0] + return result + + def get_function_profile(self, file, function): + """ + Returns the file, line number, function name, and cumulative time. + """ + try: + import pstats + except ImportError, e: + sys.stderr.write('%s: func: %s\n' % (self.name, e)) + sys.stderr.write('%s This version of Python is missing the profiler.\n' % self.name_spaces) + sys.stderr.write('%s Cannot use the "func" subcommand.\n' % self.name_spaces) + sys.exit(1) + statistics = pstats.Stats(file).stats + matches = [ e for e in statistics.items() if e[0][2] == function ] + r = matches[0] + return r[0][0], r[0][1], r[0][2], r[1][3] + + def get_function_time(self, file, function): + """ + Returns just the cumulative time for the specified function. + """ + return self.get_function_profile(file, function)[3] + + def get_memory(self, file, memory_string=None): + """ + Returns a list of integers of the amount of memory used. The + default behavior is to return all the stages. + """ + if memory_string is None: + search_string = self.memory_string_all + else: + search_string = memory_string + lines = open(file).readlines() + lines = [ l for l in lines if l.startswith(search_string) ][-4:] + result = [ int(l.split()[-1]) for l in lines[-4:] ] + if len(result) == 1: + result = result[0] + return result + + def get_object_counts(self, file, object_name, index=None): + """ + Returns the counts of the specified object_name. + """ + object_string = ' ' + object_name + '\n' + lines = open(file).readlines() + line = [ l for l in lines if l.endswith(object_string) ][0] + result = [ int(field) for field in line.split()[:4] ] + if not index is None: + result = result[index] + return result + + # + + command_alias = {} + + def execute_subcommand(self, argv): + """ + Executes the do_*() function for the specified subcommand (argv[0]). + """ + if not argv: + return + cmdName = self.command_alias.get(argv[0], argv[0]) + try: + func = getattr(self, 'do_' + cmdName) + except AttributeError: + return self.default(argv) + try: + return func(argv) + except TypeError, e: + sys.stderr.write("%s %s: %s\n" % (self.name, cmdName, e)) + import traceback + traceback.print_exc(file=sys.stderr) + sys.stderr.write("Try '%s help %s'\n" % (self.name, cmdName)) + + def default(self, argv): + """ + The default behavior for an unknown subcommand. Prints an + error message and exits. + """ + sys.stderr.write('%s: Unknown subcommand "%s".\n' % (self.name, argv[0])) + sys.stderr.write('Type "%s help" for usage.\n' % self.name) + sys.exit(1) + + # + + def do_help(self, argv): + """ + """ + if argv[1:]: + for arg in argv[1:]: + try: + func = getattr(self, 'do_' + arg) + except AttributeError: + sys.stderr.write('%s: No help for "%s"\n' % (self.name, arg)) + else: + try: + help = getattr(self, 'help_' + arg) + except AttributeError: + sys.stdout.write(self.doc_to_help(func)) + sys.stdout.flush() + else: + help() + else: + doc = self.doc_to_help(self.__class__) + if doc: + sys.stdout.write(doc) + sys.stdout.flush() + return None + + # + + def help_func(self): + help = """\ + Usage: scons-time func [OPTIONS] FILE [...] + + -C DIR, --chdir=DIR Change to DIR before looking for files + -f FILE, --file=FILE Read configuration from specified FILE + --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT + --func=NAME, --function=NAME Report time for function NAME + -h, --help Print this help and exit + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + -t NUMBER, --tail=NUMBER Only report the last NUMBER files + --title=TITLE Specify the output plot TITLE + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_func(self, argv): + """ + """ + format = 'ascii' + function_name = '_main' + tail = None + + short_opts = '?C:f:hp:t:' + + long_opts = [ + 'chdir=', + 'file=', + 'fmt=', + 'format=', + 'func=', + 'function=', + 'help', + 'prefix=', + 'tail=', + 'title=', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('-C', '--chdir'): + self.chdir = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('--fmt', '--format'): + format = a + elif o in ('--func', '--function'): + function_name = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'func']) + sys.exit(0) + elif o in ('--max',): + max_time = int(a) + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('-t', '--tail'): + tail = int(a) + elif o in ('--title',): + self.title = a + + if self.config_file: + execfile(self.config_file, self.__dict__) + + if self.chdir: + os.chdir(self.chdir) + + if not args: + + pattern = '%s*.prof' % self.prefix + args = self.args_to_files([pattern], tail) + + if not args: + if self.chdir: + directory = self.chdir + else: + directory = os.getcwd() + + sys.stderr.write('%s: func: No arguments specified.\n' % self.name) + sys.stderr.write('%s No %s*.prof files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) + sys.stderr.write('%s Type "%s help func" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + else: + + args = self.args_to_files(args, tail) + + cwd_ = os.getcwd() + os.sep + + if format == 'ascii': + + def print_function_timing(file, func): + try: + f, line, func, time = self.get_function_profile(file, func) + except ValueError, e: + sys.stderr.write("%s: func: %s: %s\n" % (self.name, file, e)) + else: + if f.startswith(cwd_): + f = f[len(cwd_):] + print "%.3f %s:%d(%s)" % (time, f, line, func) + + for file in args: + print_function_timing(file, function_name) + + elif format == 'gnuplot': + + results = self.collect_results(args, self.get_function_time, + function_name) + + self.gnuplot_results(results) + + else: + + sys.stderr.write('%s: func: Unknown format "%s".\n' % (self.name, format)) + sys.exit(1) + + # + + def help_mem(self): + help = """\ + Usage: scons-time mem [OPTIONS] FILE [...] + + -C DIR, --chdir=DIR Change to DIR before looking for files + -f FILE, --file=FILE Read configuration from specified FILE + --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT + -h, --help Print this help and exit + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + --stage=STAGE Plot memory at the specified stage: + pre-read, post-read, pre-build, + post-build (default: post-build) + -t NUMBER, --tail=NUMBER Only report the last NUMBER files + --title=TITLE Specify the output plot TITLE + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_mem(self, argv): + + format = 'ascii' + logfile_path = lambda x: x + stage = self.default_stage + tail = None + + short_opts = '?C:f:hp:t:' + + long_opts = [ + 'chdir=', + 'file=', + 'fmt=', + 'format=', + 'help', + 'prefix=', + 'stage=', + 'tail=', + 'title=', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('-C', '--chdir'): + self.chdir = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('--fmt', '--format'): + format = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'mem']) + sys.exit(0) + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('--stage',): + if not a in self.stages: + sys.stderr.write('%s: mem: Unrecognized stage "%s".\n' % (self.name, a)) + sys.exit(1) + stage = a + elif o in ('-t', '--tail'): + tail = int(a) + elif o in ('--title',): + self.title = a + + if self.config_file: + execfile(self.config_file, self.__dict__) + + if self.chdir: + os.chdir(self.chdir) + logfile_path = lambda x, c=self.chdir: os.path.join(c, x) + + if not args: + + pattern = '%s*.log' % self.prefix + args = self.args_to_files([pattern], tail) + + if not args: + if self.chdir: + directory = self.chdir + else: + directory = os.getcwd() + + sys.stderr.write('%s: mem: No arguments specified.\n' % self.name) + sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) + sys.stderr.write('%s Type "%s help mem" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + else: + + args = self.args_to_files(args, tail) + + cwd_ = os.getcwd() + os.sep + + if format == 'ascii': + + self.ascii_table(args, tuple(self.stages), self.get_memory, logfile_path) + + elif format == 'gnuplot': + + results = self.collect_results(args, self.get_memory, + self.stage_strings[stage]) + + self.gnuplot_results(results) + + else: + + sys.stderr.write('%s: mem: Unknown format "%s".\n' % (self.name, format)) + sys.exit(1) + + return 0 + + # + + def help_obj(self): + help = """\ + Usage: scons-time obj [OPTIONS] OBJECT FILE [...] + + -C DIR, --chdir=DIR Change to DIR before looking for files + -f FILE, --file=FILE Read configuration from specified FILE + --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT + -h, --help Print this help and exit + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + --stage=STAGE Plot memory at the specified stage: + pre-read, post-read, pre-build, + post-build (default: post-build) + -t NUMBER, --tail=NUMBER Only report the last NUMBER files + --title=TITLE Specify the output plot TITLE + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_obj(self, argv): + + format = 'ascii' + logfile_path = lambda x: x + stage = self.default_stage + tail = None + + short_opts = '?C:f:hp:t:' + + long_opts = [ + 'chdir=', + 'file=', + 'fmt=', + 'format=', + 'help', + 'prefix=', + 'stage=', + 'tail=', + 'title=', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('-C', '--chdir'): + self.chdir = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('--fmt', '--format'): + format = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'obj']) + sys.exit(0) + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('--stage',): + if not a in self.stages: + sys.stderr.write('%s: obj: Unrecognized stage "%s".\n' % (self.name, a)) + sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + stage = a + elif o in ('-t', '--tail'): + tail = int(a) + elif o in ('--title',): + self.title = a + + if not args: + sys.stderr.write('%s: obj: Must specify an object name.\n' % self.name) + sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + object_name = args.pop(0) + + if self.config_file: + execfile(self.config_file, self.__dict__) + + if self.chdir: + os.chdir(self.chdir) + logfile_path = lambda x, c=self.chdir: os.path.join(c, x) + + if not args: + + pattern = '%s*.log' % self.prefix + args = self.args_to_files([pattern], tail) + + if not args: + if self.chdir: + directory = self.chdir + else: + directory = os.getcwd() + + sys.stderr.write('%s: obj: No arguments specified.\n' % self.name) + sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) + sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + else: + + args = self.args_to_files(args, tail) + + cwd_ = os.getcwd() + os.sep + + if format == 'ascii': + + self.ascii_table(args, tuple(self.stages), self.get_object_counts, logfile_path, object_name) + + elif format == 'gnuplot': + + stage_index = 0 + for s in self.stages: + if stage == s: + break + stage_index = stage_index + 1 + + results = self.collect_results(args, self.get_object_counts, + object_name, stage_index) + + self.gnuplot_results(results) + + else: + + sys.stderr.write('%s: obj: Unknown format "%s".\n' % (self.name, format)) + sys.exit(1) + + return 0 + + # + + def help_run(self): + help = """\ + Usage: scons-time run [OPTIONS] [FILE ...] + + --aegis=PROJECT Use SCons from the Aegis PROJECT + --chdir=DIR Name of unpacked directory for chdir + -f FILE, --file=FILE Read configuration from specified FILE + -h, --help Print this help and exit + -n, --no-exec No execute, just print command lines + --number=NUMBER Put output in files for run NUMBER + --outdir=OUTDIR Put output files in OUTDIR + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + --python=PYTHON Time using the specified PYTHON + -q, --quiet Don't print command lines + --scons=SCONS Time using the specified SCONS + --svn=URL, --subversion=URL Use SCons from Subversion URL + -v, --verbose Display output of commands + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_run(self, argv): + """ + """ + run_number_list = [None] + + short_opts = '?f:hnp:qs:v' + + long_opts = [ + 'aegis=', + 'file=', + 'help', + 'no-exec', + 'number=', + 'outdir=', + 'prefix=', + 'python=', + 'quiet', + 'scons=', + 'svn=', + 'subdir=', + 'subversion=', + 'verbose', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('--aegis',): + self.aegis_project = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'run']) + sys.exit(0) + elif o in ('-n', '--no-exec'): + self.execute = self._do_not_execute + elif o in ('--number',): + run_number_list = self.split_run_numbers(a) + elif o in ('--outdir',): + self.outdir = a + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('--python',): + self.python = a + elif o in ('-q', '--quiet'): + self.display = self._do_not_display + elif o in ('-s', '--subdir'): + self.subdir = a + elif o in ('--scons',): + self.scons = a + elif o in ('--svn', '--subversion'): + self.subversion_url = a + elif o in ('-v', '--verbose'): + self.redirect = tee_to_file + self.verbose = True + self.svn_co_flag = '' + + if not args and not self.config_file: + sys.stderr.write('%s: run: No arguments or -f config file specified.\n' % self.name) + sys.stderr.write('%s Type "%s help run" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + if self.config_file: + execfile(self.config_file, self.__dict__) + + if args: + self.archive_list = args + + archive_file_name = os.path.split(self.archive_list[0])[1] + + if not self.subdir: + self.subdir = self.archive_splitext(archive_file_name)[0] + + if not self.prefix: + self.prefix = self.archive_splitext(archive_file_name)[0] + + prepare = None + if self.subversion_url: + prepare = self.prep_subversion_run + elif self.aegis_project: + prepare = self.prep_aegis_run + + for run_number in run_number_list: + self.individual_run(run_number, self.archive_list, prepare) + + def split_run_numbers(self, s): + result = [] + for n in s.split(','): + try: + x, y = n.split('-') + except ValueError: + result.append(int(n)) + else: + result.extend(range(int(x), int(y)+1)) + return result + + def scons_path(self, dir): + return os.path.join(dir, 'src', 'script', 'scons.py') + + def scons_lib_dir_path(self, dir): + return os.path.join(dir, 'src', 'engine') + + def prep_aegis_run(self, commands, removals): + self.aegis_tmpdir = make_temp_file(prefix = self.name + '-aegis-') + removals.append((shutil.rmtree, 'rm -rf %%s', self.aegis_tmpdir)) + + self.aegis_parent_project = os.path.splitext(self.aegis_project)[0] + self.scons = self.scons_path(self.aegis_tmpdir) + self.scons_lib_dir = self.scons_lib_dir_path(self.aegis_tmpdir) + + commands.extend([ + 'mkdir %(aegis_tmpdir)s', + (lambda: os.chdir(self.aegis_tmpdir), 'cd %(aegis_tmpdir)s'), + '%(aegis)s -cp -ind -p %(aegis_parent_project)s .', + '%(aegis)s -cp -ind -p %(aegis_project)s -delta %(run_number)s .', + ]) + + def prep_subversion_run(self, commands, removals): + self.svn_tmpdir = make_temp_file(prefix = self.name + '-svn-') + removals.append((shutil.rmtree, 'rm -rf %%s', self.svn_tmpdir)) + + self.scons = self.scons_path(self.svn_tmpdir) + self.scons_lib_dir = self.scons_lib_dir_path(self.svn_tmpdir) + + commands.extend([ + 'mkdir %(svn_tmpdir)s', + '%(svn)s co %(svn_co_flag)s -r %(run_number)s %(subversion_url)s %(svn_tmpdir)s', + ]) + + def individual_run(self, run_number, archive_list, prepare=None): + """ + Performs an individual run of the default SCons invocations. + """ + + commands = [] + removals = [] + + if prepare: + prepare(commands, removals) + + save_scons = self.scons + save_scons_wrapper = self.scons_wrapper + save_scons_lib_dir = self.scons_lib_dir + + if self.outdir is None: + self.outdir = self.orig_cwd + elif not os.path.isabs(self.outdir): + self.outdir = os.path.join(self.orig_cwd, self.outdir) + + if self.scons is None: + self.scons = self.scons_path(self.orig_cwd) + + if self.scons_lib_dir is None: + self.scons_lib_dir = self.scons_lib_dir_path(self.orig_cwd) + + if self.scons_wrapper is None: + self.scons_wrapper = self.scons + + if not run_number: + run_number = self.find_next_run_number(self.outdir, self.prefix) + + self.run_number = str(run_number) + + self.prefix_run = self.prefix + '-%03d' % run_number + + if self.targets0 is None: + self.targets0 = self.startup_targets + if self.targets1 is None: + self.targets1 = self.targets + if self.targets2 is None: + self.targets2 = self.targets + + self.tmpdir = make_temp_file(prefix = self.name + '-') + + commands.extend([ + 'mkdir %(tmpdir)s', + + (os.chdir, 'cd %%s', self.tmpdir), + ]) + + for archive in archive_list: + if not os.path.isabs(archive): + archive = os.path.join(self.orig_cwd, archive) + if os.path.isdir(archive): + dest = os.path.split(archive)[1] + commands.append((shutil.copytree, 'cp -r %%s %%s', archive, dest)) + else: + suffix = self.archive_splitext(archive)[1] + commands.append(self.unpack_map[suffix] + (archive,)) + + commands.extend([ + (os.chdir, 'cd %%s', self.subdir), + ]) + + commands.extend(self.initial_commands) + + commands.extend([ + (lambda: read_tree('.'), + 'find * -type f | xargs cat > /dev/null'), + + (self.set_env, 'export %%s=%%s', + 'SCONS_LIB_DIR', self.scons_lib_dir), + + '%(python)s %(scons_wrapper)s --version', + ]) + + index = 0 + for run_command in self.run_commands: + setattr(self, 'prof%d' % index, self.profile_name(index)) + c = ( + self.log_execute, + self.log_display, + run_command, + self.logfile_name(index), + ) + commands.append(c) + index = index + 1 + + commands.extend([ + (os.chdir, 'cd %%s', self.orig_cwd), + ]) + + if not os.environ.get('PRESERVE'): + commands.extend(removals) + + commands.append((shutil.rmtree, 'rm -rf %%s', self.tmpdir)) + + self.run_command_list(commands, self.__dict__) + + self.scons = save_scons + self.scons_lib_dir = save_scons_lib_dir + self.scons_wrapper = save_scons_wrapper + + # + + def help_time(self): + help = """\ + Usage: scons-time time [OPTIONS] FILE [...] + + -C DIR, --chdir=DIR Change to DIR before looking for files + -f FILE, --file=FILE Read configuration from specified FILE + --fmt=FORMAT, --format=FORMAT Print data in specified FORMAT + -h, --help Print this help and exit + -p STRING, --prefix=STRING Use STRING as log file/profile prefix + -t NUMBER, --tail=NUMBER Only report the last NUMBER files + --which=TIMER Plot timings for TIMER: total, + SConscripts, SCons, commands. + """ + sys.stdout.write(self.outdent(help)) + sys.stdout.flush() + + def do_time(self, argv): + + format = 'ascii' + logfile_path = lambda x: x + tail = None + which = 'total' + + short_opts = '?C:f:hp:t:' + + long_opts = [ + 'chdir=', + 'file=', + 'fmt=', + 'format=', + 'help', + 'prefix=', + 'tail=', + 'title=', + 'which=', + ] + + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + + for o, a in opts: + if o in ('-C', '--chdir'): + self.chdir = a + elif o in ('-f', '--file'): + self.config_file = a + elif o in ('--fmt', '--format'): + format = a + elif o in ('-?', '-h', '--help'): + self.do_help(['help', 'time']) + sys.exit(0) + elif o in ('-p', '--prefix'): + self.prefix = a + elif o in ('-t', '--tail'): + tail = int(a) + elif o in ('--title',): + self.title = a + elif o in ('--which',): + if not a in self.time_strings.keys(): + sys.stderr.write('%s: time: Unrecognized timer "%s".\n' % (self.name, a)) + sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + which = a + + if self.config_file: + execfile(self.config_file, self.__dict__) + + if self.chdir: + os.chdir(self.chdir) + logfile_path = lambda x, c=self.chdir: os.path.join(c, x) + + if not args: + + pattern = '%s*.log' % self.prefix + args = self.args_to_files([pattern], tail) + + if not args: + if self.chdir: + directory = self.chdir + else: + directory = os.getcwd() + + sys.stderr.write('%s: time: No arguments specified.\n' % self.name) + sys.stderr.write('%s No %s*.log files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) + sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name)) + sys.exit(1) + + else: + + args = self.args_to_files(args, tail) + + cwd_ = os.getcwd() + os.sep + + if format == 'ascii': + + columns = ("Total", "SConscripts", "SCons", "commands") + self.ascii_table(args, columns, self.get_debug_times, logfile_path) + + elif format == 'gnuplot': + + results = self.collect_results(args, self.get_debug_times, + self.time_strings[which]) + + self.gnuplot_results(results, fmt='%s %.6f') + + else: + + sys.stderr.write('%s: time: Unknown format "%s".\n' % (self.name, format)) + sys.exit(1) + +if __name__ == '__main__': + opts, args = getopt.getopt(sys.argv[1:], 'h?V', ['help', 'version']) + + ST = SConsTimer() + + for o, a in opts: + if o in ('-?', '-h', '--help'): + ST.do_help(['help']) + sys.exit(0) + elif o in ('-V', '--version'): + sys.stdout.write('scons-time version\n') + sys.exit(0) + + if not args: + sys.stderr.write('Type "%s help" for usage.\n' % ST.name) + sys.exit(1) + + ST.execute_subcommand(args) diff --git a/scons/scons.py b/scons/scons.py new file mode 100755 index 000000000..158b91757 --- /dev/null +++ b/scons/scons.py @@ -0,0 +1,161 @@ +#! /usr/bin/env python +# +# SCons - a Software Constructor +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/script/scons.py 2523 2007/12/12 09:37:41 knight" + +__version__ = "0.97.0d20071212" + +__build__ = "r2523" + +__buildsys__ = "bangkok" + +__date__ = "2007/12/12 09:37:41" + +__developer__ = "knight" + +import os +import os.path +import sys + +############################################################################## +# BEGIN STANDARD SCons SCRIPT HEADER +# +# This is the cut-and-paste logic so that a self-contained script can +# interoperate correctly with different SCons versions and installation +# locations for the engine. If you modify anything in this section, you +# should also change other scripts that use this same header. +############################################################################## + +# Strip the script directory from sys.path() so on case-insensitive +# (WIN32) systems Python doesn't think that the "scons" script is the +# "SCons" package. Replace it with our own library directories +# (version-specific first, in case they installed by hand there, +# followed by generic) so we pick up the right version of the build +# engine modules if they're in either directory. + +script_dir = sys.path[0] + +if script_dir in sys.path: + sys.path.remove(script_dir) + +libs = [] + +if os.environ.has_key("SCONS_LIB_DIR"): + libs.append(os.environ["SCONS_LIB_DIR"]) + +local = 'scons-local-' + __version__ +if script_dir: + local = os.path.join(script_dir, local) +libs.append(os.path.abspath(local)) + +scons_version = 'scons-%s' % __version__ + +prefs = [] + +if sys.platform == 'win32': + # sys.prefix is (likely) C:\Python*; + # check only C:\Python*. + prefs.append(sys.prefix) + prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages')) +else: + # On other (POSIX) platforms, things are more complicated due to + # the variety of path names and library locations. Try to be smart + # about it. + if script_dir == 'bin': + # script_dir is `pwd`/bin; + # check `pwd`/lib/scons*. + prefs.append(os.getcwd()) + else: + if script_dir == '.' or script_dir == '': + script_dir = os.getcwd() + head, tail = os.path.split(script_dir) + if tail == "bin": + # script_dir is /foo/bin; + # check /foo/lib/scons*. + prefs.append(head) + + head, tail = os.path.split(sys.prefix) + if tail == "usr": + # sys.prefix is /foo/usr; + # check /foo/usr/lib/scons* first, + # then /foo/usr/local/lib/scons*. + prefs.append(sys.prefix) + prefs.append(os.path.join(sys.prefix, "local")) + elif tail == "local": + h, t = os.path.split(head) + if t == "usr": + # sys.prefix is /foo/usr/local; + # check /foo/usr/local/lib/scons* first, + # then /foo/usr/lib/scons*. + prefs.append(sys.prefix) + prefs.append(head) + else: + # sys.prefix is /foo/local; + # check only /foo/local/lib/scons*. + prefs.append(sys.prefix) + else: + # sys.prefix is /foo (ends in neither /usr or /local); + # check only /foo/lib/scons*. + prefs.append(sys.prefix) + + temp = map(lambda x: os.path.join(x, 'lib'), prefs) + temp.extend(map(lambda x: os.path.join(x, + 'lib', + 'python' + sys.version[:3], + 'site-packages'), + prefs)) + prefs = temp + + # Add the parent directory of the current python's library to the + # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64, + # not /usr/lib. + try: + libpath = os.__file__ + except AttributeError: + pass + else: + while libpath: + libpath, tail = os.path.split(libpath) + if tail[:6] == "python": + break + if libpath: + # Python library is in /usr/libfoo/python*; + # check /usr/libfoo/scons*. + prefs.append(libpath) + +# Look first for 'scons-__version__' in all of our preference libs, +# then for 'scons'. +libs.extend(map(lambda x: os.path.join(x, scons_version), prefs)) +libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs)) + +sys.path = libs + sys.path + +############################################################################## +# END STANDARD SCons SCRIPT HEADER +############################################################################## + +import SCons.Script +SCons.Script.main() diff --git a/scons/sconsign.py b/scons/sconsign.py new file mode 100755 index 000000000..c2c224fb0 --- /dev/null +++ b/scons/sconsign.py @@ -0,0 +1,497 @@ +#! /usr/bin/env python +# +# SCons - a Software Constructor +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/script/sconsign.py 2523 2007/12/12 09:37:41 knight" + +__version__ = "0.97.0d20071212" + +__build__ = "r2523" + +__buildsys__ = "bangkok" + +__date__ = "2007/12/12 09:37:41" + +__developer__ = "knight" + +import os +import os.path +import sys +import time + +############################################################################## +# BEGIN STANDARD SCons SCRIPT HEADER +# +# This is the cut-and-paste logic so that a self-contained script can +# interoperate correctly with different SCons versions and installation +# locations for the engine. If you modify anything in this section, you +# should also change other scripts that use this same header. +############################################################################## + +# Strip the script directory from sys.path() so on case-insensitive +# (WIN32) systems Python doesn't think that the "scons" script is the +# "SCons" package. Replace it with our own library directories +# (version-specific first, in case they installed by hand there, +# followed by generic) so we pick up the right version of the build +# engine modules if they're in either directory. + +script_dir = sys.path[0] + +if script_dir in sys.path: + sys.path.remove(script_dir) + +libs = [] + +if os.environ.has_key("SCONS_LIB_DIR"): + libs.append(os.environ["SCONS_LIB_DIR"]) + +local = 'scons-local-' + __version__ +if script_dir: + local = os.path.join(script_dir, local) +libs.append(os.path.abspath(local)) + +scons_version = 'scons-%s' % __version__ + +prefs = [] + +if sys.platform == 'win32': + # sys.prefix is (likely) C:\Python*; + # check only C:\Python*. + prefs.append(sys.prefix) + prefs.append(os.path.join(sys.prefix, 'Lib', 'site-packages')) +else: + # On other (POSIX) platforms, things are more complicated due to + # the variety of path names and library locations. Try to be smart + # about it. + if script_dir == 'bin': + # script_dir is `pwd`/bin; + # check `pwd`/lib/scons*. + prefs.append(os.getcwd()) + else: + if script_dir == '.' or script_dir == '': + script_dir = os.getcwd() + head, tail = os.path.split(script_dir) + if tail == "bin": + # script_dir is /foo/bin; + # check /foo/lib/scons*. + prefs.append(head) + + head, tail = os.path.split(sys.prefix) + if tail == "usr": + # sys.prefix is /foo/usr; + # check /foo/usr/lib/scons* first, + # then /foo/usr/local/lib/scons*. + prefs.append(sys.prefix) + prefs.append(os.path.join(sys.prefix, "local")) + elif tail == "local": + h, t = os.path.split(head) + if t == "usr": + # sys.prefix is /foo/usr/local; + # check /foo/usr/local/lib/scons* first, + # then /foo/usr/lib/scons*. + prefs.append(sys.prefix) + prefs.append(head) + else: + # sys.prefix is /foo/local; + # check only /foo/local/lib/scons*. + prefs.append(sys.prefix) + else: + # sys.prefix is /foo (ends in neither /usr or /local); + # check only /foo/lib/scons*. + prefs.append(sys.prefix) + + temp = map(lambda x: os.path.join(x, 'lib'), prefs) + temp.extend(map(lambda x: os.path.join(x, + 'lib', + 'python' + sys.version[:3], + 'site-packages'), + prefs)) + prefs = temp + + # Add the parent directory of the current python's library to the + # preferences. On SuSE-91/AMD64, for example, this is /usr/lib64, + # not /usr/lib. + try: + libpath = os.__file__ + except AttributeError: + pass + else: + while libpath: + libpath, tail = os.path.split(libpath) + if tail[:6] == "python": + break + if libpath: + # Python library is in /usr/libfoo/python*; + # check /usr/libfoo/scons*. + prefs.append(libpath) + +# Look first for 'scons-__version__' in all of our preference libs, +# then for 'scons'. +libs.extend(map(lambda x: os.path.join(x, scons_version), prefs)) +libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs)) + +sys.path = libs + sys.path + +############################################################################## +# END STANDARD SCons SCRIPT HEADER +############################################################################## + +import cPickle +import imp +import string +import whichdb + +import SCons.SConsign + +def my_whichdb(filename): + if filename[-7:] == ".dblite": + return "SCons.dblite" + try: + f = open(filename + ".dblite", "rb") + f.close() + return "SCons.dblite" + except IOError: + pass + return _orig_whichdb(filename) + +_orig_whichdb = whichdb.whichdb +whichdb.whichdb = my_whichdb + +def my_import(mname): + if '.' in mname: + i = string.rfind(mname, '.') + parent = my_import(mname[:i]) + fp, pathname, description = imp.find_module(mname[i+1:], + parent.__path__) + else: + fp, pathname, description = imp.find_module(mname) + return imp.load_module(mname, fp, pathname, description) + +class Flagger: + default_value = 1 + def __setitem__(self, item, value): + self.__dict__[item] = value + self.default_value = 0 + def __getitem__(self, item): + return self.__dict__.get(item, self.default_value) + +Do_Call = None +Print_Directories = [] +Print_Entries = [] +Print_Flags = Flagger() +Verbose = 0 +Readable = 0 + +def default_mapper(entry, name): + try: + val = eval("entry."+name) + except: + val = None + return str(val) + +def map_action(entry, name): + try: + bact = entry.bact + bactsig = entry.bactsig + except AttributeError: + return None + return '%s [%s]' % (bactsig, bact) + +def map_timestamp(entry, name): + try: + timestamp = entry.timestamp + except AttributeError: + timestamp = None + if Readable and timestamp: + return "'" + time.ctime(timestamp) + "'" + else: + return str(timestamp) + +def map_bkids(entry, name): + try: + bkids = entry.bsources + entry.bdepends + entry.bimplicit + bkidsigs = entry.bsourcesigs + entry.bdependsigs + entry.bimplicitsigs + except AttributeError: + return None + result = [] + for i in xrange(len(bkids)): + result.append(nodeinfo_string(bkids[i], bkidsigs[i], " ")) + if result == []: + return None + return string.join(result, "\n ") + +map_field = { + 'action' : map_action, + 'timestamp' : map_timestamp, + 'bkids' : map_bkids, +} + +map_name = { + 'implicit' : 'bkids', +} + +def field(name, entry, verbose=Verbose): + if not Print_Flags[name]: + return None + fieldname = map_name.get(name, name) + mapper = map_field.get(fieldname, default_mapper) + val = mapper(entry, name) + if verbose: + val = name + ": " + val + return val + +def nodeinfo_raw(name, ninfo, prefix=""): + # This just formats the dictionary, which we would normally use str() + # to do, except that we want the keys sorted for deterministic output. + d = ninfo.__dict__ + try: + keys = ninfo.field_list + ['_version_id'] + except AttributeError: + keys = d.keys() + keys.sort() + l = [] + for k in keys: + l.append('%s: %s' % (repr(k), repr(d.get(k)))) + return name + ': {' + string.join(l, ', ') + '}' + +def nodeinfo_cooked(name, ninfo, prefix=""): + try: + field_list = ninfo.field_list + except AttributeError: + field_list = [] + f = lambda x, ni=ninfo, v=Verbose: field(x, ni, v) + outlist = [name+':'] + filter(None, map(f, field_list)) + if Verbose: + sep = '\n ' + prefix + else: + sep = ' ' + return string.join(outlist, sep) + +nodeinfo_string = nodeinfo_cooked + +def printfield(name, entry, prefix=""): + outlist = field("implicit", entry, 0) + if outlist: + if Verbose: + print " implicit:" + print " " + outlist + outact = field("action", entry, 0) + if outact: + if Verbose: + print " action: " + outact + else: + print " " + outact + +def printentries(entries, location): + if Print_Entries: + for name in Print_Entries: + try: + entry = entries[name] + except KeyError: + sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, location)) + else: + try: + ninfo = entry.ninfo + except AttributeError: + print name + ":" + else: + print nodeinfo_string(name, entry.ninfo) + printfield(name, entry.binfo) + else: + names = entries.keys() + names.sort() + for name in names: + entry = entries[name] + try: + ninfo = entry.ninfo + except AttributeError: + print name + ":" + else: + print nodeinfo_string(name, entry.ninfo) + printfield(name, entry.binfo) + +class Do_SConsignDB: + def __init__(self, dbm_name, dbm): + self.dbm_name = dbm_name + self.dbm = dbm + + def __call__(self, fname): + # The *dbm modules stick their own file suffixes on the names + # that are passed in. This is causes us to jump through some + # hoops here to be able to allow the user + try: + # Try opening the specified file name. Example: + # SPECIFIED OPENED BY self.dbm.open() + # --------- ------------------------- + # .sconsign => .sconsign.dblite + # .sconsign.dblite => .sconsign.dblite.dblite + db = self.dbm.open(fname, "r") + except (IOError, OSError), e: + print_e = e + try: + # That didn't work, so try opening the base name, + # so that if the actually passed in 'sconsign.dblite' + # (for example), the dbm module will put the suffix back + # on for us and open it anyway. + db = self.dbm.open(os.path.splitext(fname)[0], "r") + except (IOError, OSError): + # That didn't work either. See if the file name + # they specified just exists (independent of the dbm + # suffix-mangling). + try: + open(fname, "r") + except (IOError, OSError), e: + # Nope, that file doesn't even exist, so report that + # fact back. + print_e = e + sys.stderr.write("sconsign: %s\n" % (print_e)) + return + except KeyboardInterrupt: + raise + except cPickle.UnpicklingError: + sys.stderr.write("sconsign: ignoring invalid `%s' file `%s'\n" % (self.dbm_name, fname)) + return + except Exception, e: + sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n" % (self.dbm_name, fname, e)) + return + + if Print_Directories: + for dir in Print_Directories: + try: + val = db[dir] + except KeyError: + sys.stderr.write("sconsign: no dir `%s' in `%s'\n" % (dir, args[0])) + else: + self.printentries(dir, val) + else: + keys = db.keys() + keys.sort() + for dir in keys: + self.printentries(dir, db[dir]) + + def printentries(self, dir, val): + print '=== ' + dir + ':' + printentries(cPickle.loads(val), dir) + +def Do_SConsignDir(name): + try: + fp = open(name, 'rb') + except (IOError, OSError), e: + sys.stderr.write("sconsign: %s\n" % (e)) + return + try: + sconsign = SCons.SConsign.Dir(fp) + except KeyboardInterrupt: + raise + except cPickle.UnpicklingError: + sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s'\n" % (name)) + return + except Exception, e: + sys.stderr.write("sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e)) + return + printentries(sconsign.entries, args[0]) + +############################################################################## + +import getopt + +helpstr = """\ +Usage: sconsign [OPTIONS] FILE [...] +Options: + -a, --act, --action Print build action information. + -c, --csig Print content signature information. + -d DIR, --dir=DIR Print only info about DIR. + -e ENTRY, --entry=ENTRY Print only info about ENTRY. + -f FORMAT, --format=FORMAT FILE is in the specified FORMAT. + -h, --help Print this message and exit. + -i, --implicit Print implicit dependency information. + -r, --readable Print timestamps in human-readable form. + --raw Print raw Python object representations. + -s, --size Print file sizes. + -t, --timestamp Print timestamp information. + -v, --verbose Verbose, describe each field. +""" + +opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv", + ['act', 'action', + 'csig', 'dir=', 'entry=', + 'format=', 'help', 'implicit', + 'raw', 'readable', + 'size', 'timestamp', 'verbose']) + + +for o, a in opts: + if o in ('-a', '--act', '--action'): + Print_Flags['action'] = 1 + elif o in ('-c', '--csig'): + Print_Flags['csig'] = 1 + elif o in ('-d', '--dir'): + Print_Directories.append(a) + elif o in ('-e', '--entry'): + Print_Entries.append(a) + elif o in ('-f', '--format'): + Module_Map = {'dblite' : 'SCons.dblite', + 'sconsign' : None} + dbm_name = Module_Map.get(a, a) + if dbm_name: + try: + dbm = my_import(dbm_name) + except: + sys.stderr.write("sconsign: illegal file format `%s'\n" % a) + print helpstr + sys.exit(2) + Do_Call = Do_SConsignDB(a, dbm) + else: + Do_Call = Do_SConsignDir + elif o in ('-h', '--help'): + print helpstr + sys.exit(0) + elif o in ('-i', '--implicit'): + Print_Flags['implicit'] = 1 + elif o in ('--raw',): + nodeinfo_string = nodeinfo_raw + elif o in ('-r', '--readable'): + Readable = 1 + elif o in ('-s', '--size'): + Print_Flags['size'] = 1 + elif o in ('-t', '--timestamp'): + Print_Flags['timestamp'] = 1 + elif o in ('-v', '--verbose'): + Verbose = 1 + +if Do_Call: + for a in args: + Do_Call(a) +else: + for a in args: + dbm_name = whichdb.whichdb(a) + if dbm_name: + Map_Module = {'SCons.dblite' : 'dblite'} + dbm = my_import(dbm_name) + Do_SConsignDB(Map_Module.get(dbm_name, dbm_name), dbm)(a) + else: + Do_SConsignDir(a) + +sys.exit(0)