summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt19
-rw-r--r--RELEASE.txt24
-rw-r--r--SCons/Defaults.py95
-rw-r--r--SCons/Defaults.xml95
-rw-r--r--SCons/Environment.py306
-rw-r--r--SCons/Environment.xml247
-rw-r--r--SCons/Util/types.py24
-rw-r--r--test/CPPDEFINES/append.py214
-rwxr-xr-xtest/CPPDEFINES/basic.py55
-rw-r--r--test/CPPDEFINES/fixture/SConstruct-Append99
-rw-r--r--test/CPPDEFINES/fixture/SConstruct-Prepend100
-rw-r--r--test/CPPDEFINES/live.py33
-rw-r--r--test/CPPDEFINES/pkg-config.py50
-rw-r--r--test/CPPDEFINES/prepend.py258
-rw-r--r--test/CPPDEFINES/scan.py32
-rw-r--r--test/CPPDEFINES/undefined.py13
16 files changed, 1179 insertions, 485 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 5a3651ad8..41a966b5c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -133,6 +133,17 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
Also added "unique" keyword, to control whether a library is added
or not if it is already in the $LIBS construction var in the
configure context. (issue #2768).
+ - Special-case handling for CPPDEFINES in the Append/Prepend routines
+ split out into its own function to simplify the remaining code and
+ fix problems. The e2e test test/CPPDEFINES/append.py is expanded
+ to cover missed cases, and AppendUnique no longer mismatches with
+ what Append does (#3876). Inconsistent handling of tuples to specify
+ macro=value outputs is also cleaned up (#4254). The special handling
+ now also works for Prepend/PrependUnique, and a corresponding test
+ test/CPPDEFINES/prepend.py was added to verify the behavior.
+ SCons used to sort keys set or appended via a dict type, in order to
+ assure order of commandline flags did not change across runs. This
+ behavior has been dropped since Python now assures consistent dict order.
RELEASE 4.4.0 - Sat, 30 Jul 2022 14:08:29 -0700
@@ -295,16 +306,16 @@ RELEASE 4.4.0 - Sat, 30 Jul 2022 14:08:29 -0700
- Ninja:Added user configurable setting of ninja depfile format via NINJA_DEPFILE_PARSE_FORMAT.
Now setting NINJA_DEPFILE_PARSE_FORMAT to [msvc,gcc,clang] can force the ninja expected
format. Compiler tools will also configure the variable automatically.
- - Ninja: Made ninja tool force the ninja file as the only target.
+ - Ninja: Made ninja tool force the ninja file as the only target.
- Ninja: Improved the default targets setup and made sure there is always a default target for
the ninja file, which excludes targets that start and stop the daemon.
- Ninja: Update ninja tool so targets passed to SCons are propagated to ninja when scons
automatically executes ninja.
- - Small refactor of scons daemons using a shared StateInfo class for communication
+ - Small refactor of scons daemons using a shared StateInfo class for communication
between the scons interactive thread and the http server thread. Added error handling
for scons interactive failing to startup.
- Ninja: Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
- - Ninja: Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
+ - Ninja: Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
- Added SHELL_ENV_GENERATORS construction variable. This variable should be set to a list
(or an iterable) which contains functions to be called in order
when constructing the execution environment (Generally this is the shell environment
@@ -546,7 +557,7 @@ RELEASE 4.2.0 - Sat, 31 Jul 2021 18:12:46 -0700
- Fix Issue #3906 - `IMPLICIT_COMMAND_DEPENDENCIES` was not properly disabled when
set to any string value (For example ['none','false','no','off'])
Also previously 'All' wouldn't have the desired affect.
-
+
From Ivan Kravets:
- Provide a custom argument escape function for `TempFileMunge` using a new
`TEMPFILEARGESCFUNC` variable. Useful if you need to apply extra operations on
diff --git a/RELEASE.txt b/RELEASE.txt
index 6c1c5f281..1bf6bf859 100644
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -51,7 +51,12 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
which controls whether to add the library if it is already in the $LIBS
list. This brings the library-adding functionality in Configure in line
with the regular Append, AppendUnique, Prepend and PrependUnique methods.
-
+- CPPDEFINES values added via a dictionary type are longer sorted by
+ key. This used to be required to maintain a consistent order of
+ commandline arguments between SCons runs, but meant macros were not
+ always emitted in the order entered. Sorting is no longer required
+ after Python interpreter improvements. There might be a one-time
+ rebuild of targets that involved such sorted keys in their actions.
FIXES
-----
@@ -90,7 +95,17 @@ FIXES
actual build would not see the file as modified. Leading to incorrect incremental builds.
Now configure checks now clear node info for non conftest nodes, so they will be re-evaluated for
the real taskmaster run when the build commences.
-
+- Inconsistent behavior of adding CPPDEFINES values via tuple arguments
+ has been cleaned up (problems described in issue #4254). In order to
+ achieve consistency, a rule for for adding macros-with-values via a
+ tuple (or list) is now enforced: they need to be entered as members
+ of a containing sequence. ["NAME", "VALUE"] and ("NAME", "VALUE")
+ are now treated the same (two individual macro names), rather than
+ the latter sometimes, but not always, being handled as a single
+ NAME=VALUE macro. Use a construct like [("NAME", "VALUE")] to get the
+ macro-with-value behavior.
+- Handling of CPPDEFINES macros via Prepend and PrependUnique now works
+ (previously this was special-cased only for Append and AppendUnique).
IMPROVEMENTS
------------
@@ -133,12 +148,17 @@ DOCUMENTATION
- Updated the User Guide chapter on installation: modernized the notes
on Python installs, SCons installs, and having multiple SCons versions
present on a single system.
+<<<<<<< HEAD
- Updated the User Guide chapter on variant directories with more
explanation, and the introduction of terms like "out of tree" that
may help in forming a mental model.
- Additional explanations for MSVSProject and MSVSSolution builders.
- Updated MSVC documentation - adds "version added" annotations on recently
added construction variables and provides a version-mapping table.
+=======
+- Added further details in the documentation of Append and related methods
+ on the special handling of CPPDEFINES.
+>>>>>>> d9a82d681 (Split out CPPDEFINES handling in Append methods)
DEVELOPMENT
-----------
diff --git a/SCons/Defaults.py b/SCons/Defaults.py
index 40c3e4a52..da90260ba 100644
--- a/SCons/Defaults.py
+++ b/SCons/Defaults.py
@@ -36,6 +36,7 @@ import shutil
import stat
import sys
import time
+from collections import deque
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_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
@@ -166,7 +167,7 @@ def get_paths_str(dest) -> str:
def quote(arg):
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}")'
@@ -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)
@@ -322,7 +323,7 @@ def delete_func(dest, must_exist=False) -> None:
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)
@@ -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)
@@ -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:
@@ -511,49 +512,49 @@ def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None):
def processDefines(defs):
- """process defines, resolving strings, lists, dictionaries, into a list of
- strings
+ """Return list of strings for preprocessor defines from *defs*.
+
+ Resolves all the different forms CPPDEFINES can be assembled in.
+ Any prefix/suffix is handled elsewhere (usually :func:`_concat_ixes`).
"""
- 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_List(define) or is_Tuple(define):
+ if len(define) >= 2 and define[1] is not None:
+ # TODO: do we need to quote define[1] if it contains space?
+ dlist.append(str(define[0]) + '=' + str(define[1]))
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(str(macro) + '=' + str(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"DEFINE {d!r} is not a list, dict, string or None."
+ )
+ 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(str(macro) + '=' + str(value))
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.
"""
diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml
index 27b088294..7b37475a2 100644
--- a/SCons/Defaults.xml
+++ b/SCons/Defaults.xml
@@ -92,68 +92,111 @@ to each definition in &cv-link-CPPDEFINES;.
<summary>
<para>
A platform independent specification of C preprocessor macro definitions.
-The definitions will be added to command lines
+The definitions are added to command lines
through the automatically-generated
-&cv-link-_CPPDEFFLAGS; &consvar; (see above),
+&cv-link-_CPPDEFFLAGS; &consvar;,
which is constructed according to
-the type of value of &cv-CPPDEFINES;:
+the contents of &cv-CPPDEFINES;:
</para>
+<itemizedlist>
+<listitem>
<para>
If &cv-CPPDEFINES; is a string,
the values of the
&cv-link-CPPDEFPREFIX; and &cv-link-CPPDEFSUFFIX; &consvars;
-will be respectively prepended and appended to
-each definition in &cv-link-CPPDEFINES;.
+are respectively prepended and appended to
+each definition in &cv-CPPDEFINES;,
+split on whitespace.
</para>
<example_commands>
-# Will add -Dxyz to POSIX compiler command lines,
+# Adds -Dxyz to POSIX compiler command lines,
# and /Dxyz to Microsoft Visual C++ command lines.
env = Environment(CPPDEFINES='xyz')
</example_commands>
+</listitem>
+<listitem>
<para>
If &cv-CPPDEFINES; is a list,
the values of the
&cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX; &consvars;
-will be respectively prepended and appended to
+are respectively prepended and appended to
each element in the list.
-If any element is a list or tuple,
-then the first item is the name being
-defined and the second item is its value:
+If any element is a tuple (or list)
+then the first item of the tuple is the macro name
+and the second is the macro definition.
+If the definition is not omitted or <literal>None</literal>,
+the name and definition are combined into a single
+<literal>name=definition</literal> item
+before the preending/appending.
</para>
<example_commands>
-# Will add -DB=2 -DA to POSIX compiler command lines,
+# Adds -DB=2 -DA to POSIX compiler command lines,
# and /DB=2 /DA to Microsoft Visual C++ command lines.
env = Environment(CPPDEFINES=[('B', 2), 'A'])
</example_commands>
+</listitem>
+<listitem>
<para>
If &cv-CPPDEFINES; is a dictionary,
the values of the
&cv-CPPDEFPREFIX; and &cv-CPPDEFSUFFIX; &consvars;
-will be respectively prepended and appended to
-each item from the dictionary.
-The key of each dictionary item
-is a name being defined
-to the dictionary item's corresponding value;
-if the value is
-<literal>None</literal>,
-then the name is defined without an explicit value.
-Note that the resulting flags are sorted by keyword
-to ensure that the order of the options on the
-command line is consistent each time
-&scons;
-is run.
+are respectively prepended and appended to
+each key from the dictionary.
+If the value for a key is not <literal>None</literal>,
+then the key (macro name) and the value
+(macros definition) are combined into a single
+<literal>name=definition</literal> item
+before the prepending/appending.
</para>
<example_commands>
-# Will add -DA -DB=2 to POSIX compiler command lines,
-# and /DA /DB=2 to Microsoft Visual C++ command lines.
+# Adds -DA -DB=2 to POSIX compiler command lines,
+# or /DA /DB=2 to Microsoft Visual C++ command lines.
env = Environment(CPPDEFINES={'B':2, 'A':None})
</example_commands>
+</listitem>
+</itemizedlist>
+
+<para>
+Depending on how contents are added to &cv-CPPDEFINES;,
+it may be transformed into a compound type,
+for example a list containing strings, tuples and/or dictionaries.
+&SCons; can correctly expand such a compound type.
+</para>
+
+<para>
+Note that &SCons; may call the compiler via a shell.
+If a macro definition contains characters such as spaces that
+have meaning to the shell, or is intended to be a string value,
+you may need to use the shell's quoting syntax to avoid
+interpretation by the shell before the preprocessor sees it.
+Function-like macros are not supported via this mechanism
+(and some compilers do not even implement that functionality
+via the command lines).
+When quoting, note that
+one set of quote characters are used to define a &Python; string,
+then quotes embedded inside that would be consumed by the shell
+unless escaped. These examples may help illustrate:
+</para>
+
+<example_commands>
+env = Environment(CPPDEFINES=['USE_ALT_HEADER=\\"foo_alt.h\\"'])
+env = Environment(CPPDEFINES=[('USE_ALT_HEADER', '\\"foo_alt.h\\"')])
+</example_commands>
+
+<para>
+:<emphasis>Changed in version 4.5</emphasis>:
+&SCons; no longer sorts &cv-CPPDEFINES; values entered
+in dictionary form. &Python; now preserves dictionary
+keys in the order they are entered, so it is no longer
+necessary to sort them to ensure a stable command line.
+</para>
+
</summary>
</cvar>
diff --git a/SCons/Environment.py b/SCons/Environment.py
index 7212c89ea..bc69f05cd 100644
--- a/SCons/Environment.py
+++ b/SCons/Environment.py
@@ -35,7 +35,7 @@ import os
import sys
import re
import shlex
-from collections import UserDict
+from collections import UserDict, deque
import SCons.Action
import SCons.Builder
@@ -193,6 +193,162 @@ def _delete_duplicates(l, keep_last):
return result
+def _add_cppdefines(
+ env_dict: dict,
+ val, # add annotation?
+ prepend: bool = False,
+ unique: bool = False,
+ delete_existing: bool = False,
+) -> None:
+ """Adds to CPPDEFINES, using the rules for C preprocessor macros.
+
+ Split out from regular construction variable handling because these
+ entries can express either a macro with a replacement list or one
+ without. A macro with replacement list can be supplied three ways:
+ as a combined string ``name=value``; as a tuple contained in
+ a sequence type ``[("name", value)]``; or as a dictionary entry
+ ``{"name": value}``. Appending/prepending can be unconditional
+ (duplicates allowed) or uniquing (no dupes).
+
+ Note if a replacement list is supplied, "unique" requires a full
+ match - both the name and the replacement must be equal.
+
+ Args:
+ env_dict: the dictionary containing the ``CPPDEFINES`` to be modified.
+ val: the value to add, can be string, sequence or dict
+ prepend: whether to put *val* in front or back.
+ unique: whether to add *val* if it already exists.
+ delete_existing: if *unique* is true, add *val* after removing previous.
+ """
+
+ def _add_define(item, defines: deque, prepend: bool = False) -> None:
+ """Convenience function to prepend/append a single value.
+
+ Sole purpose is to shorten code in the outer function.
+ """
+ if prepend:
+ defines.appendleft(item)
+ else:
+ defines.append(item)
+
+
+ def _is_in(item, defines: deque):
+ """Returns match for *item* if found in *defines*.
+
+ Accounts for type differences: ("FOO", "BAR"), ["FOO", "BAR"]
+ "FOO=BAR" and {"FOO": "BAR"} all iffer as far as Python equality
+ comparison is concerned, but are the same for purposes of creating
+ the preprocessor macro. Since the caller may wish to remove a
+ matched entry, we need to return it - cannot remove *item*
+ itself unless it happened to be an exact (type) match.
+
+ Called from a place we know *defines* is always a deque, and
+ *item* will not be a dict, so don't need do much type checking.
+ If this ends up used more generally, would need to adjust that.
+
+ Note implied assumption that members of a list-valued define
+ will not be dicts - we cannot actually guarantee this, since
+ if the initial add is a list its contents are not converted.
+ """
+ def _macro_conv(v) -> list:
+ """Normalizes a macro to a list for comparisons."""
+ if is_Tuple(v):
+ return list(v)
+ elif is_String(v):
+ return v.split("=")
+ return v
+
+ if item in defines: # cheap check first
+ return item
+
+ item = _macro_conv(item)
+ for define in defines:
+ if item == _macro_conv(define):
+ return define
+
+ return False
+
+
+ key = 'CPPDEFINES'
+ try:
+ defines = env_dict[key]
+ except KeyError:
+ # This is a new entry, just save it as is. Defer conversion to
+ # deque until someone tries to amend the value, processDefines
+ # can handle all of these fine.
+ if is_String(val):
+ env_dict[key] = val.split()
+ else:
+ env_dict[key] = val
+ return
+
+ # Convert type of existing to deque to simplify processing of addition -
+ # inserting at either end is cheap.
+ if isinstance(defines, deque):
+ # filter deques out to avoid catching in is_List check below
+ pass
+ elif is_String(defines):
+ env_dict[key] = deque(defines.split())
+ elif is_Tuple(defines) or is_List(defines):
+ # a little extra work in case the initial container has dict
+ # item(s) inside it, so those can be matched by _is_in().
+ result = deque()
+ for define in defines:
+ if is_Dict(define):
+ result.extend(define.items())
+ else:
+ result.append(define)
+ env_dict[key] = result
+ elif is_Dict(defines):
+ env_dict[key] = deque(defines.items())
+ else:
+ env_dict[key] = deque(defines)
+ defines = env_dict[key] # in case we reassigned it after the try block.
+
+ if is_Dict(val):
+ # Unpack the dict while applying to existing
+ for item in val.items():
+ if unique:
+ match = _is_in(item, defines)
+ if match and delete_existing:
+ defines.remove(match)
+ _add_define(item, defines, prepend)
+ elif not match:
+ _add_define(item, defines, prepend)
+ else:
+ _add_define(item, defines, prepend)
+
+ elif is_String(val):
+ if unique:
+ match = _is_in(val, defines)
+ if match and delete_existing:
+ defines.remove(match)
+ _add_define(val, defines, prepend)
+ elif not match:
+ _add_define(val, defines, prepend)
+ else:
+ _add_define(val, defines, prepend)
+
+ elif is_List(val):
+ tmp = []
+ for item in val:
+ if unique:
+ match = _is_in(item, defines)
+ if match and delete_existing:
+ defines.remove(match)
+ tmp.append(item)
+ elif not match:
+ tmp.append(item)
+ else:
+ tmp.append(item)
+
+ if prepend:
+ defines.extendleft(tmp)
+ else:
+ defines.extend(tmp)
+
+ # else: # are there any other cases? processDefines doesn't think so.
+
# The following is partly based on code in a comment added by Peter
# Shannon at the following page (there called the "transplant" class):
@@ -837,8 +993,8 @@ class SubstitutionEnvironment:
def MergeFlags(self, args, unique=True) -> None:
"""Merge flags into construction variables.
- Merges the flags from ``args`` into this construction environent.
- If ``args`` is not a dict, it is first converted to one with
+ Merges the flags from *args* into this construction environent.
+ If *args* is not a dict, it is first converted to one with
flags distributed into appropriate construction variables.
See :meth:`ParseFlags`.
@@ -1215,16 +1371,15 @@ class Base(SubstitutionEnvironment):
kw = copy_non_reserved_keywords(kw)
for key, val in kw.items():
+ if key == 'CPPDEFINES':
+ _add_cppdefines(self._dict, val)
+ continue
+
try:
- if key == 'CPPDEFINES' and is_String(self._dict[key]):
- self._dict[key] = [self._dict[key]]
orig = self._dict[key]
except KeyError:
# No existing var in the environment, so set to the new value.
- if key == 'CPPDEFINES' and is_String(val):
- self._dict[key] = [val]
- else:
- self._dict[key] = val
+ self._dict[key] = val
continue
try:
@@ -1263,19 +1418,8 @@ class Base(SubstitutionEnvironment):
# things like UserList will incorrectly coerce the
# original dict to a list (which we don't want).
if is_List(val):
- if key == 'CPPDEFINES':
- tmp = []
- for (k, v) in orig.items():
- if v is not None:
- tmp.append((k, v))
- else:
- tmp.append((k,))
- orig = tmp
- orig += val
- self._dict[key] = orig
- else:
- for v in val:
- orig[v] = None
+ for v in val:
+ orig[v] = None
else:
try:
update_dict(val)
@@ -1330,6 +1474,9 @@ class Base(SubstitutionEnvironment):
"""
kw = copy_non_reserved_keywords(kw)
for key, val in kw.items():
+ if key == 'CPPDEFINES':
+ _add_cppdefines(self._dict, val, unique=True, delete_existing=delete_existing)
+ continue
if is_List(val):
val = _delete_duplicates(val, delete_existing)
if key not in self._dict or self._dict[key] in ('', None):
@@ -1338,46 +1485,8 @@ class Base(SubstitutionEnvironment):
self._dict[key].update(val)
elif is_List(val):
dk = self._dict[key]
- if key == 'CPPDEFINES':
- tmp = []
- for i in val:
- if is_List(i):
- if len(i) >= 2:
- tmp.append((i[0], i[1]))
- else:
- tmp.append((i[0],))
- elif is_Tuple(i):
- tmp.append(i)
- else:
- tmp.append((i,))
- val = tmp
- # Construct a list of (key, value) tuples.
- if is_Dict(dk):
- tmp = []
- for (k, v) in dk.items():
- if v is not None:
- tmp.append((k, v))
- else:
- tmp.append((k,))
- dk = tmp
- elif is_String(dk):
- dk = [(dk,)]
- else:
- tmp = []
- for i in dk:
- if is_List(i):
- if len(i) >= 2:
- tmp.append((i[0], i[1]))
- else:
- tmp.append((i[0],))
- elif is_Tuple(i):
- tmp.append(i)
- else:
- tmp.append((i,))
- dk = tmp
- else:
- if not is_List(dk):
- dk = [dk]
+ if not is_List(dk):
+ dk = [dk]
if delete_existing:
dk = [x for x in dk if x not in val]
else:
@@ -1386,70 +1495,15 @@ class Base(SubstitutionEnvironment):
else:
dk = self._dict[key]
if is_List(dk):
- if key == 'CPPDEFINES':
- tmp = []
- for i in dk:
- if is_List(i):
- if len(i) >= 2:
- tmp.append((i[0], i[1]))
- else:
- tmp.append((i[0],))
- elif is_Tuple(i):
- tmp.append(i)
- else:
- tmp.append((i,))
- dk = tmp
- # Construct a list of (key, value) tuples.
- if is_Dict(val):
- tmp = []
- for (k, v) in val.items():
- if v is not None:
- tmp.append((k, v))
- else:
- tmp.append((k,))
- val = tmp
- elif is_String(val):
- val = [(val,)]
- if delete_existing:
- dk = list(filter(lambda x, val=val: x not in val, dk))
- self._dict[key] = dk + val
- else:
- dk = [x for x in dk if x not in val]
- self._dict[key] = dk + val
+ # By elimination, val is not a list. Since dk is a
+ # list, wrap val in a list first.
+ if delete_existing:
+ dk = list(filter(lambda x, val=val: x not in val, dk))
+ self._dict[key] = dk + [val]
else:
- # By elimination, val is not a list. Since dk is a
- # list, wrap val in a list first.
- if delete_existing:
- dk = list(filter(lambda x, val=val: x not in val, dk))
+ if val not in dk:
self._dict[key] = dk + [val]
- else:
- if val not in dk:
- self._dict[key] = dk + [val]
else:
- if key == 'CPPDEFINES':
- if is_String(dk):
- dk = [dk]
- elif is_Dict(dk):
- tmp = []
- for (k, v) in dk.items():
- if v is not None:
- tmp.append((k, v))
- else:
- tmp.append((k,))
- dk = tmp
- if is_String(val):
- if val in dk:
- val = []
- else:
- val = [val]
- elif is_Dict(val):
- tmp = []
- for i,j in val.items():
- if j is not None:
- tmp.append((i,j))
- else:
- tmp.append(i)
- val = tmp
if delete_existing:
dk = [x for x in dk if x not in val]
self._dict[key] = dk + val
@@ -1726,6 +1780,9 @@ class Base(SubstitutionEnvironment):
kw = copy_non_reserved_keywords(kw)
for key, val in kw.items():
+ if key == 'CPPDEFINES':
+ _add_cppdefines(self._dict, val, prepend=True)
+ continue
try:
orig = self._dict[key]
except KeyError:
@@ -1815,6 +1872,9 @@ class Base(SubstitutionEnvironment):
"""
kw = copy_non_reserved_keywords(kw)
for key, val in kw.items():
+ if key == 'CPPDEFINES':
+ _add_cppdefines(self._dict, val, unique=True, prepend=True, delete_existing=delete_existing)
+ continue
if is_List(val):
val = _delete_duplicates(val, not delete_existing)
if key not in self._dict or self._dict[key] in ('', None):
diff --git a/SCons/Environment.xml b/SCons/Environment.xml
index 2e06b1e65..3790a225b 100644
--- a/SCons/Environment.xml
+++ b/SCons/Environment.xml
@@ -499,126 +499,191 @@ Multiple targets can be passed in to a single call to
</arguments>
<summary>
<para>
-Intelligently append values to &consvars; in the &consenv;
-named by <varname>env</varname>.
+Appends value(s) intelligently to &consvars; in
+<varname>env</varname>.
The &consvars; and values to add to them are passed as
<parameter>key=val</parameter> pairs (&Python; keyword arguments).
&f-env-Append; is designed to allow adding values
-without normally having to know the data type of an existing &consvar;.
+without having to think about the data type of an existing &consvar;.
Regular &Python; syntax can also be used to manipulate the &consvar;,
-but for that you must know the type of the &consvar;:
-for example, different &Python; syntax is needed to combine
-a list of values with a single string value, or vice versa.
+but for that you may need to know the types involved,
+for example pure &Python; lets you directly "add" two lists of strings,
+but adding a string to a list or a list to a string requires
+different syntax - things &f-Append; takes care of.
Some pre-defined &consvars; do have type expectations
-based on how &SCons; will use them,
+based on how &SCons; will use them:
for example &cv-link-CPPDEFINES; is normally a string or a list of strings,
-but can be a string,
-a list of strings,
-a list of tuples,
-or a dictionary, while &cv-link-LIBEMITTER;
-would expect a callable or list of callables,
-and &cv-link-BUILDERS; would expect a mapping type.
+but can also be a list of tuples or a dictionary;
+while &cv-link-LIBEMITTER;
+is expected to be a callable or list of callables,
+and &cv-link-BUILDERS; is expected to be a dictionary.
Consult the documentation for the various &consvars; for more details.
</para>
<para>
-The following descriptions apply to both the append
-and prepend functions, the only difference being
-the insertion point of the added values.
-</para>
-<para>
-If <varname>env</varname>. does not have a &consvar;
-indicated by <parameter>key</parameter>,
-<parameter>val</parameter>
-is added to the environment under that key as-is.
-</para>
-
-<para>
-<parameter>val</parameter> can be almost any type,
-and &SCons; will combine it with an existing value into an appropriate type,
-but there are a few special cases to be aware of.
-When two strings are combined,
-the result is normally a new string,
-with the caller responsible for supplying any needed separation.
-The exception to this is the &consvar; &cv-link-CPPDEFINES;,
-in which each item will be postprocessed by adding a prefix
-and/or suffix,
-so the contents are treated as a list of strings, that is,
-adding a string will result in a separate string entry,
-not a combined string. For &cv-CPPDEFINES; as well as
-for &cv-link-LIBS;, and the various <literal>*PATH</literal>;
-variables, &SCons; will supply the compiler-specific
-syntax (e.g. adding a <literal>-D</literal> or <literal>/D</literal>
-prefix for &cv-CPPDEFINES;), so this syntax should be omitted when
+The following descriptions apply to both the &f-Append;
+and &f-Prepend; methods, as well as their
+<emphasis role="bold">Unique</emphasis> variants,
+with the differences being the insertion point of the added values
+and whether duplication is allowed.
+</para>
+
+<para>
+<parameter>val</parameter> can be almost any type.
+If <varname>env</varname> does not have a &consvar;
+named <parameter>key</parameter>,
+then <parameter>key</parameter> is simply
+stored with a value of <parameter>val</parameter>.
+Otherwise, <parameter>val</parameter> is
+combinined with the existing value,
+possibly converting into an appropriate type
+which can hold the expanded contents.
+There are a few special cases to be aware of.
+Normally, when two strings are combined,
+the result is a new string containing their concatenation
+(and you are responsible for supplying any needed separation);
+however, the contents of &cv-link-CPPDEFINES; will
+will be postprocessed by adding a prefix and/or suffix
+to each entry when the command line is produced,
+so &SCons; keeps them separate -
+appending a string will result in a separate string entry,
+not a combined string.
+For &cv-CPPDEFINES;. as well as
+&cv-link-LIBS;, and the various <literal>*PATH</literal> variables,
+&SCons; will amend the variable by supplying the compiler-specific
+syntax (e.g. prepending a <literal>-D</literal> or <literal>/D</literal>
+prefix for &cv-CPPDEFINES;), so you should omit this syntax when
adding values to these variables.
-Example (gcc syntax shown in the expansion of &CPPDEFINES;):
+Examples (gcc syntax shown in the expansion of &CPPDEFINES;):
</para>
<example_commands>
env = Environment(CXXFLAGS="-std=c11", CPPDEFINES="RELEASE")
-print("CXXFLAGS={}, CPPDEFINES={}".format(env['CXXFLAGS'], env['CPPDEFINES']))
-# notice including a leading space in CXXFLAGS value
+print(f"CXXFLAGS = {env['CXXFLAGS']}, CPPDEFINES = {env['CPPDEFINES']}")
+# notice including a leading space in CXXFLAGS addition
env.Append(CXXFLAGS=" -O", CPPDEFINES="EXTRA")
-print("CXXFLAGS={}, CPPDEFINES={}".format(env['CXXFLAGS'], env['CPPDEFINES']))
-print("CPPDEFINES will expand to {}".format(env.subst("$_CPPDEFFLAGS")))
+print(f"CXXFLAGS = {env['CXXFLAGS']}, CPPDEFINES = {env['CPPDEFINES']}")
+print("CPPDEFINES will expand to", env.subst('$_CPPDEFFLAGS'))
</example_commands>
<screen>
$ scons -Q
-CXXFLAGS=-std=c11, CPPDEFINES=RELEASE
-CXXFLAGS=-std=c11 -O, CPPDEFINES=['RELEASE', 'EXTRA']
-CPPDEFINES will expand to -DRELEASE -DEXTRA
+CXXFLAGS = -std=c11, CPPDEFINES = RELEASE
+CXXFLAGS = -std=c11 -O, CPPDEFINES = deque(['RELEASE', 'EXTRA'])
+CPPDEFINES will expand to -DRELEASE -DEXTRA
scons: `.' is up to date.
</screen>
<para>
-Because &cv-link-CPPDEFINES; is intended to
-describe C/C++ pre-processor macro definitions,
+Because &cv-link-CPPDEFINES; is intended for command-line
+specification of C/C++ preprocessor macros,
it accepts additional syntax.
-Preprocessor macros can be valued, or un-valued, as in
-<computeroutput>-DBAR=1</computeroutput> or
-<computeroutput>-DFOO</computeroutput>.
-The macro can be be supplied as a complete string including the value,
-or as a tuple (or list) of macro, value, or as a dictionary.
-Example (again gcc syntax in the expanded defines):
+A command-line preprocessor macro can predefine a name by itself
+(<computeroutput>-DFOO</computeroutput>),
+which gives it an implicit value,
+or be given with a macro definition
+(<computeroutput>-DBAR=1</computeroutput>).
+&SCons; allows you to specify a macro with a definition
+using a <literal>name=value</literal> string,
+or a tuple <literal>(name, value)</literal>
+(which must be supplied inside a sequence type),
+or a dictionary <literal>{name: value}</literal>.
</para>
<example_commands>
env = Environment(CPPDEFINES="FOO")
-print("CPPDEFINES={}".format(env['CPPDEFINES']))
+print("CPPDEFINES =", env['CPPDEFINES'])
env.Append(CPPDEFINES="BAR=1")
-print("CPPDEFINES={}".format(env['CPPDEFINES']))
-env.Append(CPPDEFINES=("OTHER", 2))
-print("CPPDEFINES={}".format(env['CPPDEFINES']))
+print("CPPDEFINES =", env['CPPDEFINES'])
+env.Append(CPPDEFINES=[("OTHER", 2)])
+print("CPPDEFINES =", env['CPPDEFINES'])
env.Append(CPPDEFINES={"EXTRA": "arg"})
-print("CPPDEFINES={}".format(env['CPPDEFINES']))
-print("CPPDEFINES will expand to {}".format(env.subst("$_CPPDEFFLAGS")))
+print("CPPDEFINES =", env['CPPDEFINES'])
+print("CPPDEFINES will expand to", env.subst('$_CPPDEFFLAGS'))
</example_commands>
<screen>
$ scons -Q
-CPPDEFINES=FOO
-CPPDEFINES=['FOO', 'BAR=1']
-CPPDEFINES=['FOO', 'BAR=1', ('OTHER', 2)]
-CPPDEFINES=['FOO', 'BAR=1', ('OTHER', 2), {'EXTRA': 'arg'}]
+CPPDEFINES = FOO
+CPPDEFINES = deque(['FOO', 'BAR=1'])
+CPPDEFINES = deque(['FOO', 'BAR=1', ('OTHER', 2)])
+CPPDEFINES = deque(['FOO', 'BAR=1', ('OTHER', 2), ('EXTRA', 'arg')])
CPPDEFINES will expand to -DFOO -DBAR=1 -DOTHER=2 -DEXTRA=arg
scons: `.' is up to date.
</screen>
<para>
-Adding a string <parameter>val</parameter>
-to a dictonary &consvar; will enter
-<parameter>val</parameter> as the key in the dict,
+Multiple &cv-CPPDEFINES; macros can be supplied in a sequence of tuples,
+or using the dictionary form.
+If a given macro name should not have a definition,
+omit the value from tuple or give it as
+<constant>None</constant>;
+if using the dictionary form,
+specify the value as <constant>None</constant>.
+</para>
+
+<example_commands>
+env = Environment()
+env.Append(CPPDEFINES=[("ONE", 1), ("TWO", )])
+print("CPPDEFINES =", env['CPPDEFINES'])
+env.Append(CPPDEFINES={"THREE": 3, "FOUR": None})
+print("CPPDEFINES =", env['CPPDEFINES'])
+print("CPPDEFINES will expand to", env.subst('$_CPPDEFFLAGS'))
+</example_commands>
+
+<screen>
+$ scons -Q
+CPPDEFINES = [('ONE', 1), ('TWO',)]
+CPPDEFINES = [('ONE', 1), ('TWO',), {'THREE': 3, 'FOUR': None}]
+CPPDEFINES will expand to -DONE=1 -DTWO -DTHREE=3 -DFOUR
+scons: `.' is up to date.
+</screen>
+
+<para>
+<emphasis>Changed in version 4.5</emphasis>:
+clarified that to receive the special handling,
+the tuple form must be supplied <emphasis>inside</emphasis>
+a sequence (e.g. a list); a single tuple will be interpreted
+just like a single list.
+Previously this form was inconsistently interpreted by
+various &SCons; methods.
+</para>
+
+<example_commands>
+env = Environment()
+env.Append(CPPDEFINES=("MACRO1", "MACRO2"))
+print("CPPDEFINES =", env['CPPDEFINES'])
+env.Append(CPPDEFINES=[("MACRO3", "MACRO4")])
+print("CPPDEFINES =", env['CPPDEFINES'])
+print("CPPDEFINES will expand to", env.subst('$_CPPDEFFLAGS'))
+</example_commands>
+
+<screen>
+$ scons -Q
+CPPDEFINES = ('MACRO1', 'MACRO2')
+CPPDEFINES = deque(['MACRO1', 'MACRO2', ('MACRO3', 'MACRO4')])
+CPPDEFINES will expand to -DMACRO1 -DMACRO2 -DMACRO3=MACRO4
+scons: `.' is up to date.
+</screen>
+
+<para>
+See &cv-link-CPPDEFINES; for more details.
+</para>
+
+<para>
+Appending a string <parameter>val</parameter>
+to a dictonary-typed &consvar; enters
+<parameter>val</parameter> as the key in the dictionary,
and <literal>None</literal> as its value.
-Using a tuple type to supply a key + value only works
-for the special case of &cv-link-CPPDEFINES;
+Using a tuple type to supply a <literal>key, value</literal>
+only works for the special case of &cv-CPPDEFINES;
described above.
</para>
<para>
Although most combinations of types work without
needing to know the details, some combinations
-do not make sense and a &Python; exception will be raised.
+do not make sense and &Python; raises an exception.
</para>
<para>
@@ -626,7 +691,7 @@ When using &f-env-Append; to modify &consvars;
which are path specifications (conventionally,
the names of such end in <literal>PATH</literal>),
it is recommended to add the values as a list of strings,
-even if there is only a single string to add.
+even if you are only adding a single string.
The same goes for adding library names to &cv-LIBS;.
</para>
@@ -696,20 +761,20 @@ See also &f-link-env-PrependENVPath;.
<scons_function name="AppendUnique">
<arguments signature="env">
-(key=val, [...], delete_existing=False)
+(key=val, [...], [delete_existing=False])
</arguments>
<summary>
<para>
Append values to &consvars; in the current &consenv;,
maintaining uniqueness.
-Works like &f-link-env-Append; (see for details),
-except that values already present in the &consvar;
-will not be added again.
+Works like &f-link-env-Append;,
+except that values that would become duplicates
+are not added.
If <parameter>delete_existing</parameter>
-is <constant>True</constant>,
-the existing matching value is first removed,
-and the requested value is added,
-having the effect of moving such values to the end.
+is set to a true value, then for any duplicate,
+the existing instance of <parameter>val</parameter> is first removed,
+then <parameter>val</parameter> is appended,
+having the effect of moving it to the end.
</para>
<para>
@@ -2716,22 +2781,22 @@ See also &f-link-env-AppendENVPath;.
<scons_function name="PrependUnique">
<arguments signature="env">
-(key=val, delete_existing=False, [...])
+(key=val, [...], [delete_existing=False])
</arguments>
<summary>
<para>
Prepend values to &consvars; in the current &consenv;,
maintaining uniqueness.
-Works like &f-link-env-Append; (see for details),
+Works like &f-link-env-Append;,
except that values are added to the front,
-rather than the end, of any existing value of the &consvar;,
-and values already present in the &consvar;
-will not be added again.
+rather than the end, of the &consvar;,
+and values that would become duplicates
+are not added.
If <parameter>delete_existing</parameter>
-is <constant>True</constant>,
-the existing matching value is first removed,
-and the requested value is inserted,
-having the effect of moving such values to the front.
+is set to a true value, then for any duplicate,
+the existing instance of <parameter>val</parameter> is first removed,
+then <parameter>val</parameter> is inserted,
+having the effect of moving it to the front.
</para>
<para>
diff --git a/SCons/Util/types.py b/SCons/Util/types.py
index 9aef13ef9..1602055d6 100644
--- a/SCons/Util/types.py
+++ b/SCons/Util/types.py
@@ -12,7 +12,7 @@ import pprint
import re
from typing import Optional
-from collections import UserDict, UserList, UserString
+from collections import UserDict, UserList, UserString, deque
from collections.abc import MappingView
# Functions for deciding if things are like various types, mainly to
@@ -23,20 +23,22 @@ from collections.abc import MappingView
# exception, but handling the exception when it's not the right type is
# often too slow.
-# We are using the following trick to speed up these
-# functions. Default arguments are used to take a snapshot of
-# the global functions and constants used by these functions. This
-# transforms accesses to global variable into local variables
-# accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL).
-# Since checkers dislike this, it's now annotated for pylint to flag
+# A trick is used to speed up these functions. Default arguments are
+# used to take a snapshot of the global functions and constants used
+# by these functions. This transforms accesses to global variables into
+# local variable accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL).
+# Since checkers dislike this, it's now annotated for pylint, to flag
# (mostly for other readers of this code) we're doing this intentionally.
-# TODO: PY3 check these are still valid choices for all of these funcs.
+# TODO: experts affirm this is still faster, but maybe check if worth it?
DictTypes = (dict, UserDict)
-ListTypes = (list, UserList)
+ListTypes = (list, UserList, deque)
-# Handle getting dictionary views.
-SequenceTypes = (list, tuple, UserList, MappingView)
+# With Python 3, there are view types that are sequences. Other interesting
+# sequences are range and bytearray. What we don't want is strings: while
+# they are iterable sequences, in SCons usage iterating over a string is
+# almost never what we want. So basically iterable-but-not-string:
+SequenceTypes = (list, tuple, deque, UserList, MappingView)
# Note that profiling data shows a speed-up when comparing
# explicitly with str instead of simply comparing
diff --git a/test/CPPDEFINES/append.py b/test/CPPDEFINES/append.py
index 874fceb7d..4c5e562c1 100644
--- a/test/CPPDEFINES/append.py
+++ b/test/CPPDEFINES/append.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify appending to CPPPDEFINES with various data types.
@@ -33,115 +32,122 @@ import TestSCons
test = TestSCons.TestSCons()
-# Note: we explicitly set CPPDEFPREFIX here to simplify testing on
-# Windows.
-
-test.write('SConstruct', """\
-env_1738_2 = Environment(CPPDEFPREFIX='-D')
-env_1738_2['CPPDEFINES'] = ['FOO']
-env_1738_2.Append(CPPDEFINES={'value' : '1'})
-print(env_1738_2.subst('$_CPPDEFFLAGS'))
-#env_1738_2.Object('test_1738_2', 'main.c')
-
-# https://github.com/SCons/scons/issues/2300
-env_2300_1 = Environment(CPPDEFINES = 'foo', CPPDEFPREFIX='-D')
-env_2300_1.Append(CPPDEFINES='bar')
-print(env_2300_1.subst('$_CPPDEFFLAGS'))
-
-env_2300_2 = Environment(CPPDEFINES = ['foo'], CPPDEFPREFIX='-D') # note the list
-env_2300_2.Append(CPPDEFINES='bar')
-print(env_2300_2.subst('$_CPPDEFFLAGS'))
-
-# https://github.com/SCons/scons/issues/1152
-# https://github.com/SCons/scons/issues/2900
-# Python3 dicts dont preserve order. Hence we supply subclass of OrderedDict
-# whose __str__ and __repr__ act like a normal dict.
-from collections import OrderedDict
-class OrderedPrintingDict(OrderedDict):
- def __repr__(self):
- return '{' + ', '.join(['%r: %r'%(k, v) for (k, v) in self.items()]) + '}'
-
- __str__ = __repr__
-
- # Because dict-like objects (except dict and UserDict) are not deep copied
- # directly when constructing Environment(CPPDEFINES = OrderedPrintingDict(...))
- def __semi_deepcopy__(self):
- return self.copy()
-
-cases=[('string', 'FOO'),
- ('list', ['NAME1', 'NAME2']),
- ('list-of-2lists', [('NAME1','VAL1'), ['NAME2','VAL2']]),
- ('dict', OrderedPrintingDict([('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]))
- ]
-
-for (t1, c1) in cases:
- for (t2, c2) in cases:
- print("==== Testing CPPDEFINES, appending a %s to a %s"%(t2, t1))
- print(" orig = %s, append = %s"%(c1, c2))
- env=Environment(CPPDEFINES = c1, CPPDEFPREFIX='-D')
- env.Append(CPPDEFINES = c2)
- final=env.subst('$_CPPDEFFLAGS',source="src", target="tgt")
- print('Append:\\n\\tresult=%s\\n\\tfinal=%s'%\\
- (env['CPPDEFINES'], final))
- env=Environment(CPPDEFINES = c1, CPPDEFPREFIX='-D')
- env.AppendUnique(CPPDEFINES = c2)
- final=env.subst('$_CPPDEFFLAGS',source="src", target="tgt")
- print('AppendUnique:\\n\\tresult=%s\\n\\tfinal=%s'%\\
- (env['CPPDEFINES'], final))
-""")
+# Note: explicitly set CPPDEFPREFIX here to simplify testing on Windows.
+# Link: fixture/SConstruct-Append
+test.file_fixture(["fixture", "SConstruct-Append"], "SConstruct")
expect_print_output="""\
-DFOO -Dvalue=1
-Dfoo -Dbar
-Dfoo -Dbar
+-Dfoo -Dbar -Dbaz
+-Dfoo bar -Dbaz
+-Dfoo -Dbar baz
+-DMacro2=Value2 -DMacro3=Value3 -DMacro1=Value1
==== Testing CPPDEFINES, appending a string to a string
- orig = FOO, append = FOO
+ orig = 'FOO', append = 'FOO'
Append:
result=['FOO', 'FOO']
final=-DFOO -DFOO
AppendUnique:
result=['FOO']
final=-DFOO
+==== Testing CPPDEFINES, appending a valuestring to a string
+ orig = 'FOO', append = 'NAME1=VAL1'
+Append:
+ result=['FOO', 'NAME1=VAL1']
+ final=-DFOO -DNAME1=VAL1
+AppendUnique:
+ result=['FOO', 'NAME1=VAL1']
+ final=-DFOO -DNAME1=VAL1
==== Testing CPPDEFINES, appending a list to a string
- orig = FOO, append = ['NAME1', 'NAME2']
+ orig = 'FOO', append = ['NAME1', 'NAME2']
Append:
result=['FOO', 'NAME1', 'NAME2']
final=-DFOO -DNAME1 -DNAME2
AppendUnique:
- result=[('FOO',), ('NAME1',), ('NAME2',)]
+ result=['FOO', 'NAME1', 'NAME2']
final=-DFOO -DNAME1 -DNAME2
==== Testing CPPDEFINES, appending a list-of-2lists to a string
- orig = FOO, append = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ orig = 'FOO', append = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
Append:
result=['FOO', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
final=-DFOO -DNAME1=VAL1 -DNAME2=VAL2
AppendUnique:
- result=[('FOO',), ('NAME1', 'VAL1'), ('NAME2', 'VAL2')]
+ result=['FOO', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
final=-DFOO -DNAME1=VAL1 -DNAME2=VAL2
==== Testing CPPDEFINES, appending a dict to a string
- orig = FOO, append = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
+ orig = 'FOO', append = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
Append:
- result=['FOO', {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}]
+ result=['FOO', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
final=-DFOO -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
AppendUnique:
- result=['FOO', ('NAME2', 'VAL2'), 'NAME3', ('NAME1', 'VAL1')]
+ result=['FOO', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
final=-DFOO -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+==== Testing CPPDEFINES, appending a string to a valuestring
+ orig = 'NAME1=VAL1', append = 'FOO'
+Append:
+ result=['NAME1=VAL1', 'FOO']
+ final=-DNAME1=VAL1 -DFOO
+AppendUnique:
+ result=['NAME1=VAL1', 'FOO']
+ final=-DNAME1=VAL1 -DFOO
+==== Testing CPPDEFINES, appending a valuestring to a valuestring
+ orig = 'NAME1=VAL1', append = 'NAME1=VAL1'
+Append:
+ result=['NAME1=VAL1', 'NAME1=VAL1']
+ final=-DNAME1=VAL1 -DNAME1=VAL1
+AppendUnique:
+ result=['NAME1=VAL1']
+ final=-DNAME1=VAL1
+==== Testing CPPDEFINES, appending a list to a valuestring
+ orig = 'NAME1=VAL1', append = ['NAME1', 'NAME2']
+Append:
+ result=['NAME1=VAL1', 'NAME1', 'NAME2']
+ final=-DNAME1=VAL1 -DNAME1 -DNAME2
+AppendUnique:
+ result=['NAME1=VAL1', 'NAME1', 'NAME2']
+ final=-DNAME1=VAL1 -DNAME1 -DNAME2
+==== Testing CPPDEFINES, appending a list-of-2lists to a valuestring
+ orig = 'NAME1=VAL1', append = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+Append:
+ result=['NAME1=VAL1', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME1=VAL1 -DNAME1=VAL1 -DNAME2=VAL2
+AppendUnique:
+ result=['NAME1=VAL1', ['NAME2', 'VAL2']]
+ final=-DNAME1=VAL1 -DNAME2=VAL2
+==== Testing CPPDEFINES, appending a dict to a valuestring
+ orig = 'NAME1=VAL1', append = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
+Append:
+ result=['NAME1=VAL1', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+AppendUnique:
+ result=['NAME1=VAL1', ('NAME2', 'VAL2'), ('NAME3', None)]
+ final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME3
==== Testing CPPDEFINES, appending a string to a list
- orig = ['NAME1', 'NAME2'], append = FOO
+ orig = ['NAME1', 'NAME2'], append = 'FOO'
Append:
result=['NAME1', 'NAME2', 'FOO']
final=-DNAME1 -DNAME2 -DFOO
AppendUnique:
- result=[('NAME1',), ('NAME2',), ('FOO',)]
+ result=['NAME1', 'NAME2', 'FOO']
final=-DNAME1 -DNAME2 -DFOO
+==== Testing CPPDEFINES, appending a valuestring to a list
+ orig = ['NAME1', 'NAME2'], append = 'NAME1=VAL1'
+Append:
+ result=['NAME1', 'NAME2', 'NAME1=VAL1']
+ final=-DNAME1 -DNAME2 -DNAME1=VAL1
+AppendUnique:
+ result=['NAME1', 'NAME2', 'NAME1=VAL1']
+ final=-DNAME1 -DNAME2 -DNAME1=VAL1
==== Testing CPPDEFINES, appending a list to a list
orig = ['NAME1', 'NAME2'], append = ['NAME1', 'NAME2']
Append:
result=['NAME1', 'NAME2', 'NAME1', 'NAME2']
final=-DNAME1 -DNAME2 -DNAME1 -DNAME2
AppendUnique:
- result=[('NAME1',), ('NAME2',)]
+ result=['NAME1', 'NAME2']
final=-DNAME1 -DNAME2
==== Testing CPPDEFINES, appending a list-of-2lists to a list
orig = ['NAME1', 'NAME2'], append = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
@@ -149,31 +155,39 @@ Append:
result=['NAME1', 'NAME2', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
final=-DNAME1 -DNAME2 -DNAME1=VAL1 -DNAME2=VAL2
AppendUnique:
- result=[('NAME1',), ('NAME2',), ('NAME1', 'VAL1'), ('NAME2', 'VAL2')]
+ result=['NAME1', 'NAME2', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
final=-DNAME1 -DNAME2 -DNAME1=VAL1 -DNAME2=VAL2
==== Testing CPPDEFINES, appending a dict to a list
orig = ['NAME1', 'NAME2'], append = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
Append:
- result=['NAME1', 'NAME2', {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}]
+ result=['NAME1', 'NAME2', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
final=-DNAME1 -DNAME2 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
AppendUnique:
- result=[('NAME1',), ('NAME2',), ('NAME2', 'VAL2'), ('NAME3',), ('NAME1', 'VAL1')]
+ result=['NAME1', 'NAME2', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
final=-DNAME1 -DNAME2 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
==== Testing CPPDEFINES, appending a string to a list-of-2lists
- orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], append = FOO
+ orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], append = 'FOO'
Append:
result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2'], 'FOO']
final=-DNAME1=VAL1 -DNAME2=VAL2 -DFOO
AppendUnique:
- result=[('NAME1', 'VAL1'), ('NAME2', 'VAL2'), ('FOO',)]
+ result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2'], 'FOO']
final=-DNAME1=VAL1 -DNAME2=VAL2 -DFOO
+==== Testing CPPDEFINES, appending a valuestring to a list-of-2lists
+ orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], append = 'NAME1=VAL1'
+Append:
+ result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2'], 'NAME1=VAL1']
+ final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME1=VAL1
+AppendUnique:
+ result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME1=VAL1 -DNAME2=VAL2
==== Testing CPPDEFINES, appending a list to a list-of-2lists
orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], append = ['NAME1', 'NAME2']
Append:
result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2'], 'NAME1', 'NAME2']
final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME1 -DNAME2
AppendUnique:
- result=[('NAME1', 'VAL1'), ('NAME2', 'VAL2'), ('NAME1',), ('NAME2',)]
+ result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2'], 'NAME1', 'NAME2']
final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME1 -DNAME2
==== Testing CPPDEFINES, appending a list-of-2lists to a list-of-2lists
orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], append = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
@@ -181,55 +195,61 @@ Append:
result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2'], ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME1=VAL1 -DNAME2=VAL2
AppendUnique:
- result=[('NAME1', 'VAL1'), ('NAME2', 'VAL2')]
+ result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
final=-DNAME1=VAL1 -DNAME2=VAL2
==== Testing CPPDEFINES, appending a dict to a list-of-2lists
orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], append = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
Append:
- result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2'], {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}]
+ result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2'], ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
AppendUnique:
- result=[('NAME2', 'VAL2'), ('NAME3',), ('NAME1', 'VAL1')]
- final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+ result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2'], ('NAME3', None)]
+ final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME3
==== Testing CPPDEFINES, appending a string to a dict
- orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, append = FOO
+ orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, append = 'FOO'
Append:
- result={'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1', 'FOO': None}
- final=-DFOO -DNAME1=VAL1 -DNAME2=VAL2 -DNAME3
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1'), 'FOO']
+ final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1 -DFOO
AppendUnique:
- result=[('NAME2', 'VAL2'), ('NAME3',), ('NAME1', 'VAL1'), 'FOO']
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1'), 'FOO']
final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1 -DFOO
+==== Testing CPPDEFINES, appending a valuestring to a dict
+ orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, append = 'NAME1=VAL1'
+Append:
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1'), 'NAME1=VAL1']
+ final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1 -DNAME1=VAL1
+AppendUnique:
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
==== Testing CPPDEFINES, appending a list to a dict
orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, append = ['NAME1', 'NAME2']
Append:
- result=[('NAME2', 'VAL2'), ('NAME3',), ('NAME1', 'VAL1'), 'NAME1', 'NAME2']
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1'), 'NAME1', 'NAME2']
final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1 -DNAME1 -DNAME2
AppendUnique:
- result=[('NAME2', 'VAL2'), ('NAME3',), ('NAME1', 'VAL1'), ('NAME1',), ('NAME2',)]
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1'), 'NAME1', 'NAME2']
final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1 -DNAME1 -DNAME2
==== Testing CPPDEFINES, appending a list-of-2lists to a dict
orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, append = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
Append:
- result=[('NAME2', 'VAL2'), ('NAME3',), ('NAME1', 'VAL1'), ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1'), ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1 -DNAME1=VAL1 -DNAME2=VAL2
AppendUnique:
- result=[('NAME2', 'VAL2'), ('NAME3',), ('NAME1', 'VAL1')]
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
==== Testing CPPDEFINES, appending a dict to a dict
orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, append = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
Append:
- result={'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
- final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME3
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1'), ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
AppendUnique:
- result={'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
- final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME3
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
"""
build_output="scons: `.' is up to date.\n"
-
-expect = test.wrap_stdout(build_str=build_output,
- read_str = expect_print_output)
-test.run(arguments = '.', stdout=expect)
+expect = test.wrap_stdout(build_str=build_output, read_str=expect_print_output)
+test.run(arguments='.', stdout=expect)
test.pass_test()
# Local Variables:
diff --git a/test/CPPDEFINES/basic.py b/test/CPPDEFINES/basic.py
index 176f543de..b66ea8a87 100755
--- a/test/CPPDEFINES/basic.py
+++ b/test/CPPDEFINES/basic.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify basic use of CPPPDEFINES with various data types.
@@ -37,7 +36,7 @@ test_list = [
'xyz',
['x', 'y', 'z'],
['x', ['y', 123], 'z', ('int', '$INTEGER')],
- { 'c' : 3, 'b': None, 'a' : 1 },
+ {'c': 3, 'b': None, 'a': 1},
"${TESTDEFS}",
"${GEN}",
]
@@ -48,33 +47,59 @@ def generator(target, source, env, for_signature):
return 'TARGET_AND_SOURCE_ARE_MISSING'
for i in test_list:
- env = Environment(CPPDEFPREFIX='-D', CPPDEFSUFFIX='', INTEGER=0, TESTDEFS=["FOO", "BAR=1"], GEN=generator)
+ env = Environment(
+ CPPDEFPREFIX='-D',
+ CPPDEFSUFFIX='',
+ INTEGER=0,
+ TESTDEFS=["FOO", "BAR=1"],
+ GEN=generator,
+ )
ttt = env.Entry('#ttt')
sss = env.Entry('#sss')
- print(env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS', target=[ttt], source=[sss]))
+ print(
+ env.Clone(CPPDEFINES=i).subst(
+ '$_CPPDEFFLAGS',
+ target=[ttt],
+ source=[sss],
+ )
+ )
+
for i in test_list:
- env = Environment(CPPDEFPREFIX='|', CPPDEFSUFFIX='|', INTEGER=1, TESTDEFS=["FOO", "BAR=1"], GEN=generator)
+ env = Environment(
+ CPPDEFPREFIX='|',
+ CPPDEFSUFFIX='|',
+ INTEGER=1,
+ TESTDEFS=["FOO", "BAR=1"],
+ GEN=generator,
+ )
ttt = env.Entry('#ttt')
sss = env.Entry('#sss')
- print(env.Clone(CPPDEFINES=i).subst('$_CPPDEFFLAGS', target=[ttt], source=[sss]))
+ print(
+ env.Clone(CPPDEFINES=i).subst(
+ '$_CPPDEFFLAGS',
+ target=[ttt],
+ source=[sss],
+ )
+ )
""")
-expect = test.wrap_stdout(build_str="scons: `.' is up to date.\n",
- read_str = """\
+expect = test.wrap_stdout(
+ build_str="scons: `.' is up to date.\n",
+ read_str="""\
-Dxyz
-Dx -Dy -Dz
-Dx -Dy=123 -Dz -Dint=0
--Da=1 -Db -Dc=3
+-Dc=3 -Db -Da=1
-DFOO -DBAR=1
-Dttt_GENERATED_sss
|xyz|
|x| |y| |z|
|x| |y=123| |z| |int=1|
-|a=1| |b| |c=3|
+|c=3| |b| |a=1|
|FOO| |BAR=1|
|ttt_GENERATED_sss|
-""")
-
+""",
+)
test.run(arguments = '.', stdout=expect)
test.pass_test()
diff --git a/test/CPPDEFINES/fixture/SConstruct-Append b/test/CPPDEFINES/fixture/SConstruct-Append
new file mode 100644
index 000000000..a9dea8c0a
--- /dev/null
+++ b/test/CPPDEFINES/fixture/SConstruct-Append
@@ -0,0 +1,99 @@
+# Special cases:
+# https://github.com/SCons/scons/issues/1738
+env_1738_2 = Environment(CPPDEFPREFIX='-D')
+env_1738_2['CPPDEFINES'] = ['FOO']
+env_1738_2.Append(CPPDEFINES={'value': '1'})
+print(env_1738_2.subst('$_CPPDEFFLAGS'))
+# env_1738_2.Object('test_1738_2', 'main.c')
+
+# https://github.com/SCons/scons/issues/2300
+env_2300_1 = Environment(CPPDEFINES='foo', CPPDEFPREFIX='-D')
+env_2300_1.Append(CPPDEFINES='bar')
+print(env_2300_1.subst('$_CPPDEFFLAGS'))
+
+env_2300_2 = Environment(CPPDEFINES=['foo'], CPPDEFPREFIX='-D') # note the list
+env_2300_2.Append(CPPDEFINES='bar')
+print(env_2300_2.subst('$_CPPDEFFLAGS'))
+
+# an initial space-separated string will be split, but not a string in a list
+env_multi = Environment(CPPDEFPREFIX='-D')
+env_multi['CPPDEFINES'] = "foo bar"
+env_multi.Append(CPPDEFINES="baz")
+print(env_multi.subst('$_CPPDEFFLAGS'))
+env_multi = Environment(CPPDEFPREFIX='-D')
+env_multi['CPPDEFINES'] = ["foo bar"]
+env_multi.Append(CPPDEFINES="baz")
+print(env_multi.subst('$_CPPDEFFLAGS'))
+env_multi = Environment(CPPDEFPREFIX='-D')
+env_multi['CPPDEFINES'] = "foo"
+env_multi.Append(CPPDEFINES=["bar baz"])
+print(env_multi.subst('$_CPPDEFFLAGS'))
+
+# check that AppendUnique(..., delete_existing=True) works as expected
+# each addition is in different but matching form, and different order
+# so we expect a reordered list, but with the same macro defines.
+env_multi = Environment(CPPDEFPREFIX='-D')
+env_multi.Append(CPPDEFINES=["Macro1=Value1", ("Macro2", "Value2"), {"Macro3": "Value3"}])
+env_multi.AppendUnique(CPPDEFINES="Macro2=Value2", delete_existing=True)
+env_multi.AppendUnique(CPPDEFINES=[("Macro3", "Value3")], delete_existing=True)
+env_multi.AppendUnique(CPPDEFINES={"Macro1": "Value1"}, delete_existing=True)
+print(env_multi.subst('$_CPPDEFFLAGS'))
+
+# https://github.com/SCons/scons/issues/1152
+# https://github.com/SCons/scons/issues/2900
+# Python3 dicts dont preserve order. Hence we supply subclass of OrderedDict
+# whose __str__ and __repr__ act like a normal dict.
+from collections import OrderedDict
+
+class OrderedPrintingDict(OrderedDict):
+ def __repr__(self):
+ return '{' + ', '.join([f'{k!r}: {v!r}' for (k, v) in self.items()]) + '}'
+
+ __str__ = __repr__
+
+ # Because dict-like objects (except dict and UserDict) are not deep copied
+ # directly when constructing Environment(CPPDEFINES=OrderedPrintingDict(...))
+ def __semi_deepcopy__(self):
+ return self.copy()
+
+
+# each of these types will be appended to each of the others
+# the first item in each tuple is a label for the output
+cases = [
+ ('string', 'FOO'),
+ ('valuestring', 'NAME1=VAL1'),
+ ('list', ['NAME1', 'NAME2']),
+ ('list-of-2lists', [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]),
+ (
+ 'dict',
+ OrderedPrintingDict([('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]),
+ ),
+]
+
+
+def dlist(coll):
+ # if it's a deque, turn it into a list for display purposes
+ from collections import deque
+
+ if isinstance(coll, deque):
+ return list(coll)
+ return coll
+
+
+for (t1, c1) in cases:
+ for (t2, c2) in cases:
+ print(f"==== Testing CPPDEFINES, appending a {t2} to a {t1}")
+ # string-like appearance if the value is a string
+ orig = f"'{c1}'" if isinstance(c1, str) else c1
+ app = f"'{c2}'" if isinstance(c2, str) else c2
+ print(f" orig = {orig}, append = {app}")
+ env = Environment(CPPDEFINES=c1, CPPDEFPREFIX='-D')
+ env.Append(CPPDEFINES=c2)
+ final = env.subst('$_CPPDEFFLAGS', source="src", target="tgt")
+ print(f"Append:\n\tresult={dlist(env['CPPDEFINES'])}\n\tfinal={final}")
+ env = Environment(CPPDEFINES=c1, CPPDEFPREFIX='-D')
+ env.AppendUnique(CPPDEFINES=c2)
+ final = env.subst('$_CPPDEFFLAGS', source="src", target="tgt")
+ print(
+ f"AppendUnique:\n\tresult={dlist(env['CPPDEFINES'])}\n\tfinal={final}"
+ )
diff --git a/test/CPPDEFINES/fixture/SConstruct-Prepend b/test/CPPDEFINES/fixture/SConstruct-Prepend
new file mode 100644
index 000000000..59345308d
--- /dev/null
+++ b/test/CPPDEFINES/fixture/SConstruct-Prepend
@@ -0,0 +1,100 @@
+# Special cases:
+# https://github.com/SCons/scons/issues/1738
+env_1738_2 = Environment(CPPDEFPREFIX='-D')
+env_1738_2['CPPDEFINES'] = ['FOO']
+env_1738_2.Prepend(CPPDEFINES={'value': '1'})
+print(env_1738_2.subst('$_CPPDEFFLAGS'))
+# env_1738_2.Object('test_1738_2', 'main.c')
+
+# https://github.com/SCons/scons/issues/2300
+env_2300_1 = Environment(CPPDEFINES='foo', CPPDEFPREFIX='-D')
+env_2300_1.Prepend(CPPDEFINES='bar')
+print(env_2300_1.subst('$_CPPDEFFLAGS'))
+
+env_2300_2 = Environment(CPPDEFINES=['foo'], CPPDEFPREFIX='-D') # note the list
+env_2300_2.Prepend(CPPDEFINES='bar')
+print(env_2300_2.subst('$_CPPDEFFLAGS'))
+
+# an initial space-separated string will be split, but not a string in a list
+env_multi = Environment(CPPDEFPREFIX='-D')
+env_multi['CPPDEFINES'] = "foo bar"
+env_multi.Prepend(CPPDEFINES="baz")
+print(env_multi.subst('$_CPPDEFFLAGS'))
+env_multi = Environment(CPPDEFPREFIX='-D')
+env_multi['CPPDEFINES'] = ["foo bar"]
+env_multi.Prepend(CPPDEFINES="baz")
+print(env_multi.subst('$_CPPDEFFLAGS'))
+env_multi = Environment(CPPDEFPREFIX='-D')
+env_multi['CPPDEFINES'] = "foo"
+env_multi.Prepend(CPPDEFINES=["bar baz"])
+print(env_multi.subst('$_CPPDEFFLAGS'))
+
+# check that PrependUnique(..., delete_existing=True) works as expected
+# each addition is in different but matching form, and different order,
+# so we expect a reordered list, but with the same macro defines.
+env_multi = Environment(CPPDEFPREFIX='-D')
+env_multi.Prepend(CPPDEFINES=["Macro1=Value1", ("Macro2", "Value2"), {"Macro3": "Value3"}])
+env_multi.PrependUnique(CPPDEFINES="Macro2=Value2", delete_existing=True)
+env_multi.PrependUnique(CPPDEFINES=[("Macro3", "Value3")], delete_existing=True)
+env_multi.PrependUnique(CPPDEFINES={"Macro1": "Value1"}, delete_existing=True)
+print(env_multi.subst('$_CPPDEFFLAGS'))
+
+# https://github.com/SCons/scons/issues/1152
+# https://github.com/SCons/scons/issues/2900
+# Python3 dicts dont preserve order. Hence we supply subclass of OrderedDict
+# whose __str__ and __repr__ act like a normal dict.
+from collections import OrderedDict
+
+
+class OrderedPrintingDict(OrderedDict):
+ def __repr__(self):
+ return '{' + ', '.join([f'{k!r}: {v!r}' for (k, v) in self.items()]) + '}'
+
+ __str__ = __repr__
+
+ # Because dict-like objects (except dict and UserDict) are not deep copied
+ # directly when constructing Environment(CPPDEFINES=OrderedPrintingDict(...))
+ def __semi_deepcopy__(self):
+ return self.copy()
+
+
+# each of these types will be prepended to each of the others
+# the first item in each tuple is a label for the output
+cases = [
+ ('string', 'FOO'),
+ ('valuestring', 'NAME1=VAL1'),
+ ('list', ['NAME1', 'NAME2']),
+ ('list-of-2lists', [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]),
+ (
+ 'dict',
+ OrderedPrintingDict([('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]),
+ ),
+]
+
+
+def dlist(coll):
+ # if it's a deque, turn it into a list for display purposes
+ from collections import deque
+
+ if isinstance(coll, deque):
+ return list(coll)
+ return coll
+
+
+for (t1, c1) in cases:
+ for (t2, c2) in cases:
+ print(f"==== Testing CPPDEFINES, prepending a {t2} to a {t1}")
+ # string-like appearance if the value is a string
+ orig = f"'{c1}'" if isinstance(c1, str) else c1
+ pre = f"'{c2}'" if isinstance(c2, str) else c2
+ print(f" orig = {orig}, prepend = {pre}")
+ env = Environment(CPPDEFINES=c1, CPPDEFPREFIX='-D')
+ env.Prepend(CPPDEFINES=c2)
+ final = env.subst('$_CPPDEFFLAGS', source="src", target="tgt")
+ print(f"Prepend:\n\tresult={dlist(env['CPPDEFINES'])}\n\tfinal={final}")
+ env = Environment(CPPDEFINES=c1, CPPDEFPREFIX='-D')
+ env.PrependUnique(CPPDEFINES=c2)
+ final = env.subst('$_CPPDEFFLAGS', source="src", target="tgt")
+ print(
+ f"PrependUnique:\n\tresult={dlist(env['CPPDEFINES'])}\n\tfinal={final}"
+ )
diff --git a/test/CPPDEFINES/live.py b/test/CPPDEFINES/live.py
index 0c7ad7874..97e0e1323 100644
--- a/test/CPPDEFINES/live.py
+++ b/test/CPPDEFINES/live.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify basic use of CPPDEFINES with live compilation.
@@ -33,14 +32,14 @@ import TestSCons
test = TestSCons.TestSCons()
test.write('SConstruct', """\
-foo = Environment(CPPDEFINES = ['FOO', ('VAL', '$VALUE')], VALUE=7)
-bar = Environment(CPPDEFINES = {'BAR':None, 'VAL':8})
-baz = Environment(CPPDEFINES = ['BAZ', ('VAL', 9)])
-f = foo.Object(target = 'foo', source = 'prog.c')
-b = bar.Object(target = 'bar', source = 'prog.c')
-foo.Program(target = 'foo', source = f)
-bar.Program(target = 'bar', source = b)
-baz.Program(target = 'baz', source = 'baz.cpp')
+foo = Environment(CPPDEFINES=['FOO', ('VAL', '$VALUE')], VALUE=7)
+bar = Environment(CPPDEFINES={'BAR': None, 'VAL': 8})
+baz = Environment(CPPDEFINES=['BAZ', ('VAL', 9)])
+f = foo.Object(target='foo', source='prog.c')
+b = bar.Object(target='bar', source='prog.c')
+foo.Program(target='foo', source=f)
+bar.Program(target='bar', source=b)
+baz.Program(target='baz', source='baz.cpp')
""")
test.write('prog.c', r"""
@@ -74,12 +73,10 @@ main(int argc, char *argv[])
}
""")
-
-test.run(arguments = '.')
-
-test.run(program = test.workpath('foo'), stdout = "prog.c: FOO 7\n")
-test.run(program = test.workpath('bar'), stdout = "prog.c: BAR 8\n")
-test.run(program = test.workpath('baz'), stdout = "baz.cpp: BAZ 9\n")
+test.run(arguments='.')
+test.run(program=test.workpath('foo'), stdout="prog.c: FOO 7\n")
+test.run(program=test.workpath('bar'), stdout="prog.c: BAR 8\n")
+test.run(program=test.workpath('baz'), stdout="baz.cpp: BAZ 9\n")
test.pass_test()
diff --git a/test/CPPDEFINES/pkg-config.py b/test/CPPDEFINES/pkg-config.py
index 65210cec7..cd8c9dc7b 100644
--- a/test/CPPDEFINES/pkg-config.py
+++ b/test/CPPDEFINES/pkg-config.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify merging with MergeFlags to CPPPDEFINES with various data types.
@@ -69,12 +68,14 @@ else:
test.write('SConstruct', """\
import os
import sys
+
# Python3 dicts dont preserve order. Hence we supply subclass of OrderedDict
# whose __str__ and __repr__ act like a normal dict.
from collections import OrderedDict
+
class OrderedPrintingDict(OrderedDict):
def __repr__(self):
- return '{' + ', '.join(['%r: %r'%(k, v) for (k, v) in self.items()]) + '}'
+ return '{' + ', '.join(['%r: %r' % (k, v) for (k, v) in self.items()]) + '}'
__str__ = __repr__
@@ -85,29 +86,41 @@ class OrderedPrintingDict(OrderedDict):
""" + """
# https://github.com/SCons/scons/issues/2671
# Passing test cases
-env_1 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST'], tools = ['%(pkg_config_tools)s'])
+env_1 = Environment(CPPDEFINES=[('DEBUG', '1'), 'TEST'], tools=['%(pkg_config_tools)s'])
if sys.platform == 'win32':
- os.environ['PKG_CONFIG_PATH'] = env_1.Dir('.').abspath.replace("\\\\" , "/")
-env_1.ParseConfig('%(pkg_config_cl_path)s "%(pkg_config_path)s" --cflags %(pkg_config_file)s')
+ os.environ['PKG_CONFIG_PATH'] = env_1.Dir('.').abspath.replace("\\\\", "/")
+env_1.ParseConfig(
+ '%(pkg_config_cl_path)s "%(pkg_config_path)s" --cflags %(pkg_config_file)s'
+)
print(env_1.subst('$_CPPDEFFLAGS'))
-env_2 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST'], tools = ['%(pkg_config_tools)s'])
+env_2 = Environment(CPPDEFINES=[('DEBUG', '1'), 'TEST'], tools=['%(pkg_config_tools)s'])
env_2.MergeFlags('-DSOMETHING -DVARIABLE=2')
print(env_2.subst('$_CPPDEFFLAGS'))
# Failing test cases
-env_3 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)]), tools = ['%(pkg_config_tools)s'])
-env_3.ParseConfig('%(pkg_config_cl_path)s "%(pkg_config_path)s" --cflags %(pkg_config_file)s')
+env_3 = Environment(
+ CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)]),
+ tools=['%(pkg_config_tools)s'],
+)
+env_3.ParseConfig(
+ '%(pkg_config_cl_path)s "%(pkg_config_path)s" --cflags %(pkg_config_file)s'
+)
print(env_3.subst('$_CPPDEFFLAGS'))
-env_4 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)]), tools = ['%(pkg_config_tools)s'])
+env_4 = Environment(
+ CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)]),
+ tools=['%(pkg_config_tools)s'],
+)
env_4.MergeFlags('-DSOMETHING -DVARIABLE=2')
print(env_4.subst('$_CPPDEFFLAGS'))
# https://github.com/SCons/scons/issues/1738
-env_1738_1 = Environment(tools = ['%(pkg_config_tools)s'])
-env_1738_1.ParseConfig('%(pkg_config_cl_path)s "%(pkg_config_path)s" --cflags --libs %(pkg_config_file)s')
-env_1738_1.Append(CPPDEFINES={'value' : '1'})
+env_1738_1 = Environment(tools=['%(pkg_config_tools)s'])
+env_1738_1.ParseConfig(
+ '%(pkg_config_cl_path)s "%(pkg_config_path)s" --cflags --libs %(pkg_config_file)s'
+)
+env_1738_1.Append(CPPDEFINES={'value': '1'})
print(env_1738_1.subst('$_CPPDEFFLAGS'))
"""%locals() )
@@ -119,11 +132,10 @@ expect_print_output="""\
-DSOMETHING -DVARIABLE=2 -Dvalue=1
"""
-build_output="scons: `.' is up to date.\n"
+build_output = "scons: `.' is up to date.\n"
+expect = test.wrap_stdout(build_str=build_output, read_str=expect_print_output)
+test.run(arguments='.', stdout=expect)
-expect = test.wrap_stdout(build_str=build_output,
- read_str = expect_print_output)
-test.run(arguments = '.', stdout=expect)
test.pass_test()
# Local Variables:
diff --git a/test/CPPDEFINES/prepend.py b/test/CPPDEFINES/prepend.py
new file mode 100644
index 000000000..3e8d8d1c1
--- /dev/null
+++ b/test/CPPDEFINES/prepend.py
@@ -0,0 +1,258 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright 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.
+
+"""
+Verify prepending to CPPPDEFINES with various data types.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+# Note: explicitly set CPPDEFPREFIX here to simplify testing on Windows.
+
+# Link: fixture/SConstruct-Prepend
+test.file_fixture(["fixture", "SConstruct-Prepend"], "SConstruct")
+
+expect_print_output="""\
+-Dvalue=1 -DFOO
+-Dbar -Dfoo
+-Dbar -Dfoo
+-Dbaz -Dfoo -Dbar
+-Dbaz -Dfoo bar
+-Dbar baz -Dfoo
+-DMacro1=Value1 -DMacro3=Value3 -DMacro2=Value2
+==== Testing CPPDEFINES, prepending a string to a string
+ orig = 'FOO', prepend = 'FOO'
+Prepend:
+ result=['FOO', 'FOO']
+ final=-DFOO -DFOO
+PrependUnique:
+ result=['FOO']
+ final=-DFOO
+==== Testing CPPDEFINES, prepending a valuestring to a string
+ orig = 'FOO', prepend = 'NAME1=VAL1'
+Prepend:
+ result=['NAME1=VAL1', 'FOO']
+ final=-DNAME1=VAL1 -DFOO
+PrependUnique:
+ result=['NAME1=VAL1', 'FOO']
+ final=-DNAME1=VAL1 -DFOO
+==== Testing CPPDEFINES, prepending a list to a string
+ orig = 'FOO', prepend = ['NAME1', 'NAME2']
+Prepend:
+ result=['NAME2', 'NAME1', 'FOO']
+ final=-DNAME2 -DNAME1 -DFOO
+PrependUnique:
+ result=['NAME2', 'NAME1', 'FOO']
+ final=-DNAME2 -DNAME1 -DFOO
+==== Testing CPPDEFINES, prepending a list-of-2lists to a string
+ orig = 'FOO', prepend = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+Prepend:
+ result=[['NAME2', 'VAL2'], ('NAME1', 'VAL1'), 'FOO']
+ final=-DNAME2=VAL2 -DNAME1=VAL1 -DFOO
+PrependUnique:
+ result=[['NAME2', 'VAL2'], ('NAME1', 'VAL1'), 'FOO']
+ final=-DNAME2=VAL2 -DNAME1=VAL1 -DFOO
+==== Testing CPPDEFINES, prepending a dict to a string
+ orig = 'FOO', prepend = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
+Prepend:
+ result=[('NAME1', 'VAL1'), ('NAME3', None), ('NAME2', 'VAL2'), 'FOO']
+ final=-DNAME1=VAL1 -DNAME3 -DNAME2=VAL2 -DFOO
+PrependUnique:
+ result=[('NAME1', 'VAL1'), ('NAME3', None), ('NAME2', 'VAL2'), 'FOO']
+ final=-DNAME1=VAL1 -DNAME3 -DNAME2=VAL2 -DFOO
+==== Testing CPPDEFINES, prepending a string to a valuestring
+ orig = 'NAME1=VAL1', prepend = 'FOO'
+Prepend:
+ result=['FOO', 'NAME1=VAL1']
+ final=-DFOO -DNAME1=VAL1
+PrependUnique:
+ result=['FOO', 'NAME1=VAL1']
+ final=-DFOO -DNAME1=VAL1
+==== Testing CPPDEFINES, prepending a valuestring to a valuestring
+ orig = 'NAME1=VAL1', prepend = 'NAME1=VAL1'
+Prepend:
+ result=['NAME1=VAL1', 'NAME1=VAL1']
+ final=-DNAME1=VAL1 -DNAME1=VAL1
+PrependUnique:
+ result=['NAME1=VAL1']
+ final=-DNAME1=VAL1
+==== Testing CPPDEFINES, prepending a list to a valuestring
+ orig = 'NAME1=VAL1', prepend = ['NAME1', 'NAME2']
+Prepend:
+ result=['NAME2', 'NAME1', 'NAME1=VAL1']
+ final=-DNAME2 -DNAME1 -DNAME1=VAL1
+PrependUnique:
+ result=['NAME2', 'NAME1', 'NAME1=VAL1']
+ final=-DNAME2 -DNAME1 -DNAME1=VAL1
+==== Testing CPPDEFINES, prepending a list-of-2lists to a valuestring
+ orig = 'NAME1=VAL1', prepend = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+Prepend:
+ result=[['NAME2', 'VAL2'], ('NAME1', 'VAL1'), 'NAME1=VAL1']
+ final=-DNAME2=VAL2 -DNAME1=VAL1 -DNAME1=VAL1
+PrependUnique:
+ result=[['NAME2', 'VAL2'], 'NAME1=VAL1']
+ final=-DNAME2=VAL2 -DNAME1=VAL1
+==== Testing CPPDEFINES, prepending a dict to a valuestring
+ orig = 'NAME1=VAL1', prepend = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
+Prepend:
+ result=[('NAME1', 'VAL1'), ('NAME3', None), ('NAME2', 'VAL2'), 'NAME1=VAL1']
+ final=-DNAME1=VAL1 -DNAME3 -DNAME2=VAL2 -DNAME1=VAL1
+PrependUnique:
+ result=[('NAME3', None), ('NAME2', 'VAL2'), 'NAME1=VAL1']
+ final=-DNAME3 -DNAME2=VAL2 -DNAME1=VAL1
+==== Testing CPPDEFINES, prepending a string to a list
+ orig = ['NAME1', 'NAME2'], prepend = 'FOO'
+Prepend:
+ result=['FOO', 'NAME1', 'NAME2']
+ final=-DFOO -DNAME1 -DNAME2
+PrependUnique:
+ result=['FOO', 'NAME1', 'NAME2']
+ final=-DFOO -DNAME1 -DNAME2
+==== Testing CPPDEFINES, prepending a valuestring to a list
+ orig = ['NAME1', 'NAME2'], prepend = 'NAME1=VAL1'
+Prepend:
+ result=['NAME1=VAL1', 'NAME1', 'NAME2']
+ final=-DNAME1=VAL1 -DNAME1 -DNAME2
+PrependUnique:
+ result=['NAME1=VAL1', 'NAME1', 'NAME2']
+ final=-DNAME1=VAL1 -DNAME1 -DNAME2
+==== Testing CPPDEFINES, prepending a list to a list
+ orig = ['NAME1', 'NAME2'], prepend = ['NAME1', 'NAME2']
+Prepend:
+ result=['NAME2', 'NAME1', 'NAME1', 'NAME2']
+ final=-DNAME2 -DNAME1 -DNAME1 -DNAME2
+PrependUnique:
+ result=['NAME1', 'NAME2']
+ final=-DNAME1 -DNAME2
+==== Testing CPPDEFINES, prepending a list-of-2lists to a list
+ orig = ['NAME1', 'NAME2'], prepend = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+Prepend:
+ result=[['NAME2', 'VAL2'], ('NAME1', 'VAL1'), 'NAME1', 'NAME2']
+ final=-DNAME2=VAL2 -DNAME1=VAL1 -DNAME1 -DNAME2
+PrependUnique:
+ result=[['NAME2', 'VAL2'], ('NAME1', 'VAL1'), 'NAME1', 'NAME2']
+ final=-DNAME2=VAL2 -DNAME1=VAL1 -DNAME1 -DNAME2
+==== Testing CPPDEFINES, prepending a dict to a list
+ orig = ['NAME1', 'NAME2'], prepend = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
+Prepend:
+ result=[('NAME1', 'VAL1'), ('NAME3', None), ('NAME2', 'VAL2'), 'NAME1', 'NAME2']
+ final=-DNAME1=VAL1 -DNAME3 -DNAME2=VAL2 -DNAME1 -DNAME2
+PrependUnique:
+ result=[('NAME1', 'VAL1'), ('NAME3', None), ('NAME2', 'VAL2'), 'NAME1', 'NAME2']
+ final=-DNAME1=VAL1 -DNAME3 -DNAME2=VAL2 -DNAME1 -DNAME2
+==== Testing CPPDEFINES, prepending a string to a list-of-2lists
+ orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], prepend = 'FOO'
+Prepend:
+ result=['FOO', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DFOO -DNAME1=VAL1 -DNAME2=VAL2
+PrependUnique:
+ result=['FOO', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DFOO -DNAME1=VAL1 -DNAME2=VAL2
+==== Testing CPPDEFINES, prepending a valuestring to a list-of-2lists
+ orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], prepend = 'NAME1=VAL1'
+Prepend:
+ result=['NAME1=VAL1', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME1=VAL1 -DNAME1=VAL1 -DNAME2=VAL2
+PrependUnique:
+ result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME1=VAL1 -DNAME2=VAL2
+==== Testing CPPDEFINES, prepending a list to a list-of-2lists
+ orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], prepend = ['NAME1', 'NAME2']
+Prepend:
+ result=['NAME2', 'NAME1', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME2 -DNAME1 -DNAME1=VAL1 -DNAME2=VAL2
+PrependUnique:
+ result=['NAME2', 'NAME1', ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME2 -DNAME1 -DNAME1=VAL1 -DNAME2=VAL2
+==== Testing CPPDEFINES, prepending a list-of-2lists to a list-of-2lists
+ orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], prepend = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+Prepend:
+ result=[['NAME2', 'VAL2'], ('NAME1', 'VAL1'), ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME2=VAL2 -DNAME1=VAL1 -DNAME1=VAL1 -DNAME2=VAL2
+PrependUnique:
+ result=[('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME1=VAL1 -DNAME2=VAL2
+==== Testing CPPDEFINES, prepending a dict to a list-of-2lists
+ orig = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']], prepend = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
+Prepend:
+ result=[('NAME1', 'VAL1'), ('NAME3', None), ('NAME2', 'VAL2'), ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME1=VAL1 -DNAME3 -DNAME2=VAL2 -DNAME1=VAL1 -DNAME2=VAL2
+PrependUnique:
+ result=[('NAME3', None), ('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+ final=-DNAME3 -DNAME1=VAL1 -DNAME2=VAL2
+==== Testing CPPDEFINES, prepending a string to a dict
+ orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, prepend = 'FOO'
+Prepend:
+ result=['FOO', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DFOO -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+PrependUnique:
+ result=['FOO', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DFOO -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+==== Testing CPPDEFINES, prepending a valuestring to a dict
+ orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, prepend = 'NAME1=VAL1'
+Prepend:
+ result=['NAME1=VAL1', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME1=VAL1 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+PrependUnique:
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+==== Testing CPPDEFINES, prepending a list to a dict
+ orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, prepend = ['NAME1', 'NAME2']
+Prepend:
+ result=['NAME2', 'NAME1', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME2 -DNAME1 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+PrependUnique:
+ result=['NAME2', 'NAME1', ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME2 -DNAME1 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+==== Testing CPPDEFINES, prepending a list-of-2lists to a dict
+ orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, prepend = [('NAME1', 'VAL1'), ['NAME2', 'VAL2']]
+Prepend:
+ result=[['NAME2', 'VAL2'], ('NAME1', 'VAL1'), ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME2=VAL2 -DNAME1=VAL1 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+PrependUnique:
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+==== Testing CPPDEFINES, prepending a dict to a dict
+ orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, prepend = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}
+Prepend:
+ result=[('NAME1', 'VAL1'), ('NAME3', None), ('NAME2', 'VAL2'), ('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME1=VAL1 -DNAME3 -DNAME2=VAL2 -DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+PrependUnique:
+ result=[('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')]
+ final=-DNAME2=VAL2 -DNAME3 -DNAME1=VAL1
+"""
+
+build_output="scons: `.' is up to date.\n"
+expect = test.wrap_stdout(build_str=build_output, read_str=expect_print_output)
+test.run(arguments='.', stdout=expect)
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/CPPDEFINES/scan.py b/test/CPPDEFINES/scan.py
index 53186425a..49cbec240 100644
--- a/test/CPPDEFINES/scan.py
+++ b/test/CPPDEFINES/scan.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify that use of the Scanner that evaluates CPP lines works as expected.
@@ -41,12 +40,12 @@ f3_exe = 'f3' + TestSCons._exe
f4_exe = 'f4' + TestSCons._exe
test.write('SConstruct', """\
-env = Environment(CPPPATH = ['.'])
+env = Environment(CPPPATH=['.'])
-f1 = env.Object('f1', 'fff.c', CPPDEFINES = ['F1'])
-f2 = env.Object('f2', 'fff.c', CPPDEFINES = [('F2', 1)])
-f3 = env.Object('f3', 'fff.c', CPPDEFINES = {'F3':None})
-f4 = env.Object('f4', 'fff.c', CPPDEFINES = {'F4':1})
+f1 = env.Object('f1', 'fff.c', CPPDEFINES=['F1'])
+f2 = env.Object('f2', 'fff.c', CPPDEFINES=[('F2', 1)])
+f3 = env.Object('f3', 'fff.c', CPPDEFINES={'F3': None})
+f4 = env.Object('f4', 'fff.c', CPPDEFINES={'F4': 1})
env.Program('f1', ['prog.c', f1])
env.Program('f2', ['prog.c', f2])
@@ -110,20 +109,16 @@ main(int argc, char *argv[])
test.run(arguments = '.')
-
test.run(program = test.workpath('f1'), stdout = "prog.c: F1\n")
test.run(program = test.workpath('f2'), stdout = "prog.c: F2\n")
test.run(program = test.workpath('f3'), stdout = "prog.c: F3\n")
test.run(program = test.workpath('f4'), stdout = "prog.c: F4\n")
-
-
test.write('f1.h', """
#define STRING "F1 again"
""")
test.up_to_date(arguments = '%(f2_exe)s %(f3_exe)s %(f4_exe)s' % locals())
-
test.not_up_to_date(arguments = '.')
test.run(program = test.workpath('f1'), stdout = "prog.c: F1 again\n")
@@ -131,14 +126,11 @@ test.run(program = test.workpath('f2'), stdout = "prog.c: F2\n")
test.run(program = test.workpath('f3'), stdout = "prog.c: F3\n")
test.run(program = test.workpath('f4'), stdout = "prog.c: F4\n")
-
-
test.write('f2.h', """
#define STRING "F2 again"
""")
test.up_to_date(arguments = '%(f1_exe)s %(f3_exe)s %(f4_exe)s' % locals())
-
test.not_up_to_date(arguments = '.')
test.run(program = test.workpath('f1'), stdout = "prog.c: F1 again\n")
@@ -146,14 +138,11 @@ test.run(program = test.workpath('f2'), stdout = "prog.c: F2 again\n")
test.run(program = test.workpath('f3'), stdout = "prog.c: F3\n")
test.run(program = test.workpath('f4'), stdout = "prog.c: F4\n")
-
-
test.write('f3.h', """
#define STRING "F3 again"
""")
test.up_to_date(arguments = '%(f1_exe)s %(f2_exe)s %(f4_exe)s' % locals())
-
test.not_up_to_date(arguments = '.')
test.run(program = test.workpath('f1'), stdout = "prog.c: F1 again\n")
@@ -161,14 +150,11 @@ test.run(program = test.workpath('f2'), stdout = "prog.c: F2 again\n")
test.run(program = test.workpath('f3'), stdout = "prog.c: F3 again\n")
test.run(program = test.workpath('f4'), stdout = "prog.c: F4\n")
-
-
test.write('f4.h', """
#define STRING "F4 again"
""")
test.up_to_date(arguments = '%(f1_exe)s %(f2_exe)s %(f3_exe)s' % locals())
-
test.not_up_to_date(arguments = '.')
test.run(program = test.workpath('f1'), stdout = "prog.c: F1 again\n")
@@ -176,8 +162,6 @@ test.run(program = test.workpath('f2'), stdout = "prog.c: F2 again\n")
test.run(program = test.workpath('f3'), stdout = "prog.c: F3 again\n")
test.run(program = test.workpath('f4'), stdout = "prog.c: F4 again\n")
-
-
test.pass_test()
# Local Variables:
diff --git a/test/CPPDEFINES/undefined.py b/test/CPPDEFINES/undefined.py
index b26b05a32..31568ea4b 100644
--- a/test/CPPDEFINES/undefined.py
+++ b/test/CPPDEFINES/undefined.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Verify that $_CPPDEFFLAGS doesn't barf when CPPDEFINES isn't defined.
@@ -37,10 +36,8 @@ env = Environment()
print(env.subst('$_CPPDEFFLAGS'))
""")
-expect = test.wrap_stdout(build_str="scons: `.' is up to date.\n",
- read_str = "\n")
-
-test.run(arguments = '.', stdout=expect)
+expect = test.wrap_stdout(build_str="scons: `.' is up to date.\n", read_str="\n")
+test.run(arguments='.', stdout=expect)
test.pass_test()