diff options
author | Steven Knight <knight@baldmt.com> | 2005-01-08 22:38:53 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2005-01-08 22:38:53 (GMT) |
commit | 69e3c442cdfb846cbcba7702d500e237b66be71e (patch) | |
tree | 7efb9d5d344c6d7cd76ae432a075d5ac4276aace | |
parent | 3c23f9969cea7931a8dea998a33b15ab51fe1a5f (diff) | |
download | SCons-69e3c442cdfb846cbcba7702d500e237b66be71e.zip SCons-69e3c442cdfb846cbcba7702d500e237b66be71e.tar.gz SCons-69e3c442cdfb846cbcba7702d500e237b66be71e.tar.bz2 |
Support 'from SConsScript import *' to allow Python modules imported by SConscript files to get at the global builders and functions more easily.
-rw-r--r-- | doc/man/scons.1 | 24 | ||||
-rw-r--r-- | src/CHANGES.txt | 4 | ||||
-rw-r--r-- | src/engine/MANIFEST.in | 1 | ||||
-rw-r--r-- | src/engine/SCons/Script/Main.py | 1144 | ||||
-rw-r--r-- | src/engine/SCons/Script/SConscript.py | 272 | ||||
-rw-r--r-- | src/engine/SCons/Script/__init__.py | 1285 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mslink.py | 10 | ||||
-rw-r--r-- | src/engine/SCons/Tool/msvs.py | 10 | ||||
-rw-r--r-- | test/NodeOps.py | 2 | ||||
-rw-r--r-- | test/Script-import.py | 60 | ||||
-rw-r--r-- | test/option--profile.py | 19 |
11 files changed, 1514 insertions, 1317 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 7b34e8a..9fc5a6a 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -1257,6 +1257,15 @@ environment that consists of the tools and values that .B scons has determined are appropriate for the local system. +Builder methods that can be called without an explicit +environment may be called from custom Python modules that you +import into an SConscript file by adding the following +to the Python module: + +.ES +from SCons.Script import * +.EE + All builder methods return a list of Nodes that represent the target or targets that will be built. A @@ -2203,6 +2212,14 @@ environment it looks like: If you can call the functionality in both ways, then both forms are listed. +Global functions may be called from custom Python modules that you +import into an SConscript file by adding the following +to the Python module: + +.ES +from SCons.Script import * +.EE + Except where otherwise noted, the same-named construction environment method @@ -4280,6 +4297,13 @@ In addition to the global functions and methods, supports a number of Python variables that can be used in SConscript files to affect how you want the build to be performed. +These variables may be accessed from custom Python modules that you +import into an SConscript file by adding the following +to the Python module: + +.ES +from SCons.Script import * +.EE '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP diff --git a/src/CHANGES.txt b/src/CHANGES.txt index e13fa39..fb98cce 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -170,6 +170,10 @@ RELEASE 0.97 - XXX duplicate entries from multiple calls. Add a "unique" keyword argument to allow the old behavior to be specified. + - Allow the library modules imported by an SConscript file to get at + all of the normally-available global functions and variables by saying + "from SCons.Script import *". + From Wayne Lee: - Avoid "maximum recursion limit" errors when removing $(-$) pairs diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 133ccad..5c10cae 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -43,6 +43,7 @@ SCons/Scanner/IDL.py SCons/Scanner/Prog.py SCons/SConf.py SCons/SConsign.py +SCons/Script/Main.py SCons/Script/SConscript.py SCons/Script/__init__.py SCons/Sig/__init__.py diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py new file mode 100644 index 0000000..1bd6939 --- /dev/null +++ b/src/engine/SCons/Script/Main.py @@ -0,0 +1,1144 @@ +"""SCons.Script + +This file implements the main() function used by the scons script. + +Architecturally, this *is* the scons script, and will likely only be +called from the external "scons" wrapper. Consequently, anything here +should not be, or be considered, part of the build engine. If it's +something that we expect other software to want to use, it should go in +some other module. If it's specific to the "scons" script invocation, +it goes here. + +""" + +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import os +import os.path +import random +import string +import sys +import time +import traceback + +# 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 version directory so, if +# if they're there, we pick up the right version of the build engine +# modules. +#sys.path = [os.path.join(sys.prefix, +# 'lib', +# 'scons-%d' % SCons.__version__)] + sys.path[1:] + +import SCons.Debug +import SCons.Defaults +import SCons.Environment +import SCons.Errors +import SCons.Job +import SCons.Node +import SCons.Node.FS +from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError +import SCons.SConf +import SCons.Sig +import SCons.Taskmaster +import SCons.Util +import SCons.Warnings + +# +display = SCons.Util.display +progress_display = SCons.Util.DisplayEngine() + +# Task control. +# +class BuildTask(SCons.Taskmaster.Task): + """An SCons build task.""" + def display(self, message): + display('scons: ' + message) + + def execute(self): + target = self.targets[0] + if target.get_state() == SCons.Node.up_to_date: + if self.top and target.has_builder(): + display("scons: `%s' is up to date." % str(self.node)) + elif target.has_builder() and not hasattr(target.builder, 'status'): + if print_time: + start_time = time.time() + SCons.Taskmaster.Task.execute(self) + if print_time: + finish_time = time.time() + global command_time + command_time = command_time+finish_time-start_time + print "Command execution time: %f seconds"%(finish_time-start_time) + + def do_failed(self, status=2): + global exit_status + if ignore_errors: + SCons.Taskmaster.Task.executed(self) + elif keep_going_on_error: + SCons.Taskmaster.Task.fail_continue(self) + exit_status = status + else: + SCons.Taskmaster.Task.fail_stop(self) + exit_status = status + + def executed(self): + t = self.targets[0] + if self.top and not t.has_builder() and not t.side_effect: + if not t.exists(): + sys.stderr.write("scons: *** Do not know how to make target `%s'." % t) + if not keep_going_on_error: + sys.stderr.write(" Stop.") + sys.stderr.write("\n") + self.do_failed() + else: + print "scons: Nothing to be done for `%s'." % t + SCons.Taskmaster.Task.executed(self) + else: + SCons.Taskmaster.Task.executed(self) + + def failed(self): + # Handle the failure of a build task. The primary purpose here + # is to display the various types of Errors and Exceptions + # appropriately. + status = 2 + exc_info = self.exc_info() + try: + t, e, tb = exc_info + except ValueError: + t, e = exc_info + tb = None + if t is None: + # The Taskmaster didn't record an exception for this Task; + # see if the sys module has one. + t, e = sys.exc_info()[:2] + + if t == SCons.Errors.BuildError: + fname = e.node + if SCons.Util.is_List(e.node): + fname = string.join(map(str, e.node), ', ') + sys.stderr.write("scons: *** [%s] %s\n" % (fname, e.errstr)) + if e.errstr == 'Exception': + traceback.print_exception(e.args[0], e.args[1], e.args[2]) + elif t == SCons.Errors.ExplicitExit: + status = e.status + sys.stderr.write("scons: *** [%s] Explicit exit, status %s\n" % (e.node, e.status)) + else: + if e is None: + e = t + s = str(e) + if t == SCons.Errors.StopError and not keep_going_on_error: + s = s + ' Stop.' + sys.stderr.write("scons: *** %s\n" % s) + + if tb and print_stacktrace: + sys.stderr.write("scons: internal stack trace:\n") + traceback.print_tb(tb, file=sys.stderr) + + self.do_failed(status) + + self.exc_clear() + + def postprocess(self): + if self.top: + t = self.targets[0] + if print_tree: + print + SCons.Util.print_tree(t, get_all_children) + if print_stree: + print + SCons.Util.print_tree(t, get_all_children, showtags=2) + if print_dtree: + print + SCons.Util.print_tree(t, get_derived_children) + if print_includes: + tree = t.render_include_tree() + if tree: + print + print tree + SCons.Taskmaster.Task.postprocess(self) + + def make_ready(self): + """Make a task ready for execution""" + SCons.Taskmaster.Task.make_ready(self) + if self.out_of_date and print_explanations: + explanation = self.out_of_date[0].explain() + if explanation: + sys.stdout.write("scons: " + explanation) + +class CleanTask(SCons.Taskmaster.Task): + """An SCons clean task.""" + def show(self): + if (self.targets[0].has_builder() or self.targets[0].side_effect) \ + and not os.path.isdir(str(self.targets[0])): + display("Removed " + str(self.targets[0])) + if SCons.Environment.CleanTargets.has_key(self.targets[0]): + files = SCons.Environment.CleanTargets[self.targets[0]] + for f in files: + SCons.Util.fs_delete(str(f), 0) + + def remove(self): + if self.targets[0].has_builder() or self.targets[0].side_effect: + for t in self.targets: + try: + removed = t.remove() + except OSError, e: + print "scons: Could not remove '%s':" % str(t), e.strerror + else: + if removed: + display("Removed " + str(t)) + if SCons.Environment.CleanTargets.has_key(self.targets[0]): + files = SCons.Environment.CleanTargets[self.targets[0]] + for f in files: + SCons.Util.fs_delete(str(f)) + + execute = remove + + # Have the taskmaster arrange to "execute" all of the targets, because + # we'll figure out ourselves (in remove() or show() above) whether + # anything really needs to be done. + make_ready = SCons.Taskmaster.Task.make_ready_all + + def prepare(self): + pass + +class QuestionTask(SCons.Taskmaster.Task): + """An SCons task for the -q (question) option.""" + def prepare(self): + pass + + def execute(self): + if self.targets[0].get_state() != SCons.Node.up_to_date: + global exit_status + exit_status = 1 + self.tm.stop() + + def executed(self): + pass + +# Global variables + +keep_going_on_error = 0 +print_count = 0 +print_dtree = 0 +print_explanations = 0 +print_includes = 0 +print_objects = 0 +print_stacktrace = 0 +print_stree = 0 +print_time = 0 +print_tree = 0 +memory_stats = None +ignore_errors = 0 +sconscript_time = 0 +command_time = 0 +exit_status = 0 # exit status, assume success by default +profiling = 0 +repositories = [] +num_jobs = 1 # this is modifed by SConscript.SetJobs() + +# utility functions + +def get_all_children(node): return node.all_children() + +def get_derived_children(node): + children = node.all_children(None) + return filter(lambda x: x.has_builder(), children) + +def _scons_syntax_error(e): + """Handle syntax errors. Print out a message and show where the error + occurred. + """ + etype, value, tb = sys.exc_info() + lines = traceback.format_exception_only(etype, value) + for line in lines: + sys.stderr.write(line+'\n') + sys.exit(2) + +def find_deepest_user_frame(tb): + """ + Find the deepest stack frame that is not part of SCons. + + Input is a "pre-processed" stack trace in the form + returned by traceback.extract_tb() or traceback.extract_stack() + """ + + tb.reverse() + + # find the deepest traceback frame that is not part + # of SCons: + for frame in tb: + filename = frame[0] + if string.find(filename, os.sep+'SCons'+os.sep) == -1: + return frame + return tb[0] + +def _scons_user_error(e): + """Handle user errors. Print out a message and a description of the + error, along with the line number and routine where it occured. + The file and line number will be the deepest stack frame that is + not part of SCons itself. + """ + etype, value, tb = sys.exc_info() + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) + sys.stderr.write("\nscons: *** %s\n" % value) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + sys.exit(2) + +def _scons_user_warning(e): + """Handle user warnings. Print out a message and a description of + the warning, along with the line number and routine where it occured. + The file and line number will be the deepest stack frame that is + not part of SCons itself. + """ + etype, value, tb = sys.exc_info() + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) + sys.stderr.write("\nscons: warning: %s\n" % e) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + +def _scons_internal_warning(e): + """Slightly different from _scons_user_warning in that we use the + *current call stack* rather than sys.exc_info() to get our stack trace. + This is used by the warnings framework to print warnings.""" + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack()) + sys.stderr.write("\nscons: warning: %s\n" % e[0]) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + +def _scons_internal_error(): + """Handle all errors but user errors. Print out a message telling + the user what to do in this case and print a normal trace. + """ + print 'internal error' + traceback.print_exc() + sys.exit(2) + +def _varargs(option, parser): + value = None + if parser.rargs: + arg = parser.rargs[0] + if arg[0] != "-": + value = arg + del parser.rargs[0] + return value + +def _setup_warn(arg): + """The --warn option. An argument to this option + should be of the form <warning-class> or no-<warning-class>. + The warning class is munged in order to get an actual class + name from the SCons.Warnings module to enable or disable. + The supplied <warning-class> is split on hyphens, each element + is captialized, then smushed back together. Then the string + "SCons.Warnings." is added to the front and "Warning" is added + to the back to get the fully qualified class name. + + For example, --warn=deprecated will enable the + SCons.Warnings.DeprecatedWarning class. + + --warn=no-dependency will disable the + SCons.Warnings.DependencyWarning class. + + As a special case, --warn=all and --warn=no-all + will enable or disable (respectively) the base + class of all warnings, which is SCons.Warning.Warning.""" + + elems = string.split(string.lower(arg), '-') + enable = 1 + if elems[0] == 'no': + enable = 0 + del elems[0] + + if len(elems) == 1 and elems[0] == 'all': + class_name = "Warning" + else: + def _capitalize(s): + if s[:5] == "scons": + return "SCons" + s[5:] + else: + return string.capitalize(s) + class_name = string.join(map(_capitalize, elems), '') + "Warning" + try: + clazz = getattr(SCons.Warnings, class_name) + except AttributeError: + sys.stderr.write("No warning type: '%s'\n" % arg) + else: + if enable: + SCons.Warnings.enableWarningClass(clazz) + else: + SCons.Warnings.suppressWarningClass(clazz) + +def _SConstruct_exists(dirname=''): + """This function checks that an SConstruct file exists in a directory. + If so, it returns the path of the file. By default, it checks the + current directory. + """ + global repositories + for file in ['SConstruct', 'Sconstruct', 'sconstruct']: + sfile = os.path.join(dirname, file) + if os.path.isfile(sfile): + return sfile + if not os.path.isabs(sfile): + for rep in repositories: + if os.path.isfile(os.path.join(rep, sfile)): + return sfile + return None + +def _set_globals(options): + global repositories, keep_going_on_error, ignore_errors + global print_count, print_dtree + global print_explanations, print_includes + global print_objects, print_stacktrace, print_stree + global print_time, print_tree + global memory_outf, memory_stats + + if options.repository: + repositories.extend(options.repository) + keep_going_on_error = options.keep_going + try: + if options.debug: + if options.debug == "count": + print_count = 1 + elif options.debug == "dtree": + print_dtree = 1 + elif options.debug == "explain": + print_explanations = 1 + elif options.debug == "findlibs": + SCons.Scanner.Prog.print_find_libs = "findlibs" + elif options.debug == "includes": + print_includes = 1 + elif options.debug == "memory": + memory_stats = [] + memory_outf = sys.stdout + elif options.debug == "objects": + print_objects = 1 + elif options.debug == "presub": + SCons.Action.print_actions_presub = 1 + elif options.debug == "stacktrace": + print_stacktrace = 1 + elif options.debug == "stree": + print_stree = 1 + elif options.debug == "time": + print_time = 1 + elif options.debug == "tree": + print_tree = 1 + except AttributeError: + pass + ignore_errors = options.ignore_errors + +def _create_path(plist): + path = '.' + for d in plist: + if os.path.isabs(d): + path = d + else: + path = path + '/' + d + return path + + +class OptParser(OptionParser): + def __init__(self): + import __main__ + import SCons + parts = ["SCons by Steven Knight et al.:\n"] + try: + parts.append("\tscript: v%s.%s, %s, by %s on %s\n" % (__main__.__version__, + __main__.__build__, + __main__.__date__, + __main__.__developer__, + __main__.__buildsys__)) + except KeyboardInterrupt: + raise + except: + # On win32 there is no scons.py, so there is no __main__.__version__, + # hence there is no script version. + pass + parts.append("\tengine: v%s.%s, %s, by %s on %s\n" % (SCons.__version__, + SCons.__build__, + SCons.__date__, + SCons.__developer__, + SCons.__buildsys__)) + parts.append("__COPYRIGHT__") + OptionParser.__init__(self, version=string.join(parts, ''), + usage="usage: scons [OPTION] [TARGET] ...") + + # options ignored for compatibility + def opt_ignore(option, opt, value, parser): + sys.stderr.write("Warning: ignoring %s option\n" % opt) + self.add_option("-b", "-m", "-S", "-t", "--no-keep-going", "--stop", + "--touch", action="callback", callback=opt_ignore, + help="Ignored for compatibility.") + + self.add_option('-c', '--clean', '--remove', action="store_true", + dest="clean", + help="Remove specified targets and dependencies.") + + self.add_option('-C', '--directory', type="string", action = "append", + metavar="DIR", + help="Change to DIR before doing anything.") + + self.add_option('--cache-disable', '--no-cache', + action="store_true", dest='cache_disable', default=0, + help="Do not retrieve built targets from CacheDir.") + + self.add_option('--cache-force', '--cache-populate', + action="store_true", dest='cache_force', default=0, + help="Copy already-built targets into the CacheDir.") + + self.add_option('--cache-show', + action="store_true", dest='cache_show', default=0, + help="Print build actions for files from CacheDir.") + + config_options = ["auto", "force" ,"cache"] + + def opt_config(option, opt, value, parser, c_options=config_options): + if value in c_options: + parser.values.config = value + else: + raise OptionValueError("Warning: %s is not a valid config type" % value) + self.add_option('--config', action="callback", type="string", + callback=opt_config, nargs=1, dest="config", + metavar="MODE", default="auto", + help="Controls Configure subsystem: " + "%s." % string.join(config_options, ", ")) + + def opt_not_yet(option, opt, value, parser): + sys.stderr.write("Warning: the %s option is not yet implemented\n" % opt) + sys.exit(0) + self.add_option('-d', action="callback", + callback=opt_not_yet, + help = "Print file dependency information.") + + self.add_option('-D', action="store_const", const=2, dest="climb_up", + help="Search up directory tree for SConstruct, " + "build all Default() targets.") + + debug_options = ["count", "dtree", "explain", "findlibs", + "includes", "memory", "objects", + "pdb", "presub", "stacktrace", "stree", + "time", "tree"] + + def opt_debug(option, opt, value, parser, debug_options=debug_options): + if value in debug_options: + parser.values.debug = value + else: + raise OptionValueError("Warning: %s is not a valid debug type" % value) + self.add_option('--debug', action="callback", type="string", + callback=opt_debug, nargs=1, dest="debug", + metavar="TYPE", + help="Print various types of debugging information: " + "%s." % string.join(debug_options, ", ")) + + def opt_duplicate(option, opt, value, parser): + if not value in SCons.Node.FS.Valid_Duplicates: + raise OptionValueError("`%s' is not a valid duplication style." % value) + parser.values.duplicate = value + # Set the duplicate style right away so it can affect linking + # of SConscript files. + SCons.Node.FS.set_duplicate(value) + self.add_option('--duplicate', action="callback", type="string", + callback=opt_duplicate, nargs=1, dest="duplicate", + help="Set the preferred duplication methods. Must be one of " + + string.join(SCons.Node.FS.Valid_Duplicates, ", ")) + + self.add_option('-f', '--file', '--makefile', '--sconstruct', + action="append", nargs=1, + help="Read FILE as the top-level SConstruct file.") + + self.add_option('-h', '--help', action="store_true", default=0, + dest="help_msg", + help="Print defined help message, or this one.") + + self.add_option("-H", "--help-options", + action="help", + help="Print this message and exit.") + + self.add_option('-i', '--ignore-errors', action="store_true", + default=0, dest='ignore_errors', + help="Ignore errors from build actions.") + + self.add_option('-I', '--include-dir', action="append", + dest='include_dir', metavar="DIR", + help="Search DIR for imported Python modules.") + + self.add_option('--implicit-cache', action="store_true", + dest='implicit_cache', + help="Cache implicit dependencies") + + self.add_option('--implicit-deps-changed', action="store_true", + default=0, dest='implicit_deps_changed', + help="Ignore cached implicit dependencies.") + self.add_option('--implicit-deps-unchanged', action="store_true", + default=0, dest='implicit_deps_unchanged', + help="Ignore changes in implicit dependencies.") + + def opt_j(option, opt, value, parser): + value = int(value) + parser.values.num_jobs = value + self.add_option('-j', '--jobs', action="callback", type="int", + callback=opt_j, metavar="N", + help="Allow N jobs at once.") + + self.add_option('-k', '--keep-going', action="store_true", default=0, + dest='keep_going', + help="Keep going when a target can't be made.") + + self.add_option('--max-drift', type="int", action="store", + dest='max_drift', metavar="N", + help="Set maximum system clock drift to N seconds.") + + self.add_option('-n', '--no-exec', '--just-print', '--dry-run', + '--recon', action="store_true", dest='noexec', + default=0, help="Don't build; just print commands.") + + def opt_profile(option, opt, value, parser): + global profiling + if not profiling: + profiling = 1 + import profile + profile.run('SCons.Script.Main.main()', value) + sys.exit(exit_status) + self.add_option('--profile', nargs=1, action="callback", + callback=opt_profile, type="string", dest="profile", + metavar="FILE", + help="Profile SCons and put results in FILE.") + + self.add_option('-q', '--question', action="store_true", default=0, + help="Don't build; exit status says if up to date.") + + self.add_option('-Q', dest='no_progress', action="store_true", + default=0, + help="Suppress \"Reading/Building\" progress messages.") + + self.add_option('--random', dest="random", action="store_true", + default=0, help="Build dependencies in random order.") + + self.add_option('-s', '--silent', '--quiet', action="store_true", + default=0, help="Don't print commands.") + + self.add_option('-u', '--up', '--search-up', action="store_const", + dest="climb_up", default=0, const=1, + help="Search up directory tree for SConstruct, " + "build targets at or below current directory.") + self.add_option('-U', action="store_const", dest="climb_up", + default=0, const=3, + help="Search up directory tree for SConstruct, " + "build Default() targets from local SConscript.") + + self.add_option("-v", "--version", + action="version", + help="Print the SCons version number and exit.") + + self.add_option('--warn', '--warning', nargs=1, action="store", + metavar="WARNING-SPEC", + help="Enable or disable warnings.") + + self.add_option('-Y', '--repository', nargs=1, action="append", + help="Search REPOSITORY for source and target files.") + + self.add_option('-e', '--environment-overrides', action="callback", + callback=opt_not_yet, + # help="Environment variables override makefiles." + help=SUPPRESS_HELP) + self.add_option('-l', '--load-average', '--max-load', action="callback", + callback=opt_not_yet, type="int", dest="load_average", + # action="store", + # help="Don't start multiple jobs unless load is below " + # "LOAD-AVERAGE." + # type="int", + help=SUPPRESS_HELP) + self.add_option('--list-derived', action="callback", + callback=opt_not_yet, + # help="Don't build; list files that would be built." + help=SUPPRESS_HELP) + self.add_option('--list-actions', action="callback", + callback=opt_not_yet, + # help="Don't build; list files and build actions." + help=SUPPRESS_HELP) + self.add_option('--list-where', action="callback", + callback=opt_not_yet, + # help="Don't build; list files and where defined." + help=SUPPRESS_HELP) + self.add_option('-o', '--old-file', '--assume-old', action="callback", + callback=opt_not_yet, type="string", dest="old_file", + # help = "Consider FILE to be old; don't rebuild it." + help=SUPPRESS_HELP) + self.add_option('--override', action="callback", dest="override", + callback=opt_not_yet, type="string", + # help="Override variables as specified in FILE." + help=SUPPRESS_HELP) + self.add_option('-p', action="callback", + callback=opt_not_yet, + # help="Print internal environments/objects." + help=SUPPRESS_HELP) + self.add_option('-r', '-R', '--no-builtin-rules', + '--no-builtin-variables', action="callback", + callback=opt_not_yet, + # help="Clear default environments and variables." + help=SUPPRESS_HELP) + self.add_option('-w', '--print-directory', action="callback", + callback=opt_not_yet, + # help="Print the current directory." + help=SUPPRESS_HELP) + self.add_option('--no-print-directory', action="callback", + callback=opt_not_yet, + # help="Turn off -w, even if it was turned on implicitly." + help=SUPPRESS_HELP) + self.add_option('--write-filenames', action="callback", + callback=opt_not_yet, type="string", dest="write_filenames", + # help="Write all filenames examined into FILE." + help=SUPPRESS_HELP) + self.add_option('-W', '--what-if', '--new-file', '--assume-new', + dest="new_file", + action="callback", callback=opt_not_yet, type="string", + # help="Consider FILE to be changed." + help=SUPPRESS_HELP) + self.add_option('--warn-undefined-variables', action="callback", + callback=opt_not_yet, + # help="Warn when an undefined variable is referenced." + help=SUPPRESS_HELP) + + def parse_args(self, args=None, values=None): + opt, arglist = OptionParser.parse_args(self, args, values) + if opt.implicit_deps_changed or opt.implicit_deps_unchanged: + opt.implicit_cache = 1 + return opt, arglist + +class SConscriptSettableOptions: + """This class wraps an OptParser instance and provides + uniform access to options that can be either set on the command + line or from a SConscript file. A value specified on the command + line always overrides a value set in a SConscript file. + Not all command line options are SConscript settable, and the ones + that are must be explicitly added to settable dictionary and optionally + validated and coerced in the set() method.""" + + def __init__(self, options): + self.options = options + + # This dictionary stores the defaults for all the SConscript + # settable options, as well as indicating which options + # are SConscript settable. + self.settable = {'num_jobs':1, + 'max_drift':SCons.Sig.default_max_drift, + 'implicit_cache':0, + 'clean':0, + 'duplicate':'hard-soft-copy'} + + def get(self, name): + if not self.settable.has_key(name): + raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name + if hasattr(self.options, name) and getattr(self.options, name) is not None: + return getattr(self.options, name) + else: + return self.settable[name] + + def set(self, name, value): + if not self.settable.has_key(name): + raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name + + if name == 'num_jobs': + try: + value = int(value) + if value < 1: + raise ValueError + except ValueError: + raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value) + elif name == 'max_drift': + try: + value = int(value) + except ValueError: + raise SCons.Errors.UserError, "An integer is required: %s"%repr(value) + elif name == 'duplicate': + try: + value = str(value) + except ValueError: + raise SCons.Errors.UserError, "A string is required: %s"%repr(value) + if not value in SCons.Node.FS.Valid_Duplicates: + raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value + # Set the duplicate stye right away so it can affect linking + # of SConscript files. + SCons.Node.FS.set_duplicate(value) + + self.settable[name] = value + + +def _main(args, parser): + targets = [] + fs = SCons.Node.FS.default_fs + + # Enable deprecated warnings by default. + SCons.Warnings._warningOut = _scons_internal_warning + SCons.Warnings.enableWarningClass(SCons.Warnings.CorruptSConsignWarning) + SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning) + SCons.Warnings.enableWarningClass(SCons.Warnings.DuplicateEnvironmentWarning) + SCons.Warnings.enableWarningClass(SCons.Warnings.MissingSConscriptWarning) + SCons.Warnings.enableWarningClass(SCons.Warnings.NoParallelSupportWarning) + # This is good for newbies, and hopefully most everyone else too. + SCons.Warnings.enableWarningClass(SCons.Warnings.MisleadingKeywordsWarning) + + global ssoptions + ssoptions = SConscriptSettableOptions(options) + + _set_globals(options) + SCons.Node.implicit_cache = options.implicit_cache + SCons.Node.implicit_deps_changed = options.implicit_deps_changed + SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged + if options.warn: + _setup_warn(options.warn) + if options.noexec: + SCons.SConf.dryrun = 1 + SCons.Action.execute_actions = None + CleanTask.execute = CleanTask.show + if options.question: + SCons.SConf.dryrun = 1 + SCons.SConf.SetCacheMode(options.config) + SCons.SConf.SetProgressDisplay(progress_display) + + if options.no_progress or options.silent: + progress_display.set_mode(0) + if options.silent: + display.set_mode(0) + if options.silent: + SCons.Action.print_actions = None + if options.cache_disable: + def disable(self): pass + fs.CacheDir = disable + if options.cache_force: + fs.cache_force = 1 + if options.cache_show: + fs.cache_show = 1 + if options.directory: + cdir = _create_path(options.directory) + try: + os.chdir(cdir) + except OSError: + sys.stderr.write("Could not change directory to %s\n" % cdir) + + xmit_args = [] + for a in args: + if '=' in a: + xmit_args.append(a) + else: + targets.append(a) + SCons.Script._Add_Arguments(xmit_args) + SCons.Script._Add_Targets(targets) + + target_top = None + if options.climb_up: + target_top = '.' # directory to prepend to targets + script_dir = os.getcwd() # location of script + while script_dir and not _SConstruct_exists(script_dir): + script_dir, last_part = os.path.split(script_dir) + if last_part: + target_top = os.path.join(last_part, target_top) + else: + script_dir = '' + if script_dir: + display("scons: Entering directory `%s'" % script_dir) + os.chdir(script_dir) + else: + raise SCons.Errors.UserError, "No SConstruct file found." + + fs.set_toplevel_dir(os.getcwd()) + + scripts = [] + if options.file: + scripts.extend(options.file) + if not scripts: + sfile = _SConstruct_exists() + if sfile: + scripts.append(sfile) + + if options.help_msg: + if not scripts: + # There's no SConstruct, but they specified -h. + # Give them the options usage now, before we fail + # trying to read a non-existent SConstruct file. + parser.print_help() + sys.exit(0) + + if not scripts: + raise SCons.Errors.UserError, "No SConstruct file found." + + if scripts[0] == "-": + d = fs.getcwd() + else: + d = fs.File(scripts[0]).dir + fs.set_SConstruct_dir(d) + + class Unbuffered: + def __init__(self, file): + self.file = file + def write(self, arg): + self.file.write(arg) + self.file.flush() + def __getattr__(self, attr): + return getattr(self.file, attr) + + sys.stdout = Unbuffered(sys.stdout) + + if options.include_dir: + sys.path = options.include_dir + sys.path + + global repositories + for rep in repositories: + fs.Repository(rep) + + if not memory_stats is None: memory_stats.append(SCons.Debug.memory()) + + progress_display("scons: Reading SConscript files ...") + + start_time = time.time() + try: + for script in scripts: + SCons.Script._SConscript._SConscript(fs, script) + except SCons.Errors.StopError, e: + # We had problems reading an SConscript file, such as it + # couldn't be copied in to the BuildDir. Since we're just + # reading SConscript files and haven't started building + # things yet, stop regardless of whether they used -i or -k + # or anything else. + global exit_status + sys.stderr.write("scons: *** %s Stop.\n" % e) + exit_status = 2 + sys.exit(exit_status) + global sconscript_time + sconscript_time = time.time() - start_time + SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) + progress_display("scons: done reading SConscript files.") + + # Tell the Node.FS subsystem that we're all done reading the + # SConscript files and calling Repository() and BuildDir() and the + # like, so it can go ahead and start memoizing the string values of + # file system nodes. + SCons.Node.FS.save_strings(1) + + if not memory_stats is None: memory_stats.append(SCons.Debug.memory()) + + fs.chdir(fs.Top) + + if options.help_msg: + help_text = SCons.Script.help_text + if help_text is None: + # They specified -h, but there was no Help() inside the + # SConscript files. Give them the options usage. + parser.print_help(sys.stdout) + else: + print help_text + print "Use scons -H for help about command-line options." + sys.exit(0) + + # Now that we've read the SConscripts we can set the options + # that are SConscript settable: + SCons.Node.implicit_cache = ssoptions.get('implicit_cache') + SCons.Node.FS.set_duplicate(ssoptions.get('duplicate')) + + lookup_top = None + if targets: + # They specified targets on the command line, so if they + # used -u, -U or -D, we have to look up targets relative + # to the top, but we build whatever they specified. + if target_top: + lookup_top = fs.Dir(target_top) + target_top = None + else: + # There are no targets specified on the command line, + # so if they used -u, -U or -D, we may have to restrict + # what actually gets built. + d = None + if target_top: + if options.climb_up == 1: + # -u, local directory and below + target_top = fs.Dir(target_top) + lookup_top = target_top + elif options.climb_up == 2: + # -D, all Default() targets + target_top = None + lookup_top = None + elif options.climb_up == 3: + # -U, local SConscript Default() targets + target_top = fs.Dir(target_top) + def check_dir(x, target_top=target_top): + if hasattr(x, 'cwd') and not x.cwd is None: + cwd = x.cwd.srcnode() + return cwd == target_top + else: + # x doesn't have a cwd, so it's either not a target, + # or not a file, so go ahead and keep it as a default + # target and let the engine sort it out: + return 1 + d = filter(check_dir, SCons.Script.DEFAULT_TARGETS) + SCons.Script.DEFAULT_TARGETS[:] = d + target_top = None + lookup_top = None + + targets = SCons.Script._Get_Default_Targets(d, fs) + + if not targets: + sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") + sys.exit(2) + + def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): + if isinstance(x, SCons.Node.Node): + node = x + else: + node = SCons.Node.Alias.default_ans.lookup(x) + if node is None: + node = fs.Entry(x, directory=ltop, create=1) + if ttop and not node.is_under(ttop): + if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node): + node = ttop + else: + node = None + return node + + nodes = filter(lambda x: x is not None, map(Entry, targets)) + + task_class = BuildTask # default action is to build targets + opening_message = "Building targets ..." + closing_message = "done building targets." + if keep_going_on_error: + failure_message = "done building targets (errors occurred during build)." + else: + failure_message = "building terminated because of errors." + if options.question: + task_class = QuestionTask + try: + if ssoptions.get('clean'): + task_class = CleanTask + opening_message = "Cleaning targets ..." + closing_message = "done cleaning targets." + if keep_going_on_error: + closing_message = "done cleaning targets (errors occurred during clean)." + else: + failure_message = "cleaning terminated because of errors." + except AttributeError: + pass + + SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift') + + if options.random: + def order(dependencies): + """Randomize the dependencies.""" + # This is cribbed from the implementation of + # random.shuffle() in Python 2.X. + d = dependencies + for i in xrange(len(d)-1, 0, -1): + j = int(random.random() * (i+1)) + d[i], d[j] = d[j], d[i] + return d + else: + def order(dependencies): + """Leave the order of dependencies alone.""" + return dependencies + + progress_display("scons: " + opening_message) + taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order) + + nj = ssoptions.get('num_jobs') + jobs = SCons.Job.Jobs(nj, taskmaster) + if nj > 1 and jobs.num_jobs == 1: + msg = "parallel builds are unsupported by this version of Python;\n" + \ + "\tignoring -j or num_jobs option.\n" + SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) + + if not memory_stats is None: memory_stats.append(SCons.Debug.memory()) + + try: + jobs.run() + finally: + if exit_status: + progress_display("scons: " + failure_message) + else: + progress_display("scons: " + closing_message) + if not options.noexec: + SCons.SConsign.write() + + if not memory_stats is None: + memory_stats.append(SCons.Debug.memory()) + when = [ + 'before SConscript files', + 'after SConscript files', + 'before building', + 'after building', + ] + for i in xrange(len(when)): + memory_outf.write('Memory %s: %d\n' % (when[i], memory_stats[i])) + + if print_count: + SCons.Debug.countLoggedInstances('*') + + if print_objects: + SCons.Debug.listLoggedInstances('*') + #SCons.Debug.dumpLoggedInstances('*') + +def _exec_main(): + all_args = sys.argv[1:] + try: + all_args = string.split(os.environ['SCONSFLAGS']) + all_args + except KeyError: + # it's OK if there's no SCONSFLAGS + pass + parser = OptParser() + global options + options, args = parser.parse_args(all_args) + if options.debug == "pdb": + import pdb + pdb.Pdb().runcall(_main, args, parser) + else: + _main(args, parser) + +def main(): + global exit_status + + try: + _exec_main() + except SystemExit, s: + if s: + exit_status = s + except KeyboardInterrupt: + print "Build interrupted." + sys.exit(2) + except SyntaxError, e: + _scons_syntax_error(e) + except SCons.Errors.InternalError: + _scons_internal_error() + except SCons.Errors.UserError, e: + _scons_user_error(e) + except: + # An exception here is likely a builtin Python exception Python + # code in an SConscript file. Show them precisely what the + # problem was and where it happened. + SCons.Script._SConscript.SConscript_exception() + sys.exit(2) + + if print_time: + total_time = time.time()-SCons.Script.start_time + scons_time = total_time-sconscript_time-command_time + print "Total build time: %f seconds"%total_time + print "Total SConscript file execution time: %f seconds"%sconscript_time + print "Total SCons execution time: %f seconds"%scons_time + print "Total command execution time: %f seconds"%command_time + + sys.exit(exit_status) diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 30f6933..e30a979 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -42,7 +42,7 @@ import SCons.Node.FS import SCons.Options import SCons.Platform import SCons.SConf -import SCons.Script +import SCons.Script.Main import SCons.Tool import SCons.Util @@ -57,30 +57,7 @@ import UserList launch_dir = os.path.abspath(os.curdir) -help_text = None - -def HelpFunction(text): - global help_text - if help_text is None: - help_text = text - else: - help_text = help_text + text - -Arguments = {} -ArgList = [] -CommandLineTargets = [] -DefaultCalled = None -DefaultTargets = [] -GlobalDict = {} - -class TargetList(UserList.UserList): - def _do_nothing(self, *args, **kw): - pass - def _add_Default(self, list): - self.extend(list) - def _clear(self): - del self[:] -BuildTargets = TargetList() +GlobalDict = None # global exports set by Export(): global_exports = {} @@ -88,29 +65,21 @@ global_exports = {} # chdir flag sconscript_chdir = 1 -# will be set to 1, if we are reading a SConscript -sconscript_reading = 0 - -def _scons_add_args(alist): - for arg in alist: - a, b = string.split(arg, '=', 1) - Arguments[a] = b - ArgList.append((a, b)) - -def _scons_add_targets(tlist): - if tlist: - CommandLineTargets.extend(tlist) - BuildTargets.extend(tlist) - BuildTargets._add_Default = BuildTargets._do_nothing - BuildTargets._clear = BuildTargets._do_nothing - def get_calling_namespaces(): """Return the locals and globals for the function that called - into this module in the current callstack.""" + into this module in the current call stack.""" try: 1/0 except ZeroDivisionError: frame = sys.exc_info()[2].tb_frame - while frame.f_globals.get("__name__") == __name__: frame = frame.f_back + # Find the first frame that *isn't* from this file. This means + # that we expect all of the SCons frames that implement an Export() + # or SConscript() call to be in this file, so that we can identify + # the first non-Script.SConscript frame as the user's local calling + # environment, and the locals and globals dictionaries from that + # frame as the calling namespaces. See the comment below preceding + # the DefaultEnvironmentCall block for even more explanation. + while frame.f_globals.get("__name__") == __name__: + frame = frame.f_back return frame.f_locals, frame.f_globals @@ -136,7 +105,6 @@ def compute_exports(exports): return retval - class Frame: """A frame on the SConstruct/SConscript call stack""" def __init__(self, exports, sconscript): @@ -151,7 +119,7 @@ class Frame: self.sconscript = SCons.Node.FS.default_fs.File(str(sconscript)) # the SConstruct/SConscript call stack: -stack = [] +call_stack = [] # For documentation on the methods in this file, see the scons man-page @@ -160,14 +128,14 @@ def Return(*vars): try: for var in vars: for v in string.split(var): - retval.append(stack[-1].globals[v]) + retval.append(call_stack[-1].globals[v]) except KeyError, x: raise SCons.Errors.UserError, "Return of non-existent variable '%s'"%x if len(retval) == 1: - stack[-1].retval = retval[0] + call_stack[-1].retval = retval[0] else: - stack[-1].retval = tuple(retval) + call_stack[-1].retval = tuple(retval) stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) @@ -180,13 +148,12 @@ def _SConscript(fs, *files, **kw): # evaluate each SConscript file results = [] for fn in files: - stack.append(Frame(exports,fn)) + call_stack.append(Frame(exports,fn)) old_sys_path = sys.path try: - global sconscript_reading - sconscript_reading = 1 + SCons.Script.sconscript_reading = 1 if fn == "-": - exec sys.stdin in stack[-1].globals + exec sys.stdin in call_stack[-1].globals else: if isinstance(fn, SCons.Node.Node): f = fn @@ -246,16 +213,16 @@ def _SConscript(fs, *files, **kw): # exceptions that occur when processing this # SConscript can base the printed frames at this # level and not show SCons internals as well. - stack[-1].globals.update({stack_bottom:1}) - exec _file_ in stack[-1].globals + call_stack[-1].globals.update({stack_bottom:1}) + exec _file_ in call_stack[-1].globals else: SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, "Ignoring missing SConscript '%s'" % f.path) finally: - sconscript_reading = 0 + SCons.Script.sconscript_reading = 0 sys.path = old_sys_path - frame = stack.pop() + frame = call_stack.pop() try: fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir) except OSError: @@ -414,30 +381,13 @@ class SConsEnvironment(SCons.Environment.Base): # def Configure(self, *args, **kw): - if not SCons.Script.SConscript.sconscript_reading: + if not SCons.Script.sconscript_reading: raise SCons.Errors.UserError, "Calling Configure from Builders is not supported." kw['_depth'] = kw.get('_depth', 0) + 1 return apply(SCons.Environment.Base.Configure, (self,)+args, kw) def Default(self, *targets): - global DefaultCalled - global DefaultTargets - DefaultCalled = 1 - for t in targets: - if t is None: - # Delete the elements from the list in-place, don't - # reassign an empty list to DefaultTargets, so that the - # DEFAULT_TARGETS variable will still point to the - # same object we point to. - del DefaultTargets[:] - BuildTargets._clear() - elif isinstance(t, SCons.Node.Node): - DefaultTargets.append(t) - BuildTargets._add_Default([t]) - else: - nodes = self.arg2nodes(t, self.fs.Entry) - DefaultTargets.extend(nodes) - BuildTargets._add_Default(nodes) + SCons.Script._Set_Default_Targets(self, targets) def EnsureSConsVersion(self, major, minor): """Exit abnormally if the SCons version is not late enough.""" @@ -470,25 +420,28 @@ class SConsEnvironment(SCons.Environment.Base): def GetOption(self, name): name = self.subst(name) - return SCons.Script.ssoptions.get(name) + return SCons.Script.Main.ssoptions.get(name) def Help(self, text): text = self.subst(text, raw=1) - HelpFunction(text) + SCons.Script.HelpFunction(text) def Import(self, *vars): try: + frame = call_stack[-1] + globals = frame.globals + exports = frame.exports for var in vars: var = self.Split(var) for v in var: if v == '*': - stack[-1].globals.update(global_exports) - stack[-1].globals.update(stack[-1].exports) + globals.update(global_exports) + globals.update(exports) else: - if stack[-1].exports.has_key(v): - stack[-1].globals[v] = stack[-1].exports[v] + if exports.has_key(v): + globals[v] = exports[v] else: - stack[-1].globals[v] = global_exports[v] + globals[v] = global_exports[v] except KeyError,x: raise SCons.Errors.UserError, "Import of non-existent variable '%s'"%x @@ -523,23 +476,34 @@ class SConsEnvironment(SCons.Environment.Base): def SetOption(self, name, value): name = self.subst(name) - SCons.Script.ssoptions.set(name, value) + SCons.Script.Main.ssoptions.set(name, value) # # # SCons.Environment.Environment = SConsEnvironment -def Options(files=None, args=Arguments): - return SCons.Options.Options(files, args) - def Configure(*args, **kw): - if not SCons.Script.SConscript.sconscript_reading: + if not SCons.Script.sconscript_reading: raise SCons.Errors.UserError, "Calling Configure from Builders is not supported." kw['_depth'] = 1 return apply(SCons.SConf.SConf, args, kw) +# It's very important that the DefaultEnvironmentCall() class stay in this +# file, with the get_calling_namespaces() function, the compute_exports() +# function, the Frame class and the SConsEnvironment.Export() method. +# These things make up the calling stack leading up to the actual global +# Export() or SConscript() call that the user issued. We want to allow +# users to export local variables that they define, like so: +# +# def func(): +# x = 1 +# Export('x') # +# To support this, the get_calling_namespaces() function assumes that +# the *first* stack frame that's not from this file is the local frame +# for the Export() or SConscript() call. + _DefaultEnvironmentProxy = None def get_DefaultEnvironmentProxy(): @@ -565,86 +529,6 @@ class DefaultEnvironmentCall: method = getattr(proxy, self.method_name) return apply(method, args, kw) -# The list of global functions to add to the SConscript name space -# that end up calling corresponding methods or Builders in the -# DefaultEnvironment(). -GlobalDefaultEnvironmentFunctions = [ - # Methods from the SConsEnvironment class, above. - 'Default', - 'EnsurePythonVersion', - 'EnsureSConsVersion', - 'Exit', - 'Export', - 'GetLaunchDir', - 'GetOption', - 'Help', - 'Import', - 'SConscript', - 'SConscriptChdir', - 'SetOption', - - # Methods from the Environment.Base class. - 'AddPostAction', - 'AddPreAction', - 'Alias', - 'AlwaysBuild', - 'BuildDir', - 'CacheDir', - 'Clean', - 'Command', - 'Depends', - 'Dir', - 'Execute', - 'File', - 'FindFile', - 'Flatten', - 'GetBuildPath', - 'Ignore', - 'Install', - 'InstallAs', - 'Literal', - 'Local', - 'ParseDepends', - 'Precious', - 'Repository', - 'SConsignFile', - 'SideEffect', - 'SourceCode', - 'SourceSignatures', - 'Split', - 'TargetSignatures', - 'Value', -] - -GlobalDefaultBuilders = [ - # Supported builders. - 'CFile', - 'CXXFile', - 'DVI', - 'Jar', - 'Java', - 'JavaH', - 'Library', - 'M4', - 'MSVSProject', - 'Object', - 'PCH', - 'PDF', - 'PostScript', - 'Program', - 'RES', - 'RMIC', - 'SharedLibrary', - 'SharedObject', - 'StaticLibrary', - 'StaticObject', - 'Tar', - 'TypeLibrary', - 'Zip', -] - -for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: - GlobalDict[name] = DefaultEnvironmentCall(name) def BuildDefaultGlobals(): """ @@ -652,45 +536,15 @@ def BuildDefaultGlobals(): SConstruct and SConscript files. """ - globals = { - # Global functions that don't get executed through the - # default Environment. - 'Action' : SCons.Action.Action, - 'BoolOption' : SCons.Options.BoolOption, - 'Builder' : SCons.Builder.Builder, - 'Configure' : Configure, - 'EnumOption' : SCons.Options.EnumOption, - 'Environment' : SCons.Environment.Environment, - 'ListOption' : SCons.Options.ListOption, - 'Options' : Options, - 'PackageOption' : SCons.Options.PackageOption, - 'PathOption' : SCons.Options.PathOption, - 'Platform' : SCons.Platform.Platform, - 'Return' : Return, - 'Scanner' : SCons.Scanner.Base, - 'Tool' : SCons.Tool.Tool, - 'WhereIs' : SCons.Util.WhereIs, - - # Action factories. - 'Chmod' : SCons.Defaults.Chmod, - 'Copy' : SCons.Defaults.Copy, - 'Delete' : SCons.Defaults.Delete, - 'Mkdir' : SCons.Defaults.Mkdir, - 'Move' : SCons.Defaults.Move, - 'Touch' : SCons.Defaults.Touch, - - # Other variables we provide. - 'ARGUMENTS' : Arguments, - 'ARGLIST' : ArgList, - 'BUILD_TARGETS' : BuildTargets, - 'COMMAND_LINE_TARGETS' : CommandLineTargets, - 'DEFAULT_TARGETS' : DefaultTargets, - } - - # Functions we might still convert to Environment methods. - globals['CScan'] = SCons.Defaults.CScan - globals['DefaultEnvironment'] = SCons.Defaults.DefaultEnvironment - - globals.update(GlobalDict) - - return globals + global GlobalDict + if GlobalDict is None: + GlobalDict = {} + + import SCons.Script + d = SCons.Script.__dict__ + def not_a_module(m, d=d, mtype=type(SCons.Script)): + return type(d[m]) != mtype + for m in filter(not_a_module, dir(SCons.Script)): + GlobalDict[m] = d[m] + + return GlobalDict.copy() diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 71c2f03..d6d4912 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -39,1114 +39,209 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import time start_time = time.time() -import os -import os.path -import random import string -import sys -import traceback +import UserList -# 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 version directory so, if -# if they're there, we pick up the right version of the build engine -# modules. -#sys.path = [os.path.join(sys.prefix, -# 'lib', -# 'scons-%d' % SCons.__version__)] + sys.path[1:] - -import SCons.Debug -import SCons.Defaults +import SCons.Action +import SCons.Builder import SCons.Environment -import SCons.Errors -import SCons.Job -import SCons.Node -import SCons.Node.FS -from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError -import SCons.Script.SConscript -import SCons.Sig -import SCons.Taskmaster +import SCons.Options +import SCons.Platform +import SCons.Scanner +import SCons.SConf +import SCons.Tool import SCons.Util -import SCons.Warnings - -# -display = SCons.Util.display -progress_display = SCons.Util.DisplayEngine() - -# Task control. -# -class BuildTask(SCons.Taskmaster.Task): - """An SCons build task.""" - def display(self, message): - display('scons: ' + message) - - def execute(self): - target = self.targets[0] - if target.get_state() == SCons.Node.up_to_date: - if self.top and target.has_builder(): - display("scons: `%s' is up to date." % str(self.node)) - elif target.has_builder() and not hasattr(target.builder, 'status'): - if print_time: - start_time = time.time() - SCons.Taskmaster.Task.execute(self) - if print_time: - finish_time = time.time() - global command_time - command_time = command_time+finish_time-start_time - print "Command execution time: %f seconds"%(finish_time-start_time) - - def do_failed(self, status=2): - global exit_status - if ignore_errors: - SCons.Taskmaster.Task.executed(self) - elif keep_going_on_error: - SCons.Taskmaster.Task.fail_continue(self) - exit_status = status - else: - SCons.Taskmaster.Task.fail_stop(self) - exit_status = status - - def executed(self): - t = self.targets[0] - if self.top and not t.has_builder() and not t.side_effect: - if not t.exists(): - sys.stderr.write("scons: *** Do not know how to make target `%s'." % t) - if not keep_going_on_error: - sys.stderr.write(" Stop.") - sys.stderr.write("\n") - self.do_failed() - else: - print "scons: Nothing to be done for `%s'." % t - SCons.Taskmaster.Task.executed(self) - else: - SCons.Taskmaster.Task.executed(self) - - def failed(self): - # Handle the failure of a build task. The primary purpose here - # is to display the various types of Errors and Exceptions - # appropriately. - status = 2 - exc_info = self.exc_info() - try: - t, e, tb = exc_info - except ValueError: - t, e = exc_info - tb = None - if t is None: - # The Taskmaster didn't record an exception for this Task; - # see if the sys module has one. - t, e = sys.exc_info()[:2] - - if t == SCons.Errors.BuildError: - fname = e.node - if SCons.Util.is_List(e.node): - fname = string.join(map(str, e.node), ', ') - sys.stderr.write("scons: *** [%s] %s\n" % (fname, e.errstr)) - if e.errstr == 'Exception': - traceback.print_exception(e.args[0], e.args[1], e.args[2]) - elif t == SCons.Errors.ExplicitExit: - status = e.status - sys.stderr.write("scons: *** [%s] Explicit exit, status %s\n" % (e.node, e.status)) - else: - if e is None: - e = t - s = str(e) - if t == SCons.Errors.StopError and not keep_going_on_error: - s = s + ' Stop.' - sys.stderr.write("scons: *** %s\n" % s) - - if tb and print_stacktrace: - sys.stderr.write("scons: internal stack trace:\n") - traceback.print_tb(tb, file=sys.stderr) - - self.do_failed(status) - - self.exc_clear() - - def postprocess(self): - if self.top: - t = self.targets[0] - if print_tree: - print - SCons.Util.print_tree(t, get_all_children) - if print_stree: - print - SCons.Util.print_tree(t, get_all_children, showtags=2) - if print_dtree: - print - SCons.Util.print_tree(t, get_derived_children) - if print_includes: - tree = t.render_include_tree() - if tree: - print - print tree - SCons.Taskmaster.Task.postprocess(self) - - def make_ready(self): - """Make a task ready for execution""" - SCons.Taskmaster.Task.make_ready(self) - if self.out_of_date and print_explanations: - explanation = self.out_of_date[0].explain() - if explanation: - sys.stdout.write("scons: " + explanation) - -class CleanTask(SCons.Taskmaster.Task): - """An SCons clean task.""" - def show(self): - if (self.targets[0].has_builder() or self.targets[0].side_effect) \ - and not os.path.isdir(str(self.targets[0])): - display("Removed " + str(self.targets[0])) - if SCons.Environment.CleanTargets.has_key(self.targets[0]): - files = SCons.Environment.CleanTargets[self.targets[0]] - for f in files: - SCons.Util.fs_delete(str(f), 0) - - def remove(self): - if self.targets[0].has_builder() or self.targets[0].side_effect: - for t in self.targets: - try: - removed = t.remove() - except OSError, e: - print "scons: Could not remove '%s':" % str(t), e.strerror - else: - if removed: - display("Removed " + str(t)) - if SCons.Environment.CleanTargets.has_key(self.targets[0]): - files = SCons.Environment.CleanTargets[self.targets[0]] - for f in files: - SCons.Util.fs_delete(str(f)) - - execute = remove - - # Have the taskmaster arrange to "execute" all of the targets, because - # we'll figure out ourselves (in remove() or show() above) whether - # anything really needs to be done. - make_ready = SCons.Taskmaster.Task.make_ready_all - - def prepare(self): - pass - -class QuestionTask(SCons.Taskmaster.Task): - """An SCons task for the -q (question) option.""" - def prepare(self): - pass - - def execute(self): - if self.targets[0].get_state() != SCons.Node.up_to_date: - global exit_status - exit_status = 1 - self.tm.stop() - - def executed(self): - pass - -# Global variables - -keep_going_on_error = 0 -print_count = 0 -print_dtree = 0 -print_explanations = 0 -print_includes = 0 -print_objects = 0 -print_stacktrace = 0 -print_stree = 0 -print_time = 0 -print_tree = 0 -memory_stats = None -ignore_errors = 0 -sconscript_time = 0 -command_time = 0 -exit_status = 0 # exit status, assume success by default -profiling = 0 -repositories = [] -num_jobs = 1 # this is modifed by SConscript.SetJobs() - -# utility functions - -def get_all_children(node): return node.all_children() - -def get_derived_children(node): - children = node.all_children(None) - return filter(lambda x: x.has_builder(), children) - -def _scons_syntax_error(e): - """Handle syntax errors. Print out a message and show where the error - occurred. - """ - etype, value, tb = sys.exc_info() - lines = traceback.format_exception_only(etype, value) - for line in lines: - sys.stderr.write(line+'\n') - sys.exit(2) - -def find_deepest_user_frame(tb): - """ - Find the deepest stack frame that is not part of SCons. - - Input is a "pre-processed" stack trace in the form - returned by traceback.extract_tb() or traceback.extract_stack() - """ - - tb.reverse() - - # find the deepest traceback frame that is not part - # of SCons: - for frame in tb: - filename = frame[0] - if string.find(filename, os.sep+'SCons'+os.sep) == -1: - return frame - return tb[0] - -def _scons_user_error(e): - """Handle user errors. Print out a message and a description of the - error, along with the line number and routine where it occured. - The file and line number will be the deepest stack frame that is - not part of SCons itself. - """ - etype, value, tb = sys.exc_info() - filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) - sys.stderr.write("\nscons: *** %s\n" % value) - sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) - sys.exit(2) - -def _scons_user_warning(e): - """Handle user warnings. Print out a message and a description of - the warning, along with the line number and routine where it occured. - The file and line number will be the deepest stack frame that is - not part of SCons itself. - """ - etype, value, tb = sys.exc_info() - filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) - sys.stderr.write("\nscons: warning: %s\n" % e) - sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) - -def _scons_internal_warning(e): - """Slightly different from _scons_user_warning in that we use the - *current call stack* rather than sys.exc_info() to get our stack trace. - This is used by the warnings framework to print warnings.""" - filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack()) - sys.stderr.write("\nscons: warning: %s\n" % e[0]) - sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) - -def _scons_internal_error(): - """Handle all errors but user errors. Print out a message telling - the user what to do in this case and print a normal trace. - """ - print 'internal error' - traceback.print_exc() - sys.exit(2) - -def _varargs(option, parser): - value = None - if parser.rargs: - arg = parser.rargs[0] - if arg[0] != "-": - value = arg - del parser.rargs[0] - return value - -def _setup_warn(arg): - """The --warn option. An argument to this option - should be of the form <warning-class> or no-<warning-class>. - The warning class is munged in order to get an actual class - name from the SCons.Warnings module to enable or disable. - The supplied <warning-class> is split on hyphens, each element - is captialized, then smushed back together. Then the string - "SCons.Warnings." is added to the front and "Warning" is added - to the back to get the fully qualified class name. - - For example, --warn=deprecated will enable the - SCons.Warnings.DeprecatedWarning class. - - --warn=no-dependency will disable the - SCons.Warnings.DependencyWarning class. - - As a special case, --warn=all and --warn=no-all - will enable or disable (respectively) the base - class of all warnings, which is SCons.Warning.Warning.""" - - elems = string.split(string.lower(arg), '-') - enable = 1 - if elems[0] == 'no': - enable = 0 - del elems[0] - - if len(elems) == 1 and elems[0] == 'all': - class_name = "Warning" - else: - def _capitalize(s): - if s[:5] == "scons": - return "SCons" + s[5:] - else: - return string.capitalize(s) - class_name = string.join(map(_capitalize, elems), '') + "Warning" - try: - clazz = getattr(SCons.Warnings, class_name) - except AttributeError: - sys.stderr.write("No warning type: '%s'\n" % arg) - else: - if enable: - SCons.Warnings.enableWarningClass(clazz) - else: - SCons.Warnings.suppressWarningClass(clazz) - -def _SConstruct_exists(dirname=''): - """This function checks that an SConstruct file exists in a directory. - If so, it returns the path of the file. By default, it checks the - current directory. - """ - global repositories - for file in ['SConstruct', 'Sconstruct', 'sconstruct']: - sfile = os.path.join(dirname, file) - if os.path.isfile(sfile): - return sfile - if not os.path.isabs(sfile): - for rep in repositories: - if os.path.isfile(os.path.join(rep, sfile)): - return sfile - return None - -def _set_globals(options): - global repositories, keep_going_on_error, ignore_errors - global print_count, print_dtree - global print_explanations, print_includes - global print_objects, print_stacktrace, print_stree - global print_time, print_tree - global memory_outf, memory_stats - - if options.repository: - repositories.extend(options.repository) - keep_going_on_error = options.keep_going - try: - if options.debug: - if options.debug == "count": - print_count = 1 - elif options.debug == "dtree": - print_dtree = 1 - elif options.debug == "explain": - print_explanations = 1 - elif options.debug == "findlibs": - SCons.Scanner.Prog.print_find_libs = "findlibs" - elif options.debug == "includes": - print_includes = 1 - elif options.debug == "memory": - memory_stats = [] - memory_outf = sys.stdout - elif options.debug == "objects": - print_objects = 1 - elif options.debug == "presub": - SCons.Action.print_actions_presub = 1 - elif options.debug == "stacktrace": - print_stacktrace = 1 - elif options.debug == "stree": - print_stree = 1 - elif options.debug == "time": - print_time = 1 - elif options.debug == "tree": - print_tree = 1 - except AttributeError: - pass - ignore_errors = options.ignore_errors - -def _create_path(plist): - path = '.' - for d in plist: - if os.path.isabs(d): - path = d - else: - path = path + '/' + d - return path - - -class OptParser(OptionParser): - def __init__(self): - import __main__ - import SCons - parts = ["SCons by Steven Knight et al.:\n"] - try: - parts.append("\tscript: v%s.%s, %s, by %s on %s\n" % (__main__.__version__, - __main__.__build__, - __main__.__date__, - __main__.__developer__, - __main__.__buildsys__)) - except KeyboardInterrupt: - raise - except: - # On win32 there is no scons.py, so there is no __main__.__version__, - # hence there is no script version. - pass - parts.append("\tengine: v%s.%s, %s, by %s on %s\n" % (SCons.__version__, - SCons.__build__, - SCons.__date__, - SCons.__developer__, - SCons.__buildsys__)) - parts.append("__COPYRIGHT__") - OptionParser.__init__(self, version=string.join(parts, ''), - usage="usage: scons [OPTION] [TARGET] ...") - - # options ignored for compatibility - def opt_ignore(option, opt, value, parser): - sys.stderr.write("Warning: ignoring %s option\n" % opt) - self.add_option("-b", "-m", "-S", "-t", "--no-keep-going", "--stop", - "--touch", action="callback", callback=opt_ignore, - help="Ignored for compatibility.") - - self.add_option('-c', '--clean', '--remove', action="store_true", - dest="clean", - help="Remove specified targets and dependencies.") - - self.add_option('-C', '--directory', type="string", action = "append", - metavar="DIR", - help="Change to DIR before doing anything.") - - self.add_option('--cache-disable', '--no-cache', - action="store_true", dest='cache_disable', default=0, - help="Do not retrieve built targets from CacheDir.") - - self.add_option('--cache-force', '--cache-populate', - action="store_true", dest='cache_force', default=0, - help="Copy already-built targets into the CacheDir.") - - self.add_option('--cache-show', - action="store_true", dest='cache_show', default=0, - help="Print build actions for files from CacheDir.") - - config_options = ["auto", "force" ,"cache"] - - def opt_config(option, opt, value, parser, c_options=config_options): - if value in c_options: - parser.values.config = value - else: - raise OptionValueError("Warning: %s is not a valid config type" % value) - self.add_option('--config', action="callback", type="string", - callback=opt_config, nargs=1, dest="config", - metavar="MODE", default="auto", - help="Controls Configure subsystem: " - "%s." % string.join(config_options, ", ")) - - def opt_not_yet(option, opt, value, parser): - sys.stderr.write("Warning: the %s option is not yet implemented\n" % opt) - sys.exit(0) - self.add_option('-d', action="callback", - callback=opt_not_yet, - help = "Print file dependency information.") - - self.add_option('-D', action="store_const", const=2, dest="climb_up", - help="Search up directory tree for SConstruct, " - "build all Default() targets.") - - debug_options = ["count", "dtree", "explain", "findlibs", - "includes", "memory", "objects", - "pdb", "presub", "stacktrace", "stree", - "time", "tree"] - - def opt_debug(option, opt, value, parser, debug_options=debug_options): - if value in debug_options: - parser.values.debug = value - else: - raise OptionValueError("Warning: %s is not a valid debug type" % value) - self.add_option('--debug', action="callback", type="string", - callback=opt_debug, nargs=1, dest="debug", - metavar="TYPE", - help="Print various types of debugging information: " - "%s." % string.join(debug_options, ", ")) - - def opt_duplicate(option, opt, value, parser): - if not value in SCons.Node.FS.Valid_Duplicates: - raise OptionValueError("`%s' is not a valid duplication style." % value) - parser.values.duplicate = value - # Set the duplicate style right away so it can affect linking - # of SConscript files. - SCons.Node.FS.set_duplicate(value) - self.add_option('--duplicate', action="callback", type="string", - callback=opt_duplicate, nargs=1, dest="duplicate", - help="Set the preferred duplication methods. Must be one of " - + string.join(SCons.Node.FS.Valid_Duplicates, ", ")) - - self.add_option('-f', '--file', '--makefile', '--sconstruct', - action="append", nargs=1, - help="Read FILE as the top-level SConstruct file.") - - self.add_option('-h', '--help', action="store_true", default=0, - dest="help_msg", - help="Print defined help message, or this one.") - - self.add_option("-H", "--help-options", - action="help", - help="Print this message and exit.") - - self.add_option('-i', '--ignore-errors', action="store_true", - default=0, dest='ignore_errors', - help="Ignore errors from build actions.") - - self.add_option('-I', '--include-dir', action="append", - dest='include_dir', metavar="DIR", - help="Search DIR for imported Python modules.") - - self.add_option('--implicit-cache', action="store_true", - dest='implicit_cache', - help="Cache implicit dependencies") - - self.add_option('--implicit-deps-changed', action="store_true", - default=0, dest='implicit_deps_changed', - help="Ignore cached implicit dependencies.") - self.add_option('--implicit-deps-unchanged', action="store_true", - default=0, dest='implicit_deps_unchanged', - help="Ignore changes in implicit dependencies.") - - def opt_j(option, opt, value, parser): - value = int(value) - parser.values.num_jobs = value - self.add_option('-j', '--jobs', action="callback", type="int", - callback=opt_j, metavar="N", - help="Allow N jobs at once.") - - self.add_option('-k', '--keep-going', action="store_true", default=0, - dest='keep_going', - help="Keep going when a target can't be made.") - - self.add_option('--max-drift', type="int", action="store", - dest='max_drift', metavar="N", - help="Set maximum system clock drift to N seconds.") - - self.add_option('-n', '--no-exec', '--just-print', '--dry-run', - '--recon', action="store_true", dest='noexec', - default=0, help="Don't build; just print commands.") - - def opt_profile(option, opt, value, parser): - global profiling - if not profiling: - profiling = 1 - import profile - profile.run('SCons.Script.main()', value) - sys.exit(exit_status) - self.add_option('--profile', nargs=1, action="callback", - callback=opt_profile, type="string", dest="profile", - metavar="FILE", - help="Profile SCons and put results in FILE.") - - self.add_option('-q', '--question', action="store_true", default=0, - help="Don't build; exit status says if up to date.") - - self.add_option('-Q', dest='no_progress', action="store_true", - default=0, - help="Suppress \"Reading/Building\" progress messages.") - - self.add_option('--random', dest="random", action="store_true", - default=0, help="Build dependencies in random order.") - - self.add_option('-s', '--silent', '--quiet', action="store_true", - default=0, help="Don't print commands.") - - self.add_option('-u', '--up', '--search-up', action="store_const", - dest="climb_up", default=0, const=1, - help="Search up directory tree for SConstruct, " - "build targets at or below current directory.") - self.add_option('-U', action="store_const", dest="climb_up", - default=0, const=3, - help="Search up directory tree for SConstruct, " - "build Default() targets from local SConscript.") - - self.add_option("-v", "--version", - action="version", - help="Print the SCons version number and exit.") - - self.add_option('--warn', '--warning', nargs=1, action="store", - metavar="WARNING-SPEC", - help="Enable or disable warnings.") - - self.add_option('-Y', '--repository', nargs=1, action="append", - help="Search REPOSITORY for source and target files.") - - self.add_option('-e', '--environment-overrides', action="callback", - callback=opt_not_yet, - # help="Environment variables override makefiles." - help=SUPPRESS_HELP) - self.add_option('-l', '--load-average', '--max-load', action="callback", - callback=opt_not_yet, type="int", dest="load_average", - # action="store", - # help="Don't start multiple jobs unless load is below " - # "LOAD-AVERAGE." - # type="int", - help=SUPPRESS_HELP) - self.add_option('--list-derived', action="callback", - callback=opt_not_yet, - # help="Don't build; list files that would be built." - help=SUPPRESS_HELP) - self.add_option('--list-actions', action="callback", - callback=opt_not_yet, - # help="Don't build; list files and build actions." - help=SUPPRESS_HELP) - self.add_option('--list-where', action="callback", - callback=opt_not_yet, - # help="Don't build; list files and where defined." - help=SUPPRESS_HELP) - self.add_option('-o', '--old-file', '--assume-old', action="callback", - callback=opt_not_yet, type="string", dest="old_file", - # help = "Consider FILE to be old; don't rebuild it." - help=SUPPRESS_HELP) - self.add_option('--override', action="callback", dest="override", - callback=opt_not_yet, type="string", - # help="Override variables as specified in FILE." - help=SUPPRESS_HELP) - self.add_option('-p', action="callback", - callback=opt_not_yet, - # help="Print internal environments/objects." - help=SUPPRESS_HELP) - self.add_option('-r', '-R', '--no-builtin-rules', - '--no-builtin-variables', action="callback", - callback=opt_not_yet, - # help="Clear default environments and variables." - help=SUPPRESS_HELP) - self.add_option('-w', '--print-directory', action="callback", - callback=opt_not_yet, - # help="Print the current directory." - help=SUPPRESS_HELP) - self.add_option('--no-print-directory', action="callback", - callback=opt_not_yet, - # help="Turn off -w, even if it was turned on implicitly." - help=SUPPRESS_HELP) - self.add_option('--write-filenames', action="callback", - callback=opt_not_yet, type="string", dest="write_filenames", - # help="Write all filenames examined into FILE." - help=SUPPRESS_HELP) - self.add_option('-W', '--what-if', '--new-file', '--assume-new', - dest="new_file", - action="callback", callback=opt_not_yet, type="string", - # help="Consider FILE to be changed." - help=SUPPRESS_HELP) - self.add_option('--warn-undefined-variables', action="callback", - callback=opt_not_yet, - # help="Warn when an undefined variable is referenced." - help=SUPPRESS_HELP) - - def parse_args(self, args=None, values=None): - opt, arglist = OptionParser.parse_args(self, args, values) - if opt.implicit_deps_changed or opt.implicit_deps_unchanged: - opt.implicit_cache = 1 - return opt, arglist - -class SConscriptSettableOptions: - """This class wraps an OptParser instance and provides - uniform access to options that can be either set on the command - line or from a SConscript file. A value specified on the command - line always overrides a value set in a SConscript file. - Not all command line options are SConscript settable, and the ones - that are must be explicitly added to settable dictionary and optionally - validated and coerced in the set() method.""" - - def __init__(self, options): - self.options = options - - # This dictionary stores the defaults for all the SConscript - # settable options, as well as indicating which options - # are SConscript settable. - self.settable = {'num_jobs':1, - 'max_drift':SCons.Sig.default_max_drift, - 'implicit_cache':0, - 'clean':0, - 'duplicate':'hard-soft-copy'} - - def get(self, name): - if not self.settable.has_key(name): - raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name - if hasattr(self.options, name) and getattr(self.options, name) is not None: - return getattr(self.options, name) - else: - return self.settable[name] - - def set(self, name, value): - if not self.settable.has_key(name): - raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name - - if name == 'num_jobs': - try: - value = int(value) - if value < 1: - raise ValueError - except ValueError: - raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value) - elif name == 'max_drift': - try: - value = int(value) - except ValueError: - raise SCons.Errors.UserError, "An integer is required: %s"%repr(value) - elif name == 'duplicate': - try: - value = str(value) - except ValueError: - raise SCons.Errors.UserError, "A string is required: %s"%repr(value) - if not value in SCons.Node.FS.Valid_Duplicates: - raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value - # Set the duplicate stye right away so it can affect linking - # of SConscript files. - SCons.Node.FS.set_duplicate(value) - - self.settable[name] = value - - -def _main(args, parser): - targets = [] - fs = SCons.Node.FS.default_fs - - # Enable deprecated warnings by default. - SCons.Warnings._warningOut = _scons_internal_warning - SCons.Warnings.enableWarningClass(SCons.Warnings.CorruptSConsignWarning) - SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning) - SCons.Warnings.enableWarningClass(SCons.Warnings.DuplicateEnvironmentWarning) - SCons.Warnings.enableWarningClass(SCons.Warnings.MissingSConscriptWarning) - SCons.Warnings.enableWarningClass(SCons.Warnings.NoParallelSupportWarning) - # This is good for newbies, and hopefully most everyone else too. - SCons.Warnings.enableWarningClass(SCons.Warnings.MisleadingKeywordsWarning) - - global ssoptions - ssoptions = SConscriptSettableOptions(options) - - _set_globals(options) - SCons.Node.implicit_cache = options.implicit_cache - SCons.Node.implicit_deps_changed = options.implicit_deps_changed - SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged - if options.warn: - _setup_warn(options.warn) - if options.noexec: - SCons.SConf.dryrun = 1 - SCons.Action.execute_actions = None - CleanTask.execute = CleanTask.show - if options.question: - SCons.SConf.dryrun = 1 - SCons.SConf.SetCacheMode(options.config) - SCons.SConf.SetProgressDisplay(progress_display) - - if options.no_progress or options.silent: - progress_display.set_mode(0) - if options.silent: - display.set_mode(0) - if options.silent: - SCons.Action.print_actions = None - if options.cache_disable: - def disable(self): pass - fs.CacheDir = disable - if options.cache_force: - fs.cache_force = 1 - if options.cache_show: - fs.cache_show = 1 - if options.directory: - cdir = _create_path(options.directory) - try: - os.chdir(cdir) - except OSError: - sys.stderr.write("Could not change directory to %s\n" % cdir) - - xmit_args = [] - for a in args: - if '=' in a: - xmit_args.append(a) - else: - targets.append(a) - SCons.Script.SConscript._scons_add_args(xmit_args) - SCons.Script.SConscript._scons_add_targets(targets) - - target_top = None - if options.climb_up: - target_top = '.' # directory to prepend to targets - script_dir = os.getcwd() # location of script - while script_dir and not _SConstruct_exists(script_dir): - script_dir, last_part = os.path.split(script_dir) - if last_part: - target_top = os.path.join(last_part, target_top) - else: - script_dir = '' - if script_dir: - display("scons: Entering directory `%s'" % script_dir) - os.chdir(script_dir) - else: - raise SCons.Errors.UserError, "No SConstruct file found." - - fs.set_toplevel_dir(os.getcwd()) - - scripts = [] - if options.file: - scripts.extend(options.file) - if not scripts: - sfile = _SConstruct_exists() - if sfile: - scripts.append(sfile) - - if options.help_msg: - if not scripts: - # There's no SConstruct, but they specified -h. - # Give them the options usage now, before we fail - # trying to read a non-existent SConstruct file. - parser.print_help() - sys.exit(0) - SCons.Script.SConscript.print_help = 1 - - if not scripts: - raise SCons.Errors.UserError, "No SConstruct file found." - - if scripts[0] == "-": - d = fs.getcwd() - else: - d = fs.File(scripts[0]).dir - fs.set_SConstruct_dir(d) - - class Unbuffered: - def __init__(self, file): - self.file = file - def write(self, arg): - self.file.write(arg) - self.file.flush() - def __getattr__(self, attr): - return getattr(self.file, attr) - - sys.stdout = Unbuffered(sys.stdout) - - if options.include_dir: - sys.path = options.include_dir + sys.path - - global repositories - for rep in repositories: - fs.Repository(rep) - - if not memory_stats is None: memory_stats.append(SCons.Debug.memory()) - - progress_display("scons: Reading SConscript files ...") - - start_time = time.time() - try: - for script in scripts: - SCons.Script.SConscript._SConscript(fs, script) - except SCons.Errors.StopError, e: - # We had problems reading an SConscript file, such as it - # couldn't be copied in to the BuildDir. Since we're just - # reading SConscript files and haven't started building - # things yet, stop regardless of whether they used -i or -k - # or anything else. - global exit_status - sys.stderr.write("scons: *** %s Stop.\n" % e) - exit_status = 2 - sys.exit(exit_status) - global sconscript_time - sconscript_time = time.time() - start_time - SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) - progress_display("scons: done reading SConscript files.") - - # Tell the Node.FS subsystem that we're all done reading the - # SConscript files and calling Repository() and BuildDir() and the - # like, so it can go ahead and start memoizing the string values of - # file system nodes. - SCons.Node.FS.save_strings(1) - - if not memory_stats is None: memory_stats.append(SCons.Debug.memory()) - - fs.chdir(fs.Top) - - if options.help_msg: - if SCons.Script.SConscript.help_text is None: - # They specified -h, but there was no Help() inside the - # SConscript files. Give them the options usage. - parser.print_help(sys.stdout) - else: - print SCons.Script.SConscript.help_text - print "Use scons -H for help about command-line options." - sys.exit(0) - - # Now that we've read the SConscripts we can set the options - # that are SConscript settable: - SCons.Node.implicit_cache = ssoptions.get('implicit_cache') - SCons.Node.FS.set_duplicate(ssoptions.get('duplicate')) - - lookup_top = None - if targets: - # They specified targets on the command line, so if they - # used -u, -U or -D, we have to look up targets relative - # to the top, but we build whatever they specified. - if target_top: - lookup_top = fs.Dir(target_top) - target_top = None - else: - # There are no targets specified on the command line, - # so if they used -u, -U or -D, we may have to restrict - # what actually gets built. - d = None - if target_top: - if options.climb_up == 1: - # -u, local directory and below - target_top = fs.Dir(target_top) - lookup_top = target_top - elif options.climb_up == 2: - # -D, all Default() targets - target_top = None - lookup_top = None - elif options.climb_up == 3: - # -U, local SConscript Default() targets - target_top = fs.Dir(target_top) - def check_dir(x, target_top=target_top): - if hasattr(x, 'cwd') and not x.cwd is None: - cwd = x.cwd.srcnode() - return cwd == target_top - else: - # x doesn't have a cwd, so it's either not a target, - # or not a file, so go ahead and keep it as a default - # target and let the engine sort it out: - return 1 - d = filter(check_dir, SCons.Script.SConscript.DefaultTargets) - SCons.Script.SConscript.DefaultTargets[:] = d - target_top = None - lookup_top = None - - if SCons.Script.SConscript.DefaultCalled: - targets = SCons.Script.SConscript.DefaultTargets - else: - if d is None: - d = [fs.Dir('.')] - targets = d +import SCons.Defaults +import Main - if not targets: - sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") - sys.exit(2) +main = Main.main - def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): - if isinstance(x, SCons.Node.Node): - node = x - else: - node = SCons.Node.Alias.default_ans.lookup(x) - if node is None: - node = fs.Entry(x, directory=ltop, create=1) - if ttop and not node.is_under(ttop): - if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node): - node = ttop - else: - node = None - return node +import SConscript +_SConscript = SConscript - nodes = filter(lambda x: x is not None, map(Entry, targets)) +call_stack = _SConscript.call_stack - task_class = BuildTask # default action is to build targets - opening_message = "Building targets ..." - closing_message = "done building targets." - if keep_going_on_error: - failure_message = "done building targets (errors occurred during build)." - else: - failure_message = "building terminated because of errors." - if options.question: - task_class = QuestionTask - try: - if ssoptions.get('clean'): - task_class = CleanTask - opening_message = "Cleaning targets ..." - closing_message = "done cleaning targets." - if keep_going_on_error: - closing_message = "done cleaning targets (errors occurred during clean)." - else: - failure_message = "cleaning terminated because of errors." - except AttributeError: +# +Action = SCons.Action.Action +BoolOption = SCons.Options.BoolOption +Builder = SCons.Builder.Builder +Configure = _SConscript.Configure +EnumOption = SCons.Options.EnumOption +Environment = SCons.Environment.Environment +ListOption = SCons.Options.ListOption +PackageOption = SCons.Options.PackageOption +PathOption = SCons.Options.PathOption +Platform = SCons.Platform.Platform +Return = _SConscript.Return +Scanner = SCons.Scanner.Base +Tool = SCons.Tool.Tool +WhereIs = SCons.Util.WhereIs + +# Action factories. +Chmod = SCons.Defaults.Chmod +Copy = SCons.Defaults.Copy +Delete = SCons.Defaults.Delete +Mkdir = SCons.Defaults.Mkdir +Move = SCons.Defaults.Move +Touch = SCons.Defaults.Touch + +# Functions we might still convert to Environment methods. +CScan = SCons.Defaults.CScan +DefaultEnvironment = SCons.Defaults.DefaultEnvironment + +# Other variables we provide. +class TargetList(UserList.UserList): + def _do_nothing(self, *args, **kw): pass - - SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift') - - if options.random: - def order(dependencies): - """Randomize the dependencies.""" - # This is cribbed from the implementation of - # random.shuffle() in Python 2.X. - d = dependencies - for i in xrange(len(d)-1, 0, -1): - j = int(random.random() * (i+1)) - d[i], d[j] = d[j], d[i] - return d - else: - def order(dependencies): - """Leave the order of dependencies alone.""" - return dependencies - - progress_display("scons: " + opening_message) - taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order) - - nj = ssoptions.get('num_jobs') - jobs = SCons.Job.Jobs(nj, taskmaster) - if nj > 1 and jobs.num_jobs == 1: - msg = "parallel builds are unsupported by this version of Python;\n" + \ - "\tignoring -j or num_jobs option.\n" - SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) - - if not memory_stats is None: memory_stats.append(SCons.Debug.memory()) - - try: - jobs.run() - finally: - if exit_status: - progress_display("scons: " + failure_message) + def _add_Default(self, list): + self.extend(list) + def _clear(self): + del self[:] + +ARGUMENTS = {} +ARGLIST = [] +BUILD_TARGETS = TargetList() +COMMAND_LINE_TARGETS = [] +DEFAULT_TARGETS = [] + +def _Add_Arguments(alist): + for arg in alist: + a, b = string.split(arg, '=', 1) + ARGUMENTS[a] = b + ARGLIST.append((a, b)) + +def _Add_Targets(tlist): + if tlist: + COMMAND_LINE_TARGETS.extend(tlist) + BUILD_TARGETS.extend(tlist) + BUILD_TARGETS._add_Default = BUILD_TARGETS._do_nothing + BUILD_TARGETS._clear = BUILD_TARGETS._do_nothing + +def _Set_Default_Targets_Has_Been_Called(d, fs): + return DEFAULT_TARGETS + +def _Set_Default_Targets_Has_Not_Been_Called(d, fs): + if d is None: + d = [fs.Dir('.')] + return d + +_Get_Default_Targets = _Set_Default_Targets_Has_Not_Been_Called + +def _Set_Default_Targets(env, tlist): + global DEFAULT_TARGETS + global _Get_Default_Targets + _Get_Default_Targets = _Set_Default_Targets_Has_Been_Called + for t in tlist: + if t is None: + # Delete the elements from the list in-place, don't + # reassign an empty list to DEFAULT_TARGETS, so that the + # variables will still point to the same object we point to. + del DEFAULT_TARGETS[:] + BUILD_TARGETS._clear() + elif isinstance(t, SCons.Node.Node): + DEFAULT_TARGETS.append(t) + BUILD_TARGETS._add_Default([t]) else: - progress_display("scons: " + closing_message) - if not options.noexec: - SCons.SConsign.write() - - if not memory_stats is None: - memory_stats.append(SCons.Debug.memory()) - when = [ - 'before SConscript files', - 'after SConscript files', - 'before building', - 'after building', - ] - for i in xrange(len(when)): - memory_outf.write('Memory %s: %d\n' % (when[i], memory_stats[i])) - - if print_count: - SCons.Debug.countLoggedInstances('*') + nodes = env.arg2nodes(t, env.fs.Entry) + DEFAULT_TARGETS.extend(nodes) + BUILD_TARGETS._add_Default(nodes) - if print_objects: - SCons.Debug.listLoggedInstances('*') - #SCons.Debug.dumpLoggedInstances('*') +# +help_text = None -def _exec_main(): - all_args = sys.argv[1:] - try: - all_args = string.split(os.environ['SCONSFLAGS']) + all_args - except KeyError: - # it's OK if there's no SCONSFLAGS - pass - parser = OptParser() - global options - options, args = parser.parse_args(all_args) - if options.debug == "pdb": - import pdb - pdb.Pdb().runcall(_main, args, parser) +def HelpFunction(text): + global help_text + if SCons.Script.help_text is None: + SCons.Script.help_text = text else: - _main(args, parser) - -def main(): - global exit_status - - try: - _exec_main() - except SystemExit, s: - if s: - exit_status = s - except KeyboardInterrupt: - print "Build interrupted." - sys.exit(2) - except SyntaxError, e: - _scons_syntax_error(e) - except SCons.Errors.InternalError: - _scons_internal_error() - except SCons.Errors.UserError, e: - _scons_user_error(e) - except: - # An exception here is likely a builtin Python exception Python - # code in an SConscript file. Show them precisely what the - # problem was and where it happened. - SCons.Script.SConscript.SConscript_exception() - sys.exit(2) + help_text = help_text + text - if print_time: - total_time = time.time()-start_time - scons_time = total_time-sconscript_time-command_time - print "Total build time: %f seconds"%total_time - print "Total SConscript file execution time: %f seconds"%sconscript_time - print "Total SCons execution time: %f seconds"%scons_time - print "Total command execution time: %f seconds"%command_time +# +# Will be set to 1 if we are reading a SConscript. +sconscript_reading = 0 - sys.exit(exit_status) +# +def Options(files=None, args=ARGUMENTS): + return SCons.Options.Options(files, args) + +# The list of global functions to add to the SConscript name space +# that end up calling corresponding methods or Builders in the +# DefaultEnvironment(). +GlobalDefaultEnvironmentFunctions = [ + # Methods from the SConsEnvironment class, above. + 'Default', + 'EnsurePythonVersion', + 'EnsureSConsVersion', + 'Exit', + 'Export', + 'GetLaunchDir', + 'GetOption', + 'Help', + 'Import', + 'SConscript', + 'SConscriptChdir', + 'SetOption', + + # Methods from the Environment.Base class. + 'AddPostAction', + 'AddPreAction', + 'Alias', + 'AlwaysBuild', + 'BuildDir', + 'CacheDir', + 'Clean', + 'Command', + 'Depends', + 'Dir', + 'Execute', + 'File', + 'FindFile', + 'Flatten', + 'GetBuildPath', + 'Ignore', + 'Install', + 'InstallAs', + 'Literal', + 'Local', + 'ParseDepends', + 'Precious', + 'Repository', + 'SConsignFile', + 'SideEffect', + 'SourceCode', + 'SourceSignatures', + 'Split', + 'TargetSignatures', + 'Value', +] + +GlobalDefaultBuilders = [ + # Supported builders. + 'CFile', + 'CXXFile', + 'DVI', + 'Jar', + 'Java', + 'JavaH', + 'Library', + 'M4', + 'MSVSProject', + 'Object', + 'PCH', + 'PDF', + 'PostScript', + 'Program', + 'RES', + 'RMIC', + 'SharedLibrary', + 'SharedObject', + 'StaticLibrary', + 'StaticObject', + 'Tar', + 'TypeLibrary', + 'Zip', +] + +for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: + exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name)) diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index 98258c2..559a679 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -178,6 +178,16 @@ def generate(env): except (SCons.Util.RegError, SCons.Errors.InternalError): pass + # For most platforms, a loadable module is the same as a shared + # library. Platforms which are different can override these, but + # setting them the same means that LoadableModule works everywhere. + SCons.Tool.createLoadableModuleBuilder(env) + env['LDMODULE'] = '$SHLINK' + env['LDMODULEPREFIX'] = '$SHLIBPREFIX' + env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' + env['LDMODULEFLAGS'] = '$SHLINKFLAGS' + env['LDMODULECOM'] = '$SHLINKCOM' + def exists(env): if SCons.Tool.msvs.is_msvs_installed(): # there's at least one version of MSVS installed. diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index a173c3e..04e09f5 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -45,7 +45,7 @@ import types import SCons.Builder import SCons.Node.FS import SCons.Platform.win32 -import SCons.Script.SConscript +import SCons.Script import SCons.Util import SCons.Warnings @@ -1052,10 +1052,12 @@ def projectEmitter(target, source, env): dspfile = SCons.Node.FS.default_fs.File(target[0]).srcnode() dswfile = SCons.Node.FS.default_fs.File(SCons.Util.splitext(str(dspfile))[0] + env.subst('$MSVSSOLUTIONSUFFIX')) + # XXX Need to find a way to abstract this; the build engine + # shouldn't depend on anything in SCons.Script. + stack = SCons.Script.call_stack if not source: - source = [SCons.Script.SConscript.stack[-1].sconscript.srcnode()] - - source[0].attributes.sconstruct = SCons.Script.SConscript.stack[0].sconscript + source = [stack[-1].sconscript.srcnode()] + source[0].attributes.sconstruct = stack[0].sconscript bdswpath = SCons.Util.splitext(str(target[0]))[0] + env.subst('$MSVSSOLUTIONSUFFIX') bdswfile = SCons.Node.FS.default_fs.File(bdswpath) diff --git a/test/NodeOps.py b/test/NodeOps.py index ffb8994..9a28c87 100644 --- a/test/NodeOps.py +++ b/test/NodeOps.py @@ -152,7 +152,7 @@ def exists_test(node): node.is_derived() node.is_pseudo_derived() import SCons.Script - if SCons.Script.options.noexec: + if SCons.Script.Main.options.noexec: if (before,via_node,after) != (False,False,False): import sys sys.stderr.write('BuildDir exits() populated during dryrun!\n') diff --git a/test/Script-import.py b/test/Script-import.py new file mode 100644 index 0000000..90052a5 --- /dev/null +++ b/test/Script-import.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test that a module that we import into an SConscript file +can itself easily import the global SCons variables. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +import m1 +""") + +test.write("m1.py", """\ +from SCons.Script import * +SConscript('SConscript') +""") + +test.write('SConscript', """\ +import m2 +""") + +test.write("m2.py", """\ +from SCons.Script import * +Command("file.out", "file.in", Copy("$TARGET", "$SOURCE")) +""") + +test.write("file.in", "file.in\n") + +test.run(arguments = '.') + +test.must_match("file.out", "file.in\n") + +test.pass_test() diff --git a/test/option--profile.py b/test/option--profile.py index 5a6e473..412b8ce 100644 --- a/test/option--profile.py +++ b/test/option--profile.py @@ -43,15 +43,19 @@ test.fail_test(string.find(test.stdout(), 'Copyright') == -1 and stats = pstats.Stats(scons_prof) stats.sort_stats('time') -sys.stdout = StringIO.StringIO() +try: + save_stdout = sys.stdout + sys.stdout = StringIO.StringIO() -stats.strip_dirs().print_stats() + stats.strip_dirs().print_stats() -s = sys.stdout.getvalue() + s = sys.stdout.getvalue() +finally: + sys.stdout = save_stdout -test.fail_test(string.find(s, '__init__.py') == -1) +test.fail_test(string.find(s, 'Main.py') == -1) test.fail_test(string.find(s, 'print_version') == -1) -test.fail_test(string.find(s, 'SCons.Script.main()') == -1) +test.fail_test(string.find(s, 'SCons.Script.Main.main()') == -1) test.fail_test(string.find(s, 'option_parser.py') == -1) @@ -71,11 +75,10 @@ stats.strip_dirs().print_stats() s = sys.stdout.getvalue() -test.fail_test(string.find(s, '__init__.py') == -1) +test.fail_test(string.find(s, 'Main.py') == -1) test.fail_test(string.find(s, 'print_version') == -1) -test.fail_test(string.find(s, 'SCons.Script.main()') == -1) +test.fail_test(string.find(s, 'SCons.Script.Main.main()') == -1) test.fail_test(string.find(s, 'option_parser.py') == -1) test.pass_test() - |