diff options
Diffstat (limited to 'SCons/Defaults.py')
-rw-r--r-- | SCons/Defaults.py | 144 |
1 files changed, 87 insertions, 57 deletions
diff --git a/SCons/Defaults.py b/SCons/Defaults.py index 40c3e4a52..15041a5ce 100644 --- a/SCons/Defaults.py +++ b/SCons/Defaults.py @@ -36,6 +36,7 @@ import shutil import stat import sys import time +from typing import List import SCons.Action import SCons.Builder @@ -46,7 +47,7 @@ import SCons.PathList import SCons.Scanner.Dir import SCons.Subst import SCons.Tool -import SCons.Util +from SCons.Util import is_List, is_String, is_Sequence, is_Tuple, is_Dict, flatten # A placeholder for a default Environment (for fetching source files # from source code management systems and the like). This must be @@ -163,10 +164,10 @@ def get_paths_str(dest) -> str: If *dest* is a list, manually converts each elem to a string. """ - def quote(arg): + def quote(arg) -> str: return f'"{arg}"' - if SCons.Util.is_List(dest): + if is_List(dest): elem_strs = [quote(d) for d in dest] return f'[{", ".join(elem_strs)}]' else: @@ -202,11 +203,11 @@ def chmod_func(dest, mode) -> None: """ from string import digits SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): + if not is_List(dest): dest = [dest] - if SCons.Util.is_String(mode) and 0 not in [i in digits for i in mode]: + if is_String(mode) and 0 not in [i in digits for i in mode]: mode = int(mode, 8) - if not SCons.Util.is_String(mode): + if not is_String(mode): for element in dest: os.chmod(str(element), mode) else: @@ -244,7 +245,7 @@ def chmod_func(dest, mode) -> None: def chmod_strfunc(dest, mode) -> str: """strfunction for the Chmod action function.""" - if not SCons.Util.is_String(mode): + if not is_String(mode): return f'Chmod({get_paths_str(dest)}, {mode:#o})' else: return f'Chmod({get_paths_str(dest)}, "{mode}")' @@ -255,7 +256,7 @@ Chmod = ActionFactory(chmod_func, chmod_strfunc) -def copy_func(dest, src, symlinks=True) -> int: +def copy_func(dest, src, symlinks: bool=True) -> int: """Implementation of the Copy action function. Copies *src* to *dest*. If *src* is a list, *dest* must be @@ -272,10 +273,10 @@ def copy_func(dest, src, symlinks=True) -> int: """ dest = str(dest) - src = [str(n) for n in src] if SCons.Util.is_List(src) else str(src) + src = [str(n) for n in src] if is_List(src) else str(src) SCons.Node.FS.invalidate_node_memos(dest) - if SCons.Util.is_List(src): + if is_List(src): # this fails only if dest exists and is not a dir try: os.makedirs(dest, exist_ok=True) @@ -307,7 +308,7 @@ def copy_func(dest, src, symlinks=True) -> int: return 0 -def copy_strfunc(dest, src, symlinks=True) -> str: +def copy_strfunc(dest, src, symlinks: bool=True) -> str: """strfunction for the Copy action function.""" return f'Copy({get_paths_str(dest)}, {get_paths_str(src)})' @@ -315,14 +316,14 @@ def copy_strfunc(dest, src, symlinks=True) -> str: Copy = ActionFactory(copy_func, copy_strfunc) -def delete_func(dest, must_exist=False) -> None: +def delete_func(dest, must_exist: bool=False) -> None: """Implementation of the Delete action function. Lets the Python :func:`os.unlink` raise an error if *dest* does not exist, unless *must_exist* evaluates false (the default). """ SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): + if not is_List(dest): dest = [dest] for entry in dest: entry = str(entry) @@ -337,7 +338,7 @@ def delete_func(dest, must_exist=False) -> None: os.unlink(entry) -def delete_strfunc(dest, must_exist=False) -> str: +def delete_strfunc(dest, must_exist: bool=False) -> str: """strfunction for the Delete action function.""" return f'Delete({get_paths_str(dest)})' @@ -348,7 +349,7 @@ Delete = ActionFactory(delete_func, delete_strfunc) def mkdir_func(dest) -> None: """Implementation of the Mkdir action function.""" SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): + if not is_List(dest): dest = [dest] for entry in dest: os.makedirs(str(entry), exist_ok=True) @@ -372,7 +373,7 @@ Move = ActionFactory( def touch_func(dest) -> None: """Implementation of the Touch action function.""" SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): + if not is_List(dest): dest = [dest] for file in dest: file = str(file) @@ -391,7 +392,7 @@ Touch = ActionFactory(touch_func, lambda file: f'Touch({get_paths_str(file)})') # Internal utility functions # pylint: disable-msg=too-many-arguments -def _concat(prefix, items_iter, suffix, env, f=lambda x: x, target=None, source=None, affect_signature=True): +def _concat(prefix, items_iter, suffix, env, f=lambda x: x, target=None, source=None, affect_signature: bool=True): """ Creates a new list from 'items_iter' by first interpolating each element in the list using the 'env' dictionary and then calling f on the @@ -433,7 +434,7 @@ def _concat_ixes(prefix, items_iter, suffix, env): prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) - for x in SCons.Util.flatten(items_iter): + for x in flatten(items_iter): if isinstance(x, SCons.Node.FS.File): result.append(x) continue @@ -479,8 +480,8 @@ def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): else: c = _concat_ixes - stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes))) - stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes))) + stripprefixes = list(map(env.subst, flatten(stripprefixes))) + stripsuffixes = list(map(env.subst, flatten(stripsuffixes))) stripped = [] for l in SCons.PathList.PathList(itms).subst_path(env, None, None): @@ -488,7 +489,7 @@ def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): stripped.append(l) continue - if not SCons.Util.is_String(l): + if not is_String(l): l = str(l) for stripprefix in stripprefixes: @@ -510,53 +511,82 @@ def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): return c(prefix, stripped, suffix, env) -def processDefines(defs): - """process defines, resolving strings, lists, dictionaries, into a list of - strings +def processDefines(defs) -> List[str]: + """Return list of strings for preprocessor defines from *defs*. + + Resolves the different forms ``CPPDEFINES`` can be assembled in: + if the Append/Prepend routines are used beyond a initial setting it + will be a deque, but if written to only once (Environment initializer, + or direct write) it can be a multitude of types. + + Any prefix/suffix is handled elsewhere (usually :func:`_concat_ixes`). + + .. versionchanged:: 4.5.0 + Bare tuples are now treated the same as tuple-in-sequence, assumed + to describe a valued macro. Bare strings are now split on space. + A dictionary is no longer sorted before handling. """ - if SCons.Util.is_List(defs): - l = [] - for d in defs: - if d is None: + dlist = [] + if is_List(defs): + for define in defs: + if define is None: continue - elif SCons.Util.is_List(d) or isinstance(d, tuple): - if len(d) >= 2: - l.append(str(d[0]) + '=' + str(d[1])) + elif is_Sequence(define): + if len(define) > 2: + raise SCons.Errors.UserError( + f"Invalid tuple in CPPDEFINES: {define!r}, " + "must be a tuple with only two elements" + ) + name, *value = define + if value and value[0] is not None: + # TODO: do we need to quote value if it contains space? + dlist.append(f"{name}={value[0]}") else: - l.append(str(d[0])) - elif SCons.Util.is_Dict(d): - for macro, value in d.items(): + dlist.append(str(define[0])) + elif is_Dict(define): + for macro, value in define.items(): if value is not None: - l.append(str(macro) + '=' + str(value)) + # TODO: do we need to quote value if it contains space? + dlist.append(f"{macro}={value}") else: - l.append(str(macro)) - elif SCons.Util.is_String(d): - l.append(str(d)) + dlist.append(str(macro)) + elif is_String(define): + dlist.append(str(define)) else: - raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None." % repr(d)) - elif SCons.Util.is_Dict(defs): - # The items in a dictionary are stored in random order, but - # if the order of the command-line options changes from - # invocation to invocation, then the signature of the command - # line will change and we'll get random unnecessary rebuilds. - # Consequently, we have to sort the keys to ensure a - # consistent order... - l = [] - for k, v in sorted(defs.items()): - if v is None: - l.append(str(k)) + raise SCons.Errors.UserError( + f"CPPDEFINES entry {define!r} is not a tuple, list, " + "dict, string or None." + ) + elif is_Tuple(defs): + if len(defs) > 2: + raise SCons.Errors.UserError( + f"Invalid tuple in CPPDEFINES: {defs!r}, " + "must be a tuple with only two elements" + ) + name, *value = defs + if value and value[0] is not None: + # TODO: do we need to quote value if it contains space? + dlist.append(f"{name}={value[0]}") + else: + dlist.append(str(define[0])) + elif is_Dict(defs): + for macro, value in defs.items(): + if value is None: + dlist.append(str(macro)) else: - l.append(str(k) + '=' + str(v)) + dlist.append(f"{macro}={value}") + elif is_String(defs): + return defs.split() else: - l = [str(defs)] - return l + dlist.append(str(defs)) + + return dlist def _defines(prefix, defs, suffix, env, target=None, source=None, c=_concat_ixes): - """A wrapper around _concat_ixes that turns a list or string + """A wrapper around :func:`_concat_ixes` that turns a list or string into a list of C preprocessor command-line definitions. """ - return c(prefix, env.subst_list(processDefines(defs), target=target, source=source), suffix, env) @@ -572,7 +602,7 @@ class NullCmdGenerator: env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" """ - def __init__(self, cmd): + def __init__(self, cmd) -> None: self.cmd = cmd def __call__(self, target, source, env, for_signature=None): @@ -592,7 +622,7 @@ class Variable_Method_Caller: create new environment objects to hold the variables.) """ - def __init__(self, variable, method): + def __init__(self, variable, method) -> None: self.variable = variable self.method = method |