This commit is contained in:
Tom
2018-06-12 21:01:05 +02:00
commit 6bfb8402f0
201 changed files with 53127 additions and 0 deletions

View File

@@ -0,0 +1,376 @@
#
# Copyright (c) 2001 - 2017 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import print_function
__revision__ = "src/engine/SCons/Script/Interactive.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog"
__doc__ = """
SCons interactive mode
"""
# TODO:
#
# This has the potential to grow into something with a really big life
# of its own, which might or might not be a good thing. Nevertheless,
# here are some enhancements that will probably be requested some day
# and are worth keeping in mind (assuming this takes off):
#
# - A command to re-read / re-load the SConscript files. This may
# involve allowing people to specify command-line options (e.g. -f,
# -I, --no-site-dir) that affect how the SConscript files are read.
#
# - Additional command-line options on the "build" command.
#
# Of the supported options that seemed to make sense (after a quick
# pass through the list), the ones that seemed likely enough to be
# used are listed in the man page and have explicit test scripts.
#
# These had code changed in Script/Main.py to support them, but didn't
# seem likely to be used regularly, so had no test scripts added:
#
# build --diskcheck=*
# build --implicit-cache=*
# build --implicit-deps-changed=*
# build --implicit-deps-unchanged=*
#
# These look like they should "just work" with no changes to the
# existing code, but like those above, look unlikely to be used and
# therefore had no test scripts added:
#
# build --random
#
# These I'm not sure about. They might be useful for individual
# "build" commands, and may even work, but they seem unlikely enough
# that we'll wait until they're requested before spending any time on
# writing test scripts for them, or investigating whether they work.
#
# build -q [??? is there a useful analog to the exit status?]
# build --duplicate=
# build --profile=
# build --max-drift=
# build --warn=*
# build --Y
#
# - Most of the SCons command-line options that the "build" command
# supports should be settable as default options that apply to all
# subsequent "build" commands. Maybe a "set {option}" command that
# maps to "SetOption('{option}')".
#
# - Need something in the 'help' command that prints the -h output.
#
# - A command to run the configure subsystem separately (must see how
# this interacts with the new automake model).
#
# - Command-line completion of target names; maybe even of SCons options?
# Completion is something that's supported by the Python cmd module,
# so this should be doable without too much trouble.
#
import cmd
import copy
import os
import re
import shlex
import sys
try:
import readline
except ImportError:
pass
class SConsInteractiveCmd(cmd.Cmd):
"""\
build [TARGETS] Build the specified TARGETS and their dependencies. 'b' is a synonym.
clean [TARGETS] Clean (remove) the specified TARGETS and their dependencies. 'c' is a synonym.
exit Exit SCons interactive mode.
help [COMMAND] Prints help for the specified COMMAND. 'h' and '?' are synonyms.
shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!' are synonyms.
version Prints SCons version information.
"""
synonyms = {
'b' : 'build',
'c' : 'clean',
'h' : 'help',
'scons' : 'build',
'sh' : 'shell',
}
def __init__(self, **kw):
cmd.Cmd.__init__(self)
for key, val in kw.items():
setattr(self, key, val)
if sys.platform == 'win32':
self.shell_variable = 'COMSPEC'
else:
self.shell_variable = 'SHELL'
def default(self, argv):
print("*** Unknown command: %s" % argv[0])
def onecmd(self, line):
line = line.strip()
if not line:
print(self.lastcmd)
return self.emptyline()
self.lastcmd = line
if line[0] == '!':
line = 'shell ' + line[1:]
elif line[0] == '?':
line = 'help ' + line[1:]
if os.sep == '\\':
line = line.replace('\\', '\\\\')
argv = shlex.split(line)
argv[0] = self.synonyms.get(argv[0], argv[0])
if not argv[0]:
return self.default(line)
else:
try:
func = getattr(self, 'do_' + argv[0])
except AttributeError:
return self.default(argv)
return func(argv)
def do_build(self, argv):
"""\
build [TARGETS] Build the specified TARGETS and their
dependencies. 'b' is a synonym.
"""
import SCons.Node
import SCons.SConsign
import SCons.Script.Main
options = copy.deepcopy(self.options)
options, targets = self.parser.parse_args(argv[1:], values=options)
SCons.Script.COMMAND_LINE_TARGETS = targets
if targets:
SCons.Script.BUILD_TARGETS = targets
else:
# If the user didn't specify any targets on the command line,
# use the list of default targets.
SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default
nodes = SCons.Script.Main._build_targets(self.fs,
options,
targets,
self.target_top)
if not nodes:
return
# Call each of the Node's alter_targets() methods, which may
# provide additional targets that ended up as part of the build
# (the canonical example being a VariantDir() when we're building
# from a source directory) and which we therefore need their
# state cleared, too.
x = []
for n in nodes:
x.extend(n.alter_targets()[0])
nodes.extend(x)
# Clean up so that we can perform the next build correctly.
#
# We do this by walking over all the children of the targets,
# and clearing their state.
#
# We currently have to re-scan each node to find their
# children, because built nodes have already been partially
# cleared and don't remember their children. (In scons
# 0.96.1 and earlier, this wasn't the case, and we didn't
# have to re-scan the nodes.)
#
# Because we have to re-scan each node, we can't clear the
# nodes as we walk over them, because we may end up rescanning
# a cleared node as we scan a later node. Therefore, only
# store the list of nodes that need to be cleared as we walk
# the tree, and clear them in a separate pass.
#
# XXX: Someone more familiar with the inner workings of scons
# may be able to point out a more efficient way to do this.
SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
seen_nodes = {}
def get_unseen_children(node, parent, seen_nodes=seen_nodes):
def is_unseen(node, seen_nodes=seen_nodes):
return node not in seen_nodes
return [child for child in node.children(scan=1) if is_unseen(child)]
def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
seen_nodes[node] = 1
# If this file is in a VariantDir and has a
# corresponding source file in the source tree, remember the
# node in the source tree, too. This is needed in
# particular to clear cached implicit dependencies on the
# source file, since the scanner will scan it if the
# VariantDir was created with duplicate=0.
try:
rfile_method = node.rfile
except AttributeError:
return
else:
rfile = rfile_method()
if rfile != node:
seen_nodes[rfile] = 1
for node in nodes:
walker = SCons.Node.Walker(node,
kids_func=get_unseen_children,
eval_func=add_to_seen_nodes)
n = walker.get_next()
while n:
n = walker.get_next()
for node in list(seen_nodes.keys()):
# Call node.clear() to clear most of the state
node.clear()
# node.clear() doesn't reset node.state, so call
# node.set_state() to reset it manually
node.set_state(SCons.Node.no_state)
node.implicit = None
# Debug: Uncomment to verify that all Taskmaster reference
# counts have been reset to zero.
#if node.ref_count != 0:
# from SCons.Debug import Trace
# Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
SCons.SConsign.Reset()
SCons.Script.Main.progress_display("scons: done clearing node information.")
def do_clean(self, argv):
"""\
clean [TARGETS] Clean (remove) the specified TARGETS
and their dependencies. 'c' is a synonym.
"""
return self.do_build(['build', '--clean'] + argv[1:])
def do_EOF(self, argv):
print()
self.do_exit(argv)
def _do_one_help(self, arg):
try:
# If help_<arg>() exists, then call it.
func = getattr(self, 'help_' + arg)
except AttributeError:
try:
func = getattr(self, 'do_' + arg)
except AttributeError:
doc = None
else:
doc = self._doc_to_help(func)
if doc:
sys.stdout.write(doc + '\n')
sys.stdout.flush()
else:
doc = self.strip_initial_spaces(func())
if doc:
sys.stdout.write(doc + '\n')
sys.stdout.flush()
def _doc_to_help(self, obj):
doc = obj.__doc__
if doc is None:
return ''
return self._strip_initial_spaces(doc)
def _strip_initial_spaces(self, s):
lines = s.split('\n')
spaces = re.match(' *', lines[0]).group(0)
def strip_spaces(l, spaces=spaces):
if l[:len(spaces)] == spaces:
l = l[len(spaces):]
return l
lines = list(map(strip_spaces, lines))
return '\n'.join(lines)
def do_exit(self, argv):
"""\
exit Exit SCons interactive mode.
"""
sys.exit(0)
def do_help(self, argv):
"""\
help [COMMAND] Prints help for the specified COMMAND. 'h'
and '?' are synonyms.
"""
if argv[1:]:
for arg in argv[1:]:
if self._do_one_help(arg):
break
else:
# If bare 'help' is called, print this class's doc
# string (if it has one).
doc = self._doc_to_help(self.__class__)
if doc:
sys.stdout.write(doc + '\n')
sys.stdout.flush()
def do_shell(self, argv):
"""\
shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and
'!' are synonyms.
"""
import subprocess
argv = argv[1:]
if not argv:
argv = os.environ[self.shell_variable]
try:
# Per "[Python-Dev] subprocess insufficiently platform-independent?"
# http://mail.python.org/pipermail/python-dev/2008-August/081979.html "+
# Doing the right thing with an argument list currently
# requires different shell= values on Windows and Linux.
p = subprocess.Popen(argv, shell=(sys.platform=='win32'))
except EnvironmentError as e:
sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror))
else:
p.wait()
def do_version(self, argv):
"""\
version Prints SCons version information.
"""
sys.stdout.write(self.parser.version + '\n')
def interact(fs, parser, options, targets, target_top):
c = SConsInteractiveCmd(prompt = 'scons>>> ',
fs = fs,
parser = parser,
options = options,
targets = targets,
target_top = target_top)
c.cmdloop()
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,988 @@
#
# Copyright (c) 2001 - 2017 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
__revision__ = "src/engine/SCons/Script/SConsOptions.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog"
import optparse
import re
import sys
import textwrap
no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))')
try:
from gettext import gettext
except ImportError:
def gettext(message):
return message
_ = gettext
import SCons.Node.FS
import SCons.Warnings
OptionValueError = optparse.OptionValueError
SUPPRESS_HELP = optparse.SUPPRESS_HELP
diskcheck_all = SCons.Node.FS.diskcheck_types()
def diskcheck_convert(value):
if value is None:
return []
if not SCons.Util.is_List(value):
value = value.split(',')
result = []
for v in value:
v = v.lower()
if v == 'all':
result = diskcheck_all
elif v == 'none':
result = []
elif v in diskcheck_all:
result.append(v)
else:
raise ValueError(v)
return result
class SConsValues(optparse.Values):
"""
Holder class for uniform access to SCons options, regardless
of whether or not they can be set on the command line or in the
SConscript files (using the SetOption() function).
A SCons option value can originate three different ways:
1) set on the command line;
2) set in an SConscript file;
3) the default setting (from the the op.add_option()
calls in the Parser() function, below).
The command line always overrides a value set in a SConscript file,
which in turn always overrides default settings. Because we want
to support user-specified options in the SConscript file itself,
though, we may not know about all of the options when the command
line is first parsed, so we can't make all the necessary precedence
decisions at the time the option is configured.
The solution implemented in this class is to keep these different sets
of settings separate (command line, SConscript file, and default)
and to override the __getattr__() method to check them in turn.
This should allow the rest of the code to just fetch values as
attributes of an instance of this class, without having to worry
about where they came from.
Note that not all command line options are settable from SConscript
files, and the ones that are must be explicitly added to the
"settable" list in this class, and optionally validated and coerced
in the set_option() method.
"""
def __init__(self, defaults):
self.__dict__['__defaults__'] = defaults
self.__dict__['__SConscript_settings__'] = {}
def __getattr__(self, attr):
"""
Fetches an options value, checking first for explicit settings
from the command line (which are direct attributes), then the
SConscript file settings, then the default values.
"""
try:
return self.__dict__[attr]
except KeyError:
try:
return self.__dict__['__SConscript_settings__'][attr]
except KeyError:
try:
return getattr(self.__dict__['__defaults__'], attr)
except KeyError:
# Added because with py3 this is a new class,
# not a classic class, and due to the way
# In that case it will create an object without
# __defaults__, and then query for __setstate__
# which will throw an exception of KeyError
# deepcopy() is expecting AttributeError if __setstate__
# is not available.
raise AttributeError(attr)
settable = [
'clean',
'diskcheck',
'duplicate',
'help',
'implicit_cache',
'max_drift',
'md5_chunksize',
'no_exec',
'num_jobs',
'random',
'stack_size',
'warn',
'silent'
]
def set_option(self, name, value):
"""
Sets an option from an SConscript file.
"""
if not name in self.settable:
raise SCons.Errors.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 style right away so it can affect linking
# of SConscript files.
SCons.Node.FS.set_duplicate(value)
elif name == 'diskcheck':
try:
value = diskcheck_convert(value)
except ValueError as v:
raise SCons.Errors.UserError("Not a valid diskcheck value: %s"%v)
if 'diskcheck' not in self.__dict__:
# No --diskcheck= option was specified on the command line.
# Set this right away so it can affect the rest of the
# file/Node lookups while processing the SConscript files.
SCons.Node.FS.set_diskcheck(value)
elif name == 'stack_size':
try:
value = int(value)
except ValueError:
raise SCons.Errors.UserError("An integer is required: %s"%repr(value))
elif name == 'md5_chunksize':
try:
value = int(value)
except ValueError:
raise SCons.Errors.UserError("An integer is required: %s"%repr(value))
elif name == 'warn':
if SCons.Util.is_String(value):
value = [value]
value = self.__SConscript_settings__.get(name, []) + value
SCons.Warnings.process_warn_strings(value)
self.__SConscript_settings__[name] = value
class SConsOption(optparse.Option):
def convert_value(self, opt, value):
if value is not None:
if self.nargs in (1, '?'):
return self.check_value(opt, value)
else:
return tuple([self.check_value(opt, v) for v in value])
def process(self, opt, value, values, parser):
# First, convert the value(s) to the right type. Howl if any
# value(s) are bogus.
value = self.convert_value(opt, value)
# And then take whatever action is expected of us.
# This is a separate method to make life easier for
# subclasses to add new actions.
return self.take_action(
self.action, self.dest, opt, value, values, parser)
def _check_nargs_optional(self):
if self.nargs == '?' and self._short_opts:
fmt = "option %s: nargs='?' is incompatible with short options"
raise SCons.Errors.UserError(fmt % self._short_opts[0])
try:
_orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS
_orig_CHECK_METHODS = optparse.Option.CHECK_METHODS
except AttributeError:
# optparse.Option had no CONST_ACTIONS before Python 2.5.
_orig_CONST_ACTIONS = ("store_const",)
def _check_const(self):
if self.action not in self.CONST_ACTIONS and self.const is not None:
raise OptionError(
"'const' must not be supplied for action %r" % self.action,
self)
# optparse.Option collects its list of unbound check functions
# up front. This sucks because it means we can't just override
# the _check_const() function like a normal method, we have to
# actually replace it in the list. This seems to be the most
# straightforward way to do that.
_orig_CHECK_METHODS = [optparse.Option._check_action,
optparse.Option._check_type,
optparse.Option._check_choice,
optparse.Option._check_dest,
_check_const,
optparse.Option._check_nargs,
optparse.Option._check_callback]
CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional]
CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS
class SConsOptionGroup(optparse.OptionGroup):
"""
A subclass for SCons-specific option groups.
The only difference between this and the base class is that we print
the group's help text flush left, underneath their own title but
lined up with the normal "SCons Options".
"""
def format_help(self, formatter):
"""
Format an option group's help text, outdenting the title so it's
flush with the "SCons Options" title we print at the top.
"""
formatter.dedent()
result = formatter.format_heading(self.title)
formatter.indent()
result = result + optparse.OptionContainer.format_help(self, formatter)
return result
class SConsOptionParser(optparse.OptionParser):
preserve_unknown_options = False
def error(self, msg):
# overridden OptionValueError exception handler
self.print_usage(sys.stderr)
sys.stderr.write("SCons Error: %s\n" % msg)
sys.exit(2)
def _process_long_opt(self, rargs, values):
"""
SCons-specific processing of long options.
This is copied directly from the normal
optparse._process_long_opt() method, except that, if configured
to do so, we catch the exception thrown when an unknown option
is encountered and just stick it back on the "leftover" arguments
for later (re-)processing.
"""
arg = rargs.pop(0)
# Value explicitly attached to arg? Pretend it's the next
# argument.
if "=" in arg:
(opt, next_arg) = arg.split("=", 1)
rargs.insert(0, next_arg)
had_explicit_value = True
else:
opt = arg
had_explicit_value = False
try:
opt = self._match_long_opt(opt)
except optparse.BadOptionError:
if self.preserve_unknown_options:
# SCons-specific: if requested, add unknown options to
# the "leftover arguments" list for later processing.
self.largs.append(arg)
if had_explicit_value:
# The unknown option will be re-processed later,
# so undo the insertion of the explicit value.
rargs.pop(0)
return
raise
option = self._long_opt[opt]
if option.takes_value():
nargs = option.nargs
if nargs == '?':
if had_explicit_value:
value = rargs.pop(0)
else:
value = option.const
elif len(rargs) < nargs:
if nargs == 1:
if not option.choices:
self.error(_("%s option requires an argument") % opt)
else:
msg = _("%s option requires an argument " % opt)
msg += _("(choose from %s)"
% ', '.join(option.choices))
self.error(msg)
else:
self.error(_("%s option requires %d arguments")
% (opt, nargs))
elif nargs == 1:
value = rargs.pop(0)
else:
value = tuple(rargs[0:nargs])
del rargs[0:nargs]
elif had_explicit_value:
self.error(_("%s option does not take a value") % opt)
else:
value = None
option.process(opt, value, values, self)
def reparse_local_options(self):
"""
Re-parse the leftover command-line options stored
in self.largs, so that any value overridden on the
command line is immediately available if the user turns
around and does a GetOption() right away.
We mimic the processing of the single args
in the original OptionParser._process_args(), but here we
allow exact matches for long-opts only (no partial
argument names!).
Else, this would lead to problems in add_local_option()
below. When called from there, we try to reparse the
command-line arguments that
1. haven't been processed so far (self.largs), but
2. are possibly not added to the list of options yet.
So, when we only have a value for "--myargument" yet,
a command-line argument of "--myarg=test" would set it.
Responsible for this behaviour is the method
_match_long_opt(), which allows for partial matches of
the option name, as long as the common prefix appears to
be unique.
This would lead to further confusion, because we might want
to add another option "--myarg" later on (see issue #2929).
"""
rargs = []
largs_restore = []
# Loop over all remaining arguments
skip = False
for l in self.largs:
if skip:
# Accept all remaining arguments as they are
largs_restore.append(l)
else:
if len(l) > 2 and l[0:2] == "--":
# Check long option
lopt = (l,)
if "=" in l:
# Split into option and value
lopt = l.split("=", 1)
if lopt[0] in self._long_opt:
# Argument is already known
rargs.append('='.join(lopt))
else:
# Not known yet, so reject for now
largs_restore.append('='.join(lopt))
else:
if l == "--" or l == "-":
# Stop normal processing and don't
# process the rest of the command-line opts
largs_restore.append(l)
skip = True
else:
rargs.append(l)
# Parse the filtered list
self.parse_args(rargs, self.values)
# Restore the list of remaining arguments for the
# next call of AddOption/add_local_option...
self.largs = self.largs + largs_restore
def add_local_option(self, *args, **kw):
"""
Adds a local option to the parser.
This is initiated by a SetOption() call to add a user-defined
command-line option. We add the option to a separate option
group for the local options, creating the group if necessary.
"""
try:
group = self.local_option_group
except AttributeError:
group = SConsOptionGroup(self, 'Local Options')
group = self.add_option_group(group)
self.local_option_group = group
result = group.add_option(*args, **kw)
if result:
# The option was added successfully. We now have to add the
# default value to our object that holds the default values
# (so that an attempt to fetch the option's attribute will
# yield the default value when not overridden) and then
# we re-parse the leftover command-line options, so that
# any value overridden on the command line is immediately
# available if the user turns around and does a GetOption()
# right away.
setattr(self.values.__defaults__, result.dest, result.default)
self.reparse_local_options()
return result
class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
def format_usage(self, usage):
return "usage: %s\n" % usage
def format_heading(self, heading):
"""
This translates any heading of "options" or "Options" into
"SCons Options." Unfortunately, we have to do this here,
because those titles are hard-coded in the optparse calls.
"""
if heading == 'Options':
heading = "SCons Options"
return optparse.IndentedHelpFormatter.format_heading(self, heading)
def format_option(self, option):
"""
A copy of the normal optparse.IndentedHelpFormatter.format_option()
method. This has been snarfed so we can modify text wrapping to
out liking:
-- add our own regular expression that doesn't break on hyphens
(so things like --no-print-directory don't get broken);
-- wrap the list of options themselves when it's too long
(the wrapper.fill(opts) call below);
-- set the subsequent_indent when wrapping the help_text.
"""
# The help for each option consists of two parts:
# * the opt strings and metavars
# eg. ("-x", or "-fFILENAME, --file=FILENAME")
# * the user-supplied help string
# eg. ("turn on expert mode", "read data from FILENAME")
#
# If possible, we write both of these on the same line:
# -x turn on expert mode
#
# But if the opt string list is too long, we put the help
# string on a second line, indented to the same column it would
# start in if it fit on the first line.
# -fFILENAME, --file=FILENAME
# read data from FILENAME
result = []
opts = self.option_strings[option]
opt_width = self.help_position - self.current_indent - 2
if len(opts) > opt_width:
wrapper = textwrap.TextWrapper(width=self.width,
initial_indent = ' ',
subsequent_indent = ' ')
wrapper.wordsep_re = no_hyphen_re
opts = wrapper.fill(opts) + '\n'
indent_first = self.help_position
else: # start help on same line as opts
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
indent_first = 0
result.append(opts)
if option.help:
help_text = self.expand_default(option)
# SCons: indent every line of the help text but the first.
wrapper = textwrap.TextWrapper(width=self.help_width,
subsequent_indent = ' ')
wrapper.wordsep_re = no_hyphen_re
help_lines = wrapper.wrap(help_text)
result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
for line in help_lines[1:]:
result.append("%*s%s\n" % (self.help_position, "", line))
elif opts[-1] != "\n":
result.append("\n")
return "".join(result)
def Parser(version):
"""
Returns an options parser object initialized with the standard
SCons options.
"""
formatter = SConsIndentedHelpFormatter(max_help_position=30)
op = SConsOptionParser(option_class=SConsOption,
add_help_option=False,
formatter=formatter,
usage="usage: scons [OPTION] [TARGET] ...",)
op.preserve_unknown_options = True
op.version = version
# Add the options to the parser we just created.
#
# These are in the order we want them to show up in the -H help
# text, basically alphabetical. Each op.add_option() call below
# should have a consistent format:
#
# op.add_option("-L", "--long-option-name",
# nargs=1, type="string",
# dest="long_option_name", default='foo',
# action="callback", callback=opt_long_option,
# help="help text goes here",
# metavar="VAR")
#
# Even though the optparse module constructs reasonable default
# destination names from the long option names, we're going to be
# explicit about each one for easier readability and so this code
# will at least show up when grepping the source for option attribute
# names, or otherwise browsing the source code.
# options ignored for compatibility
def opt_ignore(option, opt, value, parser):
sys.stderr.write("Warning: ignoring %s option\n" % opt)
op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w",
"--environment-overrides",
"--no-keep-going",
"--no-print-directory",
"--print-directory",
"--stop",
"--touch",
action="callback", callback=opt_ignore,
help="Ignored for compatibility.")
op.add_option('-c', '--clean', '--remove',
dest="clean", default=False,
action="store_true",
help="Remove specified targets and dependencies.")
op.add_option('-C', '--directory',
nargs=1, type="string",
dest="directory", default=[],
action="append",
help="Change to DIR before doing anything.",
metavar="DIR")
op.add_option('--cache-debug',
nargs=1,
dest="cache_debug", default=None,
action="store",
help="Print CacheDir debug info to FILE.",
metavar="FILE")
op.add_option('--cache-disable', '--no-cache',
dest='cache_disable', default=False,
action="store_true",
help="Do not retrieve built targets from CacheDir.")
op.add_option('--cache-force', '--cache-populate',
dest='cache_force', default=False,
action="store_true",
help="Copy already-built targets into the CacheDir.")
op.add_option('--cache-readonly',
dest='cache_readonly', default=False,
action="store_true",
help="Do not update CacheDir with built targets.")
op.add_option('--cache-show',
dest='cache_show', default=False,
action="store_true",
help="Print build actions for files from CacheDir.")
def opt_invalid(group, value, options):
errmsg = "`%s' is not a valid %s option type, try:\n" % (value, group)
return errmsg + " %s" % ", ".join(options)
config_options = ["auto", "force" ,"cache"]
opt_config_help = "Controls Configure subsystem: %s." \
% ", ".join(config_options)
op.add_option('--config',
nargs=1, choices=config_options,
dest="config", default="auto",
help = opt_config_help,
metavar="MODE")
op.add_option('-D',
dest="climb_up", default=None,
action="store_const", const=2,
help="Search up directory tree for SConstruct, "
"build all Default() targets.")
deprecated_debug_options = {
"dtree" : '; please use --tree=derived instead',
"nomemoizer" : ' and has no effect',
"stree" : '; please use --tree=all,status instead',
"tree" : '; please use --tree=all instead',
}
debug_options = ["count", "duplicate", "explain", "findlibs",
"includes", "memoizer", "memory", "objects",
"pdb", "prepare", "presub", "stacktrace",
"time"]
def opt_debug(option, opt, value__, parser,
debug_options=debug_options,
deprecated_debug_options=deprecated_debug_options):
for value in value__.split(','):
if value in debug_options:
parser.values.debug.append(value)
elif value in list(deprecated_debug_options.keys()):
parser.values.debug.append(value)
try:
parser.values.delayed_warnings
except AttributeError:
parser.values.delayed_warnings = []
msg = deprecated_debug_options[value]
w = "The --debug=%s option is deprecated%s." % (value, msg)
t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w)
parser.values.delayed_warnings.append(t)
else:
raise OptionValueError(opt_invalid('debug', value, debug_options))
opt_debug_help = "Print various types of debugging information: %s." \
% ", ".join(debug_options)
op.add_option('--debug',
nargs=1, type="string",
dest="debug", default=[],
action="callback", callback=opt_debug,
help=opt_debug_help,
metavar="TYPE")
def opt_diskcheck(option, opt, value, parser):
try:
diskcheck_value = diskcheck_convert(value)
except ValueError as e:
raise OptionValueError("`%s' is not a valid diskcheck type" % e)
setattr(parser.values, option.dest, diskcheck_value)
op.add_option('--diskcheck',
nargs=1, type="string",
dest='diskcheck', default=None,
action="callback", callback=opt_diskcheck,
help="Enable specific on-disk checks.",
metavar="TYPE")
def opt_duplicate(option, opt, value, parser):
if not value in SCons.Node.FS.Valid_Duplicates:
raise OptionValueError(opt_invalid('duplication', value,
SCons.Node.FS.Valid_Duplicates))
setattr(parser.values, option.dest, value)
# Set the duplicate style right away so it can affect linking
# of SConscript files.
SCons.Node.FS.set_duplicate(value)
opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \
+ ", ".join(SCons.Node.FS.Valid_Duplicates)
op.add_option('--duplicate',
nargs=1, type="string",
dest="duplicate", default='hard-soft-copy',
action="callback", callback=opt_duplicate,
help=opt_duplicate_help)
op.add_option('-f', '--file', '--makefile', '--sconstruct',
nargs=1, type="string",
dest="file", default=[],
action="append",
help="Read FILE as the top-level SConstruct file.")
op.add_option('-h', '--help',
dest="help", default=False,
action="store_true",
help="Print defined help message, or this one.")
op.add_option("-H", "--help-options",
action="help",
help="Print this message and exit.")
op.add_option('-i', '--ignore-errors',
dest='ignore_errors', default=False,
action="store_true",
help="Ignore errors from build actions.")
op.add_option('-I', '--include-dir',
nargs=1,
dest='include_dir', default=[],
action="append",
help="Search DIR for imported Python modules.",
metavar="DIR")
op.add_option('--implicit-cache',
dest='implicit_cache', default=False,
action="store_true",
help="Cache implicit dependencies")
def opt_implicit_deps(option, opt, value, parser):
setattr(parser.values, 'implicit_cache', True)
setattr(parser.values, option.dest, True)
op.add_option('--implicit-deps-changed',
dest="implicit_deps_changed", default=False,
action="callback", callback=opt_implicit_deps,
help="Ignore cached implicit dependencies.")
op.add_option('--implicit-deps-unchanged',
dest="implicit_deps_unchanged", default=False,
action="callback", callback=opt_implicit_deps,
help="Ignore changes in implicit dependencies.")
op.add_option('--interact', '--interactive',
dest='interactive', default=False,
action="store_true",
help="Run in interactive mode.")
op.add_option('-j', '--jobs',
nargs=1, type="int",
dest="num_jobs", default=1,
action="store",
help="Allow N jobs at once.",
metavar="N")
op.add_option('-k', '--keep-going',
dest='keep_going', default=False,
action="store_true",
help="Keep going when a target can't be made.")
op.add_option('--max-drift',
nargs=1, type="int",
dest='max_drift', default=SCons.Node.FS.default_max_drift,
action="store",
help="Set maximum system clock drift to N seconds.",
metavar="N")
op.add_option('--md5-chunksize',
nargs=1, type="int",
dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize,
action="store",
help="Set chunk-size for MD5 signature computation to N kilobytes.",
metavar="N")
op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon',
dest='no_exec', default=False,
action="store_true",
help="Don't build; just print commands.")
op.add_option('--no-site-dir',
dest='no_site_dir', default=False,
action="store_true",
help="Don't search or use the usual site_scons dir.")
op.add_option('--profile',
nargs=1,
dest="profile_file", default=None,
action="store",
help="Profile SCons and put results in FILE.",
metavar="FILE")
op.add_option('-q', '--question',
dest="question", default=False,
action="store_true",
help="Don't build; exit status says if up to date.")
op.add_option('-Q',
dest='no_progress', default=False,
action="store_true",
help="Suppress \"Reading/Building\" progress messages.")
op.add_option('--random',
dest="random", default=False,
action="store_true",
help="Build dependencies in random order.")
op.add_option('-s', '--silent', '--quiet',
dest="silent", default=False,
action="store_true",
help="Don't print commands.")
op.add_option('--site-dir',
nargs=1,
dest='site_dir', default=None,
action="store",
help="Use DIR instead of the usual site_scons dir.",
metavar="DIR")
op.add_option('--stack-size',
nargs=1, type="int",
dest='stack_size',
action="store",
help="Set the stack size of the threads used to run jobs to N kilobytes.",
metavar="N")
op.add_option('--taskmastertrace',
nargs=1,
dest="taskmastertrace_file", default=None,
action="store",
help="Trace Node evaluation to FILE.",
metavar="FILE")
tree_options = ["all", "derived", "prune", "status"]
def opt_tree(option, opt, value, parser, tree_options=tree_options):
from . import Main
tp = Main.TreePrinter()
for o in value.split(','):
if o == 'all':
tp.derived = False
elif o == 'derived':
tp.derived = True
elif o == 'prune':
tp.prune = True
elif o == 'status':
tp.status = True
else:
raise OptionValueError(opt_invalid('--tree', o, tree_options))
parser.values.tree_printers.append(tp)
opt_tree_help = "Print a dependency tree in various formats: %s." \
% ", ".join(tree_options)
op.add_option('--tree',
nargs=1, type="string",
dest="tree_printers", default=[],
action="callback", callback=opt_tree,
help=opt_tree_help,
metavar="OPTIONS")
op.add_option('-u', '--up', '--search-up',
dest="climb_up", default=0,
action="store_const", const=1,
help="Search up directory tree for SConstruct, "
"build targets at or below current directory.")
op.add_option('-U',
dest="climb_up", default=0,
action="store_const", const=3,
help="Search up directory tree for SConstruct, "
"build Default() targets from local SConscript.")
def opt_version(option, opt, value, parser):
sys.stdout.write(parser.version + '\n')
sys.exit(0)
op.add_option("-v", "--version",
action="callback", callback=opt_version,
help="Print the SCons version number and exit.")
def opt_warn(option, opt, value, parser, tree_options=tree_options):
if SCons.Util.is_String(value):
value = value.split(',')
parser.values.warn.extend(value)
op.add_option('--warn', '--warning',
nargs=1, type="string",
dest="warn", default=[],
action="callback", callback=opt_warn,
help="Enable or disable warnings.",
metavar="WARNING-SPEC")
op.add_option('-Y', '--repository', '--srcdir',
nargs=1,
dest="repository", default=[],
action="append",
help="Search REPOSITORY for source and target files.")
# Options from Make and Cons classic that we do not yet support,
# but which we may support someday and whose (potential) meanings
# we don't want to change. These all get a "the -X option is not
# yet implemented" message and don't show up in the help output.
def opt_not_yet(option, opt, value, parser):
msg = "Warning: the %s option is not yet implemented\n" % opt
sys.stderr.write(msg)
op.add_option('-l', '--load-average', '--max-load',
nargs=1, type="float",
dest="load_average", default=0,
action="callback", callback=opt_not_yet,
# action="store",
# help="Don't start multiple jobs unless load is below "
# "LOAD-AVERAGE."
help=SUPPRESS_HELP)
op.add_option('--list-actions',
dest="list_actions",
action="callback", callback=opt_not_yet,
# help="Don't build; list files and build actions."
help=SUPPRESS_HELP)
op.add_option('--list-derived',
dest="list_derived",
action="callback", callback=opt_not_yet,
# help="Don't build; list files that would be built."
help=SUPPRESS_HELP)
op.add_option('--list-where',
dest="list_where",
action="callback", callback=opt_not_yet,
# help="Don't build; list files and where defined."
help=SUPPRESS_HELP)
op.add_option('-o', '--old-file', '--assume-old',
nargs=1, type="string",
dest="old_file", default=[],
action="callback", callback=opt_not_yet,
# action="append",
# help = "Consider FILE to be old; don't rebuild it."
help=SUPPRESS_HELP)
op.add_option('--override',
nargs=1, type="string",
action="callback", callback=opt_not_yet,
dest="override",
# help="Override variables as specified in FILE."
help=SUPPRESS_HELP)
op.add_option('-p',
action="callback", callback=opt_not_yet,
dest="p",
# help="Print internal environments/objects."
help=SUPPRESS_HELP)
op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables',
action="callback", callback=opt_not_yet,
dest="no_builtin_rules",
# help="Clear default environments and variables."
help=SUPPRESS_HELP)
op.add_option('--write-filenames',
nargs=1, type="string",
dest="write_filenames",
action="callback", callback=opt_not_yet,
# help="Write all filenames examined into FILE."
help=SUPPRESS_HELP)
op.add_option('-W', '--new-file', '--assume-new', '--what-if',
nargs=1, type="string",
dest="new_file",
action="callback", callback=opt_not_yet,
# help="Consider FILE to be changed."
help=SUPPRESS_HELP)
op.add_option('--warn-undefined-variables',
dest="warn_undefined_variables",
action="callback", callback=opt_not_yet,
# help="Warn when an undefined variable is referenced."
help=SUPPRESS_HELP)
return op
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4:

View File

@@ -0,0 +1,633 @@
"""SCons.Script.SConscript
This module defines the Python API provided to SConscript and SConstruct
files.
"""
from __future__ import print_function
#
# Copyright (c) 2001 - 2017 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
__revision__ = "src/engine/SCons/Script/SConscript.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog"
import SCons
import SCons.Action
import SCons.Builder
import SCons.Defaults
import SCons.Environment
import SCons.Errors
import SCons.Node
import SCons.Node.Alias
import SCons.Node.FS
import SCons.Platform
import SCons.SConf
import SCons.Script.Main
import SCons.Tool
import SCons.Util
import collections
import os
import os.path
import re
import sys
import traceback
class SConscriptReturn(Exception):
pass
launch_dir = os.path.abspath(os.curdir)
GlobalDict = None
# global exports set by Export():
global_exports = {}
# chdir flag
sconscript_chdir = 1
def get_calling_namespaces():
"""Return the locals and globals for the function that called
into this module in the current call stack."""
try: 1//0
except ZeroDivisionError:
# Don't start iterating with the current stack-frame to
# prevent creating reference cycles (f_back is safe).
frame = sys.exc_info()[2].tb_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
def compute_exports(exports):
"""Compute a dictionary of exports given one of the parameters
to the Export() function or the exports argument to SConscript()."""
loc, glob = get_calling_namespaces()
retval = {}
try:
for export in exports:
if SCons.Util.is_Dict(export):
retval.update(export)
else:
try:
retval[export] = loc[export]
except KeyError:
retval[export] = glob[export]
except KeyError as x:
raise SCons.Errors.UserError("Export of non-existent variable '%s'"%x)
return retval
class Frame(object):
"""A frame on the SConstruct/SConscript call stack"""
def __init__(self, fs, exports, sconscript):
self.globals = BuildDefaultGlobals()
self.retval = None
self.prev_dir = fs.getcwd()
self.exports = compute_exports(exports) # exports from the calling SConscript
# make sure the sconscript attr is a Node.
if isinstance(sconscript, SCons.Node.Node):
self.sconscript = sconscript
elif sconscript == '-':
self.sconscript = None
else:
self.sconscript = fs.File(str(sconscript))
# the SConstruct/SConscript call stack:
call_stack = []
# For documentation on the methods in this file, see the scons man-page
def Return(*vars, **kw):
retval = []
try:
fvars = SCons.Util.flatten(vars)
for var in fvars:
for v in var.split():
retval.append(call_stack[-1].globals[v])
except KeyError as x:
raise SCons.Errors.UserError("Return of non-existent variable '%s'"%x)
if len(retval) == 1:
call_stack[-1].retval = retval[0]
else:
call_stack[-1].retval = tuple(retval)
stop = kw.get('stop', True)
if stop:
raise SConscriptReturn
stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :)
def _SConscript(fs, *files, **kw):
top = fs.Top
sd = fs.SConstruct_dir.rdir()
exports = kw.get('exports', [])
# evaluate each SConscript file
results = []
for fn in files:
call_stack.append(Frame(fs, exports, fn))
old_sys_path = sys.path
try:
SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1
if fn == "-":
exec(sys.stdin.read(), call_stack[-1].globals)
else:
if isinstance(fn, SCons.Node.Node):
f = fn
else:
f = fs.File(str(fn))
_file_ = None
# Change directory to the top of the source
# tree to make sure the os's cwd and the cwd of
# fs match so we can open the SConscript.
fs.chdir(top, change_os_dir=1)
if f.rexists():
actual = f.rfile()
_file_ = open(actual.get_abspath(), "rb")
elif f.srcnode().rexists():
actual = f.srcnode().rfile()
_file_ = open(actual.get_abspath(), "rb")
elif f.has_src_builder():
# The SConscript file apparently exists in a source
# code management system. Build it, but then clear
# the builder so that it doesn't get built *again*
# during the actual build phase.
f.build()
f.built()
f.builder_set(None)
if f.exists():
_file_ = open(f.get_abspath(), "rb")
if _file_:
# Chdir to the SConscript directory. Use a path
# name relative to the SConstruct file so that if
# we're using the -f option, we're essentially
# creating a parallel SConscript directory structure
# in our local directory tree.
#
# XXX This is broken for multiple-repository cases
# where the SConstruct and SConscript files might be
# in different Repositories. For now, cross that
# bridge when someone comes to it.
try:
src_dir = kw['src_dir']
except KeyError:
ldir = fs.Dir(f.dir.get_path(sd))
else:
ldir = fs.Dir(src_dir)
if not ldir.is_under(f.dir):
# They specified a source directory, but
# it's above the SConscript directory.
# Do the sensible thing and just use the
# SConcript directory.
ldir = fs.Dir(f.dir.get_path(sd))
try:
fs.chdir(ldir, change_os_dir=sconscript_chdir)
except OSError:
# There was no local directory, so we should be
# able to chdir to the Repository directory.
# Note that we do this directly, not through
# fs.chdir(), because we still need to
# interpret the stuff within the SConscript file
# relative to where we are logically.
fs.chdir(ldir, change_os_dir=0)
os.chdir(actual.dir.get_abspath())
# Append the SConscript directory to the beginning
# of sys.path so Python modules in the SConscript
# directory can be easily imported.
sys.path = [ f.dir.get_abspath() ] + sys.path
# This is the magic line that actually reads up
# and executes the stuff in the SConscript file.
# The locals for this frame contain the special
# bottom-of-the-stack marker so that any
# exceptions that occur when processing this
# SConscript can base the printed frames at this
# level and not show SCons internals as well.
call_stack[-1].globals.update({stack_bottom:1})
old_file = call_stack[-1].globals.get('__file__')
try:
del call_stack[-1].globals['__file__']
except KeyError:
pass
try:
try:
# _file_ = SCons.Util.to_str(_file_)
exec(compile(_file_.read(), _file_.name, 'exec'),
call_stack[-1].globals)
except SConscriptReturn:
pass
finally:
if old_file is not None:
call_stack[-1].globals.update({__file__:old_file})
else:
SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning,
"Ignoring missing SConscript '%s'" % f.get_internal_path())
finally:
SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1
sys.path = old_sys_path
frame = call_stack.pop()
try:
fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir)
except OSError:
# There was no local directory, so chdir to the
# Repository directory. Like above, we do this
# directly.
fs.chdir(frame.prev_dir, change_os_dir=0)
rdir = frame.prev_dir.rdir()
rdir._create() # Make sure there's a directory there.
try:
os.chdir(rdir.get_abspath())
except OSError as e:
# We still couldn't chdir there, so raise the error,
# but only if actions are being executed.
#
# If the -n option was used, the directory would *not*
# have been created and we should just carry on and
# let things muddle through. This isn't guaranteed
# to work if the SConscript files are reading things
# from disk (for example), but it should work well
# enough for most configurations.
if SCons.Action.execute_actions:
raise e
results.append(frame.retval)
# if we only have one script, don't return a tuple
if len(results) == 1:
return results[0]
else:
return tuple(results)
def SConscript_exception(file=sys.stderr):
"""Print an exception stack trace just for the SConscript file(s).
This will show users who have Python errors where the problem is,
without cluttering the output with all of the internal calls leading
up to where we exec the SConscript."""
exc_type, exc_value, exc_tb = sys.exc_info()
tb = exc_tb
while tb and stack_bottom not in tb.tb_frame.f_locals:
tb = tb.tb_next
if not tb:
# We did not find our exec statement, so this was actually a bug
# in SCons itself. Show the whole stack.
tb = exc_tb
stack = traceback.extract_tb(tb)
try:
type = exc_type.__name__
except AttributeError:
type = str(exc_type)
if type[:11] == "exceptions.":
type = type[11:]
file.write('%s: %s:\n' % (type, exc_value))
for fname, line, func, text in stack:
file.write(' File "%s", line %d:\n' % (fname, line))
file.write(' %s\n' % text)
def annotate(node):
"""Annotate a node with the stack frame describing the
SConscript file and line number that created it."""
tb = sys.exc_info()[2]
while tb and stack_bottom not in tb.tb_frame.f_locals:
tb = tb.tb_next
if not tb:
# We did not find any exec of an SConscript file: what?!
raise SCons.Errors.InternalError("could not find SConscript stack frame")
node.creator = traceback.extract_stack(tb)[0]
# The following line would cause each Node to be annotated using the
# above function. Unfortunately, this is a *huge* performance hit, so
# leave this disabled until we find a more efficient mechanism.
#SCons.Node.Annotate = annotate
class SConsEnvironment(SCons.Environment.Base):
"""An Environment subclass that contains all of the methods that
are particular to the wrapper SCons interface and which aren't
(or shouldn't be) part of the build engine itself.
Note that not all of the methods of this class have corresponding
global functions, there are some private methods.
"""
#
# Private methods of an SConsEnvironment.
#
def _exceeds_version(self, major, minor, v_major, v_minor):
"""Return 1 if 'major' and 'minor' are greater than the version
in 'v_major' and 'v_minor', and 0 otherwise."""
return (major > v_major or (major == v_major and minor > v_minor))
def _get_major_minor_revision(self, version_string):
"""Split a version string into major, minor and (optionally)
revision parts.
This is complicated by the fact that a version string can be
something like 3.2b1."""
version = version_string.split(' ')[0].split('.')
v_major = int(version[0])
v_minor = int(re.match('\d+', version[1]).group())
if len(version) >= 3:
v_revision = int(re.match('\d+', version[2]).group())
else:
v_revision = 0
return v_major, v_minor, v_revision
def _get_SConscript_filenames(self, ls, kw):
"""
Convert the parameters passed to SConscript() calls into a list
of files and export variables. If the parameters are invalid,
throws SCons.Errors.UserError. Returns a tuple (l, e) where l
is a list of SConscript filenames and e is a list of exports.
"""
exports = []
if len(ls) == 0:
try:
dirs = kw["dirs"]
except KeyError:
raise SCons.Errors.UserError("Invalid SConscript usage - no parameters")
if not SCons.Util.is_List(dirs):
dirs = [ dirs ]
dirs = list(map(str, dirs))
name = kw.get('name', 'SConscript')
files = [os.path.join(n, name) for n in dirs]
elif len(ls) == 1:
files = ls[0]
elif len(ls) == 2:
files = ls[0]
exports = self.Split(ls[1])
else:
raise SCons.Errors.UserError("Invalid SConscript() usage - too many arguments")
if not SCons.Util.is_List(files):
files = [ files ]
if kw.get('exports'):
exports.extend(self.Split(kw['exports']))
variant_dir = kw.get('variant_dir') or kw.get('build_dir')
if variant_dir:
if len(files) != 1:
raise SCons.Errors.UserError("Invalid SConscript() usage - can only specify one SConscript with a variant_dir")
duplicate = kw.get('duplicate', 1)
src_dir = kw.get('src_dir')
if not src_dir:
src_dir, fname = os.path.split(str(files[0]))
files = [os.path.join(str(variant_dir), fname)]
else:
if not isinstance(src_dir, SCons.Node.Node):
src_dir = self.fs.Dir(src_dir)
fn = files[0]
if not isinstance(fn, SCons.Node.Node):
fn = self.fs.File(fn)
if fn.is_under(src_dir):
# Get path relative to the source directory.
fname = fn.get_path(src_dir)
files = [os.path.join(str(variant_dir), fname)]
else:
files = [fn.get_abspath()]
kw['src_dir'] = variant_dir
self.fs.VariantDir(variant_dir, src_dir, duplicate)
return (files, exports)
#
# Public methods of an SConsEnvironment. These get
# entry points in the global namespace so they can be called
# as global functions.
#
def Configure(self, *args, **kw):
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 SCons.Environment.Base.Configure(self, *args, **kw)
def Default(self, *targets):
SCons.Script._Set_Default_Targets(self, targets)
def EnsureSConsVersion(self, major, minor, revision=0):
"""Exit abnormally if the SCons version is not late enough."""
# split string to avoid replacement during build process
if SCons.__version__ == '__' + 'VERSION__':
SCons.Warnings.warn(SCons.Warnings.DevelopmentVersionWarning,
"EnsureSConsVersion is ignored for development version")
return
scons_ver = self._get_major_minor_revision(SCons.__version__)
if scons_ver < (major, minor, revision):
if revision:
scons_ver_string = '%d.%d.%d' % (major, minor, revision)
else:
scons_ver_string = '%d.%d' % (major, minor)
print("SCons %s or greater required, but you have SCons %s" % \
(scons_ver_string, SCons.__version__))
sys.exit(2)
def EnsurePythonVersion(self, major, minor):
"""Exit abnormally if the Python version is not late enough."""
if sys.version_info < (major, minor):
v = sys.version.split()[0]
print("Python %d.%d or greater required, but you have Python %s" %(major,minor,v))
sys.exit(2)
def Exit(self, value=0):
sys.exit(value)
def Export(self, *vars, **kw):
for var in vars:
global_exports.update(compute_exports(self.Split(var)))
global_exports.update(kw)
def GetLaunchDir(self):
global launch_dir
return launch_dir
def GetOption(self, name):
name = self.subst(name)
return SCons.Script.Main.GetOption(name)
def Help(self, text, append=False):
text = self.subst(text, raw=1)
SCons.Script.HelpFunction(text, append=append)
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 == '*':
globals.update(global_exports)
globals.update(exports)
else:
if v in exports:
globals[v] = exports[v]
else:
globals[v] = global_exports[v]
except KeyError as x:
raise SCons.Errors.UserError("Import of non-existent variable '%s'"%x)
def SConscript(self, *ls, **kw):
if 'build_dir' in kw:
msg = """The build_dir keyword has been deprecated; use the variant_dir keyword instead."""
SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg)
def subst_element(x, subst=self.subst):
if SCons.Util.is_List(x):
x = list(map(subst, x))
else:
x = subst(x)
return x
ls = list(map(subst_element, ls))
subst_kw = {}
for key, val in kw.items():
if SCons.Util.is_String(val):
val = self.subst(val)
elif SCons.Util.is_List(val):
result = []
for v in val:
if SCons.Util.is_String(v):
v = self.subst(v)
result.append(v)
val = result
subst_kw[key] = val
files, exports = self._get_SConscript_filenames(ls, subst_kw)
subst_kw['exports'] = exports
return _SConscript(self.fs, *files, **subst_kw)
def SConscriptChdir(self, flag):
global sconscript_chdir
sconscript_chdir = flag
def SetOption(self, name, value):
name = self.subst(name)
SCons.Script.Main.SetOption(name, value)
#
#
#
SCons.Environment.Environment = SConsEnvironment
def Configure(*args, **kw):
if not SCons.Script.sconscript_reading:
raise SCons.Errors.UserError("Calling Configure from Builders is not supported.")
kw['_depth'] = 1
return 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():
global _DefaultEnvironmentProxy
if not _DefaultEnvironmentProxy:
default_env = SCons.Defaults.DefaultEnvironment()
_DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env)
return _DefaultEnvironmentProxy
class DefaultEnvironmentCall(object):
"""A class that implements "global function" calls of
Environment methods by fetching the specified method from the
DefaultEnvironment's class. Note that this uses an intermediate
proxy class instead of calling the DefaultEnvironment method
directly so that the proxy can override the subst() method and
thereby prevent expansion of construction variables (since from
the user's point of view this was called as a global function,
with no associated construction environment)."""
def __init__(self, method_name, subst=0):
self.method_name = method_name
if subst:
self.factory = SCons.Defaults.DefaultEnvironment
else:
self.factory = get_DefaultEnvironmentProxy
def __call__(self, *args, **kw):
env = self.factory()
method = getattr(env, self.method_name)
return method(*args, **kw)
def BuildDefaultGlobals():
"""
Create a dictionary containing all the default globals for
SConstruct and SConscript files.
"""
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 not isinstance(d[m], mtype)
for m in filter(not_a_module, dir(SCons.Script)):
GlobalDict[m] = d[m]
return GlobalDict.copy()
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4:

View File

@@ -0,0 +1,429 @@
"""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 (c) 2001 - 2017 The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
__revision__ = "src/engine/SCons/Script/__init__.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog"
import time
start_time = time.time()
import collections
import os
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
import sys
# Special chicken-and-egg handling of the "--debug=memoizer" flag:
#
# SCons.Memoize contains a metaclass implementation that affects how
# the other classes are instantiated. The Memoizer may add shim methods
# to classes that have methods that cache computed values in order to
# count and report the hits and misses.
#
# If we wait to enable the Memoization until after we've parsed the
# command line options normally, it will be too late, because the Memoizer
# will have already analyzed the classes that it's Memoizing and decided
# to not add the shims. So we use a special-case, up-front check for
# the "--debug=memoizer" flag and enable Memoizer before we import any
# of the other modules that use it.
_args = sys.argv + os.environ.get('SCONSFLAGS', '').split()
if "--debug=memoizer" in _args:
import SCons.Memoize
import SCons.Warnings
try:
SCons.Memoize.EnableMemoization()
except SCons.Warnings.Warning:
# Some warning was thrown. Arrange for it to be displayed
# or not after warnings are configured.
from . import Main
exc_type, exc_value, tb = sys.exc_info()
Main.delayed_warnings.append((exc_type, exc_value))
del _args
import SCons.Action
import SCons.Builder
import SCons.Environment
import SCons.Node.FS
import SCons.Options
import SCons.Platform
import SCons.Scanner
import SCons.SConf
import SCons.Subst
import SCons.Tool
import SCons.Util
import SCons.Variables
import SCons.Defaults
from . import Main
main = Main.main
# The following are global class definitions and variables that used to
# live directly in this module back before 0.96.90, when it contained
# a lot of code. Some SConscript files in widely-distributed packages
# (Blender is the specific example) actually reached into SCons.Script
# directly to use some of these. Rather than break those SConscript
# files, we're going to propagate these names into the SCons.Script
# namespace here.
#
# Some of these are commented out because it's *really* unlikely anyone
# used them, but we're going to leave the comment here to try to make
# it obvious what to do if the situation arises.
BuildTask = Main.BuildTask
CleanTask = Main.CleanTask
QuestionTask = Main.QuestionTask
#PrintHelp = Main.PrintHelp
#SConscriptSettableOptions = Main.SConscriptSettableOptions
AddOption = Main.AddOption
PrintHelp = Main.PrintHelp
GetOption = Main.GetOption
SetOption = Main.SetOption
Progress = Main.Progress
GetBuildFailures = Main.GetBuildFailures
#keep_going_on_error = Main.keep_going_on_error
#print_dtree = Main.print_dtree
#print_explanations = Main.print_explanations
#print_includes = Main.print_includes
#print_objects = Main.print_objects
#print_time = Main.print_time
#print_tree = Main.print_tree
#memory_stats = Main.memory_stats
#ignore_errors = Main.ignore_errors
#sconscript_time = Main.sconscript_time
#command_time = Main.command_time
#exit_status = Main.exit_status
#profiling = Main.profiling
#repositories = Main.repositories
#
from . import SConscript
_SConscript = SConscript
call_stack = _SConscript.call_stack
#
Action = SCons.Action.Action
AddMethod = SCons.Util.AddMethod
AllowSubstExceptions = SCons.Subst.SetAllowableExceptions
Builder = SCons.Builder.Builder
Configure = _SConscript.Configure
Environment = SCons.Environment.Environment
#OptParser = SCons.SConsOptions.OptParser
FindPathDirs = SCons.Scanner.FindPathDirs
Platform = SCons.Platform.Platform
Return = _SConscript.Return
Scanner = SCons.Scanner.Base
Tool = SCons.Tool.Tool
WhereIs = SCons.Util.WhereIs
#
BoolVariable = SCons.Variables.BoolVariable
EnumVariable = SCons.Variables.EnumVariable
ListVariable = SCons.Variables.ListVariable
PackageVariable = SCons.Variables.PackageVariable
PathVariable = SCons.Variables.PathVariable
# Deprecated names that will go away some day.
BoolOption = SCons.Options.BoolOption
EnumOption = SCons.Options.EnumOption
ListOption = SCons.Options.ListOption
PackageOption = SCons.Options.PackageOption
PathOption = SCons.Options.PathOption
# 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
# Pre-made, public scanners.
CScanner = SCons.Tool.CScanner
DScanner = SCons.Tool.DScanner
DirScanner = SCons.Defaults.DirScanner
ProgramScanner = SCons.Tool.ProgramScanner
SourceFileScanner = SCons.Tool.SourceFileScanner
# Functions we might still convert to Environment methods.
CScan = SCons.Defaults.CScan
DefaultEnvironment = SCons.Defaults.DefaultEnvironment
# Other variables we provide.
class TargetList(collections.UserList):
def _do_nothing(self, *args, **kw):
pass
def _add_Default(self, list):
self.extend(list)
def _clear(self):
del self[:]
ARGUMENTS = {}
ARGLIST = []
BUILD_TARGETS = TargetList()
COMMAND_LINE_TARGETS = []
DEFAULT_TARGETS = []
# BUILD_TARGETS can be modified in the SConscript files. If so, we
# want to treat the modified BUILD_TARGETS list as if they specified
# targets on the command line. To do that, though, we need to know if
# BUILD_TARGETS was modified through "official" APIs or by hand. We do
# this by updating two lists in parallel, the documented BUILD_TARGETS
# list, above, and this internal _build_plus_default targets list which
# should only have "official" API changes. Then Script/Main.py can
# compare these two afterwards to figure out if the user added their
# own targets to BUILD_TARGETS.
_build_plus_default = TargetList()
def _Add_Arguments(alist):
for arg in alist:
a, b = arg.split('=', 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
_build_plus_default.extend(tlist)
_build_plus_default._add_Default = _build_plus_default._do_nothing
_build_plus_default._clear = _build_plus_default._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()
_build_plus_default._clear()
elif isinstance(t, SCons.Node.Node):
DEFAULT_TARGETS.append(t)
BUILD_TARGETS._add_Default([t])
_build_plus_default._add_Default([t])
else:
nodes = env.arg2nodes(t, env.fs.Entry)
DEFAULT_TARGETS.extend(nodes)
BUILD_TARGETS._add_Default(nodes)
_build_plus_default._add_Default(nodes)
#
help_text = None
def HelpFunction(text, append=False):
global help_text
if help_text is None:
if append:
s = StringIO()
PrintHelp(s)
help_text = s.getvalue()
s.close()
else:
help_text = ""
help_text= help_text + text
#
# Will be non-zero if we are reading an SConscript file.
sconscript_reading = 0
#
def Variables(files=[], args=ARGUMENTS):
return SCons.Variables.Variables(files, args)
def Options(files=[], 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',
'Help',
'Import',
#'SConscript', is handled separately, below.
'SConscriptChdir',
# Methods from the Environment.Base class.
'AddPostAction',
'AddPreAction',
'Alias',
'AlwaysBuild',
'BuildDir',
'CacheDir',
'Clean',
#The Command() method is handled separately, below.
'Decider',
'Depends',
'Dir',
'NoClean',
'NoCache',
'Entry',
'Execute',
'File',
'FindFile',
'FindInstalledFiles',
'FindSourceFiles',
'Flatten',
'GetBuildPath',
'Glob',
'Ignore',
'Install',
'InstallAs',
'InstallVersionedLib',
'Literal',
'Local',
'ParseDepends',
'Precious',
'PyPackageDir',
'Repository',
'Requires',
'SConsignFile',
'SideEffect',
'SourceCode',
'SourceSignatures',
'Split',
'Tag',
'TargetSignatures',
'Value',
'VariantDir',
]
GlobalDefaultBuilders = [
# Supported builders.
'CFile',
'CXXFile',
'DVI',
'Jar',
'Java',
'JavaH',
'Library',
'LoadableModule',
'M4',
'MSVSProject',
'Object',
'PCH',
'PDF',
'PostScript',
'Program',
'RES',
'RMIC',
'SharedLibrary',
'SharedObject',
'StaticLibrary',
'StaticObject',
'Tar',
'TypeLibrary',
'Zip',
'Package',
]
for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders:
exec ("%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name)))
del name
# There are a handful of variables that used to live in the
# Script/SConscript.py module that some SConscript files out there were
# accessing directly as SCons.Script.SConscript.*. The problem is that
# "SConscript" in this namespace is no longer a module, it's a global
# function call--or more precisely, an object that implements a global
# function call through the default Environment. Nevertheless, we can
# maintain backwards compatibility for SConscripts that were reaching in
# this way by hanging some attributes off the "SConscript" object here.
SConscript = _SConscript.DefaultEnvironmentCall('SConscript')
# Make SConscript look enough like the module it used to be so
# that pychecker doesn't barf.
SConscript.__name__ = 'SConscript'
SConscript.Arguments = ARGUMENTS
SConscript.ArgList = ARGLIST
SConscript.BuildTargets = BUILD_TARGETS
SConscript.CommandLineTargets = COMMAND_LINE_TARGETS
SConscript.DefaultTargets = DEFAULT_TARGETS
# The global Command() function must be handled differently than the
# global functions for other construction environment methods because
# we want people to be able to use Actions that must expand $TARGET
# and $SOURCE later, when (and if) the Action is invoked to build
# the target(s). We do this with the subst=1 argument, which creates
# a DefaultEnvironmentCall instance that wraps up a normal default
# construction environment that performs variable substitution, not a
# proxy that doesn't.
#
# There's a flaw here, though, because any other $-variables on a command
# line will *also* be expanded, each to a null string, but that should
# only be a problem in the unusual case where someone was passing a '$'
# on a command line and *expected* the $ to get through to the shell
# because they were calling Command() and not env.Command()... This is
# unlikely enough that we're going to leave this as is and cross that
# bridge if someone actually comes to it.
Command = _SConscript.DefaultEnvironmentCall('Command', subst=1)
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=4 shiftwidth=4: