# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
+from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import SCons.compat
+
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
+# (Windows) 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.
# 'lib',
# 'scons-%d' % SCons.__version__)] + sys.path[1:]
+import SCons.CacheDir
import SCons.Debug
import SCons.Defaults
import SCons.Environment
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.Script
import SCons.Taskmaster
import SCons.Util
import SCons.Warnings
+import SCons.Script.Interactive
+
+def fetch_win32_parallel_msg():
+ # A subsidiary function that exists solely to isolate this import
+ # so we don't have to pull it in on all platforms, and so that an
+ # in-line "import" statement in the _main() function below doesn't
+ # cause warnings about local names shadowing use of the 'SCons'
+ # globl in nest scopes and UnboundLocalErrors and the like in some
+ # versions (2.1) of Python.
+ import SCons.Platform.win32
+ return SCons.Platform.win32.parallel_msg
+
#
+
+class SConsPrintHelpException(Exception):
+ pass
+
display = SCons.Util.display
progress_display = SCons.Util.DisplayEngine()
+first_command_start = None
+last_command_end = None
+
+class Progressor:
+ prev = ''
+ count = 0
+ target_string = '$TARGET'
+
+ def __init__(self, obj, interval=1, file=None, overwrite=False):
+ if file is None:
+ file = sys.stdout
+
+ self.obj = obj
+ self.file = file
+ self.interval = interval
+ self.overwrite = overwrite
+
+ if callable(obj):
+ self.func = obj
+ elif SCons.Util.is_List(obj):
+ self.func = self.spinner
+ elif obj.find(self.target_string) != -1:
+ self.func = self.replace_string
+ else:
+ self.func = self.string
+
+ def write(self, s):
+ self.file.write(s)
+ self.file.flush()
+ self.prev = s
+
+ def erase_previous(self):
+ if self.prev:
+ length = len(self.prev)
+ if self.prev[-1] in ('\n', '\r'):
+ length = length - 1
+ self.write(' ' * length + '\r')
+ self.prev = ''
+
+ def spinner(self, node):
+ self.write(self.obj[self.count % len(self.obj)])
+
+ def string(self, node):
+ self.write(self.obj)
+
+ def replace_string(self, node):
+ self.write(self.obj.replace(self.target_string, str(node)))
+
+ def __call__(self, node):
+ self.count = self.count + 1
+ if (self.count % self.interval) == 0:
+ if self.overwrite:
+ self.erase_previous()
+ self.func(node)
+
+ProgressObject = SCons.Util.Null()
+
+def Progress(*args, **kw):
+ global ProgressObject
+ ProgressObject = Progressor(*args, **kw)
+
# Task control.
#
-class BuildTask(SCons.Taskmaster.Task):
+
+_BuildFailures = []
+
+def GetBuildFailures():
+ return _BuildFailures
+
+class BuildTask(SCons.Taskmaster.OutOfDateTask):
"""An SCons build task."""
+ progress = ProgressObject
+
def display(self, message):
display('scons: ' + message)
+ def prepare(self):
+ self.progress(self.targets[0])
+ return SCons.Taskmaster.OutOfDateTask.prepare(self)
+
+ def needs_execute(self):
+ if SCons.Taskmaster.OutOfDateTask.needs_execute(self):
+ return True
+ if self.top and self.targets[0].has_builder():
+ display("scons: `%s' is up to date." % str(self.node))
+ return False
+
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)
+ if print_time:
+ start_time = time.time()
+ global first_command_start
+ if first_command_start is None:
+ first_command_start = start_time
+ SCons.Taskmaster.OutOfDateTask.execute(self)
+ if print_time:
+ global cumulative_command_time
+ global last_command_end
+ finish_time = time.time()
+ last_command_end = finish_time
+ cumulative_command_time = cumulative_command_time+finish_time-start_time
+ sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time))
def do_failed(self, status=2):
+ _BuildFailures.append(self.exception[1])
global exit_status
- if ignore_errors:
- SCons.Taskmaster.Task.executed(self)
- elif keep_going_on_error:
- SCons.Taskmaster.Task.fail_continue(self)
+ global this_build_status
+ if self.options.ignore_errors:
+ SCons.Taskmaster.OutOfDateTask.executed(self)
+ elif self.options.keep_going:
+ SCons.Taskmaster.OutOfDateTask.fail_continue(self)
exit_status = status
+ this_build_status = status
else:
- SCons.Taskmaster.Task.fail_stop(self)
+ SCons.Taskmaster.OutOfDateTask.fail_stop(self)
exit_status = status
+ this_build_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:
+ def classname(obj):
+ return str(obj.__class__).split('.')[-1]
+ if classname(t) in ('File', 'Dir', 'Entry'):
+ errstr="Do not know how to make %s target `%s' (%s)." % (classname(t), t, t.abspath)
+ else: # Alias or Python or ...
+ errstr="Do not know how to make %s target `%s'." % (classname(t), t)
+ sys.stderr.write("scons: *** " + errstr)
+ if not self.options.keep_going:
sys.stderr.write(" Stop.")
sys.stderr.write("\n")
+ try:
+ raise SCons.Errors.BuildError(t, errstr)
+ except KeyboardInterrupt:
+ raise
+ except:
+ self.exception_set()
self.do_failed()
else:
print "scons: Nothing to be done for `%s'." % t
- SCons.Taskmaster.Task.executed(self)
+ SCons.Taskmaster.OutOfDateTask.executed(self)
else:
- SCons.Taskmaster.Task.executed(self)
+ SCons.Taskmaster.OutOfDateTask.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)
+ try:
+ t, e, tb = sys.exc_info()[:]
+ except ValueError:
+ t, e = exc_info
+ tb = None
+
+ # Deprecated string exceptions will have their string stored
+ # in the first entry of the tuple.
+ if e is None:
+ e = t
+
+ buildError = SCons.Errors.convert_to_BuildError(e)
+ if not buildError.node:
+ buildError.node = self.node
+
+ node = buildError.node
+ if not SCons.Util.is_List(node):
+ node = [ node ]
+ nodename = ', '.join(map(str, node))
+
+ errfmt = "scons: *** [%s] %s\n"
+ sys.stderr.write(errfmt % (nodename, buildError))
+
+ if (buildError.exc_info[2] and buildError.exc_info[1] and
+ # TODO(1.5)
+ #not isinstance(
+ # buildError.exc_info[1],
+ # (EnvironmentError, SCons.Errors.StopError, SCons.Errors.UserError))):
+ not isinstance(buildError.exc_info[1], EnvironmentError) and
+ not isinstance(buildError.exc_info[1], SCons.Errors.StopError) and
+ not isinstance(buildError.exc_info[1], SCons.Errors.UserError)):
+ type, value, trace = buildError.exc_info
+ traceback.print_exception(type, value, trace)
+ elif tb and print_stacktrace:
+ sys.stderr.write("scons: internal stack trace:\n")
+ traceback.print_tb(tb, file=sys.stderr)
+
+ self.exception = (e, buildError, tb) # type, value, traceback
+ self.do_failed(buildError.exitstatus)
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:
+ for tp in self.options.tree_printers:
+ tp.display(t)
+ if self.options.debug_includes:
tree = t.render_include_tree()
if tree:
print
print tree
- SCons.Taskmaster.Task.postprocess(self)
+ SCons.Taskmaster.OutOfDateTask.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:
+ SCons.Taskmaster.OutOfDateTask.make_ready(self)
+ if self.out_of_date and self.options.debug_explain:
explanation = self.out_of_date[0].explain()
if explanation:
sys.stdout.write("scons: " + explanation)
-class CleanTask(SCons.Taskmaster.Task):
+class CleanTask(SCons.Taskmaster.AlwaysTask):
"""An SCons clean task."""
+ def fs_delete(self, path, pathstr, remove=1):
+ try:
+ if os.path.lexists(path):
+ if os.path.isfile(path) or os.path.islink(path):
+ if remove: os.unlink(path)
+ display("Removed " + pathstr)
+ elif os.path.isdir(path) and not os.path.islink(path):
+ # delete everything in the dir
+ for e in sorted(os.listdir(path)):
+ p = os.path.join(path, e)
+ s = os.path.join(pathstr, e)
+ if os.path.isfile(p):
+ if remove: os.unlink(p)
+ display("Removed " + s)
+ else:
+ self.fs_delete(p, s, remove)
+ # then delete dir itself
+ if remove: os.rmdir(path)
+ display("Removed directory " + pathstr)
+ else:
+ errstr = "Path '%s' exists but isn't a file or directory."
+ raise SCons.Errors.UserError(errstr % (pathstr))
+ except SCons.Errors.UserError, e:
+ print e
+ except (IOError, OSError), e:
+ print "scons: Could not remove '%s':" % pathstr, e.strerror
+
def show(self):
target = self.targets[0]
- if (target.has_builder() or target.side_effect) and not target.isdir():
- display("Removed " + str(target))
- if SCons.Environment.CleanTargets.has_key(target):
+ if (target.has_builder() or target.side_effect) and not target.noclean:
+ for t in self.targets:
+ if not t.isdir():
+ display("Removed " + str(t))
+ if target in SCons.Environment.CleanTargets:
files = SCons.Environment.CleanTargets[target]
for f in files:
- SCons.Util.fs_delete(str(f), 0)
+ self.fs_delete(f.abspath, str(f), 0)
def remove(self):
target = self.targets[0]
- if target.has_builder() or target.side_effect:
+ if (target.has_builder() or target.side_effect) and not target.noclean:
for t in self.targets:
try:
removed = t.remove()
except OSError, e:
+ # An OSError may indicate something like a permissions
+ # issue, an IOError would indicate something like
+ # the file not existing. In either case, print a
+ # message and keep going to try to remove as many
+ # targets aa possible.
print "scons: Could not remove '%s':" % str(t), e.strerror
else:
if removed:
display("Removed " + str(t))
- if SCons.Environment.CleanTargets.has_key(target):
+ if target in SCons.Environment.CleanTargets:
files = SCons.Environment.CleanTargets[target]
for f in files:
- SCons.Util.fs_delete(str(f))
+ self.fs_delete(f.abspath, str(f))
execute = remove
+ # We want the Taskmaster to update the Node states (and therefore
+ # handle reference counts, etc.), but we don't want to call
+ # back to the Node's post-build methods, which would do things
+ # we don't want, like store .sconsign information.
+ executed = SCons.Taskmaster.Task.executed_without_callbacks
+
# 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.
def prepare(self):
pass
-class QuestionTask(SCons.Taskmaster.Task):
+class QuestionTask(SCons.Taskmaster.AlwaysTask):
"""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:
+ if self.targets[0].get_state() != SCons.Node.up_to_date or \
+ (self.top and not self.targets[0].exists()):
global exit_status
+ global this_build_status
exit_status = 1
+ this_build_status = 1
self.tm.stop()
def executed(self):
pass
+
+class TreePrinter:
+ def __init__(self, derived=False, prune=False, status=False):
+ self.derived = derived
+ self.prune = prune
+ self.status = status
+ def get_all_children(self, node):
+ return node.all_children()
+ def get_derived_children(self, node):
+ children = node.all_children(None)
+ return [x for x in children if x.has_builder()]
+ def display(self, t):
+ if self.derived:
+ func = self.get_derived_children
+ else:
+ func = self.get_all_children
+ s = self.status and 2 or 0
+ SCons.Util.print_tree(t, func, prune=self.prune, showtags=s)
+
+
+def python_version_string():
+ return sys.version.split()[0]
+
+def python_version_unsupported(version=sys.version_info):
+ return version < (1, 5, 2)
+
+def python_version_deprecated(version=sys.version_info):
+ return version < (2, 4, 0)
+
+
# Global variables
-keep_going_on_error = 0
-print_dtree = 0
-print_explanations = 0
-print_includes = 0
print_objects = 0
print_memoizer = 0
print_stacktrace = 0
-print_stree = 0
print_time = 0
-print_tree = 0
-ignore_errors = 0
sconscript_time = 0
-command_time = 0
-exit_status = 0 # exit status, assume success by default
-repositories = []
-num_jobs = 1 # this is modifed by SConscript.SetJobs()
-
-diskcheck_all = SCons.Node.FS.diskcheck_types()
-diskcheck_option_set = None
-
-def diskcheck_convert(value):
- if value is None:
- return []
- if not SCons.Util.is_List(value):
- value = string.split(value, ',')
- result = []
- for v in map(string.lower, value):
- if v == 'all':
- result = diskcheck_all
- elif v == 'none':
- result = []
- elif v in diskcheck_all:
- result.append(v)
- else:
- raise ValueError, v
+cumulative_command_time = 0
+exit_status = 0 # final exit status, assume success by default
+this_build_status = 0 # "exit status" of an individual build
+num_jobs = None
+delayed_warnings = []
+
+class FakeOptionParser:
+ """
+ A do-nothing option parser, used for the initial OptionsParser variable.
+
+ During normal SCons operation, the OptionsParser is created right
+ away by the main() function. Certain tests scripts however, can
+ introspect on different Tool modules, the initialization of which
+ can try to add a new, local option to an otherwise uninitialized
+ OptionsParser object. This allows that introspection to happen
+ without blowing up.
+
+ """
+ class FakeOptionValues:
+ def __getattr__(self, attr):
+ return None
+ values = FakeOptionValues()
+ def add_local_option(self, *args, **kw):
+ pass
+
+OptionsParser = FakeOptionParser()
+
+def AddOption(*args, **kw):
+ if 'default' not in kw:
+ kw['default'] = None
+ result = OptionsParser.add_local_option(*args, **kw)
return result
+def GetOption(name):
+ return getattr(OptionsParser.values, name)
+
+def SetOption(name, value):
+ return OptionsParser.values.set_option(name, value)
+
#
class Stats:
def __init__(self):
def do_print(self):
stats_table = {}
for s in self.stats:
- for n in map(lambda t: t[0], s):
+ for n in [t[0] for t in s]:
stats_table[n] = [0, 0, 0, 0]
i = 0
for s in self.stats:
for n, c in s:
stats_table[n][i] = c
i = i + 1
- keys = stats_table.keys()
- keys.sort()
self.outfp.write("Object counts:\n")
pre = [" "]
post = [" %s\n"]
l = len(self.stats)
- fmt1 = string.join(pre + [' %7s']*l + post, '')
- fmt2 = string.join(pre + [' %7d']*l + post, '')
+ fmt1 = ''.join(pre + [' %7s']*l + post)
+ fmt2 = ''.join(pre + [' %7d']*l + post)
labels = self.labels[:l]
labels.append(("", "Class"))
- self.outfp.write(fmt1 % tuple(map(lambda x: x[0], labels)))
- self.outfp.write(fmt1 % tuple(map(lambda x: x[1], labels)))
- for k in keys:
+ self.outfp.write(fmt1 % tuple([x[0] for x in labels]))
+ self.outfp.write(fmt1 % tuple([x[1] for x in labels]))
+ for k in sorted(stats_table.keys()):
r = stats_table[k][:l] + [k]
self.outfp.write(fmt2 % tuple(r))
# 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.
# of SCons:
for frame in tb:
filename = frame[0]
- if string.find(filename, os.sep+'SCons'+os.sep) == -1:
+ if filename.find(os.sep+'SCons'+os.sep) == -1:
return frame
return tb[0]
The file and line number will be the deepest stack frame that is
not part of SCons itself.
"""
+ global print_stacktrace
etype, value, tb = sys.exc_info()
+ if print_stacktrace:
+ traceback.print_exception(etype, value, tb)
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))
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=''):
+def _SConstruct_exists(dirname='', repositories=[], filelist=None):
"""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']:
+ if not filelist:
+ filelist = ['SConstruct', 'Sconstruct', 'sconstruct']
+ for file in filelist:
sfile = os.path.join(dirname, file)
if os.path.isfile(sfile):
return sfile
return sfile
return None
-def _set_globals(options):
- global keep_going_on_error, ignore_errors
- global count_stats, print_dtree
- global print_explanations, print_includes, print_memoizer
- global print_objects, print_stacktrace, print_stree
- global print_time, print_tree
- global memory_stats
+def _set_debug_values(options):
+ global print_memoizer, print_objects, print_stacktrace, print_time
- keep_going_on_error = options.keep_going
- try:
- debug_values = options.debug
- if debug_values is None:
- debug_values = []
- except AttributeError:
- pass
- else:
- if "count" in debug_values:
+ debug_values = options.debug
+
+ if "count" in debug_values:
+ # All of the object counts are within "if __debug__:" blocks,
+ # which get stripped when running optimized (with python -O or
+ # from compiled *.pyo files). Provide a warning if __debug__ is
+ # stripped, so it doesn't just look like --debug=count is broken.
+ enable_count = False
+ if __debug__: enable_count = True
+ if enable_count:
count_stats.enable(sys.stdout)
- if "dtree" in debug_values:
- print_dtree = 1
- if "explain" in debug_values:
- print_explanations = 1
- if "findlibs" in debug_values:
- SCons.Scanner.Prog.print_find_libs = "findlibs"
- if "includes" in debug_values:
- print_includes = 1
- if "memoizer" in debug_values:
- print_memoizer = 1
- if "memory" in debug_values:
- memory_stats.enable(sys.stdout)
- if "objects" in debug_values:
- print_objects = 1
- if "presub" in debug_values:
- SCons.Action.print_actions_presub = 1
- if "stacktrace" in debug_values:
- print_stacktrace = 1
- if "stree" in debug_values:
- print_stree = 1
- if "time" in debug_values:
- print_time = 1
- if "tree" in debug_values:
- print_tree = 1
- ignore_errors = options.ignore_errors
+ else:
+ msg = "--debug=count is not supported when running SCons\n" + \
+ "\twith the python -O option or optimized (.pyo) modules."
+ SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg)
+ if "dtree" in debug_values:
+ options.tree_printers.append(TreePrinter(derived=True))
+ options.debug_explain = ("explain" in debug_values)
+ if "findlibs" in debug_values:
+ SCons.Scanner.Prog.print_find_libs = "findlibs"
+ options.debug_includes = ("includes" in debug_values)
+ print_memoizer = ("memoizer" in debug_values)
+ if "memory" in debug_values:
+ memory_stats.enable(sys.stdout)
+ print_objects = ("objects" in debug_values)
+ if "presub" in debug_values:
+ SCons.Action.print_actions_presub = 1
+ if "stacktrace" in debug_values:
+ print_stacktrace = 1
+ if "stree" in debug_values:
+ options.tree_printers.append(TreePrinter(status=True))
+ if "time" in debug_values:
+ print_time = 1
+ if "tree" in debug_values:
+ options.tree_printers.append(TreePrinter())
def _create_path(plist):
path = '.'
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.")
+def _load_site_scons_dir(topdir, site_dir_name=None):
+ """Load the site_scons dir under topdir.
+ Adds site_scons to sys.path, imports site_scons/site_init.py,
+ and adds site_scons/site_tools to default toolpath."""
+ if site_dir_name:
+ err_if_not_found = True # user specified: err if missing
+ else:
+ site_dir_name = "site_scons"
+ err_if_not_found = False
- 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", "memoizer", "memory",
- "nomemoizer", "objects",
- "pdb", "presub", "stacktrace", "stree",
- "time", "tree"]
-
- def opt_debug(option, opt, value, parser, debug_options=debug_options):
- if value in debug_options:
- try:
- if parser.values.debug is None:
- parser.values.debug = []
- except AttributeError:
- parser.values.debug = []
- parser.values.debug.append(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_diskcheck(option, opt, value, parser):
- try:
- global diskcheck_option_set
- diskcheck_option_set = diskcheck_convert(value)
- SCons.Node.FS.set_diskcheck(diskcheck_option_set)
- except ValueError, e:
- raise OptionValueError("Warning: `%s' is not a valid diskcheck type" % e)
-
-
- self.add_option('--diskcheck', action="callback", type="string",
- callback=opt_diskcheck, dest='diskcheck',
- metavar="TYPE",
- help="Enable specific on-disk checks.")
-
- 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.")
-
- self.add_option('--profile', action="store",
- dest="profile_file", 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',
- 'diskcheck':diskcheck_all}
-
- 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)
- elif name == 'diskcheck':
+ site_dir = os.path.join(topdir.path, site_dir_name)
+ if not os.path.exists(site_dir):
+ if err_if_not_found:
+ raise SCons.Errors.UserError("site dir %s not found."%site_dir)
+ return
+
+ site_init_filename = "site_init.py"
+ site_init_modname = "site_init"
+ site_tools_dirname = "site_tools"
+ sys.path = [os.path.abspath(site_dir)] + sys.path
+ site_init_file = os.path.join(site_dir, site_init_filename)
+ site_tools_dir = os.path.join(site_dir, site_tools_dirname)
+ if os.path.exists(site_init_file):
+ import imp
+ # TODO(2.4): turn this into try:-except:-finally:
+ try:
try:
- value = diskcheck_convert(value)
- except ValueError, v:
- raise SCons.Errors.UserError, "Not a valid diskcheck value: %s"%v
- if not diskcheck_option_set:
- SCons.Node.FS.set_diskcheck(value)
+ fp, pathname, description = imp.find_module(site_init_modname,
+ [site_dir])
+ # Load the file into SCons.Script namespace. This is
+ # opaque and clever; m is the module object for the
+ # SCons.Script module, and the exec ... in call executes a
+ # file (or string containing code) in the context of the
+ # module's dictionary, so anything that code defines ends
+ # up adding to that module. This is really short, but all
+ # the error checking makes it longer.
+ try:
+ m = sys.modules['SCons.Script']
+ except Exception, e:
+ fmt = 'cannot import site_init.py: missing SCons.Script module %s'
+ raise SCons.Errors.InternalError(fmt % repr(e))
+ try:
+ # This is the magic.
+ exec fp in m.__dict__
+ except KeyboardInterrupt:
+ raise
+ except Exception, e:
+ fmt = '*** Error loading site_init file %s:\n'
+ sys.stderr.write(fmt % repr(site_init_file))
+ raise
+ except KeyboardInterrupt:
+ raise
+ except ImportError, e:
+ fmt = '*** cannot import site init file %s:\n'
+ sys.stderr.write(fmt % repr(site_init_file))
+ raise
+ finally:
+ if fp:
+ fp.close()
+ if os.path.exists(site_tools_dir):
+ SCons.Tool.DefaultToolpath.append(os.path.abspath(site_tools_dir))
+
+def version_string(label, module):
+ version = module.__version__
+ build = module.__build__
+ if build:
+ if build[0] != '.':
+ build = '.' + build
+ version = version + build
+ fmt = "\t%s: v%s, %s, by %s on %s\n"
+ return fmt % (label,
+ version,
+ module.__date__,
+ module.__developer__,
+ module.__buildsys__)
+
+def _main(parser):
+ global exit_status
+ global this_build_status
- self.settable[name] = value
-
+ options = parser.values
-def _main(args, parser):
# Here's where everything really happens.
- # First order of business: set up default warnings and and then
- # handle the user's warning options, so we can warn about anything
- # that happens appropriately.
+ # First order of business: set up default warnings and then
+ # handle the user's warning options, so that we can issue (or
+ # suppress) appropriate warnings about anything that might happen,
+ # as configured by the user.
+
default_warnings = [ SCons.Warnings.CorruptSConsignWarning,
SCons.Warnings.DeprecatedWarning,
SCons.Warnings.DuplicateEnvironmentWarning,
+ SCons.Warnings.FutureReservedVariableWarning,
+ SCons.Warnings.LinkWarning,
SCons.Warnings.MissingSConscriptWarning,
+ SCons.Warnings.NoMD5ModuleWarning,
+ SCons.Warnings.NoMetaclassSupportWarning,
+ SCons.Warnings.NoObjectCountWarning,
SCons.Warnings.NoParallelSupportWarning,
- SCons.Warnings.MisleadingKeywordsWarning, ]
+ SCons.Warnings.MisleadingKeywordsWarning,
+ SCons.Warnings.ReservedVariableWarning,
+ SCons.Warnings.StackSizeWarning,
+ SCons.Warnings.VisualVersionMismatch,
+ SCons.Warnings.VisualCMissingWarning,
+ ]
+
for warning in default_warnings:
SCons.Warnings.enableWarningClass(warning)
SCons.Warnings._warningOut = _scons_internal_warning
- if options.warn:
- _setup_warn(options.warn)
+ SCons.Warnings.process_warn_strings(options.warn)
+
+ # Now that we have the warnings configuration set up, we can actually
+ # issue (or suppress) any warnings about warning-worthy things that
+ # occurred while the command-line options were getting parsed.
+ try:
+ dw = options.delayed_warnings
+ except AttributeError:
+ pass
+ else:
+ delayed_warnings.extend(dw)
+ for warning_type, message in delayed_warnings:
+ SCons.Warnings.warn(warning_type, message)
+
+ if options.diskcheck:
+ SCons.Node.FS.set_diskcheck(options.diskcheck)
# Next, we want to create the FS object that represents the outside
# world's file system, as that's central to a lot of initialization.
# want to start everything, which means first handling any relevant
# options that might cause us to chdir somewhere (-C, -D, -U, -u).
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)
-
- # The SConstruct file may be in a repository, so initialize those
- # before we start the search up our path for one.
- global repositories
- if options.repository:
- repositories.extend(options.repository)
+ script_dir = os.path.abspath(_create_path(options.directory))
+ else:
+ script_dir = os.getcwd()
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):
+ while script_dir and not _SConstruct_exists(script_dir,
+ options.repository,
+ options.file):
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)
+
+ if script_dir and script_dir != os.getcwd():
+ display("scons: Entering directory `%s'" % script_dir)
+ try:
os.chdir(script_dir)
+ except OSError:
+ sys.stderr.write("Could not change directory to %s\n" % script_dir)
# Now that we're in the top-level SConstruct directory, go ahead
# and initialize the FS object that represents the file system,
# and make it the build engine default.
- fs = SCons.Node.FS.default_fs = SCons.Node.FS.FS()
+ fs = SCons.Node.FS.get_default_fs()
- for rep in repositories:
+ for rep in options.repository:
fs.Repository(rep)
# Now that we have the FS object, the next order of business is to
if options.file:
scripts.extend(options.file)
if not scripts:
- sfile = _SConstruct_exists()
+ sfile = _SConstruct_exists(repositories=options.repository,
+ filelist=options.file)
if sfile:
scripts.append(sfile)
if not scripts:
- if options.help_msg:
+ if options.help:
# 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)
- raise SCons.Errors.UserError, "No SConstruct file found."
+ raise SConsPrintHelpException
+ raise SCons.Errors.UserError("No SConstruct file found.")
if scripts[0] == "-":
d = fs.getcwd()
d = fs.File(scripts[0]).dir
fs.set_SConstruct_dir(d)
- # Now that we have the FS object and it's intialized, set up (most
- # of) the rest of the options.
- global ssoptions
- ssoptions = SConscriptSettableOptions(options)
-
- _set_globals(options)
+ _set_debug_values(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.noexec:
+
+ if options.no_exec:
SCons.SConf.dryrun = 1
SCons.Action.execute_actions = None
- CleanTask.execute = CleanTask.show
if options.question:
SCons.SConf.dryrun = 1
+ if options.clean:
+ SCons.SConf.SetBuildType('clean')
+ if options.help:
+ SCons.SConf.SetBuildType('help')
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.site_dir:
+ _load_site_scons_dir(d, options.site_dir)
+ elif not options.no_site_dir:
+ _load_site_scons_dir(d)
+
if options.include_dir:
sys.path = options.include_dir + sys.path
# read and execute have access to them.
targets = []
xmit_args = []
- for a in args:
+ for a in parser.largs:
+ if a[:1] == '-':
+ continue
if '=' in a:
xmit_args.append(a)
else:
targets.append(a)
- SCons.Script._Add_Targets(targets)
+ SCons.Script._Add_Targets(targets + parser.rargs)
SCons.Script._Add_Arguments(xmit_args)
- 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 stdout is not a tty, replace it with a wrapper object to call flush
+ # after every write.
+ #
+ # Tty devices automatically flush after every newline, so the replacement
+ # isn't necessary. Furthermore, if we replace sys.stdout, the readline
+ # module will no longer work. This affects the behavior during
+ # --interactive mode. --interactive should only be used when stdin and
+ # stdout refer to a tty.
+ if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty():
+ sys.stdout = SCons.Util.Unbuffered(sys.stdout)
+ if not hasattr(sys.stderr, 'isatty') or not sys.stderr.isatty():
+ sys.stderr = SCons.Util.Unbuffered(sys.stderr)
memory_stats.append('before reading SConscript files:')
count_stats.append(('pre-', 'read'))
+ # And here's where we (finally) read the SConscript files.
+
progress_display("scons: Reading SConscript files ...")
start_time = time.time()
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
+ # couldn't be copied in to the VariantDir. 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)
+ progress_display("scons: done reading SConscript files.")
memory_stats.append('after reading SConscript files:')
count_stats.append(('post-', 'read'))
- fs.chdir(fs.Top)
-
- if options.help_msg:
+ # Re-{enable,disable} warnings in case they disabled some in
+ # the SConscript file.
+ #
+ # We delay enabling the PythonVersionWarning class until here so that,
+ # if they explicity disabled it in either in the command line or in
+ # $SCONSFLAGS, or in the SConscript file, then the search through
+ # the list of deprecated warning classes will find that disabling
+ # first and not issue the warning.
+ SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning)
+ SCons.Warnings.process_warn_strings(options.warn)
+
+ # Now that we've read the SConscript files, we can check for the
+ # warning about deprecated Python versions--delayed until here
+ # in case they disabled the warning in the SConscript files.
+ if python_version_deprecated():
+ msg = "Support for pre-2.4 Python (%s) is deprecated.\n" + \
+ " If this will cause hardship, contact dev@scons.tigris.org."
+ SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning,
+ msg % python_version_string())
+
+ if not options.help:
+ SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
+
+ # Now re-parse the command-line options (any to the left of a '--'
+ # argument, that is) with any user-defined command-line options that
+ # the SConscript files may have added to the parser object. This will
+ # emit the appropriate error message and exit if any unknown option
+ # was specified on the command line.
+
+ parser.preserve_unknown_options = False
+ parser.parse_args(parser.largs, options)
+
+ if options.help:
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)
+ raise SConsPrintHelpException
else:
print help_text
print "Use scons -H for help about command-line options."
- sys.exit(0)
+ exit_status = 0
+ return
+
+ # Change directory to the top-level SConstruct directory, then tell
+ # the Node.FS subsystem that we're all done reading the SConscript
+ # files and calling Repository() and VariantDir() and changing
+ # directories and the like, so it can go ahead and start memoizing
+ # the string values of file system nodes.
+
+ fs.chdir(fs.Top)
+
+ SCons.Node.FS.save_strings(1)
# 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'))
+ SCons.Node.implicit_cache = options.implicit_cache
+ SCons.Node.FS.set_duplicate(options.duplicate)
+ fs.set_max_drift(options.max_drift)
+
+ SCons.Job.explicit_stack_size = options.stack_size
+
+ if options.md5_chunksize:
+ SCons.Node.FS.File.md5_chunksize = options.md5_chunksize
+
+ platform = SCons.Platform.platform_module()
+
+ if options.interactive:
+ SCons.Script.Interactive.interact(fs, OptionsParser, options,
+ targets, target_top)
+
+ else:
+
+ # Build the targets
+ nodes = _build_targets(fs, options, targets, target_top)
+ if not nodes:
+ exit_status = 2
+
+def _build_targets(fs, options, targets, target_top):
+
+ global this_build_status
+ this_build_status = 0
+
+ progress_display.set_mode(not (options.no_progress or options.silent))
+ display.set_mode(not options.silent)
+ SCons.Action.print_actions = not options.silent
+ SCons.Action.execute_actions = not options.no_exec
+ SCons.Node.FS.do_store_info = not options.no_exec
+ SCons.SConf.dryrun = options.no_exec
+
+ if options.diskcheck:
+ SCons.Node.FS.set_diskcheck(options.diskcheck)
+
+ SCons.CacheDir.cache_enabled = not options.cache_disable
+ SCons.CacheDir.cache_debug = options.cache_debug
+ SCons.CacheDir.cache_force = options.cache_force
+ SCons.CacheDir.cache_show = options.cache_show
+
+ if options.no_exec:
+ CleanTask.execute = CleanTask.show
+ else:
+ CleanTask.execute = CleanTask.remove
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 targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default:
+ # They specified targets on the command line or modified
+ # BUILD_TARGETS in the SConscript file(s), 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
+
+ targets = SCons.Script.BUILD_TARGETS
else:
# There are no targets specified on the command line,
# so if they used -u, -U or -D, we may have to restrict
# 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)
+ d = list(filter(check_dir, SCons.Script.DEFAULT_TARGETS))
SCons.Script.DEFAULT_TARGETS[:] = d
target_top = None
lookup_top = None
if not targets:
sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
- sys.exit(2)
+ return None
def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
if isinstance(x, SCons.Node.Node):
else:
node = None
# Why would ltop be None? Unfortunately this happens.
- if ltop == None: ltop = ''
+ if ltop is None: ltop = ''
# Curdir becomes important when SCons is called with -u, -C,
# or similar option that changes directory, and so the paths
# of targets given on the command line need to be adjusted.
curdir = os.path.join(os.getcwd(), str(ltop))
for lookup in SCons.Node.arg2nodes_lookups:
node = lookup(x, curdir=curdir)
- if node != None:
+ if node is not None:
break
if node is None:
node = fs.Entry(x, directory=ltop, create=1)
node = None
return node
- nodes = filter(None, map(Entry, targets))
+ nodes = [_f for _f in map(Entry, targets) if _f]
- task_class = BuildTask # default action is to build targets
+ task_class = BuildTask # default action is to build targets
opening_message = "Building targets ..."
closing_message = "done building targets."
- if keep_going_on_error:
+ if options.keep_going:
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'):
+ if options.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)."
+ if options.keep_going:
+ failure_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')
+ task_class.progress = ProgressObject
if options.random:
def order(dependencies):
"""Randomize the dependencies."""
+ import random
# This is cribbed from the implementation of
# random.shuffle() in Python 2.X.
d = dependencies
- for i in xrange(len(d)-1, 0, -1):
+ for i in range(len(d)-1, 0, -1):
j = int(random.random() * (i+1))
d[i], d[j] = d[j], d[i]
return d
"""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 options.taskmastertrace_file == '-':
+ tmtrace = sys.stdout
+ elif options.taskmastertrace_file:
+ tmtrace = open(options.taskmastertrace_file, 'wb')
+ else:
+ tmtrace = None
+ taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace)
+
+ # Let the BuildTask objects get at the options to respond to the
+ # various print_* settings, tree_printer list, etc.
+ BuildTask.options = options
+
+ global num_jobs
+ num_jobs = options.num_jobs
+ jobs = SCons.Job.Jobs(num_jobs, taskmaster)
+ if num_jobs > 1:
+ msg = None
+ if jobs.num_jobs == 1:
+ msg = "parallel builds are unsupported by this version of Python;\n" + \
+ "\tignoring -j or num_jobs option.\n"
+ elif sys.platform == 'win32':
+ msg = fetch_win32_parallel_msg()
+ if msg:
+ SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
memory_stats.append('before building targets:')
count_stats.append(('pre-', 'build'))
- try:
- jobs.run()
- finally:
- if exit_status:
+ def jobs_postfunc(
+ jobs=jobs,
+ options=options,
+ closing_message=closing_message,
+ failure_message=failure_message
+ ):
+ if jobs.were_interrupted():
+ if not options.no_progress and not options.silent:
+ sys.stderr.write("scons: Build interrupted.\n")
+ global exit_status
+ global this_build_status
+ exit_status = 2
+ this_build_status = 2
+
+ if this_build_status:
progress_display("scons: " + failure_message)
else:
progress_display("scons: " + closing_message)
- if not options.noexec:
+ if not options.no_exec:
+ if jobs.were_interrupted():
+ progress_display("scons: writing .sconsign file.")
SCons.SConsign.write()
+ progress_display("scons: " + opening_message)
+ jobs.run(postfunc = jobs_postfunc)
+
memory_stats.append('after building targets:')
count_stats.append(('post-', 'build'))
-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 type(options.debug) == type([]) and "pdb" in options.debug:
+ return nodes
+
+def _exec_main(parser, values):
+ sconsflags = os.environ.get('SCONSFLAGS', '')
+ all_args = sconsflags.split() + sys.argv[1:]
+
+ options, args = parser.parse_args(all_args, values)
+
+ if isinstance(options.debug, list) and "pdb" in options.debug:
import pdb
- pdb.Pdb().runcall(_main, args, parser)
+ pdb.Pdb().runcall(_main, parser)
elif options.profile_file:
- import profile
- prof = profile.Profile()
+ # compat layer imports "cProfile" for us if it's available.
+ from profile import Profile
+
+ # Some versions of Python 2.4 shipped a profiler that had the
+ # wrong 'c_exception' entry in its dispatch table. Make sure
+ # we have the right one. (This may put an unnecessary entry
+ # in the table in earlier versions of Python, but its presence
+ # shouldn't hurt anything).
+ try:
+ dispatch = Profile.dispatch
+ except AttributeError:
+ pass
+ else:
+ dispatch['c_exception'] = Profile.trace_dispatch_return
+
+ prof = Profile()
try:
- prof.runcall(_main, args, parser)
+ prof.runcall(_main, parser)
+ except SConsPrintHelpException, e:
+ prof.dump_stats(options.profile_file)
+ raise e
except SystemExit:
pass
prof.dump_stats(options.profile_file)
else:
- _main(args, parser)
+ _main(parser)
def main():
+ global OptionsParser
global exit_status
+ global first_command_start
+
+ # Check up front for a Python version we do not support. We
+ # delay the check for deprecated Python versions until later,
+ # after the SConscript files have been read, in case they
+ # disable that warning.
+ if python_version_unsupported():
+ msg = "scons: *** SCons version %s does not run under Python version %s.\n"
+ sys.stderr.write(msg % (SCons.__version__, python_version_string()))
+ sys.exit(1)
+
+ parts = ["SCons by Steven Knight et al.:\n"]
+ try:
+ import __main__
+ parts.append(version_string("script", __main__))
+ except (ImportError, AttributeError):
+ # On Windows there is no scons.py, so there is no
+ # __main__.__version__, hence there is no script version.
+ pass
+ parts.append(version_string("engine", SCons))
+ parts.append("__COPYRIGHT__")
+ version = ''.join(parts)
+
+ import SConsOptions
+ parser = SConsOptions.Parser(version)
+ values = SConsOptions.SConsValues(parser.get_default_values())
+
+ OptionsParser = parser
try:
- _exec_main()
+ _exec_main(parser, values)
except SystemExit, s:
if s:
exit_status = s
except KeyboardInterrupt:
- print "Build interrupted."
+ print("scons: Build interrupted.")
sys.exit(2)
except SyntaxError, e:
_scons_syntax_error(e)
_scons_internal_error()
except SCons.Errors.UserError, e:
_scons_user_error(e)
+ except SConsPrintHelpException:
+ parser.print_help()
+ exit_status = 0
+ except SCons.Errors.BuildError, e:
+ exit_status = e.exitstatus
except:
# An exception here is likely a builtin Python exception Python
# code in an SConscript file. Show them precisely what the
#SCons.Debug.dumpLoggedInstances('*')
if print_memoizer:
- print "Memoizer (memory cache) hits and misses:"
- SCons.Memoize.Dump()
+ SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:")
# Dump any development debug info that may have been enabled.
# These are purely for internal debugging during development, so
SCons.Taskmaster.dump_stats()
if print_time:
- total_time = time.time()-SCons.Script.start_time
- scons_time = total_time-sconscript_time-command_time
+ total_time = time.time() - SCons.Script.start_time
+ if num_jobs == 1:
+ ct = cumulative_command_time
+ else:
+ if last_command_end is None or first_command_start is None:
+ ct = 0.0
+ else:
+ ct = last_command_end - first_command_start
+ scons_time = total_time - sconscript_time - ct
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
+ print "Total command execution time: %f seconds"%ct
sys.exit(exit_status)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: