diff --git a/CHANGES.txt b/CHANGES.txt index 3725fa473b..1f34ec872a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -124,6 +124,18 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Cleaned up dblite module (checker warnings, etc.). - Some cleanup in the FortranCommon tool. - Changed the message about scons -H to clarify it shows built-in options. + - Introduce a new _concat_dir function which can be used for + building cli strings when you need two sets of prefix/suffix + strings. _concat_dir transforms a list so the first item is wrapped + with the first pair of prefix/suffix args and the remaining + items with the second pair. The use case is when passing + a directory-to-write-to - those typically need to be unique, + but RDirs may expand to multiples, and we want to accomodate + also searching those directories in case you have repositories + setup - those could have already built files in them which could + be searched. Example: Fortran module directory, D interface file + directory. Introduces a new construction variable of the same name, + just to keep consistent with how existing _concat works. From Jonathon Reinhart: - Fix another instance of `int main()` in CheckLib() causing failures diff --git a/RELEASE.txt b/RELEASE.txt index bffe74014a..a835f8cee3 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -23,6 +23,10 @@ NEW FUNCTIONALITY - MSVC: If necessary, automatically define VSCMD_SKIP_SENDTELEMETRY for VS2019 and later on arm64 hosts when using an arm (32-bit) build of python to prevent a powershell error pop-up window (powershell dll not found). +- Construction variable _concat_dir added which refers to a function to + perform translation on a directory to write to that needs to be passed + to a compiler. Similar to + https://scons.org/doc/production/HTML/scons-man.html#cv-_concat DEPRECATED FUNCTIONALITY ------------------------ @@ -87,6 +91,13 @@ FIXES with the python logging module was refactored to prevent test failure. - MSVS: Add arm64 to the MSVS supported architectures list for VS2017 and later to be consistent with the current documentation of MSVS_ARCH. +- When expanding $FORTRANMODDIR and $DI_FILE_DIR (the latter new in this + release), incorrect results could be obtained when Repository() has been + called, and/or when a variant directory is used with duplicate=False. + In some cases, this could cause the compiler to abort with an error. + The expansion is now handled so the path relative to the build directory + is passed to the compiler as the directory to write into, and the + additional directories are enabled as search directories. IMPROVEMENTS ------------ diff --git a/SCons/Defaults.py b/SCons/Defaults.py index ae5de898c4..029af0aedb 100644 --- a/SCons/Defaults.py +++ b/SCons/Defaults.py @@ -36,7 +36,7 @@ import stat import sys import time -from typing import List +from typing import List, Callable import SCons.Action import SCons.Builder @@ -420,6 +420,67 @@ def _concat(prefix, items_iter, suffix, env, f=lambda x: x, target=None, source= # pylint: enable-msg=too-many-arguments +# pylint: disable-msg=too-many-arguments +def _concat_dir( + prefix: str, + items, + suffix: str, + extra_prefix: str, + extra_suffix: str, + env, + f: Callable[[list], list] = lambda x: x, + target=None, + source=None, + affect_signature: bool = True, +) -> list: + """Transform *items* into a list for command-line usage. + + Like :func:`_concat`, but wraps an expanded list differently. + If *f* returns multiple items, the first item is wrapped by *prefix* + and *suffix*, while the rest are wrapped with *extra_prefix* + and *extra_suffix*. Used for special cases like Fortran module + directories, where only a single directory can be supplied as the + output destination, but repository locations and the source directory + in a variantdir setup should be added as search locations - which + takes a different syntax from different construction variables. + + Args: + prefix: text to prepend to first element + items: string or iterable to transform + suffix: text to append to first element + extra_prefix: text to prepend to additional elements + extra_suffix: text to append to additional elements + env: construction environment for interpolation + f: optional function to perform a transformation on the list. + The default is a stub which performs no transformation. + target: optional target for interpolation into *items* elements + source: optional source for interpolation into *items* elements + affect_signature: optional flag: if false, surround contents with + markers indicating not to use the contents when calculating the + build signature. The default is ``True``. + """ + if not items: + return items + + l = f(SCons.PathList.PathList(items).subst_path(env, target, source)) + if l is not None: + items = l + + if not affect_signature: + value = ['$('] + else: + value = [] + value += _concat_ixes(prefix, items[:1], suffix, env) + if len(items) > 1: + value += _concat_ixes(extra_prefix, items[1:], extra_suffix, env) + + if not affect_signature: + value += ["$)"] + + return value +# pylint: enable-msg=too-many-arguments + + def _concat_ixes(prefix, items_iter, suffix, env): """ Creates a new list from 'items_iter' by concatenating the 'prefix' and @@ -690,6 +751,7 @@ def __lib_either_version_flag(env, version_var1, version_var2, flags_var): 'ENV': {}, 'IDLSUFFIXES': SCons.Tool.IDLSuffixes, '_concat': _concat, + '_concat_dir': _concat_dir, '_defines': _defines, '_stripixes': _stripixes, '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml index 7b37475a20..bef21fe2c6 100644 --- a/SCons/Defaults.xml +++ b/SCons/Defaults.xml @@ -45,6 +45,116 @@ See its __doc__ string for a discussion of the format. + + + +A reference to a function which transforms +a value by concatenating passed prefix and suffix strings +(after variable substitution and optional additional processing), +to prepare option strings either to be passed to the external build program +or to help calculate the &buildsig; - the function will be called +for both purposes in the course of a build. +This allows values such as a module directory to be +to be stored in a portable manner, +rather than including non-portable compiler syntax. +Similar to &cv-link-_concat;, but for the special case where the +build program is being instructed to write to a directory. +For example, &cv-link-FORTRANMODDIR; +holds the name of the directory where the compiler will write +generated Fortran module files. Under some circumstances, +the processing by the specified function +can produce several names from the original variable +(if &f-link-Repository; and/or &f-link-VariantDir; are in use), +but the directory to write to must be unambiguous. +The &cv-_concat_dir; function wraps any additional directory +name strings in a different prefix/suffix pair, +which are passed in additional arguments to the function. + + + +The default value for &cv-_concat_dir; is an internal &SCons; function. +It is possible to supply a custom &cv-_concat_dir; function, +but care must be taken not not disrupt the behavior +of existing &consvars; written to use the default function, +so replacing the default is discouraged. + + + +A function used for &cv-_concat_dir; must accept six required arguments: +prefix, +items, +suffix, +extra_prefix, +extra_suffix and +env, +as well as four optional arguments: +f, +target, +source and +affect_signature. +items +is the element to proces - +often it will be the name of a &consvar;. +prefix and +suffix +are the strings to add to the front and back of the +first item of the list generated by calling the function +f. +extra_prefix and +extra_suffix +are the strings to add to the front and back of the remaining items. +env +is the &consenv; to interpolate &consvars; from; +the &consvars; will appear as local variables at evaluation time +and so are referenced without prefixing with a $. +The &cv-_concat_dir; function must return the substituted elements +as a string or list, either of which can be empty - it must not fail if +items has no value or has an empty value. +The optional arguments +target and +source +can be used if the target and/or source files +need to be interpolated into +items; +the special variables TARGET +and SOURCE are often used. +After source/target interpolation, +the function f is called. +It accepts a single list argument, and returns +a list with the necessary modifications applied. +f must be called before +the prefix/suffix additions, as it may change the +contents of the list. +&cv-link-RDirs; is often used as the function - +it may add additional directory paths if a repository +and/or variant directory is in use. +If the default internal implementation is used for &cv-_concat_dir;, +the default for f is a +pass-through which does not modify the item. +The optional affect_signature +indicates whether the contents should be used for +&buildsig; calculation. If it evaluates false, +any non-empty return should be wrapped with +$( and $) +to tell &SCons; to exclude it from the calculation. +If the default internal implementation is used for &cv-_concat;, +affect_signature defaults to +True. + + + +Example use: + + + +env['_FORTRANMODFLAG'] = ( + "$( ${_concat_dir(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, " + "INCFORTRANPREFIX, INCFORTRANSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)" +) + + + + @@ -243,7 +353,7 @@ to each directory in &cv-link-CPPPATH;. The list of directories that the C preprocessor will search for include directories. The C/C++ implicit dependency scanner will search these -directories for include files. +directories for include files. In general it's not advised to put include directory directives directly into &cv-link-CCFLAGS; or &cv-link-CXXFLAGS; as the result will be non-portable @@ -257,9 +367,9 @@ Python's os.sep. Note: directory names in &cv-CPPPATH; will be looked-up relative to the directory of the SConscript file -when they are used in a command. +when they are used in a command. To force &scons; -to look-up a directory relative to the root of the source tree use +to look-up a directory relative to the root of the source tree use the # prefix: @@ -529,9 +639,9 @@ as the result will be non-portable. Note: directory names in &cv-LIBPATH; will be looked-up relative to the directory of the SConscript file -when they are used in a command. +when they are used in a command. To force &scons; -to look-up a directory relative to the root of the source tree use +to look-up a directory relative to the root of the source tree use the # prefix: @@ -575,7 +685,7 @@ env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE A list of one or more libraries that will be added to the link line -for linking with any executable program, shared library, or loadable module +for linking with any executable program, shared library, or loadable module created by the &consenv; or override. diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index e7cb99f346..4cb21dd288 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -1522,6 +1522,45 @@ def test_concat_nested(self) -> None: x = e.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)') assert x == 'preasuf prebsuf precsuf predsuf precsuf predsuf', x + + def test_concat_dir(self) -> None: + """Test _concat_dir()""" + e1 = self.TestEnvironment( + PRE='PP', + SUF='SS', + XPRE='XP', + XSUF='XS', + STR='a b', + LIST=['a', 'b'], + LONGLIST=['a', 'b', 'c', 'd'], + ) + s = e1.subst + with self.subTest(): + x = s("${_concat_dir('', '', '', '', '', __env__)}") + self.assertEqual(x, '') + with self.subTest(): + x = s("${_concat_dir('', [], '', '', '', __env__)}") + self.assertEqual(x, '') + with self.subTest(): + x = s("${_concat_dir(PRE, '', SUF, XPRE, XSUF, __env__)}") + self.assertEqual(x, '') + with self.subTest(): + x = s("${_concat_dir(PRE, STR, SUF, XPRE, XSUF, __env__)}") + self.assertEqual(x, 'PPa bSS') + with self.subTest(): + x = s("${_concat_dir(PRE, LIST, SUF, XPRE, XSUF, __env__)}") + self.assertEqual(x, 'PPaSS XPbXS') + with self.subTest(): + x = s( + "${_concat_dir(PRE, LIST, SUF, XPRE, XSUF, __env__, affect_signature=False)}", + raw=True, + ) + self.assertEqual(x, '$( PPaSS XPbXS $)') + with self.subTest(): + x = s("${_concat_dir(PRE, LONGLIST, SUF, XPRE, XSUF, __env__)}") + self.assertEqual(x, 'PPaSS XPbXS XPcXS XPdXS') + + def test_gvars(self) -> None: """Test the Environment gvars() method""" env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z') diff --git a/SCons/Tool/FortranCommon.py b/SCons/Tool/FortranCommon.py index f221d15ec7..cb7f2801e3 100644 --- a/SCons/Tool/FortranCommon.py +++ b/SCons/Tool/FortranCommon.py @@ -204,7 +204,7 @@ def add_fortran_to_env(env) -> None: env['FORTRANMODDIR'] = '' # where the compiler should place .mod files env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX - env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['_FORTRANMODFLAG'] = '$( ${_concat_dir(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, INCFORTRANPREFIX, INCFORTRANSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' def add_f77_to_env(env) -> None: """Add Builders and construction variables for f77 dialect.""" diff --git a/SCons/Tool/dmd.py b/SCons/Tool/dmd.py index c3ac0ca6f0..75c6dad4d0 100644 --- a/SCons/Tool/dmd.py +++ b/SCons/Tool/dmd.py @@ -95,9 +95,7 @@ def generate(env) -> None: env['_DINCFLAGS'] = '${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)}' env['_DVERFLAGS'] = '${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)}' env['_DDEBUGFLAGS'] = '${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)}' - - env['_DI_FLAGS'] = "${DI_FILE_DIR and DI_FILE_DIR_PREFIX+DI_FILE_DIR+DI_FILE_DIR_SUFFFIX}" - + env['_DI_FLAGS'] = '$( ${_concat_dir(DI_FILE_DIR_PREFIX, DI_FILE_DIR, DI_FILE_DIR_SUFFFIX, DINCPREFIX, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['_DFLAGS'] = '${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)}' env['SHDC'] = '$DC' diff --git a/SCons/Tool/gdc.py b/SCons/Tool/gdc.py index 9f29d72e29..4e3be8b276 100644 --- a/SCons/Tool/gdc.py +++ b/SCons/Tool/gdc.py @@ -67,7 +67,7 @@ def generate(env) -> None: env['_DINCFLAGS'] = '${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)}' env['_DVERFLAGS'] = '${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)}' env['_DDEBUGFLAGS'] = '${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)}' - env['_DI_FLAGS'] = "${DI_FILE_DIR and DI_FILE_DIR_PREFIX+DI_FILE_DIR+DI_FILE_DIR_SUFFFIX}" + env['_DI_FLAGS'] = '$( ${_concat_dir(DI_FILE_DIR_PREFIX, DI_FILE_DIR, DI_FILE_DIR_SUFFFIX, DINCPREFIX, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['_DFLAGS'] = '${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)}' env['SHDC'] = '$DC' diff --git a/SCons/Tool/ldc.py b/SCons/Tool/ldc.py index 5fb60705a2..dd49341627 100644 --- a/SCons/Tool/ldc.py +++ b/SCons/Tool/ldc.py @@ -71,9 +71,7 @@ def generate(env) -> None: env['_DINCFLAGS'] = '${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)}' env['_DVERFLAGS'] = '${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)}' env['_DDEBUGFLAGS'] = '${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)}' - - env['_DI_FLAGS'] = "${DI_FILE_DIR and DI_FILE_DIR_PREFIX+DI_FILE_DIR+DI_FILE_DIR_SUFFFIX}" - + env['_DI_FLAGS'] = '$( ${_concat_dir(DI_FILE_DIR_PREFIX, DI_FILE_DIR, DI_FILE_DIR_SUFFFIX, DINCPREFIX, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['_DFLAGS'] = '${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)}' env['SHDC'] = '$DC' @@ -96,10 +94,10 @@ def generate(env) -> None: env['DFLAGPREFIX'] = '-' env['DFLAGSUFFIX'] = '' env['DFILESUFFIX'] = '.d' - + env['DI_FILE_DIR'] = '' env['DI_FILE_SUFFIX'] = '.di' - + env['DI_FILE_DIR_PREFIX'] = '-Hd=' env['DI_FILE_DIR_SUFFFIX'] = ''