summaryrefslogtreecommitdiff
path: root/SCons/Script
diff options
context:
space:
mode:
Diffstat (limited to 'SCons/Script')
-rw-r--r--SCons/Script/Interactive.py24
-rw-r--r--SCons/Script/Main.py193
-rw-r--r--SCons/Script/Main.xml127
-rw-r--r--SCons/Script/SConsOptions.py22
-rw-r--r--SCons/Script/SConscript.py22
-rw-r--r--SCons/Script/__init__.py16
6 files changed, 224 insertions, 180 deletions
diff --git a/SCons/Script/Interactive.py b/SCons/Script/Interactive.py
index d6947402e..8fd3aa014 100644
--- a/SCons/Script/Interactive.py
+++ b/SCons/Script/Interactive.py
@@ -112,7 +112,7 @@ version Prints SCons version information.
'sh' : 'shell',
}
- def __init__(self, **kw):
+ def __init__(self, **kw) -> None:
cmd.Cmd.__init__(self)
for key, val in kw.items():
setattr(self, key, val)
@@ -122,7 +122,7 @@ version Prints SCons version information.
else:
self.shell_variable = 'SHELL'
- def default(self, argv):
+ def default(self, argv) -> None:
print("*** Unknown command: %s" % argv[0])
def onecmd(self, line):
@@ -148,7 +148,7 @@ version Prints SCons version information.
return self.default(argv)
return func(argv)
- def do_build(self, argv):
+ def do_build(self, argv) -> None:
"""\
build [TARGETS] Build the specified TARGETS and their
dependencies. 'b' is a synonym.
@@ -213,11 +213,11 @@ version Prints SCons version information.
seen_nodes = {}
def get_unseen_children(node, parent, seen_nodes=seen_nodes):
- def is_unseen(node, seen_nodes=seen_nodes):
+ def is_unseen(node, seen_nodes=seen_nodes) -> bool:
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):
+ def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes) -> None:
seen_nodes[node] = 1
# If this file is in a VariantDir and has a
@@ -272,11 +272,11 @@ version Prints SCons version information.
"""
return self.do_build(['build', '--clean'] + argv[1:])
- def do_EOF(self, argv):
+ def do_EOF(self, argv) -> None:
print()
self.do_exit(argv)
- def _do_one_help(self, arg):
+ def _do_one_help(self, arg) -> None:
try:
# If help_<arg>() exists, then call it.
func = getattr(self, 'help_' + arg)
@@ -312,13 +312,13 @@ version Prints SCons version information.
lines = list(map(strip_spaces, lines))
return '\n'.join(lines)
- def do_exit(self, argv):
+ def do_exit(self, argv) -> None:
"""\
exit Exit SCons interactive mode.
"""
sys.exit(0)
- def do_help(self, argv):
+ def do_help(self, argv) -> None:
"""\
help [COMMAND] Prints help for the specified COMMAND. 'h'
and '?' are synonyms.
@@ -335,7 +335,7 @@ version Prints SCons version information.
sys.stdout.write(doc + '\n')
sys.stdout.flush()
- def do_shell(self, argv):
+ def do_shell(self, argv) -> None:
"""\
shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and
'!' are synonyms.
@@ -355,13 +355,13 @@ version Prints SCons version information.
else:
p.wait()
- def do_version(self, argv):
+ def do_version(self, argv) -> None:
"""\
version Prints SCons version information.
"""
sys.stdout.write(self.parser.version + '\n')
-def interact(fs, parser, options, targets, target_top):
+def interact(fs, parser, options, targets, target_top) -> None:
c = SConsInteractiveCmd(prompt = 'scons>>> ',
fs = fs,
parser = parser,
diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py
index b90284278..c3f31ca3e 100644
--- a/SCons/Script/Main.py
+++ b/SCons/Script/Main.py
@@ -31,13 +31,8 @@ some other module. If it's specific to the "scons" script invocation,
it goes here.
"""
-# these define the range of versions SCons supports
-minimum_python_version = (3, 6, 0)
-deprecated_python_version = (3, 6, 0)
-
import SCons.compat
-import atexit
import importlib.util
import os
import re
@@ -46,6 +41,7 @@ import time
import traceback
import platform
import threading
+from typing import Optional, List
import SCons.CacheDir
import SCons.Debug
@@ -66,6 +62,20 @@ import SCons.Script.Interactive
from SCons import __version__ as SConsVersion
+# these define the range of versions SCons supports
+minimum_python_version = (3, 6, 0)
+deprecated_python_version = (3, 6, 0)
+
+# ordered list of SConsctruct names to look for if there is no -f flag
+KNOWN_SCONSTRUCT_NAMES = [
+ 'SConstruct',
+ 'Sconstruct',
+ 'sconstruct',
+ 'SConstruct.py',
+ 'Sconstruct.py',
+ 'sconstruct.py',
+]
+
# Global variables
first_command_start = None
last_command_end = None
@@ -82,7 +92,7 @@ num_jobs = None
delayed_warnings = []
-def revert_io():
+def revert_io() -> None:
# This call is added to revert stderr and stdout to the original
# ones just in case some build rule or something else in the system
# has redirected them elsewhere.
@@ -103,7 +113,7 @@ class Progressor:
count = 0
target_string = '$TARGET'
- def __init__(self, obj, interval=1, file=None, overwrite=False):
+ def __init__(self, obj, interval: int=1, file=None, overwrite: bool=False) -> None:
if file is None:
file = sys.stdout
@@ -121,12 +131,12 @@ class Progressor:
else:
self.func = self.string
- def write(self, s):
+ def write(self, s) -> None:
self.file.write(s)
self.file.flush()
self.prev = s
- def erase_previous(self):
+ def erase_previous(self) -> None:
if self.prev:
length = len(self.prev)
if self.prev[-1] in ('\n', '\r'):
@@ -134,16 +144,16 @@ class Progressor:
self.write(' ' * length + '\r')
self.prev = ''
- def spinner(self, node):
+ def spinner(self, node) -> None:
self.write(self.obj[self.count % len(self.obj)])
- def string(self, node):
+ def string(self, node) -> None:
self.write(self.obj)
- def replace_string(self, node):
+ def replace_string(self, node) -> None:
self.write(self.obj.replace(self.target_string, str(node)))
- def __call__(self, node):
+ def __call__(self, node) -> None:
self.count = self.count + 1
if (self.count % self.interval) == 0:
if self.overwrite:
@@ -152,7 +162,7 @@ class Progressor:
ProgressObject = SCons.Util.Null()
-def Progress(*args, **kw):
+def Progress(*args, **kw) -> None:
global ProgressObject
ProgressObject = Progressor(*args, **kw)
@@ -170,7 +180,7 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
"""An SCons build task."""
progress = ProgressObject
- def display(self, message):
+ def display(self, message) -> None:
display('scons: ' + message)
def prepare(self):
@@ -179,14 +189,14 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
self.progress(target)
return SCons.Taskmaster.OutOfDateTask.prepare(self)
- def needs_execute(self):
+ def needs_execute(self) -> bool:
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):
+ def execute(self) -> None:
if print_time:
start_time = time.time()
global first_command_start
@@ -213,7 +223,7 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
% (str(self.node), (finish_time - start_time))
)
- def do_failed(self, status=2):
+ def do_failed(self, status: int=2) -> None:
_BuildFailures.append(self.exception[1])
global exit_status
global this_build_status
@@ -253,7 +263,7 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
else:
SCons.Taskmaster.OutOfDateTask.executed(self)
- def failed(self):
+ def failed(self) -> None:
# Handle the failure of a build task. The primary purpose here
# is to display the various types of Errors and Exceptions
# appropriately.
@@ -284,7 +294,7 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
node = buildError.node
if not SCons.Util.is_List(node):
- node = [ node ]
+ node = [node]
nodename = ', '.join(map(str, node))
errfmt = "scons: *** [%s] %s\n"
@@ -309,7 +319,7 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
self.exc_clear()
- def postprocess(self):
+ def postprocess(self) -> None:
if self.top:
t = self.targets[0]
for tp in self.options.tree_printers:
@@ -321,7 +331,7 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
print(tree)
SCons.Taskmaster.OutOfDateTask.postprocess(self)
- def make_ready(self):
+ def make_ready(self) -> None:
"""Make a task ready for execution"""
SCons.Taskmaster.OutOfDateTask.make_ready(self)
if self.out_of_date and self.options.debug_explain:
@@ -332,7 +342,7 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
class CleanTask(SCons.Taskmaster.AlwaysTask):
"""An SCons clean task."""
- def fs_delete(self, path, pathstr, remove=True):
+ def fs_delete(self, path, pathstr, remove: bool=True):
try:
if os.path.lexists(path):
if os.path.isfile(path) or os.path.islink(path):
@@ -366,20 +376,20 @@ class CleanTask(SCons.Taskmaster.AlwaysTask):
result = [t for t in self.targets if not t.noclean]
return result
- def _clean_targets(self, remove=True):
+ def _clean_targets(self, remove: bool=True) -> None:
target = self.targets[0]
if target in SCons.Environment.CleanTargets:
files = SCons.Environment.CleanTargets[target]
for f in files:
self.fs_delete(f.get_abspath(), str(f), remove)
- def show(self):
+ def show(self) -> None:
for t in self._get_files_to_clean():
if not t.isdir():
display("Removed " + str(t))
self._clean_targets(remove=False)
- def remove(self):
+ def remove(self) -> None:
for t in self._get_files_to_clean():
try:
removed = t.remove()
@@ -408,15 +418,15 @@ class CleanTask(SCons.Taskmaster.AlwaysTask):
# anything really needs to be done.
make_ready = SCons.Taskmaster.Task.make_ready_all
- def prepare(self):
+ def prepare(self) -> None:
pass
class QuestionTask(SCons.Taskmaster.AlwaysTask):
"""An SCons task for the -q (question) option."""
- def prepare(self):
+ def prepare(self) -> None:
pass
- def execute(self):
+ def execute(self) -> None:
if self.targets[0].get_state() != SCons.Node.up_to_date or \
(self.top and not self.targets[0].exists()):
global exit_status
@@ -425,12 +435,12 @@ class QuestionTask(SCons.Taskmaster.AlwaysTask):
this_build_status = 1
self.tm.stop()
- def executed(self):
+ def executed(self) -> None:
pass
class TreePrinter:
- def __init__(self, derived=False, prune=False, status=False, sLineDraw=False):
+ def __init__(self, derived: bool=False, prune: bool=False, status: bool=False, sLineDraw: bool=False) -> None:
self.derived = derived
self.prune = prune
self.status = status
@@ -440,7 +450,7 @@ class TreePrinter:
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):
+ def display(self, t) -> None:
if self.derived:
func = self.get_derived_children
else:
@@ -460,24 +470,29 @@ def python_version_deprecated(version=sys.version_info):
class FakeOptionParser:
- """
- A do-nothing option parser, used for the initial OptionsParser variable.
+ """A do-nothing option parser, used for the initial OptionsParser value.
During normal SCons operation, the OptionsParser is created right
- away by the main() function. Certain tests scripts however, can
+ away by the main() function. Certain test 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):
+
+ # TODO: to quiet checkers, FakeOptionParser should also define
+ # raise_exception_on_error, preserve_unknown_options, largs and parse_args
+
+ def add_local_option(self, *args, **kw) -> None:
pass
+
OptionsParser = FakeOptionParser()
def AddOption(*args, **kw):
@@ -492,47 +507,51 @@ def GetOption(name):
def SetOption(name, value):
return OptionsParser.values.set_option(name, value)
-
-def ValidateOptions(throw_exception=False) -> None:
+def ValidateOptions(throw_exception: bool=False) -> None:
"""Validate options passed to SCons on the command line.
- If you call this after you set all your command line options with AddOption(),
- it will verify that all command line options are valid.
- So if you added an option --xyz and you call SCons with --xyy you can cause
+ Checks that all options given on the command line are known to this
+ instance of SCons. Call after all of the cli options have been set
+ up through :func:`AddOption` calls. For example, if you added an
+ option ``--xyz`` and you call SCons with ``--xyy`` you can cause
SCons to issue an error message and exit by calling this function.
- :param bool throw_exception: (Optional) Should this function raise an error if there's an invalid option on the command line, or issue a message and exit with error status.
+ Arguments:
+ throw_exception: if an invalid option is present on the command line,
+ raises an exception if this optional parameter evaluates true;
+ if false (the default), issue a message and exit with error status.
- :raises SConsBadOptionError: If throw_exception is True and there are invalid options on command line.
+ Raises:
+ SConsBadOptionError: If *throw_exception* is true and there are invalid
+ options on the command line.
- .. versionadded:: 4.4.1
+ .. versionadded:: 4.5.0
"""
-
OptionsParser.raise_exception_on_error = throw_exception
OptionsParser.preserve_unknown_options = False
OptionsParser.parse_args(OptionsParser.largs, OptionsParser.values)
-def PrintHelp(file=None):
+def PrintHelp(file=None) -> None:
OptionsParser.print_help(file=file)
class Stats:
- def __init__(self):
+ def __init__(self) -> None:
self.stats = []
self.labels = []
self.append = self.do_nothing
self.print_stats = self.do_nothing
- def enable(self, outfp):
+ def enable(self, outfp) -> None:
self.outfp = outfp
self.append = self.do_append
self.print_stats = self.do_print
- def do_nothing(self, *args, **kw):
+ def do_nothing(self, *args, **kw) -> None:
pass
class CountStats(Stats):
- def do_append(self, label):
+ def do_append(self, label) -> None:
self.labels.append(label)
self.stats.append(SCons.Debug.fetchLoggedInstances())
- def do_print(self):
+ def do_print(self) -> None:
stats_table = {}
for s in self.stats:
for n in [t[0] for t in s]:
@@ -559,10 +578,10 @@ class CountStats(Stats):
count_stats = CountStats()
class MemStats(Stats):
- def do_append(self, label):
+ def do_append(self, label) -> None:
self.labels.append(label)
self.stats.append(SCons.Debug.memory())
- def do_print(self):
+ def do_print(self) -> None:
fmt = 'Memory %-32s %12d\n'
for label, stats in zip(self.labels, self.stats):
self.outfp.write(fmt % (label, stats))
@@ -571,7 +590,7 @@ memory_stats = MemStats()
# utility functions
-def _scons_syntax_error(e):
+def _scons_syntax_error(e) -> None:
"""Handle syntax errors. Print out a message and show where the error
occurred.
"""
@@ -599,7 +618,7 @@ def find_deepest_user_frame(tb):
return frame
return tb[0]
-def _scons_user_error(e):
+def _scons_user_error(e) -> None:
"""Handle user errors. Print out a message and a description of the
error, along with the line number and routine where it occured.
The file and line number will be the deepest stack frame that is
@@ -614,7 +633,7 @@ def _scons_user_error(e):
sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
sys.exit(2)
-def _scons_user_warning(e):
+def _scons_user_warning(e) -> None:
"""Handle user warnings. Print out a message and a description of
the warning, along with the line number and routine where it occured.
The file and line number will be the deepest stack frame that is
@@ -625,7 +644,7 @@ def _scons_user_warning(e):
sys.stderr.write("\nscons: warning: %s\n" % e)
sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
-def _scons_internal_warning(e):
+def _scons_internal_warning(e) -> None:
"""Slightly different from _scons_user_warning in that we use the
*current call stack* rather than sys.exc_info() to get our stack trace.
This is used by the warnings framework to print warnings."""
@@ -633,7 +652,7 @@ def _scons_internal_warning(e):
sys.stderr.write("\nscons: warning: %s\n" % e.args[0])
sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
-def _scons_internal_error():
+def _scons_internal_error() -> None:
"""Handle all errors but user errors. Print out a message telling
the user what to do in this case and print a normal trace.
"""
@@ -641,13 +660,24 @@ def _scons_internal_error():
traceback.print_exc()
sys.exit(2)
-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.
+def _SConstruct_exists(
+ dirname: str, repositories: List[str], filelist: List[str]
+) -> Optional[str]:
+ """Check that an SConstruct file exists in a directory.
+
+ Arguments:
+ dirname: the directory to search. If empty, look in cwd.
+ repositories: a list of repositories to search in addition to the
+ project directory tree.
+ filelist: names of SConstruct file(s) to search for.
+ If empty list, use the built-in list of names.
+
+ Returns:
+ The path to the located SConstruct file, or ``None``.
+
"""
if not filelist:
- filelist = ['SConstruct', 'Sconstruct', 'sconstruct', 'SConstruct.py', 'Sconstruct.py', 'sconstruct.py']
+ filelist = KNOWN_SCONSTRUCT_NAMES
for file in filelist:
sfile = os.path.join(dirname, file)
if os.path.isfile(sfile):
@@ -658,7 +688,7 @@ def _SConstruct_exists(dirname='', repositories=[], filelist=None):
return sfile
return None
-def _set_debug_values(options):
+def _set_debug_values(options) -> None:
global print_memoizer, print_objects, print_stacktrace, print_time, print_action_timestamps
debug_values = options.debug
@@ -679,14 +709,14 @@ def _set_debug_values(options):
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)
+ 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)
+ 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)
+ print_objects = "objects" in debug_values
if print_objects:
SCons.Debug.track_instances = True
if "presub" in debug_values:
@@ -798,7 +828,7 @@ def _load_site_scons_dir(topdir, site_dir_name=None):
raise
-def _load_all_site_scons_dirs(topdir, verbose=False):
+def _load_all_site_scons_dirs(topdir, verbose: bool=False) -> None:
"""Load all of the predefined site_scons dir.
Order is significant; we load them in order from most generic
(machine-wide) to most specific (topdir).
@@ -841,7 +871,7 @@ def _load_all_site_scons_dirs(topdir, verbose=False):
print("Loading site dir ", d)
_load_site_scons_dir(d)
-def test_load_all_site_scons_dirs(d):
+def test_load_all_site_scons_dirs(d) -> None:
_load_all_site_scons_dirs(d, True)
def version_string(label, module):
@@ -858,7 +888,7 @@ def version_string(label, module):
module.__developer__,
module.__buildsys__)
-def path_string(label, module):
+def path_string(label, module) -> str:
path = module.__path__
return "\t%s path: %s\n"%(label,path)
@@ -919,9 +949,9 @@ def _main(parser):
target_top = None
if options.climb_up:
target_top = '.' # directory to prepend to targets
- while script_dir and not _SConstruct_exists(script_dir,
- options.repository,
- options.file):
+ 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)
@@ -951,8 +981,7 @@ def _main(parser):
if options.file:
scripts.extend(options.file)
if not scripts:
- sfile = _SConstruct_exists(repositories=options.repository,
- filelist=options.file)
+ sfile = _SConstruct_exists("", options.repository, options.file)
if sfile:
scripts.append(sfile)
@@ -1011,8 +1040,8 @@ def _main(parser):
# Next, set up the variables that hold command-line arguments,
# so the SConscript files that we read and execute have access to them.
# TODO: for options defined via AddOption which take space-separated
- # option-args, the option-args will collect into targets here,
- # because we don't yet know to do any different.
+ # option-args, the option-args will collect into targets here,
+ # because we don't yet know to do any different.
targets = []
xmit_args = []
for a in parser.largs:
@@ -1327,7 +1356,7 @@ def _build_targets(fs, options, targets, target_top):
options=options,
closing_message=closing_message,
failure_message=failure_message
- ):
+ ) -> None:
if jobs.were_interrupted():
if not options.no_progress and not options.silent:
sys.stderr.write("scons: Build interrupted.\n")
@@ -1353,7 +1382,7 @@ def _build_targets(fs, options, targets, target_top):
return nodes
-def _exec_main(parser, values):
+def _exec_main(parser, values) -> None:
sconsflags = os.environ.get('SCONSFLAGS', '')
all_args = sconsflags.split() + sys.argv[1:]
@@ -1374,7 +1403,7 @@ def _exec_main(parser, values):
_main(parser)
-def main():
+def main() -> None:
global OptionsParser
global exit_status
global first_command_start
diff --git a/SCons/Script/Main.xml b/SCons/Script/Main.xml
index fbb90fd07..379d5347e 100644
--- a/SCons/Script/Main.xml
+++ b/SCons/Script/Main.xml
@@ -749,7 +749,7 @@ Sets &scons; option variable <parameter>name</parameter>
to <parameter>value</parameter>.
These options are all also settable via
command-line options but the variable name
-may differ from the command-line option name -
+may differ from the command-line option name -
see the table for correspondences.
A value set via command-line option will take
precedence over one set with &f-SetOption;, which
@@ -946,64 +946,79 @@ SetOption('max_drift', 0)
</scons_function>
- <scons_function name="ValidateOptions">
- <arguments signature="global">
- ([throw_exception=False])
- </arguments>
-
- <summary>
- <para>
- Check that all the options specified on the command line are either defined by SCons itself
- or defined by calls to &f-link-AddOption;.
- </para>
- <para>
- This function should only be called after the last &f-link-AddOption; call in your &SConscript;
- logic.
- </para>
- <para>
- Be aware that some tools call &f-link-AddOption;, if you are getting error messages for arguments
- that they add, you will need to ensure that you load those tools before you call &f-ValidateOptions;.
- </para>
- <para>
- If there are any command line options not defined, calling this function will cause SCons to issue an
- error message and then exit with an error exit
- status.</para>
- <para>If the optional <parameter>throw_exception</parameter> is <literal>True</literal>, &f-ValidateOptions; will raise a
- <exceptionname>SConsBadOptionError</exceptionname>
- exception. This would allow the calling
- &SConscript; logic can catch that exception and handle invalid options itself.
- </para>
-
- <para>
- Example:
- </para>
-
- <example_commands>
+ <scons_function name="ValidateOptions">
+ <arguments signature="global">([throw_exception=False])</arguments>
+
+ <summary>
+ <para>
+ Check that all the options specified on the command line are either
+ &SCons; built-in options or defined via calls to &f-link-AddOption;.
+ &SCons; will eventually fail on unknown options anyway, but calling
+ this function allows the build to "fail fast" before executing
+ expensive logic later in the build.
+ </para>
+
+ <para>
+ This function should only be called after the last &f-AddOption;
+ call in your &SConscript; logic.
+ Be aware that some tools call &f-AddOption;, if you are getting
+ error messages for arguments that they add, you will need to ensure
+ that those tools are loaded before calling &f-ValidateOptions;.
+ </para>
+
+ <para>
+ If there are any unknown command line options, &f-ValidateOptions;
+ prints an error message and exits with an error exit status.
+ If the optional <parameter>throw_exception</parameter> argument is
+ <literal>True</literal> (default is <literal>False</literal>),
+ a <exceptionname>SConsBadOptionError</exceptionname> is raised,
+ giving an opportunity for the &SConscript; logic to catch that
+ exception and handle invalid options appropriately. Note that
+ this exception name needs to be imported (see the example below).
+ </para>
+
+ <para>
+ A common build problem is typos (or thinkos) - a user enters an option
+ that is just a little off the expected value, or perhaps a different
+ word with a similar meaning. It may be useful to abort the build
+ before going too far down the wrong path. For example:
+ </para>
+
+ <screen>
+$ <userinput>scons --compilers=mingw</userinput> # the correct flag is --compiler
+ </screen>
+
+ <para>
+ Here &SCons; could go off and run a bunch of configure steps with
+ the default value of <literal>--compiler</literal>, since the
+ incorrect command line did not actually supply a value to it,
+ costing developer time to track down why the configure logic
+ made the "wrong" choices. This example shows catching this:
+ </para>
+
+ <programlisting language="python">
+from SCons.Script.SConsOptions import SConsBadOptionError
+
+AddOption(
+ '--compiler',
+ dest='compiler',
+ action='store',
+ default='gcc',
+ type='string',
+)
+
+# ... other SConscript logic ...
+
try:
ValidateOptions(throw_exception=True)
except SConsBadOptionError as e:
- print("Parser is SConsOptionParser:%s" % (isinstance(e.parser, SConsOptionParser)))
- print("Message is :%s" % e.opt_str)
+ print(f"ValidateOptions detects a fail: ", e.opt_str)
Exit(3)
- </example_commands>
-
- <para>
- This function is useful to force SCons to fail fast before you execute any expensive logic later in your
- build logic.
- For example if you specify build options via any flags, a simple typo could yield the incorrect build
- option throughout your entire build.
- </para>
- <example_commands>
-scons --compilers=mingw (the correct flag is --compiler)
- </example_commands>
- <para>
- Could cause SCons to run configure steps with the incorrect compiler. Costing developer time trying to
- track down why the configure logic failed with a compiler which should work.
- </para>
-
-
-
- </summary>
- </scons_function>
+ </programlisting>
+
+ <para><emphasis>New in version 4.5.0</emphasis></para>
+
+ </summary>
+ </scons_function>
</sconsdoc>
diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py
index 8391d6208..fda744535 100644
--- a/SCons/Script/SConsOptions.py
+++ b/SCons/Script/SConsOptions.py
@@ -98,7 +98,7 @@ class SConsValues(optparse.Values):
in the set_option() method.
"""
- def __init__(self, defaults):
+ def __init__(self, defaults) -> None:
self.__defaults__ = defaults
self.__SConscript_settings__ = {}
@@ -300,11 +300,11 @@ class SConsBadOptionError(optparse.BadOptionError):
"""
- def __init__(self, opt_str, parser=None):
+ def __init__(self, opt_str, parser=None) -> None:
self.opt_str = opt_str
self.parser = parser
- def __str__(self):
+ def __str__(self) -> str:
return _("no such option: %s") % self.opt_str
@@ -396,7 +396,7 @@ class SConsOptionParser(optparse.OptionParser):
option.process(opt, value, values, self)
- def reparse_local_options(self):
+ def reparse_local_options(self) -> None:
""" Re-parse the leftover command-line options.
Parse options stored in `self.largs`, so that any value
@@ -491,7 +491,7 @@ class SConsOptionParser(optparse.OptionParser):
class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
- def format_usage(self, usage):
+ def format_usage(self, usage) -> str:
""" Formats the usage message. """
return "usage: %s\n" % usage
@@ -610,7 +610,7 @@ def Parser(version):
op.version = version
# options ignored for compatibility
- def opt_ignore(option, opt, value, parser):
+ def opt_ignore(option, opt, value, parser) -> None:
sys.stderr.write("Warning: ignoring %s option\n" % opt)
op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w",
@@ -822,7 +822,7 @@ def Parser(version):
action="help",
help="Print this message and exit")
- def warn_md5_chunksize_deprecated(option, opt, value, parser):
+ def warn_md5_chunksize_deprecated(option, opt, value, parser) -> None:
if opt == '--md5-chunksize':
SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
"Parameter %s is deprecated. Use "
@@ -865,7 +865,7 @@ def Parser(version):
action="store_true",
help="Cache implicit dependencies")
- def opt_implicit_deps(option, opt, value, parser):
+ def opt_implicit_deps(option, opt, value, parser) -> None:
setattr(parser.values, 'implicit_cache', True)
setattr(parser.values, option.dest, True)
@@ -1002,7 +1002,7 @@ def Parser(version):
help="Search up directory tree for SConstruct, "
"build Default() targets from local SConscript")
- def opt_version(option, opt, value, parser):
+ def opt_version(option, opt, value, parser) -> None:
sys.stdout.write(parser.version + '\n')
sys.exit(0)
@@ -1010,7 +1010,7 @@ def Parser(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):
+ def opt_warn(option, opt, value, parser, tree_options=tree_options) -> None:
if SCons.Util.is_String(value):
value = value.split(',')
parser.values.warn.extend(value)
@@ -1033,7 +1033,7 @@ def Parser(version):
# 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):
+ def opt_not_yet(option, opt, value, parser) -> None:
msg = "Warning: the %s option is not yet implemented\n" % opt
sys.stderr.write(msg)
diff --git a/SCons/Script/SConscript.py b/SCons/Script/SConscript.py
index b72f30eee..c0b556c68 100644
--- a/SCons/Script/SConscript.py
+++ b/SCons/Script/SConscript.py
@@ -104,7 +104,7 @@ def compute_exports(exports):
class Frame:
"""A frame on the SConstruct/SConscript call stack"""
- def __init__(self, fs, exports, sconscript):
+ def __init__(self, fs, exports, sconscript) -> None:
self.globals = BuildDefaultGlobals()
self.retval = None
self.prev_dir = fs.getcwd()
@@ -332,7 +332,7 @@ def _SConscript(fs, *files, **kw):
else:
return tuple(results)
-def SConscript_exception(file=sys.stderr):
+def SConscript_exception(file=sys.stderr) -> None:
"""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
@@ -481,11 +481,11 @@ class SConsEnvironment(SCons.Environment.Base):
kw['_depth'] = kw.get('_depth', 0) + 1
return SCons.Environment.Base.Configure(self, *args, **kw)
- def Default(self, *targets):
+ def Default(self, *targets) -> None:
SCons.Script._Set_Default_Targets(self, targets)
@staticmethod
- def EnsureSConsVersion(major, minor, revision=0):
+ def EnsureSConsVersion(major, minor, revision: int=0) -> None:
"""Exit abnormally if the SCons version is not late enough."""
# split string to avoid replacement during build process
if SCons.__version__ == '__' + 'VERSION__':
@@ -503,7 +503,7 @@ class SConsEnvironment(SCons.Environment.Base):
sys.exit(2)
@staticmethod
- def EnsurePythonVersion(major, minor):
+ def EnsurePythonVersion(major, minor) -> None:
"""Exit abnormally if the Python version is not late enough."""
if sys.version_info < (major, minor):
v = sys.version.split()[0]
@@ -511,10 +511,10 @@ class SConsEnvironment(SCons.Environment.Base):
sys.exit(2)
@staticmethod
- def Exit(value=0):
+ def Exit(value: int=0) -> None:
sys.exit(value)
- def Export(self, *vars, **kw):
+ def Export(self, *vars, **kw) -> None:
for var in vars:
global_exports.update(compute_exports(self.Split(var)))
global_exports.update(kw)
@@ -529,7 +529,7 @@ class SConsEnvironment(SCons.Environment.Base):
return SCons.Script.Main.GetOption(name)
- def Help(self, text, append=False):
+ def Help(self, text, append: bool=False) -> None:
text = self.subst(text, raw=1)
SCons.Script.HelpFunction(text, append=append)
@@ -602,7 +602,7 @@ class SConsEnvironment(SCons.Environment.Base):
global sconscript_chdir
sconscript_chdir = flag
- def SetOption(self, name, value):
+ def SetOption(self, name, value) -> None:
name = self.subst(name)
SCons.Script.Main.SetOption(name, value)
@@ -650,7 +650,7 @@ class DefaultEnvironmentCall:
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):
+ def __init__(self, method_name, subst: int=0) -> None:
self.method_name = method_name
if subst:
self.factory = SCons.Defaults.DefaultEnvironment
@@ -674,7 +674,7 @@ def BuildDefaultGlobals():
import SCons.Script
d = SCons.Script.__dict__
- def not_a_module(m, d=d, mtype=type(SCons.Script)):
+ def not_a_module(m, d=d, mtype=type(SCons.Script)) -> bool:
return not isinstance(d[m], mtype)
for m in filter(not_a_module, dir(SCons.Script)):
GlobalDict[m] = d[m]
diff --git a/SCons/Script/__init__.py b/SCons/Script/__init__.py
index 6cfea1b43..c8666c4ca 100644
--- a/SCons/Script/__init__.py
+++ b/SCons/Script/__init__.py
@@ -175,11 +175,11 @@ DefaultEnvironment = SCons.Defaults.DefaultEnvironment
# Other variables we provide.
class TargetList(collections.UserList):
- def _do_nothing(self, *args, **kw):
+ def _do_nothing(self, *args, **kw) -> None:
pass
- def _add_Default(self, list):
+ def _add_Default(self, list) -> None:
self.extend(list)
- def _clear(self):
+ def _clear(self) -> None:
del self[:]
ARGUMENTS = {}
@@ -199,13 +199,13 @@ DEFAULT_TARGETS = []
# own targets to BUILD_TARGETS.
_build_plus_default = TargetList()
-def _Add_Arguments(alist):
+def _Add_Arguments(alist) -> None:
for arg in alist:
a, b = arg.split('=', 1)
ARGUMENTS[a] = b
ARGLIST.append((a, b))
-def _Add_Targets(tlist):
+def _Add_Targets(tlist) -> None:
if tlist:
COMMAND_LINE_TARGETS.extend(tlist)
BUILD_TARGETS.extend(tlist)
@@ -225,7 +225,7 @@ def _Set_Default_Targets_Has_Not_Been_Called(d, fs):
_Get_Default_Targets = _Set_Default_Targets_Has_Not_Been_Called
-def _Set_Default_Targets(env, tlist):
+def _Set_Default_Targets(env, tlist) -> None:
global DEFAULT_TARGETS
global _Get_Default_Targets
_Get_Default_Targets = _Set_Default_Targets_Has_Been_Called
@@ -250,7 +250,7 @@ def _Set_Default_Targets(env, tlist):
#
help_text = None
-def HelpFunction(text, append=False):
+def HelpFunction(text, append: bool=False) -> None:
global help_text
if help_text is None:
if append:
@@ -271,7 +271,7 @@ sconscript_reading = 0
_no_missing_sconscript = False
_warn_missing_sconscript_deprecated = True
-def set_missing_sconscript_error(flag=1):
+def set_missing_sconscript_error(flag: int=1):
"""Set behavior on missing file in SConscript() call.
Returns: