From 7d0f80485e5629bed900d9145e16749d6df1a89c Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 7 Sep 2023 21:50:10 -0400 Subject: [PATCH 001/386] MSVC: detection fixes and changes Changes: * VS/VC roots are classified by their installed features (e.g, devenv.com, vcexpress.com, etc.). This provides for special-case verification of batch file arguments based on the internal classification. * Consistent with earlier behavior, express versions are used for the non-express msvc version symbol for 14.1 and 8.0 when the express version is the only version installed. * There is a strong possibility that 14.0Exp may not have been detected correctly. It appears that registry keys for earlier versions of msvc are not populated. Refined detection of 14.0Exp was added. * Special case handling of VS2015 BuildTools was added. The msvc batch files restrict the arguments available as compared to full versions. The arguments may be accepted and ignored possibly resulting in build failures that are likely not easy to diagnose. * Special case handling og VS2015 Express was added. The msvc batch files restrict the arguments available as compared to full versions. For example, store/UWP build are only available for x86 targets. * Windows/Platform SDK installations of 7.1, 7.0, and 6.1 populate registry keys and installation folders that were detected by scons (versions 10.0 and 9.0). Unfortunately, the generated files are intended to be used via SetEnv.cmd and result in errors. The detection of sdk-only installations was added and the roots are ignored. * The relative imports of the MSCommon module were changed to top-level absolute imports in a number of microsoft tools. Moving any of the tools to the site tools folder failed on import (i.e., the relative paths become invalid when moved). * VS2005 to VS2015 vcvarsall.bat dispatches to a dependent batch file when configuring the msvc environment. In certain installation scenarios, the dependent batch file (e.g., vcvars64.bat) may not exist. The existence of vcvarsall.bat, the dependent batch file, and the compiler executable are now verified. * MSVC configuration data specific to versions VS2005 to VS2008 was added as the dependent batch files have different names than the batch files for VS2010 and later. VC++ For Python is handled as a special case as the dependent batch files: are not used and are in different locations. * When VC++ For Python is installed using the ALLUSERS=1 command-line option, the registry keys written are under HKLM rather than HKCU. VC++ For Python installed for all users is now correctly detected. * The existing detection configuration for vswhere and the registry was refactored to separate the two methods of detection. * The detection of the msvc compiler executable has been modified and no longer considers the os environment. The detection of the msvc compiler executable was modified to provide more detailed warning messages. --- SCons/Tool/MSCommon/MSVC/Kind.py | 671 +++++++++++ SCons/Tool/MSCommon/MSVC/Registry.py | 3 + SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 52 +- .../MSCommon/MSVC/ScriptArgumentsTests.py | 127 +- SCons/Tool/MSCommon/MSVC/Util.py | 20 + SCons/Tool/MSCommon/MSVC/Warnings.py | 3 + SCons/Tool/MSCommon/MSVC/__init__.py | 1 + SCons/Tool/MSCommon/vc.py | 1033 ++++++++++++++--- SCons/Tool/MSCommon/vcTests.py | 125 +- SCons/Tool/MSCommon/vs.py | 2 +- SCons/Tool/midl.py | 2 +- SCons/Tool/mslib.py | 5 +- SCons/Tool/mslink.py | 7 +- SCons/Tool/mssdk.py | 6 +- SCons/Tool/msvc.py | 9 +- SCons/Tool/msvs.py | 5 +- SCons/Tool/msvsTests.py | 6 +- test/MSVC/MSVC_SDK_VERSION.py | 63 +- test/MSVC/MSVC_TOOLSET_VERSION.py | 1 + test/MSVC/MSVC_USE_SETTINGS.py | 2 +- test/MSVC/MSVC_UWP_APP.py | 137 ++- test/MSVC/VSWHERE.py | 5 + test/MSVC/msvc_cache_force_defaults.py | 15 +- 23 files changed, 1956 insertions(+), 344 deletions(-) create mode 100644 SCons/Tool/MSCommon/MSVC/Kind.py diff --git a/SCons/Tool/MSCommon/MSVC/Kind.py b/SCons/Tool/MSCommon/MSVC/Kind.py new file mode 100644 index 0000000000..8b254a23b4 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/Kind.py @@ -0,0 +1,671 @@ +# 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. + +""" +Version kind categorization for Microsoft Visual C/C++. +""" + +import os +import re + +from collections import ( + namedtuple, +) + +from ..common import ( + debug, +) + +from . import Registry +from . import Util + +from . import Dispatcher +Dispatcher.register_modulename(__name__) + + +# use express install for non-express msvc_version if no other installations found +USE_EXPRESS_FOR_NONEXPRESS = True + +# productdir kind + +VCVER_KIND_UNKNOWN = 0 # undefined +VCVER_KIND_DEVELOP = 1 # devenv binary +VCVER_KIND_EXPRESS = 2 # express binary +VCVER_KIND_BTDISPATCH = 3 # no ide binaries (buildtools dispatch folder) +VCVER_KIND_VCFORPYTHON = 4 # no ide binaries (2008/9.0) +VCVER_KIND_EXPRESS_WIN = 5 # express for windows binary (VSWinExpress) +VCVER_KIND_EXPRESS_WEB = 6 # express for web binary (VWDExpress) +VCVER_KIND_SDK = 7 # no ide binaries +VCVER_KIND_CMDLINE = 8 # no ide binaries + +VCVER_KIND_STR = { + VCVER_KIND_UNKNOWN: '', + VCVER_KIND_DEVELOP: 'Develop', + VCVER_KIND_EXPRESS: 'Express', + VCVER_KIND_BTDISPATCH: 'BTDispatch', + VCVER_KIND_VCFORPYTHON: 'VCForPython', + VCVER_KIND_EXPRESS_WIN: 'Express-Win', + VCVER_KIND_EXPRESS_WEB: 'Express-Web', + VCVER_KIND_SDK: 'SDK', + VCVER_KIND_CMDLINE: 'CmdLine', +} + +BITFIELD_KIND_DEVELOP = 0b_1000 +BITFIELD_KIND_EXPRESS = 0b_0100 +BITFIELD_KIND_EXPRESS_WIN = 0b_0010 +BITFIELD_KIND_EXPRESS_WEB = 0b_0001 + +VCVER_KIND_PROGRAM = namedtuple("VCVerKindProgram", [ + 'kind', # relpath from pdir to vsroot + 'program', # ide binaries + 'bitfield', +]) + +# + +IDE_PROGRAM_DEVENV_COM = VCVER_KIND_PROGRAM( + kind=VCVER_KIND_DEVELOP, + program='devenv.com', + bitfield=BITFIELD_KIND_DEVELOP, +) + +IDE_PROGRAM_MSDEV_COM = VCVER_KIND_PROGRAM( + kind=VCVER_KIND_DEVELOP, + program='msdev.com', + bitfield=BITFIELD_KIND_DEVELOP, +) + +IDE_PROGRAM_WDEXPRESS_EXE = VCVER_KIND_PROGRAM( + kind=VCVER_KIND_EXPRESS, + program='WDExpress.exe', + bitfield=BITFIELD_KIND_EXPRESS, +) + +IDE_PROGRAM_VCEXPRESS_EXE = VCVER_KIND_PROGRAM( + kind=VCVER_KIND_EXPRESS, + program='VCExpress.exe', + bitfield=BITFIELD_KIND_EXPRESS, +) + +IDE_PROGRAM_VSWINEXPRESS_EXE = VCVER_KIND_PROGRAM( + kind=VCVER_KIND_EXPRESS_WIN, + program='VSWinExpress.exe', + bitfield=BITFIELD_KIND_EXPRESS_WIN, +) + +IDE_PROGRAM_VWDEXPRESS_EXE = VCVER_KIND_PROGRAM( + kind=VCVER_KIND_EXPRESS_WEB, + program='VWDExpress.exe', + bitfield=BITFIELD_KIND_EXPRESS_WEB, +) + +# detection configuration + +VCVER_KIND_DETECT = namedtuple("VCVerKindDetect", [ + 'root', # relpath from pdir to vsroot + 'path', # vsroot to ide dir + 'programs', # ide binaries +]) + +# detected binaries + +VCVER_DETECT_BINARIES = namedtuple("VCVerDetectBinaries", [ + 'bitfields', # detect values + 'have_dev', # develop ide binary + 'have_exp', # express ide binary + 'have_exp_win', # express windows ide binary + 'have_exp_web', # express web ide binary +]) + + +VCVER_DETECT_KIND = namedtuple("VCVerDetectKind", [ + 'skip', # skip vs root + 'save', # save in case no other kind found + 'kind', # internal kind + 'binaries_t', + 'extended', +]) + +# unknown value + +_VCVER_DETECT_KIND_UNKNOWN = VCVER_DETECT_KIND( + skip=True, + save=False, + kind=VCVER_KIND_UNKNOWN, + binaries_t=VCVER_DETECT_BINARIES( + bitfields=0b0, + have_dev=False, + have_exp=False, + have_exp_win=False, + have_exp_web=False, + ), + extended={}, +) + +# + +_msvc_pdir_func = None + +def register_msvc_version_pdir_func(func): + global _msvc_pdir_func + if func: + _msvc_pdir_func = func + +_cache_vcver_kind_map = {} + +def msvc_version_register_kind(msvc_version, kind_t) -> None: + global _cache_vcver_kind_map + if kind_t is None: + kind_t = _VCVER_DETECT_KIND_UNKNOWN + # print('register_kind: msvc_version=%s, kind=%s' % (repr(msvc_version), repr(VCVER_KIND_STR[kind_t.kind]))) + debug('msvc_version=%s, kind=%s', repr(msvc_version), repr(VCVER_KIND_STR[kind_t.kind])) + _cache_vcver_kind_map[msvc_version] = kind_t + +def _msvc_version_kind_lookup(msvc_version, env=None): + global _cache_vcver_kind_map + global _msvc_pdir_func + if msvc_version not in _cache_vcver_kind_map: + _msvc_pdir_func(msvc_version, env) + kind_t = _cache_vcver_kind_map.get(msvc_version, _VCVER_DETECT_KIND_UNKNOWN) + debug( + 'kind=%s, dev=%s, exp=%s, msvc_version=%s', + repr(VCVER_KIND_STR[kind_t.kind]), + kind_t.binaries_t.have_dev, kind_t.binaries_t.have_exp, + repr(msvc_version) + ) + return kind_t + +def msvc_version_is_btdispatch(msvc_version, env=None): + kind_t = _msvc_version_kind_lookup(msvc_version, env) + is_btdispatch = bool(kind_t.kind == VCVER_KIND_BTDISPATCH) + debug( + 'is_btdispatch=%s, kind:%s, msvc_version=%s', + repr(is_btdispatch), repr(VCVER_KIND_STR[kind_t.kind]), repr(msvc_version) + ) + return is_btdispatch + +def msvc_version_is_express(msvc_version, env=None): + kind_t = _msvc_version_kind_lookup(msvc_version, env) + is_express = bool(kind_t.kind == VCVER_KIND_EXPRESS) + debug( + 'is_express=%s, kind:%s, msvc_version=%s', + repr(is_express), repr(VCVER_KIND_STR[kind_t.kind]), repr(msvc_version) + ) + return is_express + +def msvc_version_is_vcforpython(msvc_version, env=None): + kind_t = _msvc_version_kind_lookup(msvc_version, env) + is_vcforpython = bool(kind_t.kind == VCVER_KIND_VCFORPYTHON) + debug( + 'is_vcforpython=%s, kind:%s, msvc_version=%s', + repr(is_vcforpython), repr(VCVER_KIND_STR[kind_t.kind]), repr(msvc_version) + ) + return is_vcforpython + +def msvc_version_skip_uwp_target(env, msvc_version): + + vernum = float(Util.get_msvc_version_prefix(msvc_version)) + vernum_int = int(vernum * 10) + + if vernum_int != 140: + return False + + kind_t = _msvc_version_kind_lookup(msvc_version, env) + if kind_t.kind != VCVER_KIND_EXPRESS: + return False + + target_arch = env.get('TARGET_ARCH') + + uwp_is_supported = kind_t.extended.get('uwp_is_supported', {}) + is_supported = uwp_is_supported.get(target_arch, True) + + if is_supported: + return False + + return True + +def _pdir_detect_binaries(pdir, detect): + + vs_root = os.path.join(pdir, detect.root) + ide_path = os.path.join(vs_root, detect.path) + + bitfields = 0b_0000 + for ide_program in detect.programs: + prog = os.path.join(ide_path, ide_program.program) + if not os.path.exists(prog): + continue + bitfields |= ide_program.bitfield + + have_dev = bool(bitfields & BITFIELD_KIND_DEVELOP) + have_exp = bool(bitfields & BITFIELD_KIND_EXPRESS) + have_exp_win = bool(bitfields & BITFIELD_KIND_EXPRESS_WIN) + have_exp_web = bool(bitfields & BITFIELD_KIND_EXPRESS_WEB) + + binaries_t = VCVER_DETECT_BINARIES( + bitfields=bitfields, + have_dev=have_dev, + have_exp=have_exp, + have_exp_win=have_exp_win, + have_exp_web=have_exp_web, + ) + + debug( + 'vs_root=%s, dev=%s, exp=%s, exp_win=%s, exp_web=%s, pdir=%s', + repr(vs_root), + binaries_t.have_dev, binaries_t.have_exp, + binaries_t.have_exp_win, binaries_t.have_exp_web, + repr(pdir) + ) + + return vs_root, binaries_t + +_cache_pdir_vswhere_kind = {} + +def msvc_version_pdir_vswhere_kind(msvc_version, pdir, detect_t): + global _cache_pdir_vswhere_kind + + vc_dir = os.path.normcase(os.path.normpath(pdir)) + cache_key = (msvc_version, vc_dir) + + rval = _cache_pdir_vswhere_kind.get(cache_key) + if rval is not None: + debug('cache=%s', repr(rval)) + return rval + + extended = {} + + prefix, suffix = Util.get_msvc_version_prefix_suffix(msvc_version) + + vs_root, binaries_t = _pdir_detect_binaries(pdir, detect_t) + + if binaries_t.have_dev: + kind = VCVER_KIND_DEVELOP + elif binaries_t.have_exp: + kind = VCVER_KIND_EXPRESS + else: + kind = VCVER_KIND_CMDLINE + + skip = False + save = False + + if suffix != 'Exp' and kind == VCVER_KIND_EXPRESS: + skip = True + save = USE_EXPRESS_FOR_NONEXPRESS + elif suffix == 'Exp' and kind != VCVER_KIND_EXPRESS: + skip = True + + kind_t = VCVER_DETECT_KIND( + skip=skip, + save=save, + kind=kind, + binaries_t=binaries_t, + extended=extended, + ) + + debug( + 'skip=%s, save=%s, kind=%s, msvc_version=%s, pdir=%s', + kind_t.skip, kind_t.save, repr(VCVER_KIND_STR[kind_t.kind]), + repr(msvc_version), repr(pdir) + ) + + _cache_pdir_vswhere_kind[cache_key] = kind_t + + return kind_t + +# VS2015 buildtools batch file call detection +# vs2015 buildtools do not support sdk_version or UWP arguments + +_VS2015BT_PATH = r'..\Microsoft Visual C++ Build Tools\vcbuildtools.bat' + +_VS2015BT_REGEX_STR = ''.join([ + r'^\s*if\s+exist\s+', + re.escape(fr'"%~dp0..\{_VS2015BT_PATH}"'), + r'\s+goto\s+setup_buildsku\s*$', +]) + +_VS2015BT_VCVARS_BUILDTOOLS = re.compile(_VS2015BT_REGEX_STR, re.IGNORECASE) +_VS2015BT_VCVARS_STOP = re.compile(r'^\s*[:]Setup_VS\s*$', re.IGNORECASE) + +def _vs_buildtools_2015_vcvars(vcvars_file): + have_buildtools_vcvars = False + with open(vcvars_file) as fh: + for line in fh: + if _VS2015BT_VCVARS_BUILDTOOLS.match(line): + have_buildtools_vcvars = True + break + if _VS2015BT_VCVARS_STOP.match(line): + break + return have_buildtools_vcvars + +def _vs_buildtools_2015(vs_root, vc_dir): + + is_btdispatch = False + + do_once = True + while do_once: + do_once = False + + buildtools_file = os.path.join(vs_root, _VS2015BT_PATH) + have_buildtools = os.path.exists(buildtools_file) + debug('have_buildtools=%s', have_buildtools) + if not have_buildtools: + break + + vcvars_file = os.path.join(vc_dir, 'vcvarsall.bat') + have_vcvars = os.path.exists(vcvars_file) + debug('have_vcvars=%s', have_vcvars) + if not have_vcvars: + break + + have_buildtools_vcvars = _vs_buildtools_2015_vcvars(vcvars_file) + debug('have_buildtools_vcvars=%s', have_buildtools_vcvars) + if not have_buildtools_vcvars: + break + + is_btdispatch = True + + debug('is_btdispatch=%s', is_btdispatch) + return is_btdispatch + +_VS2015EXP_VCVARS_LIBPATH = re.compile( + ''.join([ + r'^\s*\@if\s+exist\s+\"\%VCINSTALLDIR\%LIB\\store\\(amd64|arm)"\s+', + r'set (LIB|LIBPATH)=\%VCINSTALLDIR\%LIB\\store\\(amd64|arm);.*\%(LIB|LIBPATH)\%\s*$' + ]), + re.IGNORECASE +) + +_VS2015EXP_VCVARS_STOP = re.compile(r'^\s*[:]GetVSCommonToolsDir\s*$', re.IGNORECASE) + +def _vs_express_2015_vcvars(vcvars_file): + n_libpath = 0 + with open(vcvars_file) as fh: + for line in fh: + if _VS2015EXP_VCVARS_LIBPATH.match(line): + n_libpath += 1 + elif _VS2015EXP_VCVARS_STOP.match(line): + break + have_uwp_fix = n_libpath >= 2 + return have_uwp_fix + +def _vs_express_2015(pdir): + + have_uwp_amd64 = False + have_uwp_arm = False + + vcvars_file = os.path.join(pdir, r'vcvarsall.bat') + if os.path.exists(vcvars_file): + + vcvars_file = os.path.join(pdir, r'bin\x86_amd64\vcvarsx86_amd64.bat') + if os.path.exists(vcvars_file): + have_uwp_fix = _vs_express_2015_vcvars(vcvars_file) + if have_uwp_fix: + have_uwp_amd64 = True + + vcvars_file = os.path.join(pdir, r'bin\x86_arm\vcvarsx86_arm.bat') + if os.path.exists(vcvars_file): + have_uwp_fix = _vs_express_2015_vcvars(vcvars_file) + if have_uwp_fix: + have_uwp_arm = True + + debug('have_uwp_amd64=%s, have_uwp_arm=%s', have_uwp_amd64, have_uwp_arm) + return have_uwp_amd64, have_uwp_arm + +# winsdk installed 2010 [7.1], 2008 [7.0, 6.1] folders + +_REGISTRY_WINSDK_VERSIONS = {'10.0', '9.0'} + +_cache_pdir_registry_winsdk = {} + +def _msvc_version_pdir_registry_winsdk(msvc_version, pdir): + global _cache_pdir_registry_winsdk + + # detect winsdk-only installations + # + # registry keys: + # [prefix]\VisualStudio\SxS\VS7\10.0 + # [prefix]\VisualStudio\SxS\VC7\10.0 product directory + # [prefix]\VisualStudio\SxS\VS7\9.0 + # [prefix]\VisualStudio\SxS\VC7\9.0 product directory + # + # winsdk notes: + # - winsdk installs do not define the common tools env var + # - the product dir is detected but the vcvars batch files will fail + # - regular installations populate the VS7 registry keys + # + + vc_dir = os.path.normcase(os.path.normpath(pdir)) + cache_key = (msvc_version, vc_dir) + + rval = _cache_pdir_registry_winsdk.get(cache_key) + if rval is not None: + debug('cache=%s', repr(rval)) + return rval + + if msvc_version not in _REGISTRY_WINSDK_VERSIONS: + + is_sdk = False + + debug('is_sdk=%s, msvc_version=%s', is_sdk, repr(msvc_version)) + + else: + + vc_dir = os.path.normcase(os.path.normpath(pdir)) + + vc_suffix = Registry.vstudio_sxs_vc7(msvc_version) + vc_qresults = [record[0] for record in Registry.microsoft_query_paths(vc_suffix)] + vc_root = os.path.normcase(os.path.normpath(vc_qresults[0])) if vc_qresults else None + + if vc_dir != vc_root: + # registry vc path is not the current pdir + + is_sdk = False + + debug( + 'is_sdk=%s, msvc_version=%s, pdir=%s, vc_root=%s', + is_sdk, repr(msvc_version), repr(vc_dir), repr(vc_root) + ) + + else: + # registry vc path is the current pdir + + vs_suffix = Registry.vstudio_sxs_vs7(msvc_version) + vs_qresults = [record[0] for record in Registry.microsoft_query_paths(vs_suffix)] + vs_root = vs_qresults[0] if vs_qresults else None + + is_sdk = bool(not vs_root and vc_root) + + debug( + 'is_sdk=%s, msvc_version=%s, vs_root=%s, vc_root=%s', + is_sdk, repr(msvc_version), repr(vs_root), repr(vc_root) + ) + + _cache_pdir_registry_winsdk[cache_key] = is_sdk + + return is_sdk + +_cache_pdir_registry_kind = {} + +def msvc_version_pdir_registry_kind(msvc_version, pdir, detect_t, is_vcforpython=False): + global _cache_pdir_registry_kind + + vc_dir = os.path.normcase(os.path.normpath(pdir)) + cache_key = (msvc_version, vc_dir) + + rval = _cache_pdir_registry_kind.get(cache_key) + if rval is not None: + debug('cache=%s', repr(rval)) + return rval + + extended = {} + + prefix, suffix = Util.get_msvc_version_prefix_suffix(msvc_version) + + vs_root, binaries_t = _pdir_detect_binaries(pdir, detect_t) + + if binaries_t.have_dev: + kind = VCVER_KIND_DEVELOP + elif binaries_t.have_exp: + kind = VCVER_KIND_EXPRESS + elif msvc_version == '14.0' and _vs_buildtools_2015(vs_root, pdir): + kind = VCVER_KIND_BTDISPATCH + elif msvc_version == '9.0' and is_vcforpython: + kind = VCVER_KIND_VCFORPYTHON + elif binaries_t.have_exp_win: + kind = VCVER_KIND_EXPRESS_WIN + elif binaries_t.have_exp_web: + kind = VCVER_KIND_EXPRESS_WEB + elif _msvc_version_pdir_registry_winsdk(msvc_version, pdir): + kind = VCVER_KIND_SDK + else: + kind = VCVER_KIND_CMDLINE + + skip = False + save = False + + if kind in (VCVER_KIND_EXPRESS_WIN, VCVER_KIND_EXPRESS_WEB, VCVER_KIND_SDK): + skip = True + elif suffix != 'Exp' and kind == VCVER_KIND_EXPRESS: + skip = True + save = USE_EXPRESS_FOR_NONEXPRESS + elif suffix == 'Exp' and kind != VCVER_KIND_EXPRESS: + skip = True + + if prefix == '14.0' and kind == VCVER_KIND_EXPRESS: + have_uwp_amd64, have_uwp_arm = _vs_express_2015(pdir) + uwp_is_supported = { + 'x86': True, + 'amd64': have_uwp_amd64, + 'arm': have_uwp_arm, + } + extended['uwp_is_supported'] = uwp_is_supported + + kind_t = VCVER_DETECT_KIND( + skip=skip, + save=save, + kind=kind, + binaries_t=binaries_t, + extended=extended, + ) + + debug( + 'skip=%s, save=%s, kind=%s, msvc_version=%s, pdir=%s', + kind_t.skip, kind_t.save, repr(VCVER_KIND_STR[kind_t.kind]), + repr(msvc_version), repr(pdir) + ) + + _cache_pdir_registry_kind[cache_key] = kind_t + + return kind_t + +# queries + +def get_msvc_version_kind(msvc_version, env=None): + kind_t = _msvc_version_kind_lookup(msvc_version, env) + kind_str = VCVER_KIND_STR[kind_t.kind] + debug( + 'kind=%s, kind_str=%s, msvc_version=%s', + repr(kind_t.kind), repr(kind_str), repr(msvc_version) + ) + return (kind_t.kind, kind_str) + +def msvc_version_sdk_version_is_supported(msvc_version, env=None): + + vernum = float(Util.get_msvc_version_prefix(msvc_version)) + vernum_int = int(vernum * 10) + + kind_t = _msvc_version_kind_lookup(msvc_version, env) + + if vernum_int >= 141: + # VS2017 and later + is_supported = True + elif vernum_int == 140: + # VS2015: + # True: Develop, CmdLine + # False: Express, BTDispatch + is_supported = True + if kind_t.kind == VCVER_KIND_EXPRESS: + is_supported = False + elif kind_t.kind == VCVER_KIND_BTDISPATCH: + is_supported = False + else: + # VS2013 and earlier + is_supported = False + + debug( + 'is_supported=%s, msvc_version=%s, kind=%s', + is_supported, repr(msvc_version), repr(VCVER_KIND_STR[kind_t.kind]) + ) + return is_supported + +def msvc_version_uwp_is_supported(msvc_version, target_arch=None, env=None): + + vernum = float(Util.get_msvc_version_prefix(msvc_version)) + vernum_int = int(vernum * 10) + + kind_t = _msvc_version_kind_lookup(msvc_version, env) + + is_target = False + + if vernum_int >= 141: + # VS2017 and later + is_supported = True + elif vernum_int == 140: + # VS2015: + # True: Develop, CmdLine + # Maybe: Express + # False: BTDispatch + is_supported = True + if kind_t.kind == VCVER_KIND_EXPRESS: + uwp_is_supported = kind_t.extended.get('uwp_is_supported', {}) + is_supported = uwp_is_supported.get(target_arch, True) + is_target = True + elif kind_t.kind == VCVER_KIND_BTDISPATCH: + is_supported = False + else: + # VS2013 and earlier + is_supported = False + + debug( + 'is_supported=%s, is_target=%s, msvc_version=%s, kind=%s, target_arch=%s', + is_supported, is_target, repr(msvc_version), repr(VCVER_KIND_STR[kind_t.kind]), repr(target_arch) + ) + + return is_supported, is_target + +# reset cache + +def reset() -> None: + global _cache_installed_vcs + global _cache_vcver_kind_map + global _cache_pdir_vswhere_kind + global _cache_pdir_registry_kind + global _cache_pdir_registry_winsdk + + debug('') + + _cache_installed_vcs = None + _cache_vcver_kind_map = {} + _cache_pdir_vswhere_kind = {} + _cache_pdir_registry_kind = {} + _cache_pdir_registry_winsdk = {} diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py index eee20ccbc7..970b4d4412 100644 --- a/SCons/Tool/MSCommon/MSVC/Registry.py +++ b/SCons/Tool/MSCommon/MSVC/Registry.py @@ -110,6 +110,9 @@ def windows_kit_query_paths(version): q = windows_kits(version) return microsoft_query_paths(q) +def vstudio_sxs_vs7(version): + return '\\'.join([r'VisualStudio\SxS\VS7', version]) + def vstudio_sxs_vc7(version): return '\\'.join([r'VisualStudio\SxS\VC7', version]) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 8848095cf9..c689d7a0b5 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -42,6 +42,7 @@ from . import Config from . import Registry from . import WinSDK +from . import Kind from .Exceptions import ( MSVCInternalError, @@ -196,7 +197,7 @@ def _toolset_version(version): return version_args -def _msvc_script_argument_uwp(env, msvc, arglist): +def _msvc_script_argument_uwp(env, msvc, arglist, target_arch): uwp_app = env['MSVC_UWP_APP'] debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app)) @@ -218,6 +219,23 @@ def _msvc_script_argument_uwp(env, msvc, arglist): ) raise MSVCArgumentError(err_msg) + is_supported, is_target = Kind.msvc_version_uwp_is_supported(msvc.version, target_arch, env) + if not is_supported: + _, kind_str = Kind.get_msvc_version_kind(msvc.version) + debug( + 'invalid: msvc_version constraint: %s %s %s', + repr(msvc.version), repr(kind_str), repr(target_arch) + ) + if is_target and target_arch: + err_msg = "MSVC_UWP_APP ({}) TARGET_ARCH ({}) is not supported for MSVC_VERSION {} ({})".format( + repr(uwp_app), repr(target_arch), repr(msvc.version), repr(kind_str) + ) + else: + err_msg = "MSVC_UWP_APP ({}) is not supported for MSVC_VERSION {} ({})".format( + repr(uwp_app), repr(msvc.version), repr(kind_str) + ) + raise MSVCArgumentError(err_msg) + # VS2017+ rewrites uwp => store for 14.0 toolset uwp_arg = msvc.vs_def.vc_uwp @@ -250,7 +268,7 @@ def _user_script_argument_uwp(env, uwp, user_argstr) -> bool: raise MSVCArgumentError(err_msg) -def _msvc_script_argument_sdk_constraints(msvc, sdk_version): +def _msvc_script_argument_sdk_constraints(msvc, sdk_version, env): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: debug( @@ -263,6 +281,14 @@ def _msvc_script_argument_sdk_constraints(msvc, sdk_version): ) return err_msg + if not Kind.msvc_version_sdk_version_is_supported(msvc.version, env): + _, kind_str = Kind.get_msvc_version_kind(msvc.version) + debug('invalid: msvc_version constraint: %s %s', repr(msvc.version), repr(kind_str)) + err_msg = "MSVC_SDK_VERSION ({}) is not supported for MSVC_VERSION {} ({})".format( + repr(sdk_version), repr(msvc.version), repr(kind_str) + ) + return err_msg + for msvc_sdk_version in msvc.vs_def.vc_sdk_versions: re_sdk_version = re_sdk_dispatch_map[msvc_sdk_version] if re_sdk_version.match(sdk_version): @@ -310,7 +336,7 @@ def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist): if not sdk_version: return None - err_msg = _msvc_script_argument_sdk_constraints(msvc, sdk_version) + err_msg = _msvc_script_argument_sdk_constraints(msvc, sdk_version, env) if err_msg: raise MSVCArgumentError(err_msg) @@ -336,6 +362,9 @@ def _msvc_script_default_sdk(env, msvc, platform_def, arglist, force_sdk: bool=F if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: return None + if not Kind.msvc_version_sdk_version_is_supported(msvc.version, env): + return None + sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def) if not len(sdk_list): return None @@ -873,7 +902,18 @@ def _msvc_process_construction_variables(env) -> bool: return False -def msvc_script_arguments(env, version, vc_dir, arg): +def msvc_script_arguments_has_uwp(env): + + if not _msvc_process_construction_variables(env): + return False + + uwp_app = env.get('MSVC_UWP_APP') + is_uwp = bool(uwp_app and uwp_app in _ARGUMENT_BOOLEAN_TRUE_LEGACY) + + debug('is_uwp=%s', is_uwp) + return is_uwp + +def msvc_script_arguments(env, version, vc_dir, arg=None): arguments = [arg] if arg else [] @@ -889,10 +929,12 @@ def msvc_script_arguments(env, version, vc_dir, arg): if _msvc_process_construction_variables(env): + target_arch = env.get('TARGET_ARCH') + # MSVC_UWP_APP if 'MSVC_UWP_APP' in env: - uwp = _msvc_script_argument_uwp(env, msvc, arglist) + uwp = _msvc_script_argument_uwp(env, msvc, arglist, target_arch) else: uwp = None diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index 670576b046..c79f044c55 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -36,6 +36,10 @@ from SCons.Tool.MSCommon.MSVC import Util from SCons.Tool.MSCommon.MSVC import WinSDK from SCons.Tool.MSCommon.MSVC import ScriptArguments +from SCons.Tool.MSCommon.MSVC.Kind import ( + msvc_version_is_express, + msvc_version_is_btdispatch, +) from SCons.Tool.MSCommon.MSVC.Exceptions import ( MSVCInternalError, @@ -127,7 +131,7 @@ class Data: for vcver in Config.MSVC_VERSION_SUFFIX.keys(): version_def = Util.msvc_version_components(vcver) - vc_dir = vc.find_vc_pdir(None, vcver) + vc_dir = vc.find_vc_pdir(vcver) t = (version_def, vc_dir) ALL_VERSIONS_PAIRS.append(t) if vc_dir: @@ -229,7 +233,13 @@ def test_msvc_script_arguments_defaults(self) -> None: for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: for arg in ('', 'arch'): scriptargs = func(env, version_def.msvc_version, vc_dir, arg) - if version_def.msvc_vernum >= 14.0: + sdk_supported = True + if version_def.msvc_verstr == '14.0': + if msvc_version_is_express(version_def.msvc_version): + sdk_supported = False + elif msvc_version_is_btdispatch(version_def.msvc_version): + sdk_supported = False + if version_def.msvc_vernum >= 14.0 and sdk_supported: if arg and scriptargs.startswith(arg): testargs = scriptargs[len(arg):].lstrip() else: @@ -289,7 +299,7 @@ def run_msvc_script_args_none(self) -> None: {'MSVC_SCRIPT_ARGS': None, 'MSVC_SPECTRE_LIBS': None}, ]: env = Environment(**kwargs) - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) def run_msvc_script_args(self) -> None: func = ScriptArguments.msvc_script_arguments @@ -312,7 +322,7 @@ def run_msvc_script_args(self) -> None: # should not raise exception (argument not validated) env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol') - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) for kwargs in [ {'MSVC_UWP_APP': False, 'MSVC_SCRIPT_ARGS': None}, @@ -323,7 +333,7 @@ def run_msvc_script_args(self) -> None: {'MSVC_SPECTRE_LIBS': 'True', 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre'}, # not boolean ignored ]: env = Environment(**kwargs) - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) for msvc_uwp_app in (True, False): @@ -368,9 +378,9 @@ def run_msvc_script_args(self) -> None: env = Environment(**kwargs) if exc: with self.assertRaises(MSVCArgumentError): - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) else: - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) else: @@ -379,14 +389,14 @@ def run_msvc_script_args(self) -> None: {'MSVC_SDK_VERSION': sdk_def.sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, ]: env = Environment(**kwargs) - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) for kwargs in [ {'MSVC_SCRIPT_ARGS': '-vcvars_ver={}'.format(version_def.msvc_verstr)}, {'MSVC_TOOLSET_VERSION': version_def.msvc_verstr}, ]: env = Environment(**kwargs) - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) msvc_toolset_notfound_version = Data.msvc_toolset_notfound_version(version_def.msvc_version) @@ -398,7 +408,7 @@ def run_msvc_script_args(self) -> None: ]: env = Environment(**kwargs) with self.assertRaises(MSVCToolsetVersionNotFound): - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) msvc_sdk_notfound_version = Data.msvc_sdk_notfound_version(version_def.msvc_version) @@ -407,15 +417,15 @@ def run_msvc_script_args(self) -> None: ]: env = Environment(**kwargs) with self.assertRaises(MSVCSDKVersionNotFound): - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) have_spectre = toolset_def.msvc_toolset_version in Data.SPECTRE_TOOLSET_VERSIONS.get(version_def.msvc_version,[]) env = Environment(MSVC_SPECTRE_LIBS=True, MSVC_TOOLSET_VERSION=toolset_def.msvc_toolset_version) if not have_spectre: with self.assertRaises(MSVCSpectreLibsNotFound): - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) else: - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) msvc_sdk_version = Data.msvc_sdk_version(version_def.msvc_version) @@ -519,25 +529,90 @@ def run_msvc_script_args(self) -> None: ] + more_tests: env = Environment(**kwargs) with self.assertRaises(exc_t): - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) elif version_def.msvc_verstr == '14.0': - # VS2015: MSVC_SDK_VERSION and MSVC_UWP_APP + + if msvc_version_is_express(version_def.msvc_version): + sdk_supported = False + uwp_supported = True # based on target arch + elif msvc_version_is_btdispatch(version_def.msvc_version): + sdk_supported = False + uwp_supported = False + else: + sdk_supported = True + uwp_supported = True env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol') - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) - for msvc_uwp_app in (True, False): + if sdk_supported: + # VS2015: MSVC_SDK_VERSION + + if uwp_supported: + # VS2015: MSVC_UWP_APP + + for msvc_uwp_app in (True, False): + + sdk_list = WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app) + for sdk_version in sdk_list: + + for kwargs in [ + {'MSVC_SCRIPT_ARGS': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + {'MSVC_SDK_VERSION': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + ]: + env = Environment(**kwargs) + _ = func(env, version_def.msvc_version, vc_dir) + + else: + # VS2015: MSVC_UWP_APP error + + for msvc_uwp_app in (True,): + + sdk_list = WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app) + for sdk_version in sdk_list: + + for kwargs in [ + {'MSVC_SCRIPT_ARGS': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + {'MSVC_SDK_VERSION': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + ]: + env = Environment(**kwargs) + with self.assertRaises(MSVCArgumentError): + _ = func(env, version_def.msvc_version, vc_dir) + + else: + # VS2015: MSVC_SDK_VERSION error sdk_list = WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app) for sdk_version in sdk_list: - for kwargs in [ - {'MSVC_SCRIPT_ARGS': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, - {'MSVC_SDK_VERSION': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, - ]: - env = Environment(**kwargs) - _ = func(env, version_def.msvc_version, vc_dir, '') + env = Environment(MSVC_SDK_VERSION=sdk_version) + with self.assertRaises(MSVCArgumentError): + _ = func(env, version_def.msvc_version, vc_dir) + + # MSVC_SCRIPT_ARGS sdk_version not validated + env = Environment(MSVC_SCRIPT_ARGS=sdk_version) + _ = func(env, version_def.msvc_version, vc_dir) + + if uwp_supported: + # VS2015: MSVC_UWP_APP + + for msvc_uwp_app in (True, False): + env = Environment(MSVC_UWP_APP=msvc_uwp_app) + _ = func(env, version_def.msvc_version, vc_dir) + + else: + # VS2015: MSVC_UWP_APP error + + for msvc_uwp_app in (True,): + + env = Environment(MSVC_UWP_APP=msvc_uwp_app) + with self.assertRaises(MSVCArgumentError): + _ = func(env, version_def.msvc_version, vc_dir) + + # MSVC_SCRIPT_ARGS store not validated + env = Environment(MSVC_SCRIPT_ARGS='store') + _ = func(env, version_def.msvc_version, vc_dir) for kwargs in [ {'MSVC_SPECTRE_LIBS': True, 'MSVC_SCRIPT_ARGS': None}, @@ -545,14 +620,14 @@ def run_msvc_script_args(self) -> None: ]: env = Environment(**kwargs) with self.assertRaises(MSVCArgumentError): - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) else: # VS2013 and earlier: no arguments env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol') with self.assertRaises(MSVCArgumentError): - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) for kwargs in [ {'MSVC_UWP_APP': True, 'MSVC_SCRIPT_ARGS': None}, @@ -563,7 +638,7 @@ def run_msvc_script_args(self) -> None: ]: env = Environment(**kwargs) with self.assertRaises(MSVCArgumentError): - _ = func(env, version_def.msvc_version, vc_dir, '') + _ = func(env, version_def.msvc_version, vc_dir) def test_msvc_script_args_none(self) -> None: force = ScriptArguments.msvc_force_default_arguments(force=False) diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index 64b8d673eb..d41ff7d9f6 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -155,6 +155,26 @@ def get_msvc_version_prefix(version): rval = m.group('version') return rval +def get_msvc_version_prefix_suffix(version): + """ + Get the msvc version number prefix and suffix from a string. + + Args: + version: str + version specification + + Returns: + (str, str): the msvc version prefix and suffix + + """ + prefix = suffix = '' + if version: + m = re_msvc_version.match(version) + if m: + prefix = m.group('msvc_version') + suffix = m.group('suffix') if m.group('suffix') else '' + return prefix, suffix + # toolset version query utilities def is_toolset_full(toolset_version) -> bool: diff --git a/SCons/Tool/MSCommon/MSVC/Warnings.py b/SCons/Tool/MSCommon/MSVC/Warnings.py index cab5145a99..902de299a6 100644 --- a/SCons/Tool/MSCommon/MSVC/Warnings.py +++ b/SCons/Tool/MSCommon/MSVC/Warnings.py @@ -30,6 +30,9 @@ class VisualCWarning(SCons.Warnings.WarningOnByDefault): pass +class VSWherePathWarning(VisualCWarning): + pass + class MSVCScriptExecutionWarning(VisualCWarning): pass diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py index 766894d9bb..f87b0f1d8c 100644 --- a/SCons/Tool/MSCommon/MSVC/__init__.py +++ b/SCons/Tool/MSCommon/MSVC/__init__.py @@ -40,6 +40,7 @@ from . import Config # noqa: F401 from . import Util # noqa: F401 from . import Registry # noqa: F401 +from . import Kind # noqa: F401 from . import SetupEnvDefault # noqa: F401 from . import Policy # noqa: F401 from . import WinSDK # noqa: F401 diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index fd806bdd0a..9891ac3506 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -25,9 +25,6 @@ MS Compilers: Visual C/C++ detection and configuration. # TODO: -# * gather all the information from a single vswhere call instead -# of calling repeatedly (use json format?) -# * support passing/setting location for vswhere in env. # * supported arch for versions: for old versions of batch file without # argument, giving bogus argument cannot be detected, so we have to hardcode # this here @@ -52,10 +49,13 @@ namedtuple, OrderedDict, ) +import json +from functools import cmp_to_key import SCons.Util import SCons.Warnings from SCons.Tool import find_program_path +import SCons.Script from . import common from .common import CONFIG_CACHE, debug @@ -65,11 +65,28 @@ from .MSVC.Exceptions import ( VisualCException, + MSVCInternalError, MSVCUserError, MSVCArgumentError, MSVCToolsetVersionNotFound, ) +# user vswhere.exe location as command-line option + +_vswhere_cmdline_arg = '--vswhere-path' +_vswhere_cmdline_var = 'vswhere_path' + +SCons.Script.AddOption( + _vswhere_cmdline_arg, + dest=_vswhere_cmdline_var, + type="string", + nargs=1, + action="store", + metavar='PATH', + default=None, + help='Fully qualified path to vswhere.exe.', +) + # external exceptions class MSVCUnsupportedHostArch(VisualCException): @@ -89,9 +106,6 @@ class MSVCUseSettingsError(MSVCUserError): class UnsupportedVersion(VisualCException): pass -class MissingConfiguration(VisualCException): - pass - class BatchFileExecutionError(VisualCException): pass @@ -105,7 +119,7 @@ class BatchFileExecutionError(VisualCException): # MSVC 9.0 preferred query order: # True: VCForPython, VisualStudio -# FAlse: VisualStudio, VCForPython +# False: VisualStudio, VCForPython _VC90_Prefer_VCForPython = True # Dict to 'canonalize' the arch @@ -396,7 +410,7 @@ def _make_target_host_map(all_hosts, host_all_targets_map): # debug("_LE2019_HOST_TARGET_CFG: %s", _LE2019_HOST_TARGET_CFG) -# 14.0 (VS2015) to 8.0 (VS2005) +# 14.0 (VS2015) to 10.0 (VS2010) # Given a (host, target) tuple, return a tuple containing the argument for # the batch file and a tuple of the path components to find cl.exe. @@ -405,23 +419,23 @@ def _make_target_host_map(all_hosts, host_all_targets_map): # bin directory (i.e., /VC/bin). Any other tools are in subdirectory # named for the the host/target pair or a single name if the host==target. -_LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS = { +_LE2015_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS = { - ('amd64', 'amd64') : ('amd64', ('bin', 'amd64')), - ('amd64', 'x86') : ('amd64_x86', ('bin', 'amd64_x86')), - ('amd64', 'arm') : ('amd64_arm', ('bin', 'amd64_arm')), + ('amd64', 'amd64') : ('amd64', 'vcvars64.bat', ('bin', 'amd64')), + ('amd64', 'x86') : ('amd64_x86', 'vcvarsamd64_x86.bat', ('bin', 'amd64_x86')), + ('amd64', 'arm') : ('amd64_arm', 'vcvarsamd64_arm.bat', ('bin', 'amd64_arm')), - ('x86', 'amd64') : ('x86_amd64', ('bin', 'x86_amd64')), - ('x86', 'x86') : ('x86', ('bin', )), - ('x86', 'arm') : ('x86_arm', ('bin', 'x86_arm')), - ('x86', 'ia64') : ('x86_ia64', ('bin', 'x86_ia64')), + ('x86', 'amd64') : ('x86_amd64', 'vcvarsx86_amd64.bat', ('bin', 'x86_amd64')), + ('x86', 'x86') : ('x86', 'vcvars32.bat', ('bin', )), + ('x86', 'arm') : ('x86_arm', 'vcvarsx86_arm.bat', ('bin', 'x86_arm')), + ('x86', 'ia64') : ('x86_ia64', 'vcvarsx86_ia64.bat', ('bin', 'x86_ia64')), - ('arm64', 'amd64') : ('amd64', ('bin', 'amd64')), - ('arm64', 'x86') : ('amd64_x86', ('bin', 'amd64_x86')), - ('arm64', 'arm') : ('amd64_arm', ('bin', 'amd64_arm')), + ('arm64', 'amd64') : ('amd64', 'vcvars64.bat', ('bin', 'amd64')), + ('arm64', 'x86') : ('amd64_x86', 'vcvarsamd64_x86.bat', ('bin', 'amd64_x86')), + ('arm64', 'arm') : ('amd64_arm', 'vcvarsamd64_arm.bat', ('bin', 'amd64_arm')), - ('arm', 'arm') : ('arm', ('bin', 'arm')), - ('ia64', 'ia64') : ('ia64', ('bin', 'ia64')), + ('arm', 'arm') : ('arm', 'vcvarsarm.bat', ('bin', 'arm')), + ('ia64', 'ia64') : ('ia64', 'vcvars64.bat', ('bin', 'ia64')), } @@ -457,6 +471,53 @@ def _make_target_host_map(all_hosts, host_all_targets_map): # debug("_LE2015_HOST_TARGET_CFG: %s", _LE2015_HOST_TARGET_CFG) +# 9.0 (VS2008) to 8.0 (VS2005) + +_LE2008_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS = { + + ('amd64', 'amd64') : ('amd64', 'vcvarsamd64.bat', ('bin', 'amd64')), + ('amd64', 'x86') : ('x86', 'vcvars32.bat', ('bin', )), + + ('x86', 'amd64') : ('x86_amd64', 'vcvarsx86_amd64.bat', ('bin', 'x86_amd64')), + ('x86', 'x86') : ('x86', 'vcvars32.bat', ('bin', )), + ('x86', 'ia64') : ('x86_ia64', 'vcvarsx86_ia64.bat', ('bin', 'x86_ia64')), + + ('arm64', 'amd64') : ('amd64', 'vcvarsamd64.bat', ('bin', 'amd64')), + ('arm64', 'x86') : ('x86', 'vcvars32.bat', ('bin', )), + + ('ia64', 'ia64') : ('ia64', 'vcvarsia64.bat', ('bin', 'ia64')), + +} + +_LE2008_HOST_TARGET_CFG = _host_target_config_factory( + + label = 'LE2008', + + host_all_hosts = OrderedDict([ + ('amd64', ['amd64', 'x86']), + ('x86', ['x86']), + ('arm64', ['amd64', 'x86']), + ('ia64', ['ia64']), + ]), + + host_all_targets = { + 'amd64': ['amd64', 'x86'], + 'x86': ['x86', 'amd64', 'ia64'], + 'arm64': ['amd64', 'x86'], + 'ia64': ['ia64'], + }, + + host_def_targets = { + 'amd64': ['amd64', 'x86'], + 'x86': ['x86'], + 'arm64': ['amd64', 'x86'], + 'ia64': ['ia64'], + }, + +) + +# debug("_LE2008_HOST_TARGET_CFG: %s", _LE2008_HOST_TARGET_CFG) + # 7.1 (VS2003) and earlier # For 7.1 (VS2003) and earlier, there are only x86 targets and the batch files @@ -490,6 +551,11 @@ def _make_target_host_map(all_hosts, host_all_targets_map): _CL_EXE_NAME = 'cl.exe' +_VSWHERE_EXE = 'vswhere.exe' + +# case-insensitive endswith vswhere.exe +_re_match_vswhere = re.compile('^.*' + re.escape(_VSWHERE_EXE) + '$', re.IGNORECASE) + def get_msvc_version_numeric(msvc_version): """Get the raw version numbers from a MSVC_VERSION string, so it could be cast to float or other numeric values. For example, '14.0Exp' @@ -569,9 +635,12 @@ def get_host_target(env, msvc_version, all_host_targets: bool=False): elif 143 > vernum_int >= 141: # 14.2 (VS2019) to 14.1 (VS2017) host_target_cfg = _LE2019_HOST_TARGET_CFG - elif 141 > vernum_int >= 80: - # 14.0 (VS2015) to 8.0 (VS2005) + elif 141 > vernum_int >= 100: + # 14.0 (VS2015) to 10.0 (VS2010) host_target_cfg = _LE2015_HOST_TARGET_CFG + elif 100 > vernum_int >= 80: + # 9.0 (VS2008) to 8.0 (VS2005) + host_target_cfg = _LE2008_HOST_TARGET_CFG else: # 80 > vernum_int # 7.1 (VS2003) and earlier host_target_cfg = _LE2003_HOST_TARGET_CFG @@ -690,38 +759,58 @@ def _skip_sendtelemetry(env): "7.0", "6.0"] -# if using vswhere, configure command line arguments to probe for installed VC editions -_VCVER_TO_VSWHERE_VER = { - '14.3': [ - ["-version", "[17.0, 18.0)"], # default: Enterprise, Professional, Community (order unpredictable?) - ["-version", "[17.0, 18.0)", "-products", "Microsoft.VisualStudio.Product.BuildTools"], # BuildTools - ], - '14.2': [ - ["-version", "[16.0, 17.0)"], # default: Enterprise, Professional, Community (order unpredictable?) - ["-version", "[16.0, 17.0)", "-products", "Microsoft.VisualStudio.Product.BuildTools"], # BuildTools - ], - '14.1': [ - ["-version", "[15.0, 16.0)"], # default: Enterprise, Professional, Community (order unpredictable?) - ["-version", "[15.0, 16.0)", "-products", "Microsoft.VisualStudio.Product.BuildTools"], # BuildTools - ], - '14.1Exp': [ - ["-version", "[15.0, 16.0)", "-products", "Microsoft.VisualStudio.Product.WDExpress"], # Express - ], -} - +# VS2017 and later: use a single vswhere json query to find all installations + +# vswhere query: +# map vs major version to vc version (no suffix) +# build set of supported vc versions (including suffix) + +_VSWHERE_VSMAJOR_TO_VCVERSION = {} +_VSWHERE_SUPPORTED_VCVER = set() + +for vs_major, vc_version, vc_ver_list in ( + ('17', '14.3', None), + ('16', '14.2', None), + ('15', '14.1', ['14.1Exp']), +): + _VSWHERE_VSMAJOR_TO_VCVERSION[vs_major] = vc_version + _VSWHERE_SUPPORTED_VCVER.add(vc_version) + if vc_ver_list: + for vc_ver in vc_ver_list: + _VSWHERE_SUPPORTED_VCVER.add(vc_ver) + +# vwhere query: +# build of set of candidate component ids +# preferred ranking: Enterprise, Professional, Community, BuildTools, Express +# Ent, Pro, Com, BT, Exp are in the same list +# Exp also has it's own list +# currently, only the express (Exp) suffix is expected + +_VSWHERE_COMPONENTID_CANDIDATES = set() +_VSWHERE_COMPONENTID_RANKING = {} +_VSWHERE_COMPONENTID_SUFFIX = {} +_VSWHERE_COMPONENTID_SCONS_SUFFIX = {} + +for component_id, component_rank, component_suffix, scons_suffix in ( + ('Enterprise', 140, 'Ent', ''), + ('Professional', 130, 'Pro', ''), + ('Community', 120, 'Com', ''), + ('BuildTools', 110, 'BT', ''), + ('WDExpress', 100, 'Exp', 'Exp'), +): + _VSWHERE_COMPONENTID_CANDIDATES.add(component_id) + _VSWHERE_COMPONENTID_RANKING[component_id] = component_rank + _VSWHERE_COMPONENTID_SUFFIX[component_id] = component_suffix + _VSWHERE_COMPONENTID_SCONS_SUFFIX[component_id] = scons_suffix + +# VS2015 and earlier: configure registry queries to probe for installed VC editions _VCVER_TO_PRODUCT_DIR = { - '14.3': [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # not set by this version - '14.2': [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # not set by this version - '14.1': [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # not set by this version - '14.1Exp': [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # not set by this version '14.0': [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir'),], '14.0Exp': [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir')], + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\WDExpress\14.0\Setup\VS\ProductDir'), # vs root + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir'), # not populated? + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], # kind detection '12.0': [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\12.0\Setup\VC\ProductDir'), ], @@ -741,17 +830,20 @@ def _skip_sendtelemetry(env): (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'), ], '9.0': [ - (SCons.Util.HKEY_CURRENT_USER, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), + (SCons.Util.HKEY_CURRENT_USER, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), # vs root + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), # vs root (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',), ] if _VC90_Prefer_VCForPython else [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',), - (SCons.Util.HKEY_CURRENT_USER, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), + (SCons.Util.HKEY_CURRENT_USER, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), # vs root + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), # vs root ], '9.0Exp': [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'), ], '8.0': [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'), + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'), ], '8.0Exp': [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'), @@ -767,6 +859,62 @@ def _skip_sendtelemetry(env): ] } +# detect ide binaries + +VS2022_VS2002_DEV = ( + MSVC.Kind.IDE_PROGRAM_DEVENV_COM, # devenv.com +) + +VS1998_DEV = ( + MSVC.Kind.IDE_PROGRAM_MSDEV_COM, # MSDEV.COM +) + +VS2017_EXP = ( + MSVC.Kind.IDE_PROGRAM_WDEXPRESS_EXE, # WDExpress.exe +) + +VS2015_VS2012_EXP = ( + MSVC.Kind.IDE_PROGRAM_WDEXPRESS_EXE, # WDExpress.exe [Desktop] + MSVC.Kind.IDE_PROGRAM_VSWINEXPRESS_EXE, # VSWinExpress.exe [Windows] + MSVC.Kind.IDE_PROGRAM_VWDEXPRESS_EXE, # VWDExpress.exe [Web] +) + +VS2010_VS2005_EXP = ( + MSVC.Kind.IDE_PROGRAM_VCEXPRESS_EXE, +) + +# detect productdir kind + +_DETECT = MSVC.Kind.VCVER_KIND_DETECT + +_VCVER_KIND_DETECT = { + + # 'VCVer': (relpath from pdir to vsroot, path from vsroot to ide binaries, ide binaries) + + '14.3': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV), # 2022 + '14.2': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV), # 2019 + '14.1': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2017_EXP), # 2017 + '14.1Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2017_EXP), # 2017 + + '14.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2015 + '14.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2015 + '12.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2013 + '12.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2013 + '11.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2012 + '11.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2012 + + '10.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2010 + '10.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2010 + '9.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2008 + '9.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2008 + '8.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2005 + '8.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2005 + + '7.1': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV), # 2003 + '7.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV), # 2001 + + '6.0': _DETECT(root='..', path=r'Common\MSDev98\Bin', programs=VS1998_DEV), # 1998 +} def msvc_version_to_maj_min(msvc_version): msvc_version_numeric = get_msvc_version_numeric(msvc_version) @@ -788,6 +936,88 @@ def msvc_version_to_maj_min(msvc_version): os.path.expandvars(r"%ChocolateyInstall%\bin"), ]] +# normalize user-specified vswhere paths + +_cache_user_vswhere_paths = {} + +def _vswhere_user_path(pval): + global _cache_user_vswhere_path + + rval = _cache_user_vswhere_paths.get(pval, UNDEFINED) + if rval != UNDEFINED: + debug('vswhere_path=%s', rval) + return rval + + vswhere_path = None + if pval: + + if not os.path.exists(pval): + + warn_msg = f'vswhere path not found: {pval!r}' + SCons.Warnings.warn(MSVC.Warnings.VSWherePathWarning, warn_msg) + debug(warn_msg) + + elif not _re_match_vswhere.match(pval): + + warn_msg = f'vswhere.exe not found in vswhere path: {pval!r}' + SCons.Warnings.warn(MSVC.Warnings.VSWherePathWarning, warn_msg) + debug(warn_msg) + + else: + + vswhere_path = MSVC.Util.process_path(pval) + debug('vswhere_path=%s', vswhere_path) + + _cache_user_vswhere_paths[pval] = vswhere_path + + return vswhere_path + +# normalized user-specified command-line vswhere path + +_vswhere_path_cmdline = UNDEFINED + +def _msvc_cmdline_vswhere(): + global _vswhere_path_cmdline + + if _vswhere_path_cmdline == UNDEFINED: + + vswhere_path = None + vswhere_user = SCons.Script.GetOption(_vswhere_cmdline_var) + + if vswhere_user: + vswhere_path = _vswhere_user_path(vswhere_user) + + _vswhere_path_cmdline = vswhere_path + debug('vswhere_path=%s', vswhere_path) + + return _vswhere_path_cmdline + +# normalized default vswhere path + +_vswhere_paths_processed = [ + MSVC.Util.process_path(pval) + for pval in VSWHERE_PATHS + if os.path.exists(pval) +] + +_vswhere_path_default = UNDEFINED + +def _msvc_default_vswhere(): + global _vswhere_paths_processed + global _vswhere_path_default + + if _vswhere_path_default == UNDEFINED: + + if _vswhere_paths_processed: + vswhere_path = _vswhere_paths_processed[0] + else: + vswhere_path = None + + _vswhere_path_default = vswhere_path + debug('vswhere_path=%s', vswhere_path) + + return _vswhere_path_default + def msvc_find_vswhere(): """ Find the location of vswhere """ # For bug 3333: support default location of vswhere for both @@ -795,14 +1025,265 @@ def msvc_find_vswhere(): # For bug 3542: also accommodate not being on C: drive. # NB: this gets called from testsuite on non-Windows platforms. # Whether that makes sense or not, don't break it for those. - vswhere_path = None - for pf in VSWHERE_PATHS: - if os.path.exists(pf): - vswhere_path = pf - break + vswhere_path = _msvc_cmdline_vswhere() + if not vswhere_path: + for pf in VSWHERE_PATHS: + if os.path.exists(pf): + vswhere_path = pf + break return vswhere_path +class _VSWhere: + + reset_funcs = [] + + @classmethod + def reset(cls): + + cls.seen_vswhere = set() + cls.seen_root = set() + + cls.vswhere_executables = [] + + cls.msvc_instances = [] + cls.msvc_map = {} + + @staticmethod + def msvc_instances_default_order(a, b): + # vc version numeric: descending order + if a.vc_version_numeric != b.vc_version_numeric: + return 1 if a.vc_version_numeric < b.vc_version_numeric else -1 + # vc release: descending order (release, preview) + if a.vc_release != b.vc_release: + return 1 if a.vc_release < b.vc_release else -1 + # component rank: descending order + if a.vc_component_rank != b.vc_component_rank: + return 1 if a.vc_component_rank < b.vc_component_rank else -1 + return 0 + + @classmethod + def register_reset_func(cls, func): + cls.reset_funcs.append(func) + + @classmethod + def call_reset_funcs(cls): + for func in cls.reset_funcs: + func() + +_VSWhere.reset() + +def _filter_vswhere_paths(env): + + vswhere_paths = [] + + if env and 'VSWHERE' in env: + vswhere_environ = _vswhere_user_path(env.subst('$VSWHERE')) + if vswhere_environ and vswhere_environ not in _VSWhere.seen_vswhere: + vswhere_paths.append(vswhere_environ) + + vswhere_cmdline = _msvc_cmdline_vswhere() + if vswhere_cmdline and vswhere_cmdline not in _VSWhere.seen_vswhere: + vswhere_paths.append(vswhere_cmdline) + + vswhere_default = _msvc_default_vswhere() + if vswhere_default and vswhere_default not in _VSWhere.seen_vswhere: + vswhere_paths.append(vswhere_default) + + debug('vswhere_paths=%s', vswhere_paths) + return vswhere_paths + +def _vswhere_query_json_output(vswhere_exe, vswhere_args): + + vswhere_json = None + + once = True + while once: + once = False + # using break for single exit (unless exception) + + vswhere_cmd = [vswhere_exe] + vswhere_args + ['-format', 'json', '-utf8'] + debug("running: %s", vswhere_cmd) + + try: + cp = subprocess.run(vswhere_cmd, stdout=PIPE, stderr=PIPE, check=True) + except OSError as e: + errmsg = str(e) + debug("%s: %s", type(e).__name__, errmsg) + break + except Exception as e: + errmsg = str(e) + debug("%s: %s", type(e).__name__, errmsg) + raise + + if not cp.stdout: + debug("no vswhere information returned") + break + + vswhere_output = cp.stdout.decode('utf8', errors='replace') + if not vswhere_output: + debug("no vswhere information output") + break + + try: + vswhere_output_json = json.loads(vswhere_output) + except json.decoder.JSONDecodeError: + debug("json decode exception loading vswhere output") + break + + vswhere_json = vswhere_output_json + break + + debug('vswhere_json=%s, vswhere_exe=%s', bool(vswhere_json), repr(vswhere_exe)) + + return vswhere_json + +MSVC_INSTANCE = namedtuple('MSVCInstance', [ + 'vc_path', + 'vc_version', + 'vc_version_numeric', + 'vc_version_scons', + 'vc_release', + 'vc_component_id', + 'vc_component_rank', + 'vc_component_suffix', +]) + +def _update_vswhere_msvc_map(env): + + vswhere_paths = _filter_vswhere_paths(env) + if not vswhere_paths: + debug('new_roots=False, msvc_instances=%s', len(_VSWhere.msvc_instances)) + return _VSWhere.msvc_map + + n_instances = len(_VSWhere.msvc_instances) + + for vswhere_exe in vswhere_paths: + + if vswhere_exe in _VSWhere.seen_vswhere: + continue + + _VSWhere.seen_vswhere.add(vswhere_exe) + _VSWhere.vswhere_executables.append(vswhere_exe) + + debug('vswhere_exe=%s', repr(vswhere_exe)) + + vswhere_json = _vswhere_query_json_output( + vswhere_exe, + ['-all', '-products', '*'] + ) + + if not vswhere_json: + continue + + for instance in vswhere_json: + + #print(json.dumps(instance, indent=4, sort_keys=True)) + + installation_path = instance.get('installationPath') + if not installation_path or not os.path.exists(installation_path): + continue + + vc_root = os.path.join(installation_path, 'VC') + if not os.path.exists(vc_root): + continue + + vc_root = MSVC.Util.process_path(vc_root) + if vc_root in _VSWhere.seen_root: + continue + + _VSWhere.seen_root.add(vc_root) + + installation_version = instance.get('installationVersion') + if not installation_version: + continue + + vs_major = installation_version.split('.')[0] + if not vs_major in _VSWHERE_VSMAJOR_TO_VCVERSION: + debug('ignore vs_major: %s', vs_major) + continue + + vc_version = _VSWHERE_VSMAJOR_TO_VCVERSION[vs_major] + + product_id = instance.get('productId') + if not product_id: + continue + + component_id = product_id.split('.')[-1] + if component_id not in _VSWHERE_COMPONENTID_CANDIDATES: + debug('ignore component_id: %s', component_id) + continue + + component_rank = _VSWHERE_COMPONENTID_RANKING.get(component_id,0) + if component_rank == 0: + raise MSVCInternalError(f'unknown component_rank for component_id: {component_id!r}') + + scons_suffix = _VSWHERE_COMPONENTID_SCONS_SUFFIX[component_id] + + if scons_suffix: + vc_version_scons = vc_version + scons_suffix + else: + vc_version_scons = vc_version + + is_prerelease = True if instance.get('isPrerelease', False) else False + is_release = False if is_prerelease else True + + msvc_instance = MSVC_INSTANCE( + vc_path = vc_root, + vc_version = vc_version, + vc_version_numeric = float(vc_version), + vc_version_scons = vc_version_scons, + vc_release = is_release, + vc_component_id = component_id, + vc_component_rank = component_rank, + vc_component_suffix = component_suffix, + ) + + _VSWhere.msvc_instances.append(msvc_instance) + + new_roots = bool(len(_VSWhere.msvc_instances) > n_instances) + if new_roots: + + _VSWhere.msvc_instances = sorted( + _VSWhere.msvc_instances, + key=cmp_to_key(_VSWhere.msvc_instances_default_order) + ) + + _VSWhere.msvc_map = {} + + for msvc_instance in _VSWhere.msvc_instances: + + debug( + 'msvc instance: msvc_version=%s, is_release=%s, component_id=%s, vc_path=%s', + repr(msvc_instance.vc_version_scons), msvc_instance.vc_release, + repr(msvc_instance.vc_component_id), repr(msvc_instance.vc_path) + ) + + key = (msvc_instance.vc_version_scons, msvc_instance.vc_release) + _VSWhere.msvc_map.setdefault(key,[]).append(msvc_instance) + + if msvc_instance.vc_version_scons == msvc_instance.vc_version: + continue + + key = (msvc_instance.vc_version, msvc_instance.vc_release) + _VSWhere.msvc_map.setdefault(key,[]).append(msvc_instance) + + _VSWhere.call_reset_funcs() + + debug('new_roots=%s, msvc_instances=%s', new_roots, len(_VSWhere.msvc_instances)) + + return _VSWhere.msvc_map + +_cache_pdir_vswhere_queries = {} + +def _reset_pdir_vswhere_queries(): + global _cache_pdir_vswhere_queries + _cache_pdir_vswhere_queries = {} + debug('reset _cache_pdir_vswhere_queries') + +# register pdir vswhere cache reset function with vswhere state manager +_VSWhere.register_reset_func(_reset_pdir_vswhere_queries) + def find_vc_pdir_vswhere(msvc_version, env=None): """ Find the MSVC product directory using the vswhere program. @@ -817,109 +1298,225 @@ def find_vc_pdir_vswhere(msvc_version, env=None): UnsupportedVersion: if the version is not known by this file """ + global _cache_pdir_vswhere_queries + + msvc_map = _update_vswhere_msvc_map(env) + if not msvc_map: + return None + + rval = _cache_pdir_vswhere_queries.get(msvc_version, UNDEFINED) + if rval != UNDEFINED: + debug('msvc_version=%s, pdir=%s', repr(msvc_version), repr(rval)) + return rval + + if msvc_version not in _VSWHERE_SUPPORTED_VCVER: + debug("Unknown version of MSVC: %s", msvc_version) + raise UnsupportedVersion("Unknown version %s" % msvc_version) + + is_release = True + key = (msvc_version, is_release) + + msvc_instances = msvc_map.get(key, UNDEFINED) + if msvc_instances == UNDEFINED: + debug( + 'msvc instances lookup failed: msvc_version=%s, is_release=%s', + repr(msvc_version), repr(is_release) + ) + msvc_instances = [] + + save_pdir_kind = [] + + pdir = None + kind_t = None + + for msvc_instance in msvc_instances: + + vcdir = msvc_instance.vc_path + + vckind_t = MSVC.Kind.msvc_version_pdir_vswhere_kind(msvc_version, vcdir, _VCVER_KIND_DETECT[msvc_version]) + if vckind_t.skip: + if vckind_t.save: + debug('save kind: msvc_version=%s, pdir=%s', repr(msvc_version), repr(vcdir)) + save_pdir_kind.append((vcdir, vckind_t)) + else: + debug('skip kind: msvc_version=%s, pdir=%s', repr(msvc_version), repr(vcdir)) + continue + + pdir = vcdir + kind_t = vckind_t + break + + if not pdir and not kind_t: + if save_pdir_kind: + pdir, kind_t = save_pdir_kind[0] + + MSVC.Kind.msvc_version_register_kind(msvc_version, kind_t) + + debug('msvc_version=%s, pdir=%s', repr(msvc_version), repr(pdir)) + _cache_pdir_vswhere_queries[msvc_version] = pdir + + return pdir + +_cache_pdir_registry_queries = {} + +def find_vc_pdir_registry(msvc_version): + """ Find the MSVC product directory using the registry. + + Args: + msvc_version: MSVC version to search for + + Returns: + MSVC install dir or None + + Raises: + UnsupportedVersion: if the version is not known by this file + + """ + global _cache_pdir_registry_queries + + rval = _cache_pdir_registry_queries.get(msvc_version, UNDEFINED) + if rval != UNDEFINED: + debug('msvc_version=%s, pdir=%s', repr(msvc_version), repr(rval)) + return rval + try: - vswhere_version = _VCVER_TO_VSWHERE_VER[msvc_version] + regkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] except KeyError: debug("Unknown version of MSVC: %s", msvc_version) raise UnsupportedVersion("Unknown version %s" % msvc_version) from None - if env is None or not env.get('VSWHERE'): - vswhere_path = msvc_find_vswhere() - else: - vswhere_path = env.subst('$VSWHERE') + save_pdir_kind = [] - if vswhere_path is None: - return None + is_win64 = common.is_win64() - debug('VSWHERE: %s', vswhere_path) - for vswhere_version_args in vswhere_version: + pdir = None + kind_t = None - vswhere_cmd = [vswhere_path] + vswhere_version_args + ["-property", "installationPath"] + root = 'Software\\' + for hkroot, key in regkeys: - debug("running: %s", vswhere_cmd) + if not hkroot or not key: + continue - # TODO: Python 3.7 - # cp = subprocess.run(vswhere_cmd, capture_output=True, check=True) # 3.7+ only - cp = subprocess.run(vswhere_cmd, stdout=PIPE, stderr=PIPE, check=True) - - if cp.stdout: - # vswhere could return multiple lines, e.g. if Build Tools - # and {Community,Professional,Enterprise} are both installed. - # We could define a way to pick the one we prefer, but since - # this data is currently only used to make a check for existence, - # returning the first hit should be good enough. - lines = cp.stdout.decode("mbcs").splitlines() - return os.path.join(lines[0], 'VC') + if is_win64: + msregkeys = [root + 'Wow6432Node\\' + key, root + key] else: - # We found vswhere, but no install info available for this version - pass + msregkeys = [root + key] - return None + vcdir = None + for msregkey in msregkeys: + debug('trying VC registry key %s', repr(msregkey)) + try: + vcdir = common.read_reg(msregkey, hkroot) + except OSError: + continue + if vcdir: + break + + if not vcdir: + debug('no VC registry key %s', repr(key)) + continue + + is_vcforpython = False + + is_vsroot = False + if msvc_version == '9.0' and key.lower().endswith('\\vcforpython\\9.0\\installdir'): + # Visual C++ for Python registry key is VS installdir (root) not VC productdir + is_vsroot = True + is_vcforpython = True + elif msvc_version == '14.0Exp' and key.lower().endswith('\\14.0\\setup\\vs\\productdir'): + # 2015Exp VS productdir (root) not VC productdir + is_vsroot = True + + if is_vsroot: + vcdir = os.path.join(vcdir, 'VC') + debug('convert vs root to vc dir: %s', repr(vcdir)) + + if not os.path.exists(vcdir): + debug('reg says dir is %s, but it does not exist. (ignoring)', repr(vcdir)) + continue + + vckind_t = MSVC.Kind.msvc_version_pdir_registry_kind(msvc_version, vcdir, _VCVER_KIND_DETECT[msvc_version], is_vcforpython) + if vckind_t.skip: + if vckind_t.save: + debug('save kind: msvc_version=%s, pdir=%s', repr(msvc_version), repr(vcdir)) + save_pdir_kind.append((vcdir, vckind_t)) + else: + debug('skip kind: msvc_version=%s, pdir=%s', repr(msvc_version), repr(vcdir)) + continue + + pdir = vcdir + kind_t = vckind_t + break + + if not pdir and not kind_t: + if save_pdir_kind: + pdir, kind_t = save_pdir_kind[0] + + MSVC.Kind.msvc_version_register_kind(msvc_version, kind_t) + debug('msvc_version=%s, pdir=%s', repr(msvc_version), repr(pdir)) + _cache_pdir_registry_queries[msvc_version] = pdir -def find_vc_pdir(env, msvc_version): + return pdir + +def find_vc_pdir(msvc_version, env=None): """Find the MSVC product directory for the given version. - Tries to look up the path using a registry key from the table - _VCVER_TO_PRODUCT_DIR; if there is no key, calls find_vc_pdir_wshere - for help instead. + Use find_vc_pdir_vsvwhere for msvc versions 14.1 and later. + Use find_vc_pdir_registry for msvc versions 14.0 and earlier. Args: msvc_version: str msvc version (major.minor, e.g. 10.0) + env: + optional to look up VSWHERE variable Returns: str: Path found in registry, or None Raises: UnsupportedVersion: if the version is not known by this file. - MissingConfiguration: found version but the directory is missing. - Both exceptions inherit from VisualCException. + UnsupportedVersion inherits from VisualCException. """ - root = 'Software\\' - try: - hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] - except KeyError: - debug("Unknown version of MSVC: %s", msvc_version) - raise UnsupportedVersion("Unknown version %s" % msvc_version) from None - for hkroot, key in hkeys: - try: - comps = None - if not key: - comps = find_vc_pdir_vswhere(msvc_version, env) - if not comps: - debug('no VC found for version %s', repr(msvc_version)) - raise OSError - debug('VC found: %s', repr(msvc_version)) - return comps - else: - if common.is_win64(): - try: - # ordinarily at win64, try Wow6432Node first. - comps = common.read_reg(root + 'Wow6432Node\\' + key, hkroot) - except OSError: - # at Microsoft Visual Studio for Python 2.7, value is not in Wow6432Node - pass - if not comps: - # not Win64, or Microsoft Visual Studio for Python 2.7 - comps = common.read_reg(root + key, hkroot) - except OSError: - debug('no VC registry key %s', repr(key)) - else: - if msvc_version == '9.0' and key.lower().endswith('\\vcforpython\\9.0\\installdir'): - # Visual C++ for Python registry key is installdir (root) not productdir (vc) - comps = os.path.join(comps, 'VC') - debug('found VC in registry: %s', comps) - if os.path.exists(comps): - return comps - else: - debug('reg says dir is %s, but it does not exist. (ignoring)', comps) - raise MissingConfiguration(f"registry dir {comps} not found on the filesystem") + if msvc_version in _VSWHERE_SUPPORTED_VCVER: + + pdir = find_vc_pdir_vswhere(msvc_version, env) + if pdir: + debug('VC found: %s, dir=%s', repr(msvc_version), repr(pdir)) + return pdir + + elif msvc_version in _VCVER_TO_PRODUCT_DIR: + + pdir = find_vc_pdir_registry(msvc_version) + if pdir: + debug('VC found: %s, dir=%s', repr(msvc_version), repr(pdir)) + return pdir + + else: + + debug("Unknown version of MSVC: %s", repr(msvc_version)) + raise UnsupportedVersion("Unknown version %s" % repr(msvc_version)) from None + + debug('no VC found for version %s', repr(msvc_version)) return None +# register find_vc_pdir function with Kind routines +# in case of unknown msvc_version (just in case) +MSVC.Kind.register_msvc_version_pdir_func(find_vc_pdir) + +def _reset_vc_pdir(): + debug('reset pdir caches') + global _cache_user_vswhere_path + global _cache_pdir_vswhere_queries + global _cache_pdir_registry_queries + _cache_user_vswhere_paths = {} + _cache_pdir_vswhere_queries = {} + _cache_pdir_registry_queries = {} + def find_batch_file(msvc_version, host_arch, target_arch, pdir): """ Find the location of the batch script which should set up the compiler @@ -939,6 +1536,7 @@ def find_batch_file(msvc_version, host_arch, target_arch, pdir): arg = '' vcdir = None clexe = None + depbat = None if vernum_int >= 143: # 14.3 (VS2022) and later @@ -952,15 +1550,26 @@ def find_batch_file(msvc_version, host_arch, target_arch, pdir): batfile, _ = _LE2019_HOST_TARGET_BATCHFILE_CLPATHCOMPS[(host_arch, target_arch)] batfilename = os.path.join(batfiledir, batfile) vcdir = pdir - elif 141 > vernum_int >= 80: - # 14.0 (VS2015) to 8.0 (VS2005) - arg, cl_path_comps = _LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS[(host_arch, target_arch)] + elif 141 > vernum_int >= 100: + # 14.0 (VS2015) to 10.0 (VS2010) + arg, batfile, cl_path_comps = _LE2015_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS[(host_arch, target_arch)] batfilename = os.path.join(pdir, "vcvarsall.bat") - if msvc_version == '9.0' and not os.path.exists(batfilename): - # Visual C++ for Python batch file is in installdir (root) not productdir (vc) - batfilename = os.path.normpath(os.path.join(pdir, os.pardir, "vcvarsall.bat")) - # Visual C++ for Python sdk batch files do not point to the VCForPython installation + depbat = os.path.join(pdir, *cl_path_comps, batfile) + clexe = os.path.join(pdir, *cl_path_comps, _CL_EXE_NAME) + elif 100 > vernum_int >= 80: + # 9.0 (VS2008) to 8.0 (VS2005) + arg, batfile, cl_path_comps = _LE2008_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS[(host_arch, target_arch)] + if vernum_int == 90 and MSVC.Kind.msvc_version_is_vcforpython(msvc_version): + # 9.0 (VS2008) Visual C++ for Python: + # sdk batch files do not point to the VCForPython installation + # vcvarsall batch file is in installdir not productdir (e.g., vc\..\vcvarsall.bat) + # dependency batch files are not called from vcvarsall.bat sdk_pdir = None + batfilename = os.path.join(pdir, os.pardir, "vcvarsall.bat") + depbat = None + else: + batfilename = os.path.join(pdir, "vcvarsall.bat") + depbat = os.path.join(pdir, *cl_path_comps, batfile) clexe = os.path.join(pdir, *cl_path_comps, _CL_EXE_NAME) else: # 80 > vernum_int # 7.1 (VS2003) and earlier @@ -973,7 +1582,11 @@ def find_batch_file(msvc_version, host_arch, target_arch, pdir): batfilename = None if clexe and not os.path.exists(clexe): - debug("cl.exe not found: %s", clexe) + debug("%s not found: %s", _CL_EXE_NAME, clexe) + batfilename = None + + if depbat and not os.path.exists(depbat): + debug("dependency batch file not found: %s", depbat) batfilename = None return batfilename, arg, vcdir, sdk_pdir @@ -998,16 +1611,26 @@ def find_batch_file_sdk(host_arch, target_arch, sdk_pdir): return None __INSTALLED_VCS_RUN = None + +def _reset_installed_vcs(): + global __INSTALLED_VCS_RUN + debug('reset __INSTALLED_VCS_RUN') + __INSTALLED_VCS_RUN = None + +# register vcs cache reset function with vswhere state manager +_VSWhere.register_reset_func(_reset_installed_vcs) + _VC_TOOLS_VERSION_FILE_PATH = ['Auxiliary', 'Build', 'Microsoft.VCToolsVersion.default.txt'] _VC_TOOLS_VERSION_FILE = os.sep.join(_VC_TOOLS_VERSION_FILE_PATH) -def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version) -> bool: - """Return status of finding a cl.exe to use. +def _check_files_exist_in_vc_dir(env, vc_dir, msvc_version) -> bool: + """Return status of finding batch file and cl.exe to use. - Locates cl in the vc_dir depending on TARGET_ARCH, HOST_ARCH and the - msvc version. TARGET_ARCH and HOST_ARCH can be extracted from the - passed env, unless the env is None, in which case the native platform is - assumed for the host and all associated targets. + Locates required vcvars batch files and cl in the vc_dir depending on + TARGET_ARCH, HOST_ARCH and the msvc version. TARGET_ARCH and HOST_ARCH + can be extracted from the passed env, unless the env is None, in which + case the native platform is assumed for the host and all associated + targets. Args: env: Environment @@ -1066,33 +1689,72 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version) -> bool: debug('unsupported host/target platform combo: (%s,%s)', host_platform, target_platform) continue - _, cl_path_comps = batchfile_clpathcomps + batfile, cl_path_comps = batchfile_clpathcomps + + batfile_path = os.path.join(vc_dir, "Auxiliary", "Build", batfile) + if not os.path.exists(batfile_path): + debug("batch file not found: %s", batfile_path) + continue + cl_path = os.path.join(vc_dir, 'Tools', 'MSVC', vc_specific_version, *cl_path_comps, _CL_EXE_NAME) - debug('checking for %s at %s', _CL_EXE_NAME, cl_path) + if not os.path.exists(cl_path): + debug("%s not found: %s", _CL_EXE_NAME, cl_path) + continue - if os.path.exists(cl_path): - debug('found %s!', _CL_EXE_NAME) - return True + debug('%s found: %s', _CL_EXE_NAME, cl_path) + return True elif 141 > vernum_int >= 80: # 14.0 (VS2015) to 8.0 (VS2005) + if vernum_int >= 100: + # 14.0 (VS2015) to 10.0 (VS2010) + host_target_batcharg_batchfile_clpathcomps = _LE2015_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS + else: + # 9.0 (VS2008) to 8.0 (VS2005) + host_target_batcharg_batchfile_clpathcomps = _LE2008_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS + + if vernum_int == 90 and MSVC.Kind.msvc_version_is_vcforpython(msvc_version): + # 9.0 (VS2008) Visual C++ for Python: + # vcvarsall batch file is in installdir not productdir (e.g., vc\..\vcvarsall.bat) + # dependency batch files are not called from vcvarsall.bat + batfile_path = os.path.join(vc_dir, os.pardir, "vcvarsall.bat") + check_depbat = False + else: + batfile_path = os.path.join(vc_dir, "vcvarsall.bat") + check_depbat = True + + if not os.path.exists(batfile_path): + debug("batch file not found: %s", batfile_path) + return False + for host_platform, target_platform in host_target_list: debug('host platform %s, target platform %s for version %s', host_platform, target_platform, msvc_version) - batcharg_clpathcomps = _LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS.get((host_platform, target_platform), None) - if batcharg_clpathcomps is None: + batcharg_batchfile_clpathcomps = host_target_batcharg_batchfile_clpathcomps.get( + (host_platform, target_platform), None + ) + + if batcharg_batchfile_clpathcomps is None: debug('unsupported host/target platform combo: (%s,%s)', host_platform, target_platform) continue - _, cl_path_comps = batcharg_clpathcomps + _, batfile, cl_path_comps = batcharg_batchfile_clpathcomps + + if check_depbat: + batfile_path = os.path.join(vc_dir, *cl_path_comps, batfile) + if not os.path.exists(batfile_path): + debug("batch file not found: %s", batfile_path) + continue + cl_path = os.path.join(vc_dir, *cl_path_comps, _CL_EXE_NAME) - debug('checking for %s at %s', _CL_EXE_NAME, cl_path) + if not os.path.exists(cl_path): + debug("%s not found: %s", _CL_EXE_NAME, cl_path) + continue - if os.path.exists(cl_path): - debug('found %s', _CL_EXE_NAME) - return True + debug('%s found: %s', _CL_EXE_NAME, cl_path) + return True elif 80 > vernum_int >= 60: # 7.1 (VS2003) to 6.0 (VS6) @@ -1109,7 +1771,7 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version) -> bool: for cl_dir in cl_dirs: cl_path = os.path.join(cl_root, cl_dir, _CL_EXE_NAME) if os.path.exists(cl_path): - debug('%s found %s', _CL_EXE_NAME, cl_path) + debug('%s found: %s', _CL_EXE_NAME, cl_path) return True return False @@ -1122,6 +1784,10 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version) -> bool: def get_installed_vcs(env=None): global __INSTALLED_VCS_RUN + # the installed vcs cache is cleared + # if new vc roots are discovered + _update_vswhere_msvc_map(env) + if __INSTALLED_VCS_RUN is not None: return __INSTALLED_VCS_RUN @@ -1137,10 +1803,10 @@ def get_installed_vcs(env=None): for ver in _VCVER: debug('trying to find VC %s', ver) try: - VC_DIR = find_vc_pdir(env, ver) + VC_DIR = find_vc_pdir(ver, env) if VC_DIR: debug('found VC %s', ver) - if _check_cl_exists_in_vc_dir(env, VC_DIR, ver): + if _check_files_exist_in_vc_dir(env, VC_DIR, ver): installed_versions.append(ver) else: debug('no compiler found %s', ver) @@ -1163,8 +1829,9 @@ def get_installed_vcs(env=None): def reset_installed_vcs() -> None: """Make it try again to find VC. This is just for the tests.""" - global __INSTALLED_VCS_RUN - __INSTALLED_VCS_RUN = None + _reset_installed_vcs() + _reset_vc_pdir() + _VSWhere.reset() MSVC._reset() def msvc_default_version(env=None): @@ -1340,13 +2007,10 @@ def msvc_find_valid_batch_script(env, version): # Find the product directory pdir = None try: - pdir = find_vc_pdir(env, version) + pdir = find_vc_pdir(version, env) except UnsupportedVersion: # Unsupported msvc version (raise MSVCArgumentError?) pass - except MissingConfiguration: - # Found version, directory missing - pass debug('product directory: version=%s, pdir=%s', version, pdir) # Find the host, target, and all candidate (host, target) platform combinations: @@ -1388,6 +2052,13 @@ def msvc_find_valid_batch_script(env, version): if not vc_script: continue + if not target_platform and MSVC.ScriptArguments.msvc_script_arguments_has_uwp(env): + # no target arch specified and is a store/uwp build + if MSVC.Kind.msvc_version_skip_uwp_target(env, version): + # store/uwp may not be supported for all express targets (prevent constraint error) + debug('skip uwp target arch: version=%s, target=%s', repr(version), repr(target_arch)) + continue + # Try to use the located batch file for this host/target platform combo arg = MSVC.ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg) debug('trying vc_script:%s, vc_script_args:%s', repr(vc_script), arg) @@ -1523,19 +2194,32 @@ def msvc_setup_env(env): SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) return None + found_cl_path = None + found_cl_envpath = None + + seen_path = False for k, v in d.items(): + if not seen_path and k.upper() == 'PATH': + seen_path = True + found_cl_path = SCons.Util.WhereIs('cl', v) + found_cl_envpath = SCons.Util.WhereIs('cl', env['ENV'].get(k, [])) env.PrependENVPath(k, v, delete_existing=True) debug("env['ENV']['%s'] = %s", k, env['ENV'][k]) - # final check to issue a warning if the compiler is not present - if not find_program_path(env, 'cl'): - debug("did not find %s", _CL_EXE_NAME) + debug("cl paths: d['PATH']=%s, ENV['PATH']=%s", repr(found_cl_path), repr(found_cl_envpath)) + + # final check to issue a warning if the requested compiler is not present + if not found_cl_path: if CONFIG_CACHE: propose = f"SCONS_CACHE_MSVC_CONFIG caching enabled, remove cache file {CONFIG_CACHE} if out of date." else: propose = "It may need to be installed separately with Visual Studio." - warn_msg = f"Could not find MSVC compiler 'cl'. {propose}" - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + warn_msg = "Could not find requested MSVC compiler 'cl'." + if found_cl_envpath: + warn_msg += " A 'cl' was found on the scons ENV path which may be erroneous." + warn_msg += " %s" + debug(warn_msg, propose) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg % propose) def msvc_exists(env=None, version=None): vcs = get_installed_vcs(env) @@ -1612,7 +2296,6 @@ def msvc_sdk_versions(version=None, msvc_uwp_app: bool=False): def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False): debug('msvc_version=%s, full=%s, sxs=%s', repr(msvc_version), repr(full), repr(sxs)) - env = None rval = [] if not msvc_version: @@ -1626,7 +2309,7 @@ def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False): msg = f'Unsupported msvc version {msvc_version!r}' raise MSVCArgumentError(msg) - vc_dir = find_vc_pdir(env, msvc_version) + vc_dir = find_vc_pdir(msvc_version) if not vc_dir: debug('VC folder not found for version %s', repr(msvc_version)) return rval @@ -1637,7 +2320,6 @@ def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False): def msvc_toolset_versions_spectre(msvc_version=None): debug('msvc_version=%s', repr(msvc_version)) - env = None rval = [] if not msvc_version: @@ -1651,7 +2333,7 @@ def msvc_toolset_versions_spectre(msvc_version=None): msg = f'Unsupported msvc version {msvc_version!r}' raise MSVCArgumentError(msg) - vc_dir = find_vc_pdir(env, msvc_version) + vc_dir = find_vc_pdir(msvc_version) if not vc_dir: debug('VC folder not found for version %s', repr(msvc_version)) return rval @@ -1706,7 +2388,6 @@ def msvc_query_version_toolset(version=None, prefer_newest: bool=True): """ debug('version=%s, prefer_newest=%s', repr(version), repr(prefer_newest)) - env = None msvc_version = None msvc_toolset_version = None @@ -1771,7 +2452,7 @@ def msvc_query_version_toolset(version=None, prefer_newest: bool=True): continue seen_msvc_version.add(query_msvc_version) - vc_dir = find_vc_pdir(env, query_msvc_version) + vc_dir = find_vc_pdir(query_msvc_version) if not vc_dir: continue diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index 9c2bd0ac8c..8c22ba37e9 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -75,7 +75,7 @@ def testDefaults(self) -> None: class MSVcTestCase(unittest.TestCase): @staticmethod - def _createDummyCl(path, add_bin: bool=True) -> None: + def _createDummyFile(path, filename, add_bin: bool=True) -> None: """ Creates a dummy cl.exe in the correct directory. It will create all missing parent directories as well @@ -94,7 +94,7 @@ def _createDummyCl(path, add_bin: bool=True) -> None: if create_path and not os.path.isdir(create_path): os.makedirs(create_path) - create_this = os.path.join(create_path,'cl.exe') + create_this = os.path.join(create_path, filename) # print("Creating: %s"%create_this) with open(create_this,'w') as ct: @@ -105,18 +105,10 @@ def runTest(self) -> None: """ Check that all proper HOST_PLATFORM and TARGET_PLATFORM are handled. Verify that improper HOST_PLATFORM and/or TARGET_PLATFORM are properly handled. - by SCons.Tool.MSCommon.vc._check_cl_exists_in_vc_dir() + by SCons.Tool.MSCommon.vc._check_files_exist_in_vc_dir() """ - check = SCons.Tool.MSCommon.vc._check_cl_exists_in_vc_dir - - env={'TARGET_ARCH':'x86'} - _, clpathcomps = SCons.Tool.MSCommon.vc._LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS[('x86','x86')] - path = os.path.join('.', *clpathcomps) - MSVcTestCase._createDummyCl(path, add_bin=False) - - # print("retval:%s"%check(env, '.', '8.0')) - + check = SCons.Tool.MSCommon.vc._check_files_exist_in_vc_dir # Setup for 14.1 (VS2017) and later tests @@ -131,122 +123,70 @@ def runTest(self) -> None: except IOError as e: print("Failed trying to write :%s :%s" % (tools_version_file, e)) - # Test 14.3 (VS2022) and later vc_ge2022_list = SCons.Tool.MSCommon.vc._GE2022_HOST_TARGET_CFG.all_pairs - for host, target in vc_ge2022_list: batfile, clpathcomps = SCons.Tool.MSCommon.vc._GE2022_HOST_TARGET_BATCHFILE_CLPATHCOMPS[(host,target)] # print("GE 14.3 Got: (%s, %s) -> (%s, %s)"%(host,target,batfile,clpathcomps)) env={'TARGET_ARCH':target, 'HOST_ARCH':host} + path = os.path.join('.', "Auxiliary", "Build", batfile) + MSVcTestCase._createDummyFile(path, batfile, add_bin=False) path = os.path.join('.', 'Tools', 'MSVC', MS_TOOLS_VERSION, *clpathcomps) - MSVcTestCase._createDummyCl(path, add_bin=False) + MSVcTestCase._createDummyFile(path, 'cl.exe', add_bin=False) result=check(env, '.', '14.3') # print("for:(%s, %s) got :%s"%(host, target, result)) self.assertTrue(result, "Checking host: %s target: %s" % (host, target)) - # Now test bogus value for HOST_ARCH - env={'TARGET_ARCH':'x86', 'HOST_ARCH':'GARBAGE'} - try: - result=check(env, '.', '14.3') - # print("for:%s got :%s"%(env, result)) - self.assertFalse(result, "Did not fail with bogus HOST_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) - except MSVCUnsupportedHostArch: - pass - else: - self.fail('Did not fail when HOST_ARCH specified as: %s' % env['HOST_ARCH']) - - # Now test bogus value for TARGET_ARCH - env={'TARGET_ARCH':'GARBAGE', 'HOST_ARCH':'x86'} - try: - result=check(env, '.', '14.3') - # print("for:%s got :%s"%(env, result)) - self.assertFalse(result, "Did not fail with bogus TARGET_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) - except MSVCUnsupportedTargetArch: - pass - else: - self.fail('Did not fail when TARGET_ARCH specified as: %s' % env['TARGET_ARCH']) - # Test 14.2 (VS2019) to 14.1 (VS2017) versions vc_le2019_list = SCons.Tool.MSCommon.vc._LE2019_HOST_TARGET_CFG.all_pairs - for host, target in vc_le2019_list: batfile, clpathcomps = SCons.Tool.MSCommon.vc._LE2019_HOST_TARGET_BATCHFILE_CLPATHCOMPS[(host,target)] # print("LE 14.2 Got: (%s, %s) -> (%s, %s)"%(host,target,batfile,clpathcomps)) env={'TARGET_ARCH':target, 'HOST_ARCH':host} path = os.path.join('.', 'Tools', 'MSVC', MS_TOOLS_VERSION, *clpathcomps) - MSVcTestCase._createDummyCl(path, add_bin=False) + MSVcTestCase._createDummyFile(path, 'cl.exe', add_bin=False) result=check(env, '.', '14.1') # print("for:(%s, %s) got :%s"%(host, target, result)) self.assertTrue(result, "Checking host: %s target: %s" % (host, target)) - # Now test bogus value for HOST_ARCH - env={'TARGET_ARCH':'x86', 'HOST_ARCH':'GARBAGE'} - try: - result=check(env, '.', '14.1') - # print("for:%s got :%s"%(env, result)) - self.assertFalse(result, "Did not fail with bogus HOST_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) - except MSVCUnsupportedHostArch: - pass - else: - self.fail('Did not fail when HOST_ARCH specified as: %s' % env['HOST_ARCH']) - - # Now test bogus value for TARGET_ARCH - env={'TARGET_ARCH':'GARBAGE', 'HOST_ARCH':'x86'} - try: - result=check(env, '.', '14.1') - # print("for:%s got :%s"%(env, result)) - self.assertFalse(result, "Did not fail with bogus TARGET_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) - except MSVCUnsupportedTargetArch: - pass - else: - self.fail('Did not fail when TARGET_ARCH specified as: %s' % env['TARGET_ARCH']) - - # Test 14.0 (VS2015) to 8.0 (VS2005) versions + # Test 14.0 (VS2015) to 10.0 (VS2010) versions vc_le2015_list = SCons.Tool.MSCommon.vc._LE2015_HOST_TARGET_CFG.all_pairs - for host, target in vc_le2015_list: - batarg, clpathcomps = SCons.Tool.MSCommon.vc._LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS[(host, target)] - # print("LE 14.0 Got: (%s, %s) -> (%s, %s)"%(host,target,batarg,clpathcomps)) + batarg, batfile, clpathcomps = SCons.Tool.MSCommon.vc._LE2015_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS[(host, target)] + # print("LE 14.0 Got: (%s, %s) -> (%s, %s, %s)"%(host,target,batarg,batfile,clpathcomps)) env={'TARGET_ARCH':target, 'HOST_ARCH':host} + MSVcTestCase._createDummyFile('.', 'vcvarsall.bat', add_bin=False) path = os.path.join('.', *clpathcomps) - MSVcTestCase._createDummyCl(path, add_bin=False) - result=check(env, '.', '9.0') + MSVcTestCase._createDummyFile(path, batfile, add_bin=False) + MSVcTestCase._createDummyFile(path, 'cl.exe', add_bin=False) + result=check(env, '.', '10.0') # print("for:(%s, %s) got :%s"%(host, target, result)) self.assertTrue(result, "Checking host: %s target: %s" % (host, target)) - # Now test bogus value for HOST_ARCH - env={'TARGET_ARCH':'x86', 'HOST_ARCH':'GARBAGE'} - try: - result=check(env, '.', '9.0') - # print("for:%s got :%s"%(env, result)) - self.assertFalse(result, "Did not fail with bogus HOST_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) - except MSVCUnsupportedHostArch: - pass - else: - self.fail('Did not fail when HOST_ARCH specified as: %s' % env['HOST_ARCH']) - - # Now test bogus value for TARGET_ARCH - env={'TARGET_ARCH':'GARBAGE', 'HOST_ARCH':'x86'} - try: - result=check(env, '.', '9.0') - # print("for:%s got :%s"%(env, result)) - self.assertFalse(result, "Did not fail with bogus TARGET_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) - except MSVCUnsupportedTargetArch: - pass - else: - self.fail('Did not fail when TARGET_ARCH specified as: %s' % env['TARGET_ARCH']) + # Test 9.0 (VC2008) to 8.0 (VS2005) + vc_le2008_list = SCons.Tool.MSCommon.vc._LE2008_HOST_TARGET_CFG.all_pairs + for host, target in vc_le2008_list: + batarg, batfile, clpathcomps = SCons.Tool.MSCommon.vc._LE2008_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS[(host, target)] + # print("LE 9.0 Got: (%s, %s) -> (%s, %s, %s)"%(host,target,batarg,batfile,clpathcomps)) + env={'TARGET_ARCH':target, 'HOST_ARCH':host} + MSVcTestCase._createDummyFile('.', 'vcvarsall.bat', add_bin=False) + path = os.path.join('.', *clpathcomps) + MSVcTestCase._createDummyFile(path, batfile, add_bin=False) + MSVcTestCase._createDummyFile(path, 'cl.exe', add_bin=False) + # check will fail if '9.0' and VCForPython (layout different) + result=check(env, '.', '8.0') + # print("for:(%s, %s) got :%s"%(host, target, result)) + self.assertTrue(result, "Checking host: %s target: %s" % (host, target)) # Test 7.1 (VS2003) and earlier vc_le2003_list = SCons.Tool.MSCommon.vc._LE2003_HOST_TARGET_CFG.all_pairs - for host, target in vc_le2003_list: # print("LE 7.1 Got: (%s, %s)"%(host,target)) env={'TARGET_ARCH':target, 'HOST_ARCH':host} path = os.path.join('.') - MSVcTestCase._createDummyCl(path) + MSVcTestCase._createDummyFile(path, 'cl.exe') result=check(env, '.', '6.0') # print("for:(%s, %s) got :%s"%(host, target, result)) self.assertTrue(result, "Checking host: %s target: %s" % (host, target)) @@ -254,7 +194,7 @@ def runTest(self) -> None: # Now test bogus value for HOST_ARCH env={'TARGET_ARCH':'x86', 'HOST_ARCH':'GARBAGE'} try: - result=check(env, '.', '6.0') + result=check(env, '.', '14.3') # print("for:%s got :%s"%(env, result)) self.assertFalse(result, "Did not fail with bogus HOST_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) except MSVCUnsupportedHostArch: @@ -265,7 +205,7 @@ def runTest(self) -> None: # Now test bogus value for TARGET_ARCH env={'TARGET_ARCH':'GARBAGE', 'HOST_ARCH':'x86'} try: - result=check(env, '.', '6.0') + result=check(env, '.', '14.3') # print("for:%s got :%s"%(env, result)) self.assertFalse(result, "Did not fail with bogus TARGET_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) except MSVCUnsupportedTargetArch: @@ -273,7 +213,6 @@ def runTest(self) -> None: else: self.fail('Did not fail when TARGET_ARCH specified as: %s' % env['TARGET_ARCH']) - class Data: HAVE_MSVC = True if MSCommon.vc.msvc_default_version() else False diff --git a/SCons/Tool/MSCommon/vs.py b/SCons/Tool/MSCommon/vs.py index af0fd26e5a..ef4f13cdfb 100644 --- a/SCons/Tool/MSCommon/vs.py +++ b/SCons/Tool/MSCommon/vs.py @@ -66,7 +66,7 @@ def find_batch_file(self): return batch_file def find_vs_dir_by_vc(self, env): - dir = SCons.Tool.MSCommon.vc.find_vc_pdir(env, self.vc_version) + dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version, env) if not dir: debug('no installed VC %s', self.vc_version) return None diff --git a/SCons/Tool/midl.py b/SCons/Tool/midl.py index 0c640f5092..2ae3f73d06 100644 --- a/SCons/Tool/midl.py +++ b/SCons/Tool/midl.py @@ -37,7 +37,7 @@ import SCons.Scanner.IDL import SCons.Util -from .MSCommon import msvc_setup_env_tool +from SCons.Tool.MSCommon import msvc_setup_env_tool tool_name = 'midl' diff --git a/SCons/Tool/mslib.py b/SCons/Tool/mslib.py index 6e15a808fa..bdce135f81 100644 --- a/SCons/Tool/mslib.py +++ b/SCons/Tool/mslib.py @@ -41,7 +41,10 @@ import SCons.Tool.msvc import SCons.Util -from .MSCommon import msvc_setup_env_tool, msvc_setup_env_once +from SCons.Tool.MSCommon import ( + msvc_setup_env_tool, + msvc_setup_env_once, +) tool_name = 'mslib' diff --git a/SCons/Tool/mslink.py b/SCons/Tool/mslink.py index 1e5b71ae10..74ceaa8572 100644 --- a/SCons/Tool/mslink.py +++ b/SCons/Tool/mslink.py @@ -43,8 +43,11 @@ import SCons.Tool.msvs import SCons.Util -from .MSCommon import msvc_setup_env_once, msvc_setup_env_tool -from .MSCommon.common import get_pch_node +from SCons.Tool.MSCommon import ( + msvc_setup_env_once, + msvc_setup_env_tool, +) +from SCons.Tool.MSCommon.common import get_pch_node tool_name = 'mslink' diff --git a/SCons/Tool/mssdk.py b/SCons/Tool/mssdk.py index 0151eff2b8..ef272c033d 100644 --- a/SCons/Tool/mssdk.py +++ b/SCons/Tool/mssdk.py @@ -33,8 +33,10 @@ selection method. """ -from .MSCommon import mssdk_exists, \ - mssdk_setup_env +from SCons.Tool.MSCommon import ( + mssdk_exists, + mssdk_setup_env, +) def generate(env) -> None: """Add construction variables for an MS SDK to an Environment.""" diff --git a/SCons/Tool/msvc.py b/SCons/Tool/msvc.py index 33a67d0f4f..6afa171c97 100644 --- a/SCons/Tool/msvc.py +++ b/SCons/Tool/msvc.py @@ -44,8 +44,13 @@ import SCons.Warnings import SCons.Scanner.RC -from .MSCommon import msvc_setup_env_tool, msvc_setup_env_once, msvc_version_to_maj_min, msvc_find_vswhere -from .MSCommon.common import get_pch_node +from SCons.Tool.MSCommon import ( + msvc_setup_env_tool, + msvc_setup_env_once, + msvc_version_to_maj_min, + msvc_find_vswhere, +) +from SCons.Tool.MSCommon.common import get_pch_node tool_name = 'msvc' diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index 153b84ecd6..16e422dace 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -45,7 +45,10 @@ import SCons.Warnings from SCons.Defaults import processDefines from SCons.compat import PICKLE_PROTOCOL -from .MSCommon import msvc_setup_env_tool, msvc_setup_env_once +from SCons.Tool.MSCommon import ( + msvc_setup_env_tool, + msvc_setup_env_once, +) tool_name = 'msvs' diff --git a/SCons/Tool/msvsTests.py b/SCons/Tool/msvsTests.py index dd708d0a7e..1266055494 100644 --- a/SCons/Tool/msvsTests.py +++ b/SCons/Tool/msvsTests.py @@ -880,9 +880,9 @@ class msvs71TestCase(msvsTestCase): class msvs8ExpTestCase(msvsTestCase): # XXX: only one still not working """Test MSVS 8 Express Registry""" registry = DummyRegistry(regdata_8exp + regdata_cv) - default_version = '8.0Exp' - highest_version = '8.0Exp' - number_of_versions = 1 + default_version = '8.0' + highest_version = '8.0' + number_of_versions = 2 install_locs = { '6.0' : {}, '7.0' : {}, diff --git a/test/MSVC/MSVC_SDK_VERSION.py b/test/MSVC/MSVC_SDK_VERSION.py index e5eae6762c..f3f9913b52 100644 --- a/test/MSVC/MSVC_SDK_VERSION.py +++ b/test/MSVC/MSVC_SDK_VERSION.py @@ -31,6 +31,10 @@ from SCons.Tool.MSCommon.vc import get_installed_vcs_components from SCons.Tool.MSCommon import msvc_sdk_versions from SCons.Tool.MSCommon import msvc_toolset_versions +from SCons.Tool.MSCommon.MSVC.Kind import ( + msvc_version_is_express, + msvc_version_is_btdispatch, +) import TestSCons test = TestSCons.TestSCons() @@ -38,12 +42,28 @@ installed_versions = get_installed_vcs_components() default_version = installed_versions[0] -GE_VS2015_versions = [v for v in installed_versions if v.msvc_vernum >= 14.0] -LT_VS2015_versions = [v for v in installed_versions if v.msvc_vernum < 14.0] + +GE_VS2015_supported_versions = [] +GE_VS2015_unsupported_versions = [] +LT_VS2015_unsupported_versions = [] + +for v in installed_versions: + if v.msvc_vernum > 14.0: + GE_VS2015_supported_versions.append(v) + elif v.msvc_verstr == '14.0': + if msvc_version_is_express(v.msvc_version): + GE_VS2015_unsupported_versions.append((v, 'Express')) + elif msvc_version_is_btdispatch(v.msvc_version): + GE_VS2015_unsupported_versions.append((v, 'BTDispatch')) + else: + GE_VS2015_supported_versions.append(v) + else: + LT_VS2015_unsupported_versions.append(v) + default_sdk_versions_uwp = msvc_sdk_versions(version=None, msvc_uwp_app=True) default_sdk_versions_def = msvc_sdk_versions(version=None, msvc_uwp_app=False) -have_140 = any(v.msvc_verstr == '14.0' for v in GE_VS2015_versions) +have_140 = any(v.msvc_verstr == '14.0' for v in installed_versions) def version_major(version): components = version.split('.') @@ -64,9 +84,10 @@ def version_major_list(version_list): seen_major.add(major) return versions -if GE_VS2015_versions: +if GE_VS2015_supported_versions: - for supported in GE_VS2015_versions: + for supported in GE_VS2015_supported_versions: + # VS2017+ and VS2015 ('14.0') sdk_versions_uwp = msvc_sdk_versions(version=supported.msvc_version, msvc_uwp_app=True) sdk_versions_def = msvc_sdk_versions(version=supported.msvc_version, msvc_uwp_app=False) @@ -203,9 +224,37 @@ def version_major_list(version_list): )) test.run(arguments='-Q -s', stdout='') -if LT_VS2015_versions: +if GE_VS2015_unsupported_versions: + + for unsupported, kind_str in GE_VS2015_unsupported_versions: + # VS2015 Express + + sdk_version = default_sdk_versions_def[0] if default_sdk_versions_def else '8.1' + + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, tools=['msvc']) + """.format(repr(unsupported.msvc_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_SDK_VERSION ({}) is not supported for MSVC_VERSION {} ({}):".format( + repr(sdk_version), repr(unsupported.msvc_version), repr(kind_str) + ) + test.must_contain_all(test.stderr(), expect) + + # MSVC_SCRIPT_ARGS sdk_version is not validated + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS={}, tools=['msvc']) + """.format(repr(unsupported.msvc_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', stdout='') + +if LT_VS2015_unsupported_versions: - for unsupported in LT_VS2015_versions: + for unsupported in LT_VS2015_unsupported_versions: # must be VS2015 or later sdk_version = default_sdk_versions_def[0] if default_sdk_versions_def else '8.1' diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index a20cf8a319..7c93938019 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -38,6 +38,7 @@ installed_versions = get_installed_vcs_components() default_version = installed_versions[0] + GE_VS2017_versions = [v for v in installed_versions if v.msvc_vernum >= 14.1] LT_VS2017_versions = [v for v in installed_versions if v.msvc_vernum < 14.1] LT_VS2015_versions = [v for v in LT_VS2017_versions if v.msvc_vernum < 14.0] diff --git a/test/MSVC/MSVC_USE_SETTINGS.py b/test/MSVC/MSVC_USE_SETTINGS.py index 7c58c7b9a5..fd6f85ceb5 100644 --- a/test/MSVC/MSVC_USE_SETTINGS.py +++ b/test/MSVC/MSVC_USE_SETTINGS.py @@ -56,7 +56,7 @@ """ % locals()) test.run(arguments="--warn=visual-c-missing .", status=0, stderr=None) -test.must_contain_all(test.stderr(), "Could not find MSVC compiler 'cl'") +test.must_contain_all(test.stderr(), "Could not find requested MSVC compiler 'cl'") test.write('SConstruct', """ env = Environment(MSVC_USE_SETTINGS='dict or None') diff --git a/test/MSVC/MSVC_UWP_APP.py b/test/MSVC/MSVC_UWP_APP.py index 30b07ef771..fd52fd8f5e 100644 --- a/test/MSVC/MSVC_UWP_APP.py +++ b/test/MSVC/MSVC_UWP_APP.py @@ -30,14 +30,33 @@ import re from SCons.Tool.MSCommon.vc import get_installed_vcs_components +from SCons.Tool.MSCommon.vc import get_native_host_platform +from SCons.Tool.MSCommon.vc import _GE2022_HOST_TARGET_CFG +from SCons.Tool.MSCommon.MSVC.Kind import ( + msvc_version_is_express, + msvc_version_is_btdispatch, +) import TestSCons test = TestSCons.TestSCons() test.skip_if_not_msvc() installed_versions = get_installed_vcs_components() -GE_VS2015_versions = [v for v in installed_versions if v.msvc_vernum >= 14.0] -LT_VS2015_versions = [v for v in installed_versions if v.msvc_vernum < 14.0] + +GE_VS2015_supported_versions = [] +GE_VS2015_unsupported_versions = [] +LT_VS2015_unsupported_versions = [] + +for v in installed_versions: + if v.msvc_vernum > 14.0: + GE_VS2015_supported_versions.append(v) + elif v.msvc_verstr == '14.0': + if msvc_version_is_btdispatch(v.msvc_version): + GE_VS2015_unsupported_versions.append((v, 'BTDispatch')) + else: + GE_VS2015_supported_versions.append(v) + else: + LT_VS2015_unsupported_versions.append(v) # Look for the Store VC Lib paths in the LIBPATH: # [VS install path]\VC\LIB\store[\arch] and @@ -46,32 +65,52 @@ # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\amd64 # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\references +# By default, 2015 Express supports the store argument only for x86 targets. +# Using MSVC_SCRIPT_ARGS to set the store argument is not validated and +# will result in the store paths not found on 64-bit hosts when using the +# default target architecture. + +# By default, 2015 BTDispatch silently ignores the store argument. +# Using MSVC_SCRIPT_ARGS to set the store argument is not validated and +# will result in the store paths not found. + +re_lib_eq2015exp_1 = re.compile(r'\\vc\\lib\\store', re.IGNORECASE) + re_lib_eq2015_1 = re.compile(r'\\vc\\lib\\store\\references', re.IGNORECASE) re_lib_eq2015_2 = re.compile(r'\\vc\\lib\\store', re.IGNORECASE) re_lib_ge2017_1 = re.compile(r'\\lib\\x86\\store\\references', re.IGNORECASE) re_lib_ge2017_2 = re.compile(r'\\lib\\x64\\store', re.IGNORECASE) - def check_libpath(msvc, active, output): + def _check_libpath(msvc, output): + is_supported = True outdict = {key.strip(): val.strip() for key, val in [line.split('|') for line in output.splitlines()]} platform = outdict.get('PLATFORM', '') libpath = outdict.get('LIBPATH', '') + uwpsupported = outdict.get('UWPSUPPORTED', '') + if uwpsupported and uwpsupported.split('|')[-1] == '0': + is_supported = False n_matches = 0 if msvc.msvc_verstr == '14.0': + if msvc_version_is_express(msvc.msvc_version): + for regex in (re_lib_eq2015exp_1,): + if regex.search(libpath): + n_matches += 1 + return n_matches > 0, 'store', libpath, is_supported for regex in (re_lib_eq2015_1, re_lib_eq2015_2): if regex.search(libpath): n_matches += 1 - return n_matches >= 2, 'store', libpath + return n_matches >= 2, 'store', libpath, is_supported elif platform == 'UWP': for regex in (re_lib_ge2017_1, re_lib_ge2017_2): if regex.search(libpath): n_matches += 1 - return n_matches > 0, 'uwp', libpath - return False, 'uwp', libpath + return n_matches > 0, 'uwp', libpath, is_supported + return False, 'uwp', libpath, is_supported - found, kind, libpath = _check_libpath(msvc, output) + found, kind, libpath, is_supported = _check_libpath(msvc, output) failmsg = None @@ -84,11 +123,13 @@ def _check_libpath(msvc, output): repr(msvc.msvc_version), repr(kind), repr(libpath) ) - return failmsg + return failmsg, is_supported -if GE_VS2015_versions: +if GE_VS2015_supported_versions: # VS2015 and later for uwp/store argument - for supported in GE_VS2015_versions: + + for supported in GE_VS2015_supported_versions: + for msvc_uwp_app in (True, '1', False, '0', None): active = msvc_uwp_app in (True, '1') @@ -102,7 +143,7 @@ def _check_libpath(msvc, output): """.format(repr(supported.msvc_version), repr(msvc_uwp_app)) )) test.run(arguments='-Q -s', stdout=None) - failmsg = check_libpath(supported, active, test.stdout()) + failmsg, _ = check_libpath(supported, active, test.stdout()) if failmsg: test.fail_test(message=failmsg) @@ -120,23 +161,77 @@ def _check_libpath(msvc, output): if not test.stderr().strip().startswith("MSVCArgumentError: multiple uwp declarations:"): test.fail_test(message='Expected MSVCArgumentError') - # uwp using script argument + if supported.msvc_verstr == '14.0' and msvc_version_is_express(supported.msvc_version): + + # uwp using script argument may not be supported for default target architecture + test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon.MSVC.Kind import msvc_version_uwp_is_supported + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='store', tools=['msvc']) + is_supported, _ = msvc_version_uwp_is_supported(env['MSVC_VERSION'], env['TARGET_ARCH']) + uwpsupported = '1' if is_supported else '0' + print('LIBPATH|' + env['ENV'].get('LIBPATH', '')) + print('PLATFORM|' + env['ENV'].get('VSCMD_ARG_app_plat','')) + print('UWPSUPPORTED|' + uwpsupported) + """.format(repr(supported.msvc_version)) + )) + test.run(arguments='-Q -s', stdout=None) + + else: + + # uwp using script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='store', tools=['msvc']) + print('LIBPATH|' + env['ENV'].get('LIBPATH', '')) + print('PLATFORM|' + env['ENV'].get('VSCMD_ARG_app_plat','')) + """.format(repr(supported.msvc_version)) + )) + test.run(arguments='-Q -s', stdout=None) + + failmsg, is_supported = check_libpath(supported, True, test.stdout()) + if failmsg and is_supported: + test.fail_test(message=failmsg) + +if GE_VS2015_unsupported_versions: + # VS2015 and later for uwp/store error + + for unsupported, kind_str in GE_VS2015_unsupported_versions: + + for msvc_uwp_app in (True, '1'): + + # uwp using construction variable + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_UWP_APP={}, tools=['msvc']) + """.format(repr(unsupported.msvc_version), repr(msvc_uwp_app)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_UWP_APP ({}) is not supported for MSVC_VERSION {} ({}):".format( + repr(msvc_uwp_app), repr(unsupported.msvc_version), repr(kind_str) + ) + test.must_contain_all(test.stderr(), expect) + + # MSVC_SCRIPT_ARGS store is not validated test.write('SConstruct', textwrap.dedent( """ DefaultEnvironment(tools=[]) env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='store', tools=['msvc']) - print('LIBPATH|' + env['ENV'].get('LIBPATH', '')) - print('PLATFORM|' + env['ENV'].get('VSCMD_ARG_app_plat','')) - """.format(repr(supported.msvc_version)) + """.format(repr(unsupported.msvc_version)) )) - test.run(arguments='-Q -s', stdout=None) - failmsg = check_libpath(supported, True, test.stdout()) - if failmsg: - test.fail_test(message=failmsg) + test.run(arguments='-Q -s', stdout='') + failmsg, _ = check_libpath(unsupported, True, test.stdout()) + if not failmsg: + test.fail_test(message='unexpected: store found in libpath') -if LT_VS2015_versions: +if LT_VS2015_unsupported_versions: # VS2013 and earlier for uwp/store error - for unsupported in LT_VS2015_versions: + + for unsupported in LT_VS2015_unsupported_versions: + for msvc_uwp_app in (True, '1', False, '0', None): active = msvc_uwp_app in (True, '1') diff --git a/test/MSVC/VSWHERE.py b/test/MSVC/VSWHERE.py index 8212415f37..e50e42a26e 100644 --- a/test/MSVC/VSWHERE.py +++ b/test/MSVC/VSWHERE.py @@ -28,6 +28,7 @@ Also test that vswhere.exe is found and sets VSWHERE to the correct values """ import os.path +import SCons.Tool.MSCommon import TestSCons _python_ = TestSCons._python_ @@ -36,6 +37,10 @@ test.skip_if_not_msvc() test.verbose_set(1) +_default_vc = SCons.Tool.MSCommon.vc.get_installed_vcs_components()[0] +if _default_vc.msvc_vernum < 14.1: + test.skip_test("no installed msvc requires vswhere.exe; skipping test\n") + test.dir_fixture('VSWHERE-fixture') test.run(arguments=".") diff --git a/test/MSVC/msvc_cache_force_defaults.py b/test/MSVC/msvc_cache_force_defaults.py index e0ed1c3543..ad67304d56 100644 --- a/test/MSVC/msvc_cache_force_defaults.py +++ b/test/MSVC/msvc_cache_force_defaults.py @@ -30,6 +30,10 @@ import textwrap from SCons.Tool.MSCommon.vc import get_installed_vcs_components +from SCons.Tool.MSCommon.MSVC.Kind import ( + msvc_version_is_express, + msvc_version_is_btdispatch, +) import TestSCons test = TestSCons.TestSCons() @@ -68,8 +72,15 @@ test.run(arguments="-Q -s", status=0, stdout=None) cache_arg = test.stdout().strip() if default_version.msvc_verstr == '14.0': - # VS2015: target_arch msvc_sdk_version - expect = r'^SCRIPT_ARGS: .* [0-9.]+$' + if msvc_version_is_express(default_version.msvc_version): + # VS2015 Express: target_arch + expect = r'^SCRIPT_ARGS: [a-zA-Z0-9_]+$' + elif msvc_version_is_btdispatch(default_version.msvc_version): + # VS2015 BTDispatch: target_arch + expect = r'^SCRIPT_ARGS: [a-zA-Z0-9_]+$' + else: + # VS2015: target_arch msvc_sdk_version + expect = r'^SCRIPT_ARGS: [a-zA-Z0-9_]+ [0-9.]+$' else: # VS2017+ msvc_sdk_version msvc_toolset_version expect = r'^SCRIPT_ARGS: [0-9.]+ -vcvars_ver=[0-9.]+$' From caf8b0fbc7cf21bc5c89791d183dc852a4bdaf18 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 8 Sep 2023 00:13:19 -0400 Subject: [PATCH 002/386] Temporarily disable command-line argument and keep original case of vc path from json output. Test failures when the vc path from json is normalized. Test failure for AddOption help output. --- SCons/Tool/MSCommon/vc.py | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 9891ac3506..a9e12a88a9 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -55,7 +55,8 @@ import SCons.Util import SCons.Warnings from SCons.Tool import find_program_path -import SCons.Script + +# import SCons.Script from . import common from .common import CONFIG_CACHE, debug @@ -73,19 +74,19 @@ # user vswhere.exe location as command-line option -_vswhere_cmdline_arg = '--vswhere-path' -_vswhere_cmdline_var = 'vswhere_path' - -SCons.Script.AddOption( - _vswhere_cmdline_arg, - dest=_vswhere_cmdline_var, - type="string", - nargs=1, - action="store", - metavar='PATH', - default=None, - help='Fully qualified path to vswhere.exe.', -) +# _vswhere_cmdline_arg = '--vswhere-path' +# _vswhere_cmdline_var = 'vswhere_path' +# +# SCons.Script.AddOption( +# _vswhere_cmdline_arg, +# dest=_vswhere_cmdline_var, +# type="string", +# nargs=1, +# action="store", +# metavar='PATH', +# default=None, +# help='Fully qualified path to vswhere.exe.', +# ) # external exceptions @@ -982,10 +983,10 @@ def _msvc_cmdline_vswhere(): if _vswhere_path_cmdline == UNDEFINED: vswhere_path = None - vswhere_user = SCons.Script.GetOption(_vswhere_cmdline_var) + # vswhere_user = SCons.Script.GetOption(_vswhere_cmdline_var) - if vswhere_user: - vswhere_path = _vswhere_user_path(vswhere_user) + # if vswhere_user: + # vswhere_path = _vswhere_user_path(vswhere_user) _vswhere_path_cmdline = vswhere_path debug('vswhere_path=%s', vswhere_path) @@ -1184,14 +1185,13 @@ def _update_vswhere_msvc_map(env): if not installation_path or not os.path.exists(installation_path): continue - vc_root = os.path.join(installation_path, 'VC') - if not os.path.exists(vc_root): + vc_path = os.path.join(installation_path, 'VC') + if not os.path.exists(vc_path): continue - vc_root = MSVC.Util.process_path(vc_root) + vc_root = MSVC.Util.process_path(vc_path) if vc_root in _VSWhere.seen_root: continue - _VSWhere.seen_root.add(vc_root) installation_version = instance.get('installationVersion') @@ -1229,7 +1229,7 @@ def _update_vswhere_msvc_map(env): is_release = False if is_prerelease else True msvc_instance = MSVC_INSTANCE( - vc_path = vc_root, + vc_path = vc_path, vc_version = vc_version, vc_version_numeric = float(vc_version), vc_version_scons = vc_version_scons, From 1867d37a2269502a520b1fe75b92d869e92fd77f Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 8 Sep 2023 08:28:35 -0400 Subject: [PATCH 003/386] Update verification of vswhere executable and temporary removal of vswhere command-line argument --- SCons/Tool/MSCommon/vc.py | 60 +++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index a9e12a88a9..7b4820e650 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -73,10 +73,12 @@ ) # user vswhere.exe location as command-line option - +# +# TODO: INTENTIONALLY DISABLED +# # _vswhere_cmdline_arg = '--vswhere-path' # _vswhere_cmdline_var = 'vswhere_path' -# +# # SCons.Script.AddOption( # _vswhere_cmdline_arg, # dest=_vswhere_cmdline_var, @@ -554,9 +556,6 @@ def _make_target_host_map(all_hosts, host_all_targets_map): _VSWHERE_EXE = 'vswhere.exe' -# case-insensitive endswith vswhere.exe -_re_match_vswhere = re.compile('^.*' + re.escape(_VSWHERE_EXE) + '$', re.IGNORECASE) - def get_msvc_version_numeric(msvc_version): """Get the raw version numbers from a MSVC_VERSION string, so it could be cast to float or other numeric values. For example, '14.0Exp' @@ -807,7 +806,7 @@ def _skip_sendtelemetry(env): # VS2015 and earlier: configure registry queries to probe for installed VC editions _VCVER_TO_PRODUCT_DIR = { '14.0': [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir'),], + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], '14.0Exp': [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\WDExpress\14.0\Setup\VS\ProductDir'), # vs root (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir'), # not populated? @@ -954,20 +953,25 @@ def _vswhere_user_path(pval): if not os.path.exists(pval): - warn_msg = f'vswhere path not found: {pval!r}' + warn_msg = f'vswhere executable path not found: {pval!r}' SCons.Warnings.warn(MSVC.Warnings.VSWherePathWarning, warn_msg) debug(warn_msg) - elif not _re_match_vswhere.match(pval): + else: - warn_msg = f'vswhere.exe not found in vswhere path: {pval!r}' - SCons.Warnings.warn(MSVC.Warnings.VSWherePathWarning, warn_msg) - debug(warn_msg) + vswhere_norm = MSVC.Util.process_path(pval) - else: + tail = os.path.split(vswhere_norm)[-1] + if tail != _VSWHERE_EXE: + + warn_msg = f'unsupported vswhere executable (expected {_VSWHERE_EXE!r}, found {tail!r}): {pval!r}' + SCons.Warnings.warn(MSVC.Warnings.VSWherePathWarning, warn_msg) + debug(warn_msg) + + else: - vswhere_path = MSVC.Util.process_path(pval) - debug('vswhere_path=%s', vswhere_path) + vswhere_path = vswhere_norm + debug('vswhere_path=%s', vswhere_path) _cache_user_vswhere_paths[pval] = vswhere_path @@ -983,10 +987,12 @@ def _msvc_cmdline_vswhere(): if _vswhere_path_cmdline == UNDEFINED: vswhere_path = None + # TODO: INTENTIONALLY DISABLED # vswhere_user = SCons.Script.GetOption(_vswhere_cmdline_var) + vswhere_user = None - # if vswhere_user: - # vswhere_path = _vswhere_user_path(vswhere_user) + if vswhere_user: + vswhere_path = _vswhere_user_path(vswhere_user) _vswhere_path_cmdline = vswhere_path debug('vswhere_path=%s', vswhere_path) @@ -1027,11 +1033,14 @@ def msvc_find_vswhere(): # NB: this gets called from testsuite on non-Windows platforms. # Whether that makes sense or not, don't break it for those. vswhere_path = _msvc_cmdline_vswhere() - if not vswhere_path: - for pf in VSWHERE_PATHS: - if os.path.exists(pf): - vswhere_path = pf - break + if vswhere_path: + return + + vswhere_path = None + for pf in VSWHERE_PATHS: + if os.path.exists(pf): + vswhere_path = pf + break return vswhere_path @@ -1083,11 +1092,12 @@ def _filter_vswhere_paths(env): if vswhere_environ and vswhere_environ not in _VSWhere.seen_vswhere: vswhere_paths.append(vswhere_environ) - vswhere_cmdline = _msvc_cmdline_vswhere() - if vswhere_cmdline and vswhere_cmdline not in _VSWhere.seen_vswhere: - vswhere_paths.append(vswhere_cmdline) + # TODO: INTENTIONALLY DISABLED + # vswhere_cmdline = _msvc_cmdline_vswhere() + # if vswhere_cmdline and vswhere_cmdline not in _VSWhere.seen_vswhere: + # vswhere_paths.append(vswhere_cmdline) - vswhere_default = _msvc_default_vswhere() + vswhere_default = _msvc_default_vswhere() if vswhere_default and vswhere_default not in _VSWhere.seen_vswhere: vswhere_paths.append(vswhere_default) From 7a9c3361fb2057aa1cae2112095522afff2cbd3a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 9 Sep 2023 20:08:34 -0400 Subject: [PATCH 004/386] Remove vswhere command-line option and replace retrieval with stub returning None. Change processing of user-specified vswhere path. TODO: * revisit MSVC.Util.process_path due to realpath behavior for some windows specifications (drive specifications, etc). --- SCons/Tool/MSCommon/vc.py | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 7b4820e650..a92ceb7cd8 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -72,24 +72,6 @@ MSVCToolsetVersionNotFound, ) -# user vswhere.exe location as command-line option -# -# TODO: INTENTIONALLY DISABLED -# -# _vswhere_cmdline_arg = '--vswhere-path' -# _vswhere_cmdline_var = 'vswhere_path' -# -# SCons.Script.AddOption( -# _vswhere_cmdline_arg, -# dest=_vswhere_cmdline_var, -# type="string", -# nargs=1, -# action="store", -# metavar='PATH', -# default=None, -# help='Fully qualified path to vswhere.exe.', -# ) - # external exceptions class MSVCUnsupportedHostArch(VisualCException): @@ -959,7 +941,8 @@ def _vswhere_user_path(pval): else: - vswhere_norm = MSVC.Util.process_path(pval) + # vswhere_norm = MSVC.Util.process_path(pval) + vswhere_norm = os.path.normcase(os.path.normpath(pval)) tail = os.path.split(vswhere_norm)[-1] if tail != _VSWHERE_EXE: @@ -979,7 +962,8 @@ def _vswhere_user_path(pval): # normalized user-specified command-line vswhere path -_vswhere_path_cmdline = UNDEFINED +# TODO: stub for command-line specification of vswhere +_vswhere_path_cmdline = None def _msvc_cmdline_vswhere(): global _vswhere_path_cmdline @@ -987,8 +971,6 @@ def _msvc_cmdline_vswhere(): if _vswhere_path_cmdline == UNDEFINED: vswhere_path = None - # TODO: INTENTIONALLY DISABLED - # vswhere_user = SCons.Script.GetOption(_vswhere_cmdline_var) vswhere_user = None if vswhere_user: @@ -1092,10 +1074,9 @@ def _filter_vswhere_paths(env): if vswhere_environ and vswhere_environ not in _VSWhere.seen_vswhere: vswhere_paths.append(vswhere_environ) - # TODO: INTENTIONALLY DISABLED - # vswhere_cmdline = _msvc_cmdline_vswhere() - # if vswhere_cmdline and vswhere_cmdline not in _VSWhere.seen_vswhere: - # vswhere_paths.append(vswhere_cmdline) + vswhere_cmdline = _msvc_cmdline_vswhere() + if vswhere_cmdline and vswhere_cmdline not in _VSWhere.seen_vswhere: + vswhere_paths.append(vswhere_cmdline) vswhere_default = _msvc_default_vswhere() if vswhere_default and vswhere_default not in _VSWhere.seen_vswhere: From 8b4fcdeec0dec4d8cb805db76e3626907290c333 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 10 Sep 2023 09:31:45 -0400 Subject: [PATCH 005/386] Update MSCommon/README.rst documentation [ci skip] Changes: * Added MSVC detection priority. * Added VS2015 edition limitations. * Updated SDK batch file known issues. * Added footnotes for MSVC batch file argument limitations. * Added footnote for Windows SDK version numbers (Windows 10 and Windows 11) --- SCons/Tool/MSCommon/README.rst | 198 +++++++++++++++++++++++++++++---- 1 file changed, 178 insertions(+), 20 deletions(-) diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index 5ab07ad631..bc57ca43b0 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -27,16 +27,166 @@ Design Notes ``MSCommon/vc.py`` and are available via the ``SCons.Tool.MSCommon`` namespace. +MSVC Detection Priority +======================= + +For msvc version specifications without an 'Exp' suffix, an express +installation is used only when no other installation is detected. + +======= ======= ======================================================== +Product VCVer Priority +======= ======= ======================================================== +VS2022 14.3 Enterprise, Professional, Community, BuildTools +------- ------- -------------------------------------------------------- +VS2019 14.2 Enterprise, Professional, Community, BuildTools +------- ------- -------------------------------------------------------- +VS2017 14.1 Enterprise, Professional, Community, BuildTools, Express +------- ------- -------------------------------------------------------- +VS2017 14.1Exp Express +------- ------- -------------------------------------------------------- +VS2015 14.0 [Develop, BuildTools, CmdLine], Express +------- ------- -------------------------------------------------------- +VS2015 14.0Exp Express +------- ------- -------------------------------------------------------- +VS2013 12.0 Develop, Express +------- ------- -------------------------------------------------------- +VS2013 12.0Exp Express +------- ------- -------------------------------------------------------- +VS2012 11.0 Develop, Express +------- ------- -------------------------------------------------------- +VS2012 11.0Exp Express +------- ------- -------------------------------------------------------- +VS2010 10.0 Develop, Express +------- ------- -------------------------------------------------------- +VS2010 10.0Exp Express +------- ------- -------------------------------------------------------- +VS2008 9.0 VCForPython, Develop, Express +------- ------- -------------------------------------------------------- +VS2008 9.0Exp Express +------- ------- -------------------------------------------------------- +VS2005 8.0 Develop, Express +------- ------- -------------------------------------------------------- +VS2005 8.0Exp Express +------- ------- -------------------------------------------------------- +VS2003 7.1 Develop +------- ------- -------------------------------------------------------- +VS2002 7.0 Develop +------- ------- -------------------------------------------------------- +VS6.0 6.0 Develop +======= ======= ======================================================== + +Legend: + + Develop + devenv.com (or msdev.com) is detected. + + Express + WDExpress.exe (or VCExpress.exe) is detected. + + BuildTools [VS2015] + The vcvarsall batch file dispatches to the buildtools batch file. + + CmdLine [VS2015] + Neither Develop, Express, or BuildTools. + +VS2015 Edition Limitations +========================== + +VS2015 BuildTools +----------------- + +The VS2015 BuildTools stand-alone batch file does not support the ``sdk version`` argument. + +The VS2015 BuildTools stand-alone batch file does not support the ``store`` argument. + +These arguments appear to be silently ignored and likely would result in compiler +and/or linker build failures. + +The VS2015 BuildTools ``vcvarsall.bat`` batch file dispatches to the stand-alone buildtools +batch file under certain circumstances. A fragment from the vcvarsall batch file is: +:: + if exist "%~dp0..\common7\IDE\devenv.exe" goto setup_VS + if exist "%~dp0..\common7\IDE\wdexpress.exe" goto setup_VS + if exist "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" goto setup_buildsku + + :setup_VS + + ... + + :setup_buildsku + if not exist "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" goto usage + set CurrentDir=%CD% + call "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" %1 %2 + cd /d %CurrentDir% + goto :eof + +VS2015 Express +-------------- + +The VS2015 Express batch file does not support the ``sdk version`` argument. + +The VS2015 Express batch file does not support the ``store`` argument for the ``amd64`` and +``arm`` target architectures + +amd64 Target Architecture +^^^^^^^^^^^^^^^^^^^^^^^^^ + +As installed, VS2015 Express does not support the ``store`` argument for the ``amd64`` target +architecture. The generated ``store`` library paths include directories that do not exist. + +The store library paths appear in two places in the ``vcvarsx86_amd64`` batch file: +:: + :setstorelib + @if exist "%VCINSTALLDIR%LIB\amd64\store" set LIB=%VCINSTALLDIR%LIB\amd64\store;%LIB% + ... + :setstorelibpath + @if exist "%VCINSTALLDIR%LIB\amd64\store" set LIBPATH=%VCINSTALLDIR%LIB\amd64\store;%LIBPATH% + +The correct store library paths would be: +:: + :setstorelib + @if exist "%VCINSTALLDIR%LIB\store\amd64" set LIB=%VCINSTALLDIR%LIB\store\amd64;%LIB% + ... + :setstorelibpath + @if exist "%VCINSTALLDIR%LIB\store\amd64" set LIBPATH=%VCINSTALLDIR%LIB\store\amd64;%LIBPATH% + +arm Target Architecture +^^^^^^^^^^^^^^^^^^^^^^^ + +As installed, VS2015 Express does not support the ``store`` argument for the ``arm`` target +architecture. The generated ``store`` library paths include directories that do not exist. + +The store library paths appear in two places in the ``vcvarsx86_arm`` batch file: +:: + :setstorelib + @if exist "%VCINSTALLDIR%LIB\ARM\store" set LIB=%VCINSTALLDIR%LIB\ARM\store;%LIB% + ... + :setstorelibpath + @if exist "%VCINSTALLDIR%LIB\ARM\store" set LIBPATH=%VCINSTALLDIR%LIB\ARM\store;%LIBPATH% + +The correct store library paths would be file: +:: + :setstorelib + @if exist "%VCINSTALLDIR%LIB\store\ARM" set LIB=%VCINSTALLDIR%LIB\store\ARM;%LIB% + ... + :setstorelibpath + @if exist "%VCINSTALLDIR%LIB\store\ARM" set LIBPATH=%VCINSTALLDIR%LIB\store\ARM;%LIBPATH% + + Known Issues ============ The following issues are known to exist: * Using ``MSVC_USE_SCRIPT`` and ``MSVC_USE_SCRIPT_ARGS`` to call older Microsoft SDK - ``SetEnv.cmd`` batch files may result in build failures. Some of these batch files - require delayed expansion to be enabled which is not usually the Windows default. - One solution would be to launch the MSVC batch file command in a new command interpreter - instance with delayed expansion enabled via command-line options. + ``SetEnv.cmd`` batch files may result in build failures. + + Typically, the reasons for build failures with SDK batch files are one, or both, of: + + * The batch files require delayed expansion to be enabled which is not usually the Windows default. + + * The batch files inspect environment variables that are not defined in the minimal subprocess + environment in which the batch files are invoked. * The code to suppress the "No versions of the MSVC compiler were found" warning for the default environment was moved from ``MSCommon/vc.py`` to ``MSCommon/MSVC/SetupEnvDefault.py``. @@ -188,17 +338,21 @@ Batch File Arguments Supported MSVC batch file arguments by product: -======= === === ======= ======= -Product UWP SDK Toolset Spectre -======= === === ======= ======= -VS2022 X X X X -------- --- --- ------- ------- -VS2019 X X X X -------- --- --- ------- ------- -VS2017 X X X X -------- --- --- ------- ------- -VS2015 X X -======= === === ======= ======= +======= ======= ====== ======= ======= +Product UWP SDK Toolset Spectre +======= ======= ====== ======= ======= +VS2022 X X X X +------- ------- ------ ------- ------- +VS2019 X X X X +------- ------- ------ ------- ------- +VS2017 X X X X +------- ------- ------ ------- ------- +VS2015 X [1]_ X [2]_ +======= ======= ====== ======= ======= + +.. [1] The BuildTools edition does not support the ``store`` argument. The Express edition + supports the ``store`` argument for the ``x86`` target only. +.. [2] The ``sdk version`` argument is not supported in the BuildTools and Express editions. Supported MSVC batch file arguments in SCons: @@ -315,13 +469,17 @@ Visual Studio Version Notes SDK Versions ------------ -==== ============ +==== ================= SDK Format -==== ============ -10.0 10.0.XXXXX.Y ----- ------------ +==== ================= +10.0 10.0.XXXXX.Y [*]_ +---- ----------------- 8.1 8.1 -==== ============ +==== ================= + +.. [*] The Windows 10 SDK version number is 10.0.20348.0 and earlier. + + The Windows 11 SDK version number is 10.0.22000.194 and later. BuildTools Versions ------------------- From 3fbef22bf24568e27ad71a3a6b8853e8f99246d9 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 10 Sep 2023 13:21:37 -0400 Subject: [PATCH 006/386] Initial additions for CHANGES.txt. [ci skip] --- CHANGES.txt | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 9336e5d2f1..4467bee773 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -38,6 +38,65 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER module was refactored. - Add arm64 to the MSVS supported architectures list for VS2017 and later to be consistent with the current documentation of MSVS_ARCH. + - For msvc version specifications without an 'Exp' suffix, an express installation + is used when no other edition is detected for the msvc version. + - VS2015 Express (14.1Exp) may not have been detected. The registry keys written + during installation appear to be different than for earlier express versions. + VS2015 Express should be correctly detected now. + - VS2015 Express (14.1Exp) does not support the sdk version argument. VS2015 Express + does not support the store argument for target architectures other than x86. + Script argument validation now takes into account these restrictions. + - VS2015 BuildTools (14.0) does not support the sdk version argument and does not + support the store argument. Script argument validation now takes into account + these restrictions. + - The Windows SDK for Windows 7 and .NET Framework 4" (SDK 7.1) populates the + registry keys in a manner in which the msvc detection would report that VS2010 + (10.0) is installed when only the SDK was installed. The installed files are + intended to be used via the sdk batch file setenv.cmd. The installed msvc + batch files will fail. The msvc detection logic now ignores SDK-only VS2010 + installations. Similar protection is implemented for the sdk-only installs that + populate the installation folder and registry keys for VS2008 (9.0), if necessary. + - The MSCommon module import was changed from a relative import to a top-level + absolute import in the following Microsoft tools: midl, mslib, mslink, mssdk, msvc, + msvs. Moving any of these tools that used relative imports to the scons site tools + folder would fail on import (i.e., the relative import paths become invalid when + moved). + - For VS2005 (8.0) to VS2015 (14.0), vsvarsall.bat is employed to dispatch to a + dependent batch file when configuring the msvc environment. Previously, only the + existence of the compiler executable was verified. In certain installations, the + dependent batch file (e.g., vcvars64.bat) may not exist while the compiler + executable does exist resulting in build failures. The existence of vcvarsall.bat, + the dependent batch file, and the compiler executable are now validated. + - MSVC configuration data specific to versions VS2005 (8.0) to VS2008 (9.0) was added + as the dependent batch files have different names than the batch file names used + for VS2010 (10.0) and later. The VS2008 (9.0) Visual C++ For Python installation + is handled as a special case as the dependent batch files are: (a) not used and (b) + in different locations. + - When VS2008 (9.0) Visual C++ For Python is installed using the ALLUSERS=1 option + (i.e., msiexec /i VCForPython27.msi ALLUSERS=1), the registry keys are written to + HKEY_LOCAL_MACHINE rather than HKEY_CURRENT_USER. An entry was added to query the + Visual C++ For Python keys in HKLM following the HKCU query, if necessary. + - The registry detection of VS2015 (14.0), and earlier, is now cached at runtime and + is only evaluated once for each msvc version. + - The vswhere detection of VS2017 (14.1), and later, is now cached at runtime and is + only evaluated once for each vswhere executable. The detected msvc instances for + all vswhere executables are merged (i.e., detected msvc instances are the union + of the results from all vswhere executables). There is a single invocation for + each vswhere executable that processes the output for all installations. Prior + implementations called the vswhere executable for each supported msvc version. + - The detection of the msvc compiler executable (cl.exe) has been modified. The + compiler detection no longer considers the host os environment path. In addition, + existence of the msvc compiler executable is checked in the detection dictionary + and the scons ENV path before the detection dictionary is merged into the scons + ENV. Different warnings are produced when the msvc compiler is not detected in the + detection dictionary based on whether or not an msvc compiler was detected in the + scons ENV path (i.e., already exists in the user's ENV path prior to detection). + - Previously, the installed vcs list was constructed once and cached at runtime. If + a vswhere executable was specified via the construction variable VSWHERE and found + additional msvc installations, the new installations were not reflected in the + installed vcs list. During runtime, if a user-specified vswhere executable finds + new msvc installations, internal runtime caches are cleared and the installed vcs + list is reconstructed. From Vitaly Cheptsov: - Fix race condition in `Mkdir` which can happen when two `SConscript` From 0fc09ba19f4c00fb40f7909c3b111a6a037b5bf1 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 10 Sep 2023 17:29:10 -0400 Subject: [PATCH 007/386] Initial additions for RELEASE.txt. [ci skip] --- RELEASE.txt | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/RELEASE.txt b/RELEASE.txt index b1e49ff12d..37f1a44d13 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -59,6 +59,14 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY batch file exists but an individual msvc toolset may not support the host/target architecture combination. For example, when using VS2022 on arm64, the arm64 native tools are only installed for the 14.3x toolsets. +- MSVC: For msvc version specifications without an 'Exp' suffix, an express + installation is used when no other edition is detected for the msvc version. + This was the behavior for VS2008 (9.0) through VS2015 (14.0). This behavior + was extended to VS2017 (14.1) and VS2008 (8.0). +- MSVC: When the msvc compiler executable is not found during setup of the msvc + environment, the warning message issued takes into account whether or not a + possibly erroneous compiler executable was already present in the scons environment + path. - Extend range of recognized Java versions to 20. - Builder calls (like Program()) now accept pathlib objects in source lists. - The Help() function now takes an additional keyword argument keep_local: @@ -95,6 +103,26 @@ FIXES - MSVC: Erroneous construction of the installed msvc list (as described above) caused an index error in the msvc support code. An explicit check was added to prevent indexing into an empty list. Fixes #4312. +- MSVC: VS2015 Express (14.1Exp) may not have been detected. VS2015 Express should + be correctly detected. +- MSVC: VS2010 (10.0) could be inadvertently detected due to an sdk-only install + of Windows SDK 7.1. An sdk-only install of VS2010 is ignored as the msvc batch + files will fail. The installed files are intended to be used in conjunction with + the SDK batch file. Similar protection was added for VS2008 (9.0). +- MSVC: For VS2005 (8.0) to VS2015 (14.0), detection of installed files was expanded + to include the primary msvc batch file, dependent msvc batch file, and compiler + executable. In certain installations, the dependent msvc batch file may not exist + while the compiler executable does exists resulting in a build failure. +- MSVC: VS2008 (9.0) Visual C++ For Python was not detected when installed using the + ALLUSERS=1 option (i.e., msiexec /i VCForPython27.msi ALLUSERS=1). When installed + for all users, VS2008 (9.0) Visual C++ For Python is now correctly detected. +- MSVC: The search for the msvc compiler executable (cl.exe) no longer inspects the + OS system path in certain situations when setting up the msvc environment. +- MSVC: The installed vcs list was constructed and cached during the initial + invocation. If a vswhere executable was specified via the construction variable + VSWHERE and found additional msvc installations, the new installations were not + reflected in the installed vcs list. Now, when a user-specified vswhere executable + finds new msvc installations, the installed vcs list is reconstructed. - MSCommon: Test SConfTests.py would fail when mscommon debugging was enabled via the MSVC_MSCOMMON_DEBUG environment variable. The mscommon logging filter class registered with the python logging module was refactored to prevent test failure. @@ -114,6 +142,17 @@ IMPROVEMENTS ------------ - Now tries to find mingw if it comes from Chocolatey install of msys2. +- MSVC: VS2015 Express (14.1Exp) does not support the sdk version argument. VS2015 + Express does not support the store argument for target architectures other than + x86. Script argument validation now takes into account these restrictions. +- MSVC: VS2015 BuildTools (14.0) does not support the sdk version argument and + does not support the store argument. Script argument validation now takes into + account these restrictions. +- MSVC: Module imports were changed from a relative import to a top-level + absolute import in the following Microsoft tools: midl, mslib, mslink, mssdk, msvc, + msvs. Moving any of these tools that used relative imports to the scons site tools + folder would fail on import (i.e., the relative import paths become invalid when + moved). PACKAGING --------- From 0d37f067a4ec411ab7bf3464a693b3716f099dec Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 10 Sep 2023 17:59:21 -0400 Subject: [PATCH 008/386] Initial update for SCons/Tool/msvc.xml. [ci skip] --- SCons/Tool/msvc.xml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index bf2e267347..84ca2bd800 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -371,11 +371,17 @@ loaded into the environment. The valid values for &cv-MSVC_VERSION; represent major versions -of the compiler, except that versions ending in Exp -refer to "Express" or "Express for Desktop" Visual Studio editions, -which require distict entries because they use a different -filesystem layout and have some feature limitations compared to -the full version. +of the compiler. Versions ending in Exp +refer to "Express" or "Express for Desktop" Visual Studio editions. +An express version may have some feature limitations as compared to +the full version. It is necessary to specify the Exp +suffix when both express and non-express editions are installed +simulaneously and the express version is desired. An express edition +is used for the msvc version without the 'Exp' suffix when there is one +edition installed and it is the express edition. + + + The following table shows correspondence of the selector string to various version indicators ('x' is used as a placeholder for From 518fd13dcb964c4740a5275167aad2b346d47282 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:04:18 -0400 Subject: [PATCH 009/386] Second update for SCons/Tool/msvc.xml. [ci skip] --- SCons/Tool/msvc.xml | 51 ++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 84ca2bd800..f02c5f405f 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -370,28 +370,10 @@ loaded into the environment. -The valid values for &cv-MSVC_VERSION; represent major versions -of the compiler. Versions ending in Exp -refer to "Express" or "Express for Desktop" Visual Studio editions. -An express version may have some feature limitations as compared to -the full version. It is necessary to specify the Exp -suffix when both express and non-express editions are installed -simulaneously and the express version is desired. An express edition -is used for the msvc version without the 'Exp' suffix when there is one -edition installed and it is the express edition. - - - -The following table shows correspondence -of the selector string to various version indicators -('x' is used as a placeholder for -a single digit that can vary). -Note that it is not necessary to install Visual Studio -to build with &SCons; (for example, you can install only -Build Tools), but if Visual Studio is installed, -additional builders such as &b-link-MSVSSolution; and -&b-link-MSVSProject; become avaialable and will -correspond to the indicated versions. +The valid values for &cv-MSVC_VERSION; represent major versions of the +compiler suite. The following table shows the correspondence of +&cv-MSVC_VERSION; version specifications to various Visual Studio version +numbers. 'x' is used as a placeholder for a single digit that may vary. @@ -548,6 +530,31 @@ correspond to the indicated versions. + + + + + It is not necessary to install a Visual Studio IDE + to build with &SCons; (for example, you can install only + Build Tools), but when a Visual Studio IDE is installed, + additional builders such as &b-link-MSVSSolution; and + &b-link-MSVSProject; become available and correspond to + the specified versions. + + + + Versions ending in Exp refer to historical + "Express" or "Express for Desktop" Visual Studio editions, + which had feature limitations compared to the full editions. + It is only necessary to specify the Exp + suffix to select the express edition when both express and + non-express editions of the same product are installed + simulaneously. The Exp suffix is unnecessary, + but accepted, when only the express edition is installed. + + + + The compilation environment can be further or more precisely specified through the use of several other &consvars;: see the descriptions of From 6424fe3661135ac02f97b1da92b2528bdb56f68e Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:13:12 -0400 Subject: [PATCH 010/386] Third update for SCons/Tool/msvc.xml (remove hard tabs). [ci skip] --- SCons/Tool/msvc.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index f02c5f405f..37dccdd9f9 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -534,12 +534,12 @@ numbers. 'x' is used as a placeholder for a single digit that may vary. - It is not necessary to install a Visual Studio IDE - to build with &SCons; (for example, you can install only - Build Tools), but when a Visual Studio IDE is installed, - additional builders such as &b-link-MSVSSolution; and - &b-link-MSVSProject; become available and correspond to - the specified versions. + It is not necessary to install a Visual Studio IDE + to build with &SCons; (for example, you can install only + Build Tools), but when a Visual Studio IDE is installed, + additional builders such as &b-link-MSVSSolution; and + &b-link-MSVSProject; become available and correspond to + the specified versions. @@ -549,8 +549,8 @@ numbers. 'x' is used as a placeholder for a single digit that may vary. It is only necessary to specify the Exp suffix to select the express edition when both express and non-express editions of the same product are installed - simulaneously. The Exp suffix is unnecessary, - but accepted, when only the express edition is installed. + simulaneously. The Exp suffix is unnecessary, + but accepted, when only the express edition is installed. From 0d1aaa76358027ae94992e8220c4d475d600b3a0 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:19:17 -0400 Subject: [PATCH 011/386] Update for SCons/Tool/msvc.xml (remove hard tabs). [ci skip] --- SCons/Tool/msvc.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 37dccdd9f9..8beebf9a2d 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -544,7 +544,7 @@ numbers. 'x' is used as a placeholder for a single digit that may vary. Versions ending in Exp refer to historical - "Express" or "Express for Desktop" Visual Studio editions, + "Express" or "Express for Desktop" Visual Studio editions, which had feature limitations compared to the full editions. It is only necessary to specify the Exp suffix to select the express edition when both express and From ed18bdf70a2f8ff20f4debcc202684d7d4e737a4 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 7 Jan 2024 09:27:02 -0500 Subject: [PATCH 012/386] Update CHANGES.txt and RELEASE.txt: move text items and fix typographical errors to prepare for merge with latest. --- CHANGES.txt | 28 +++++++++++++-------------- RELEASE.txt | 54 ++++++++++++++++++++++++++--------------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 270861bf6e..e47c13e87a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -42,12 +42,24 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER registry query that returns a path that does not exist. Multiple invocation paths were not prepared to handle the MissingConfiguration exception. The MissingConfiguration exception type was removed. + - The MSCommon module import was changed from a relative import to a top-level + absolute import in the following Microsoft tools: midl, mslib, mslink, mssdk, msvc, + msvs. Moving any of these tools that used relative imports to the scons site tools + folder would fail on import (i.e., the relative import paths become invalid when + moved). + - The detection of the msvc compiler executable (cl.exe) has been modified. The + compiler detection no longer considers the host os environment path. In addition, + existence of the msvc compiler executable is checked in the detection dictionary + and the scons ENV path before the detection dictionary is merged into the scons + ENV. Different warnings are produced when the msvc compiler is not detected in the + detection dictionary based on whether or not an msvc compiler was detected in the + scons ENV path (i.e., already exists in the user's ENV path prior to detection). - For msvc version specifications without an 'Exp' suffix, an express installation is used when no other edition is detected for the msvc version. - - VS2015 Express (14.1Exp) may not have been detected. The registry keys written + - VS2015 Express (14.0Exp) may not have been detected. The registry keys written during installation appear to be different than for earlier express versions. VS2015 Express should be correctly detected now. - - VS2015 Express (14.1Exp) does not support the sdk version argument. VS2015 Express + - VS2015 Express (14.0Exp) does not support the sdk version argument. VS2015 Express does not support the store argument for target architectures other than x86. Script argument validation now takes into account these restrictions. - VS2015 BuildTools (14.0) does not support the sdk version argument and does not @@ -60,11 +72,6 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER batch files will fail. The msvc detection logic now ignores SDK-only VS2010 installations. Similar protection is implemented for the sdk-only installs that populate the installation folder and registry keys for VS2008 (9.0), if necessary. - - The MSCommon module import was changed from a relative import to a top-level - absolute import in the following Microsoft tools: midl, mslib, mslink, mssdk, msvc, - msvs. Moving any of these tools that used relative imports to the scons site tools - folder would fail on import (i.e., the relative import paths become invalid when - moved). - For VS2005 (8.0) to VS2015 (14.0), vsvarsall.bat is employed to dispatch to a dependent batch file when configuring the msvc environment. Previously, only the existence of the compiler executable was verified. In certain installations, the @@ -88,13 +95,6 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER of the results from all vswhere executables). There is a single invocation for each vswhere executable that processes the output for all installations. Prior implementations called the vswhere executable for each supported msvc version. - - The detection of the msvc compiler executable (cl.exe) has been modified. The - compiler detection no longer considers the host os environment path. In addition, - existence of the msvc compiler executable is checked in the detection dictionary - and the scons ENV path before the detection dictionary is merged into the scons - ENV. Different warnings are produced when the msvc compiler is not detected in the - detection dictionary based on whether or not an msvc compiler was detected in the - scons ENV path (i.e., already exists in the user's ENV path prior to detection). - Previously, the installed vcs list was constructed once and cached at runtime. If a vswhere executable was specified via the construction variable VSWHERE and found additional msvc installations, the new installations were not reflected in the diff --git a/RELEASE.txt b/RELEASE.txt index a79f24e2b6..6cfdb33e69 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -59,10 +59,6 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY batch file exists but an individual msvc toolset may not support the host/target architecture combination. For example, when using VS2022 on arm64, the arm64 native tools are only installed for the 14.3x toolsets. -- MSVC: For msvc version specifications without an 'Exp' suffix, an express - installation is used when no other edition is detected for the msvc version. - This was the behavior for VS2008 (9.0) through VS2015 (14.0). This behavior - was extended to VS2017 (14.1) and VS2008 (8.0). - MSVC: When the msvc compiler executable is not found during setup of the msvc environment, the warning message issued takes into account whether or not a possibly erroneous compiler executable was already present in the scons environment @@ -86,6 +82,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY although the preferred usage remains to import from SCons.Util only. Any code that did the direct import will have to change to import from SCons.Util.sctypes. +- MSVC: For msvc version specifications without an 'Exp' suffix, an express + installation is used when no other edition is detected for the msvc version. + This was the behavior for VS2008 (9.0) through VS2015 (14.0). This behavior + was extended to VS2017 (14.1) and VS2008 (8.0). FIXES ----- @@ -109,7 +109,23 @@ FIXES - MSVC: Erroneous construction of the installed msvc list (as described above) caused an index error in the msvc support code. An explicit check was added to prevent indexing into an empty list. Fixes #4312. -- MSVC: VS2015 Express (14.1Exp) may not have been detected. VS2015 Express should +- MSVC: The search for the msvc compiler executable (cl.exe) no longer inspects the + OS system path in certain situations when setting up the msvc environment. +- MSCommon: Test SConfTests.py would fail when mscommon debugging was enabled via the + MSVC_MSCOMMON_DEBUG environment variable. The mscommon logging filter class registered + 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. +- FORTRAN: Fix gfortran tool initialization. Defaults to using binary named gfortran + as would be expected, and properly set's SHFORTRAN flags to include -fPIC + where previously it was only doing so for the other fortran versions (F77,..) +- MSCommon: Added more error handling while reading msvc config cache. + (Enabled/specified by SCONS_CACHE_MSVC_CONFIG). + The existing cache will be discarded if there's a decode error reading it. + It's possible there's a race condition creating this issue it in certain CI builds. +- Fixed: race condition in `Mkdir` which can happen when two `SConscript` + are processed simultaneously by two separate build commands. +- MSVC: VS2015 Express (14.0Exp) may not have been detected. VS2015 Express should be correctly detected. - MSVC: VS2010 (10.0) could be inadvertently detected due to an sdk-only install of Windows SDK 7.1. An sdk-only install of VS2010 is ignored as the msvc batch @@ -122,43 +138,27 @@ FIXES - MSVC: VS2008 (9.0) Visual C++ For Python was not detected when installed using the ALLUSERS=1 option (i.e., msiexec /i VCForPython27.msi ALLUSERS=1). When installed for all users, VS2008 (9.0) Visual C++ For Python is now correctly detected. -- MSVC: The search for the msvc compiler executable (cl.exe) no longer inspects the - OS system path in certain situations when setting up the msvc environment. - MSVC: The installed vcs list was constructed and cached during the initial invocation. If a vswhere executable was specified via the construction variable VSWHERE and found additional msvc installations, the new installations were not reflected in the installed vcs list. Now, when a user-specified vswhere executable finds new msvc installations, the installed vcs list is reconstructed. -- MSCommon: Test SConfTests.py would fail when mscommon debugging was enabled via the - MSVC_MSCOMMON_DEBUG environment variable. The mscommon logging filter class registered - 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. -- FORTRAN: Fix gfortran tool initialization. Defaults to using binary named gfortran - as would be expected, and properly set's SHFORTRAN flags to include -fPIC - where previously it was only doing so for the other fortran versions (F77,..) -- MSCommon: Added more error handling while reading msvc config cache. - (Enabled/specified by SCONS_CACHE_MSVC_CONFIG). - The existing cache will be discarded if there's a decode error reading it. - It's possible there's a race condition creating this issue it in certain CI builds. -- Fixed: race condition in `Mkdir` which can happen when two `SConscript` - are processed simultaneously by two separate build commands. IMPROVEMENTS ------------ - Now tries to find mingw if it comes from Chocolatey install of msys2. -- MSVC: VS2015 Express (14.1Exp) does not support the sdk version argument. VS2015 - Express does not support the store argument for target architectures other than - x86. Script argument validation now takes into account these restrictions. -- MSVC: VS2015 BuildTools (14.0) does not support the sdk version argument and - does not support the store argument. Script argument validation now takes into - account these restrictions. - MSVC: Module imports were changed from a relative import to a top-level absolute import in the following Microsoft tools: midl, mslib, mslink, mssdk, msvc, msvs. Moving any of these tools that used relative imports to the scons site tools folder would fail on import (i.e., the relative import paths become invalid when moved). +- MSVC: VS2015 Express (14.0Exp) does not support the sdk version argument. VS2015 + Express does not support the store argument for target architectures other than + x86. Script argument validation now takes into account these restrictions. +- MSVC: VS2015 BuildTools (14.0) does not support the sdk version argument and + does not support the store argument. Script argument validation now takes into + account these restrictions. PACKAGING --------- From d06ad9e5bd309ba75163e3c06df539f7378d2363 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 7 Jan 2024 09:42:04 -0500 Subject: [PATCH 013/386] Merge branch 'master' into HEAD Manually resolve conflicts: * CHANGES.txt * RELEASE.txt * SCons/Tool/MSCommon/vc.py --- .github/workflows/experimental_tests.yml | 7 +- .github/workflows/framework_tests.yml | 4 +- .github/workflows/runtest.yml | 6 +- .github/workflows/scons-package.yml | 4 +- CHANGES.txt | 169 +++++--- RELEASE.txt | 203 +++------- ReleaseConfig | 2 +- SCons/Action.py | 228 +++++------ SCons/ActionTests.py | 54 ++- SCons/Conftest.py | 15 +- SCons/Debug.py | 3 + SCons/Defaults.py | 53 ++- SCons/Defaults.xml | 137 +++++-- SCons/Environment.py | 16 +- SCons/EnvironmentTests.py | 49 ++- SCons/PathList.py | 21 +- SCons/Platform/Platform.xml | 12 +- SCons/Platform/cygwin.py | 5 +- SCons/Platform/os2.py | 5 +- SCons/Platform/posix.py | 5 +- SCons/Platform/win32.py | 10 +- SCons/SConf.py | 4 +- SCons/SConfTests.py | 17 +- SCons/Script/Interactive.py | 2 +- SCons/Script/Main.py | 2 + SCons/Script/SConsOptions.py | 2 +- SCons/Script/SConscript.py | 9 +- SCons/Subst.py | 10 +- SCons/Tool/JavaCommon.py | 2 + SCons/Tool/MSCommon/MSVC/Config.py | 3 +- SCons/Tool/MSCommon/MSVC/Registry.py | 4 +- SCons/Tool/MSCommon/MSVC/Util.py | 87 ++++- SCons/Tool/MSCommon/MSVC/UtilTests.py | 103 ++++- SCons/Tool/MSCommon/MSVC/WinSDK.py | 4 +- SCons/Tool/MSCommon/common.py | 80 +++- SCons/Tool/MSCommon/sdk.py | 5 +- SCons/Tool/MSCommon/vc.py | 13 +- SCons/Tool/compilation_db.py | 23 +- SCons/Tool/cyglink.py | 25 +- SCons/Tool/dmd.py | 4 +- SCons/Tool/docbook/__init__.py | 4 +- SCons/Tool/gnulink.py | 3 + SCons/Tool/ldc.py | 8 +- SCons/Tool/link.py | 2 +- SCons/Tool/mingw.py | 2 +- SCons/Tool/msvc.xml | 67 +++- SCons/Tool/msvs.py | 6 +- SCons/Variables/ListVariable.py | 12 +- SCons/__init__.py | 10 +- SCons/cpp.py | 11 +- SConstruct | 2 +- doc/generated/builders.gen | 55 ++- .../examples/caching_ex-random_1.xml | 6 +- .../examples/commandline_EnumVariable_3.xml | 2 +- .../examples/commandline_Variables_Help_1.xml | 2 +- doc/generated/examples/output_ex1_1.xml | 2 +- doc/generated/examples/output_ex2_1.xml | 2 +- doc/generated/examples/output_ex2_2.xml | 2 +- .../examples/troubleshoot_Dump_1.xml | 1 + .../examples/troubleshoot_Dump_2.xml | 1 + .../examples/troubleshoot_explain1_3.xml | 2 +- doc/generated/functions.gen | 316 +++++++++------ doc/generated/tools.gen | 6 +- doc/generated/variables.gen | 361 ++++++++++++------ doc/generated/variables.mod | 2 + doc/man/scons.xml | 31 +- doc/sphinx/conf.py | 5 +- doc/user/README | 5 +- doc/user/SConstruct | 60 +-- doc/user/actions.xml | 35 +- doc/user/add-method.xml | 8 +- doc/user/alias.xml | 35 +- doc/user/ant.xml | 35 +- doc/user/build-install.xml | 4 +- doc/user/builders-built-in.xml | 35 +- doc/user/builders-commands.xml | 35 +- doc/user/builders-writing.xml | 35 +- doc/user/builders.xml | 33 +- doc/user/caching.xml | 4 +- doc/user/chtml.xsl | 26 +- doc/user/command-line.xml | 35 +- doc/user/depends.xml | 37 +- doc/user/environments.xml | 31 +- doc/user/epub.xsl | 26 +- doc/user/errors.xml | 33 +- doc/user/example.xml | 35 +- doc/user/external.xml | 33 +- doc/user/factories.xml | 37 +- doc/user/file-removal.xml | 35 +- doc/user/functions.xml | 33 +- doc/user/gettext.xml | 41 +- doc/user/hierarchy.xml | 6 +- doc/user/html.xsl | 26 +- doc/user/install.xml | 31 +- doc/user/java.xml | 14 +- doc/user/less-simple.xml | 5 +- doc/user/libraries.xml | 37 +- doc/user/main.xml | 29 +- doc/user/make.xml | 35 +- doc/user/mergeflags.xml | 35 +- doc/user/misc.xml | 31 +- doc/user/nodes.xml | 35 +- doc/user/output.xml | 4 +- doc/user/parse_flags_arg.xml | 31 +- doc/user/parseconfig.xml | 35 +- doc/user/parseflags.xml | 35 +- doc/user/pdf.xsl | 25 +- doc/user/preface.xml | 35 +- doc/user/python.xml | 35 +- doc/user/repositories.xml | 35 +- doc/user/run.xml | 33 +- doc/user/scanners.xml | 51 +-- doc/user/sconf.xml | 35 +- doc/user/scons_title.xsl | 72 ++-- doc/user/separate.xml | 4 +- doc/user/sideeffect.xml | 33 +- doc/user/simple.xml | 4 +- doc/user/tasks.xml | 39 +- doc/user/tools.xml | 33 +- doc/user/troubleshoot.xml | 31 +- doc/user/variables.xml | 33 +- doc/user/variants.xml | 7 +- setup.cfg | 4 - test/AS/as-live.py | 73 ++-- test/Configure/config-h.py | 2 +- test/Docbook/basic/man/image/refdb.xml | 6 +- test/LINK/VersionedLib-j2.py | 10 +- test/LINK/VersionedLib-subdir.py | 12 +- test/Libs/LIBLITERALPREFIX.py | 101 +++++ test/Libs/LIBPREFIXES.py | 23 +- test/Libs/LIBSUFFIXES.py | 33 +- test/Libs/Library.py | 6 +- test/MSVC/PCH-source.py | 2 +- test/MSVS/vs-10.0-exec.py | 2 + test/MSVS/vs-10.0Exp-exec.py | 2 + test/MSVS/vs-11.0-exec.py | 2 + test/MSVS/vs-11.0Exp-exec.py | 2 + test/MSVS/vs-14.0-exec.py | 2 + test/MSVS/vs-14.0Exp-exec.py | 10 +- test/MSVS/vs-14.1-exec.py | 2 + test/MSVS/vs-14.2-exec.py | 2 + test/MSVS/vs-14.3-exec.py | 2 + test/MSVS/vs-6.0-exec.py | 4 +- test/MSVS/vs-7.0-exec.py | 2 + test/MSVS/vs-7.1-exec.py | 2 + test/MSVS/vs-8.0-exec.py | 2 + test/MSVS/vs-8.0Exp-exec.py | 2 + test/MSVS/vs-9.0-exec.py | 2 + test/MSVS/vs-9.0Exp-exec.py | 2 + test/SWIG/SWIG.py | 9 +- test/TEX/generated_files.py | 11 +- test/TEX/variant_dir.py | 2 +- test/TEX/variant_dir_bibunit.py | 9 +- test/TEX/variant_dir_dup0.py | 9 +- test/TEX/variant_dir_style_dup0.py | 9 +- test/option/debug-sconscript.py | 72 ++++ test/packaging/ipkg.py | 4 +- test/packaging/option--package-type.py | 9 +- test/packaging/rpm/cleanup.py | 9 +- test/packaging/rpm/explicit-target.py | 9 +- test/packaging/rpm/internationalization.py | 11 +- test/packaging/rpm/multipackage.py | 9 +- test/packaging/rpm/package.py | 9 +- test/packaging/rpm/tagging.py | 9 +- test/rebuild-generated.py | 3 +- testing/framework/TestCmd.py | 27 +- testing/framework/TestCmdTests.py | 110 +++++- testing/framework/TestSCons.py | 2 +- testing/framework/TestUnit/taprunner.py | 2 +- 169 files changed, 2415 insertions(+), 2183 deletions(-) create mode 100644 test/Libs/LIBLITERALPREFIX.py create mode 100644 test/option/debug-sconscript.py diff --git a/.github/workflows/experimental_tests.yml b/.github/workflows/experimental_tests.yml index c272d51e82..639260a999 100644 --- a/.github/workflows/experimental_tests.yml +++ b/.github/workflows/experimental_tests.yml @@ -32,18 +32,19 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4.1.1 # experiment: maybe don't need this? # update: looks like we do: with this commented out, the build hung - name: Set up MinGW - uses: egor-tensin/setup-mingw@v2 + uses: egor-tensin/setup-mingw@v2.2.0 if: matrix.os == 'windows-2019' with: platform: x64 + static: 0 - name: Set up Python 3.11 ${{ matrix.os }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5.0.0 with: python-version: '3.11' diff --git a/.github/workflows/framework_tests.yml b/.github/workflows/framework_tests.yml index 1c4754b7c3..680c8b97c0 100644 --- a/.github/workflows/framework_tests.yml +++ b/.github/workflows/framework_tests.yml @@ -29,10 +29,10 @@ jobs: steps: # Checkouut repository under $GITHUB_WORKSPACE - - uses: actions/checkout@v2 + - uses: actions/checkout@v4.1.1 - name: Set up Python 3.11 ${{ matrix.os }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5.0.0 with: python-version: '3.11' diff --git a/.github/workflows/runtest.yml b/.github/workflows/runtest.yml index 02be7c1140..4f1b3f4f26 100644 --- a/.github/workflows/runtest.yml +++ b/.github/workflows/runtest.yml @@ -28,10 +28,10 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4.1.1 - name: Set up Python 3.10 ${{ matrix.os }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5.0.0 with: python-version: '3.10' @@ -46,7 +46,7 @@ jobs: python runtest.py --all --time --jobs=2 - name: Archive Failed tests ${{ matrix.os }} - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.3 with: name: ${{ matrix.os }}-failed-tests path: | diff --git a/.github/workflows/scons-package.yml b/.github/workflows/scons-package.yml index c0c4e523f3..6c36cf049b 100644 --- a/.github/workflows/scons-package.yml +++ b/.github/workflows/scons-package.yml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4.1.1 - name: Set up Python 3.10 - uses: actions/setup-python@v2 + uses: actions/setup-python@v5.0.0 with: python-version: '3.10' diff --git a/CHANGES.txt b/CHANGES.txt index e47c13e87a..cc3aa392ef 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,56 +9,41 @@ NOTE: 4.3.0 now requires Python 3.6.0 and above. Python 3.5.x is no longer suppo RELEASE VERSION/DATE TO BE FILLED IN LATER - From Max Bachmann: - - Add missing directories to searched paths for mingw installs + From Ataf Fazledin Ahamed: + - Use of NotImplemented instead of NotImplementedError for special methods + of _ListVariable class From Joseph Brill: - - Fix issue #4312: the cached installed msvc list had an indirect dependency - on the target architecture in the environment dictionary. The first call - to construct the installed msvc list now forces the target architecture to be - undefined, constructs the installed msvc list, and then restores the original - target architecture. - Note: an indirect dependency on the VSWHERE construction variable in the - environment remains. - - Fix issue #4312: explicitly guard against an empty regular expression list - when msvc is not installed. - - When trying to find a valid msvc batch file, check that the compiler executable - (cl.exe) exists for VS6 to VS2015 to avoid executing the msvc batch file. Always - check that the compiler executable is found on the msvc script environment path - after running the msvc batch file. Only use the sdk batch files when all of the - msvc script host/target combinations have been exhausted and a valid script was - not found. - - Add ARM64 host configurations for windows and msvc. - Note: VS2013 and earlier has not been tested on ARM64. - - If necessary, automatically define VSCMD_SKIP_SENDTELEMETRY for VS2019 and later - on ARM64 hosts when using an arm32 build of python to prevent a powershell dll - not found error pop-up window. - - Fix an issue where test SConfTests.py would fail when mscommon debugging - was enabled. The mscommon debug filter class registered with the logging - module was refactored. - - Add arm64 to the MSVS supported architectures list for VS2017 and later to be - consistent with the current documentation of MSVS_ARCH. - - Fix an issue with an unhandled MissingConfiguration exception due to an msvc - registry query that returns a path that does not exist. Multiple invocation - paths were not prepared to handle the MissingConfiguration exception. The - MissingConfiguration exception type was removed. - - The MSCommon module import was changed from a relative import to a top-level - absolute import in the following Microsoft tools: midl, mslib, mslink, mssdk, msvc, - msvs. Moving any of these tools that used relative imports to the scons site tools - folder would fail on import (i.e., the relative import paths become invalid when - moved). - - The detection of the msvc compiler executable (cl.exe) has been modified. The - compiler detection no longer considers the host os environment path. In addition, - existence of the msvc compiler executable is checked in the detection dictionary - and the scons ENV path before the detection dictionary is merged into the scons - ENV. Different warnings are produced when the msvc compiler is not detected in the - detection dictionary based on whether or not an msvc compiler was detected in the - scons ENV path (i.e., already exists in the user's ENV path prior to detection). + - Fix issue #2755: the msvs tool no longer writes the OS environment SCONS_HOME + value into the SCons environment when the SCONS_HOME variable already exists + in the SCons environment. Prior to this change, a valid user-defined SCons + environment value for SCONS_HOME would be overwritten with the OS environment + value of SCONS_HOME which could be None (i.e., undefined). + - Update the windows registry keys for detection of Visual Studio 2015 Express + ('14.0Exp'): the VS2015 registry key ('WDExpress') appears to be different + than the registry key ('VCExpress') for earlier Visual Studio express + versions. The registry key value is relative to the installation root rather + than the VC folder and requires additional path components during evaluation. + - Fix the vs-6.0-exec.py test script: the msvs generated project is 'foo.dsp' + and the command-line invocation of the Visual Studio development environment + program was attempting to build 'test.dsp'. The command-line invocation was + changed to build 'foo.dsp'. + - Update the msvs project generation test scripts: the msvs project execution + tests could produce a "false positive" test result when the test executable is + correctly built via the SConstruct env.Program() call and the command-line + invocation of the Visual Studio development environment program fails. The + test passes due to the existence of the test executable from the initial + build. The tests were modified to delete the test executable, object file, + and sconsign file prior to the command-line invocation of the VS development + binary. + - Method unlink_files was added to the TestCmd class that unlinks a list of + files from a specified directory. An attempt to unlink a file is made only + when the file exists; otherwise, the file is ignored. + - Fix issue #4320: add an optional argument list string to configure's CheckFunc + method so that the generated function argument list matches the function's + prototype when including a header file. - For msvc version specifications without an 'Exp' suffix, an express installation is used when no other edition is detected for the msvc version. - - VS2015 Express (14.0Exp) may not have been detected. The registry keys written - during installation appear to be different than for earlier express versions. - VS2015 Express should be correctly detected now. - VS2015 Express (14.0Exp) does not support the sdk version argument. VS2015 Express does not support the store argument for target architectures other than x86. Script argument validation now takes into account these restrictions. @@ -101,6 +86,76 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER installed vcs list. During runtime, if a user-specified vswhere executable finds new msvc installations, internal runtime caches are cleared and the installed vcs list is reconstructed. + + From William Deegan: + - Fix sphinx config to handle SCons versions with post such as: 4.6.0.post1 + + From Michał Górny: + - Remove unecessary dependencies on pypi packages from setup.cfg + + From Sten Grüner: + - Fix of the --debug=sconscript option to return exist statements when using return + statement with stop flag enabled + + +RELEASE 4.6.0 - Sun, 19 Nov 2023 17:22:20 -0700 + + From Max Bachmann: + - Add missing directories to searched paths for mingw installs + + From Joseph Brill: + - Fix issue #4312: the cached installed msvc list had an indirect dependency + on the target architecture in the environment dictionary. The first call + to construct the installed msvc list now forces the target architecture to be + undefined, constructs the installed msvc list, and then restores the original + target architecture. + Note: an indirect dependency on the VSWHERE construction variable in the + environment remains. + - Fix issue #4312: explicitly guard against an empty regular expression list + when msvc is not installed. + - When trying to find a valid msvc batch file, check that the compiler executable + (cl.exe) exists for VS6 to VS2015 to avoid executing the msvc batch file. Always + check that the compiler executable is found on the msvc script environment path + after running the msvc batch file. Only use the sdk batch files when all of the + msvc script host/target combinations have been exhausted and a valid script was + not found. + - Add ARM64 host configurations for windows and msvc. + Note: VS2013 and earlier has not been tested on ARM64. + - If necessary, automatically define VSCMD_SKIP_SENDTELEMETRY for VS2019 and later + on ARM64 hosts when using an arm32 build of python to prevent a powershell dll + not found error pop-up window. + - Fix an issue where test SConfTests.py would fail when mscommon debugging + was enabled. The mscommon debug filter class registered with the logging + module was refactored. + - Add arm64 to the MSVS supported architectures list for VS2017 and later to be + consistent with the current documentation of MSVS_ARCH. + - Fix an issue with an unhandled MissingConfiguration exception due to an msvc + registry query that returns a path that does not exist. Multiple invocation + paths were not prepared to handle the MissingConfiguration exception. The + MissingConfiguration exception type was removed. + - The MSCommon module import was changed from a relative import to a top-level + absolute import in the following Microsoft tools: midl, mslib, mslink, mssdk, msvc, + msvs. Moving any of these tools that used relative imports to the scons site tools + folder would fail on import (i.e., the relative import paths become invalid when + moved). + - The detection of the msvc compiler executable (cl.exe) has been modified: + * The host os environment path is no longer evaluated for the existence of the + msvc compiler executable when searching the detection dictionary. + * The existence of the msvc compiler executable is checked in the detection + dictionary and the scons ENV path before the detection dictionary is merged + into the scons ENV. + * Different warnings are produced when the msvc compiler is not detected in the + detection dictionary based on whether or not an msvc compiler was detected in + the scons ENV path (i.e., a msvc compiler executable already exists in the + user's ENV path prior to detection). + * The warning message issued when a msvc compiler executable is not found in the + detection dictionary was modified by adding the word "requested": + Old warning: "Could not find MSVC compiler 'cl'." + New warning: "Could not find requested MSVC compiler 'cl'.". + * An additonal sentence is appended to the warning message issued when an msvc + compiler executable is not found in the msvc detection dictionary and is found + in the user's ENV path prior to detection: + " A 'cl' was found on the scons ENV path which may be erroneous." From Vitaly Cheptsov: - Fix race condition in `Mkdir` which can happen when two `SConscript` @@ -113,11 +168,19 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Obsoleted YACCVCGFILESUFFIX, being replaced by YACC_GRAPH_FILE_SUFFIX. If YACC_GRAPH_FILE_SUFFIX is not set, it will respect YACCVCGFILESUFFIX. + From Sten Grüner + - The newly added --debug=sconscript option (new) will output notices when + entering an exiting each SConscript as they are processed. + From Philipp Maierhöfer: - Fix gfortran tool initialization. Defaults to using binary named gfortran as would be expected, and properly set's SHFORTRAN flags to include -fPIC where previously it was only doing so for the other fortran versions (F77,..) + From Jonathon Reinhart: + - Fix another instance of `int main()` in CheckLib() causing failures + when using -Wstrict-prototypes. + From Mats Wichmann - C scanner's dictifyCPPDEFINES routine did not understand the possible combinations of CPPDEFINES - not aware of a "name=value" string either @@ -238,11 +301,15 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Python stdlib types module. - TeX tests: skip tests that use makeindex or epstopdf not installed, or if `kpsewhich glossaries.sty` fails. - - - From Jonathon Reinhart: - - Fix another instance of `int main()` in CheckLib() causing failures - when using -Wstrict-prototypes. + - Added a .note.GNU-stack section to the test assembler files to + avoid the GNU linker issuing warnings for its absence. + - Eliminate more http: references (mostly in comments/docstrings where + they really weren't harmful). A few links labeled dead with no alt. + - Add JDK 21 LTS support + - Add a LIBLITERALPREFIX variable which can be set to the linker's + prefix for considering a library argument unmodified (e.g. for the + GNU linker, the ':' in '-l:libfoo.a'). Fixes Github issue #3951. + - Update PCH builder docs with some usage notes. RELEASE 4.5.2 - Sun, 21 Mar 2023 14:08:29 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 6cfdb33e69..fb12a6feab 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -6,23 +6,20 @@ Past official release announcements appear at: ================================================================== -A new SCons release, 4.5.3, is now available on the SCons download page: +A new SCons release, 4.6.1, is now available on the SCons download page: https://scons.org/pages/download.html -Here is a summary of the changes since 4.5.2: +Here is a summary of the changes since 4.6.0: NEW FUNCTIONALITY ----------------- -- D compilers : added support for generation of .di interface files. - New variables DI_FILE_DIR, DI_FILE_DIR_PREFIX, DI_FILE_DIR_SUFFIX, - DI_FILE_SUFFIX. -- MSVC: If available, native arm64 tools will be used on arm64 hosts for VS2022. -- 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). +- Method unlink_files was added to the TestCmd class that unlinks a list of files + from a specified directory. An attempt to unlink a file is made only when the + file exists; otherwise, the file is ignored. + DEPRECATED FUNCTIONALITY ------------------------ @@ -32,173 +29,83 @@ DEPRECATED FUNCTIONALITY CHANGED/ENHANCED EXISTING FUNCTIONALITY --------------------------------------- -- When debugging (--debug=pdb), the filenames SConstruct and SConscript - are now recognized when manipulating breakpoints. Previously, - only a full pathname to an sconscript file worked, as pdb requires - a .py extension to open a file that is not an absolute path. -- Three unused non-public methods of the Environment Base class - were dropped: get_src_sig_type, get_tgt_sig_type, _changed_source. - These were unused remnants of the previously removed SourceSignatures - and TargetSignatures features (dropped in 3.1.2). -- The --debug flag now has a 'json' option which will write information - generated by --debug={count, memory, time, action-timestamps} and about - the build. -- Obsoleted YACCVCGFILESUFFIX, it's being replaced by YACC_GRAPH_FILE_SUFFIX. - If YACC_GRAPH_FILE_SUFFIX is not set, it will respect YACCVCGFILESUFFIX. -- The yacc tool now understands the bison behavior of --header, --defines - and --graph being called without an option-argument as being synonyms - for -d (first two) and -g. -H also recognized as a synonym for -d. - Default value for $YACC_GRAPH_FILE_SUFFIX changed to '.gv' to match - current bison default (since bison 3.8). Set this variable to '.dot' - if using byacc. Fixes #4326 and #4327. -- MSVC: When trying to find a valid msvc batch file, the existence of the msvc compiler - executable is verified for VS6 to VS2015 to avoid executing the msvc batch file when - the host/target tool is known not to be present. Always check that the msvc compiler - executable is found on the msvc script environment path after running the msvc batch - file. This is predominately needed for recent versions of Visual Studio where the msvc - batch file exists but an individual msvc toolset may not support the host/target - architecture combination. For example, when using VS2022 on arm64, the arm64 native - tools are only installed for the 14.3x toolsets. -- MSVC: When the msvc compiler executable is not found during setup of the msvc - environment, the warning message issued takes into account whether or not a - possibly erroneous compiler executable was already present in the scons environment - path. -- Extend range of recognized Java versions to 20. -- Builder calls (like Program()) now accept pathlib objects in source lists. -- The Help() function now takes an additional keyword argument keep_local: - when starting to build a help message, you can now retain help from AddOption - calls (options added for the project_, but omit help for SCons' own cmdline - options with "Help(newtext, append=True, local_only=True)". -- Calling SConscript() with a nonexistent file is now an error. - Previously this succeeded - prior to SCons 3.0, silently; since 3.0, with - a warning. Developers can still instruct such an SConscript() call not - to fail by being explicit: pass keyword argument "must_exist=False". - The "--warn=missing-sconscript" commandline option is no longer available - as the warning was part of the transitional phase. -- Add missing directories to searched paths for mingw installs -- SCons.Util.types renamed to to SCons.Util.sctypes to avoid any possible - confusion with the Python stdlib "types" module. Note that it was briefly - (for 4.5.x only) possible to import directly from SCons.Util.types, - although the preferred usage remains to import from SCons.Util only. - Any code that did the direct import will have to change to import from - SCons.Util.sctypes. +- Add an optional argument list string to configure's CheckFunc method so + that the generated function argument list matches the function's + prototype when including a header file. Fixes GH Issue #4320 - MSVC: For msvc version specifications without an 'Exp' suffix, an express installation is used when no other edition is detected for the msvc version. - This was the behavior for VS2008 (9.0) through VS2015 (14.0). This behavior - was extended to VS2017 (14.1) and VS2008 (8.0). + This was the behavior for Visual Studio 2008 (9.0) through Visual Studio 2015 + (14.0). This behavior was extended to Visual Studio 2017 (14.1) and Visual + Studio 2008 (8.0). FIXES ----- -- Fixed: when using the mingw tool, if an msys2 Python is used (os.sep - is '/' rather than the Windows default '\'), certain Configure checks - could fail due to the construction of the path to run the compiled check. -- C scanner's dictifyCPPDEFINES routine did not understand the possible - combinations of CPPDEFINES - not aware of a "name=value" string either - embedded in a sequence, or by itself. The conditional C scanner thus - did not always properly apply the defines. The regular C scanner does - not use these, so was not affected. [fixes #4193] -- MSVC: The installed msvc list is calculated once and cached. There was an indirect - dependency on the target architecture when determining if each version of msvc - was installed based on the initial invocation. It was possible that an msvc instance - would not be considered installed due to the tools for the requested target - architecture not being installed. The initial call to construct the installed msvc - list now uses an undefined target architecture to evaluate all potential host/target - combinations when evaluating if the msvc tools are installed for a given Visual Studio - installation. -- MSVC: Erroneous construction of the installed msvc list (as described above) caused an - index error in the msvc support code. An explicit check was added to prevent indexing - into an empty list. Fixes #4312. -- MSVC: The search for the msvc compiler executable (cl.exe) no longer inspects the - OS system path in certain situations when setting up the msvc environment. -- MSCommon: Test SConfTests.py would fail when mscommon debugging was enabled via the - MSVC_MSCOMMON_DEBUG environment variable. The mscommon logging filter class registered - 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. -- FORTRAN: Fix gfortran tool initialization. Defaults to using binary named gfortran - as would be expected, and properly set's SHFORTRAN flags to include -fPIC - where previously it was only doing so for the other fortran versions (F77,..) -- MSCommon: Added more error handling while reading msvc config cache. - (Enabled/specified by SCONS_CACHE_MSVC_CONFIG). - The existing cache will be discarded if there's a decode error reading it. - It's possible there's a race condition creating this issue it in certain CI builds. -- Fixed: race condition in `Mkdir` which can happen when two `SConscript` - are processed simultaneously by two separate build commands. -- MSVC: VS2015 Express (14.0Exp) may not have been detected. VS2015 Express should - be correctly detected. -- MSVC: VS2010 (10.0) could be inadvertently detected due to an sdk-only install - of Windows SDK 7.1. An sdk-only install of VS2010 is ignored as the msvc batch - files will fail. The installed files are intended to be used in conjunction with - the SDK batch file. Similar protection was added for VS2008 (9.0). -- MSVC: For VS2005 (8.0) to VS2015 (14.0), detection of installed files was expanded - to include the primary msvc batch file, dependent msvc batch file, and compiler - executable. In certain installations, the dependent msvc batch file may not exist - while the compiler executable does exists resulting in a build failure. -- MSVC: VS2008 (9.0) Visual C++ For Python was not detected when installed using the - ALLUSERS=1 option (i.e., msiexec /i VCForPython27.msi ALLUSERS=1). When installed - for all users, VS2008 (9.0) Visual C++ For Python is now correctly detected. +- Fix of the --debug=sconscript option to return exist statements when using return + statement with stop flag enabled +- MSVS: prevent overwriting the SCons environment variable SCONS_HOME with the OS + environment value of SCONS_HOME in the msvs tool. +- MSVC: Fix the detection of Visual Studio 2015 Express ('14.0Exp') by adding a + registry key definition and updating the installation root-relative registry value + at runtime for the location of the VC folder. +- MSVS: Fix the msvs project generation test for MSVS 6.0 to use the correct name of + the generated project file. +- MSVS: Fix the msvs project generation test scripts so that "false positive" tests + results are not possible when the initial build is successful and the command-line + build of the project file fails. +- MSVC: Visual Studio 2010 (10.0) could be inadvertently detected due to an + sdk-only install of Windows SDK 7.1. An sdk-only install of Visual Studio + 2010 is ignored as the msvc batch files will fail. The installed files are + intended to be used in conjunction with the SDK batch file. Similar protection + was added for Visual Studio 2008 (9.0). +- MSVC: For Visual Studio 2005 (8.0) to Visual Studio 2015 (14.0), detection of + installed files was expanded to include the primary msvc batch file, dependent + msvc batch file, and compiler executable. In certain installations, the + dependent msvc batch file may not exist while the compiler executable does exists + resulting in a build failure. +- MSVC: Visual Studio 2008 (9.0) Visual C++ For Python was not detected when + installed using the ALLUSERS command-line option: + msiexec /i VCForPython27.msi ALLUSERS=1 + When installed for all users, Visual Studio 2008 (9.0) Visual C++ For Python is + now correctly detected. - MSVC: The installed vcs list was constructed and cached during the initial invocation. If a vswhere executable was specified via the construction variable VSWHERE and found additional msvc installations, the new installations were not - reflected in the installed vcs list. Now, when a user-specified vswhere executable - finds new msvc installations, the installed vcs list is reconstructed. + reflected in the installed vcs list. Now, when a user-specified vswhere + executable finds new msvc installations, the installed vcs list is reconstructed. IMPROVEMENTS ------------ -- Now tries to find mingw if it comes from Chocolatey install of msys2. -- MSVC: Module imports were changed from a relative import to a top-level - absolute import in the following Microsoft tools: midl, mslib, mslink, mssdk, msvc, - msvs. Moving any of these tools that used relative imports to the scons site tools - folder would fail on import (i.e., the relative import paths become invalid when - moved). -- MSVC: VS2015 Express (14.0Exp) does not support the sdk version argument. VS2015 - Express does not support the store argument for target architectures other than - x86. Script argument validation now takes into account these restrictions. -- MSVC: VS2015 BuildTools (14.0) does not support the sdk version argument and - does not support the store argument. Script argument validation now takes into +- Use of NotImplemented instead of NotImplementedError for special methods + of _ListVariable class +- MSVC: Visual Studio 2015 Express (14.0Exp) does not support the sdk version + argument. Visual Studio 2015 Express does not support the store argument for + target architectures other than x86. Script argument validation now takes into account these restrictions. +- MSVC: Visual Studio 2015 BuildTools (14.0) does not support the sdk version + argument and does not support the store argument. Script argument validation now + takes into account these restrictions. PACKAGING --------- -- The build of scons now matches the help text displayed - the targets - listed there can all now be given as a target to build (except for - the two full source balls; the source tar.gz is currently generated directly - by the GitHub release process). +- Remove unecessary dependencies on pypi packages from setup.cfg DOCUMENTATION ------------- -- Aligned manpage signature for Alias function to match implementation - - if the previous *targets* parameter had been used as a keyword argument, - the results would be incorrect (does not apply to positional argument - usage, which had no problem). -- Changed the message about scons -H to clarify it shows built-in options only. -- Cachedir description updated. -- Updated the first two chapters on building with SCons in the User Guide. -- Clarify that exported variables are shared - if mutable, changes made in - an SConscript are visible everywhere that takes a refereence to that object. +- List any significant changes to the documentation (not individual + typo fixes, even if they're mentioned in src/CHANGES.txt to give + the contributor credit) DEVELOPMENT ----------- -- SCons test runner now uses pathlib to normalize and compare paths - to test files, which allows test lists, exclude lists, and tests on - the command line to "not care" about the OS convention for pathname - separators. -- Class ActionBase is now an abstract base class to more accurately - reflect its usage. Derived _ActionAction inherits the ABC, so it - now declares (actually raises NotImplementedError) two methods it - doesn't use so it can be instantiated by unittests and others. -- Added more type annotations to internal routines. -- Cleaned up dblite module (checker warnings, etc.). -- TeX tests: skip tests that use makeindex or epstopdf not installed, or - if `kpsewhich glossaries.sty` fails. +- Fix sphinx config to handle SCons versions with post such as: 4.6.0.post1 Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.5.2..HEAD + git shortlog --no-merges -ns 4.6.0..HEAD diff --git a/ReleaseConfig b/ReleaseConfig index 7cec710666..7ad834fb42 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -31,7 +31,7 @@ # If the release type is not 'final', the patchlevel is set to the # release date. This value is mandatory and must be present in this file. #version_tuple = (2, 2, 0, 'final', 0) -version_tuple = (4, 5, 3, 'a', 0) +version_tuple = (4, 6, 1, 'a', 0) # Python versions prior to unsupported_python_version cause a fatal error # when that version is used. Python versions prior to deprecate_python_version diff --git a/SCons/Action.py b/SCons/Action.py index e85e5e177b..0dd02cc287 100644 --- a/SCons/Action.py +++ b/SCons/Action.py @@ -108,16 +108,16 @@ import sys from abc import ABC, abstractmethod from collections import OrderedDict -from subprocess import DEVNULL -from typing import Union +from subprocess import DEVNULL, PIPE import SCons.Debug import SCons.Errors import SCons.Subst import SCons.Util -from SCons.Debug import logInstanceCreation # we use these a lot, so try to optimize them +from SCons.Debug import logInstanceCreation +from SCons.Subst import SUBST_SIG, SUBST_RAW from SCons.Util import is_String, is_List class _null: @@ -387,9 +387,10 @@ def _object_instance_content(obj): # print("Inst Methods :\n%s"%pp.pformat(methods)) def _actionAppend(act1, act2): - # This function knows how to slap two actions together. - # Mainly, it handles ListActions by concatenating into - # a single ListAction. + """Joins two actions together. + + Mainly, it handles ListActions by concatenating into a single ListAction. + """ a1 = Action(act1) a2 = Action(act2) if a1 is None: @@ -399,13 +400,12 @@ def _actionAppend(act1, act2): if isinstance(a1, ListAction): if isinstance(a2, ListAction): return ListAction(a1.list + a2.list) - else: - return ListAction(a1.list + [ a2 ]) - else: - if isinstance(a2, ListAction): - return ListAction([ a1 ] + a2.list) - else: - return ListAction([ a1, a2 ]) + return ListAction(a1.list + [ a2 ]) + + if isinstance(a2, ListAction): + return ListAction([ a1 ] + a2.list) + + return ListAction([ a1, a2 ]) def _do_create_keywords(args, kw): @@ -468,11 +468,7 @@ def _do_create_action(act, kw): return CommandAction(act, **kw) if callable(act): - try: - gen = kw['generator'] - del kw['generator'] - except KeyError: - gen = 0 + gen = kw.pop('generator', False) if gen: action_type = CommandGeneratorAction else: @@ -480,7 +476,7 @@ def _do_create_action(act, kw): return action_type(act, kw) # Catch a common error case with a nice message: - if isinstance(act, int) or isinstance(act, float): + if isinstance(act, (int, float)): raise TypeError("Don't know how to create an Action from a number (%s)"%act) # Else fail silently (???) return None @@ -504,10 +500,9 @@ def _do_create_list_action(act, kw) -> "ListAction": acts.append(aa) if not acts: return ListAction([]) - elif len(acts) == 1: + if len(acts) == 1: return acts[0] - else: - return ListAction(acts) + return ListAction(acts) def Action(act, *args, **kw): @@ -547,7 +542,7 @@ def no_batch_key(self, env, target, source): batch_key = no_batch_key - def genstring(self, target, source, env): + def genstring(self, target, source, env, executor=None) -> str: return str(self) @abstractmethod @@ -561,7 +556,7 @@ def get_implicit_deps(self, target, source, env, executor=None): def get_contents(self, target, source, env): result = self.get_presig(target, source, env) - if not isinstance(result,(bytes, bytearray)): + if not isinstance(result, (bytes, bytearray)): result = bytearray(result, 'utf-8') else: # Make a copy and put in bytearray, without this the contents returned by get_presig @@ -579,17 +574,15 @@ def get_contents(self, target, source, env): for v in vl: # do the subst this way to ignore $(...$) parts: if isinstance(result, bytearray): - result.extend(SCons.Util.to_bytes(env.subst_target_source('${'+v+'}', SCons.Subst.SUBST_SIG, target, source))) + result.extend(SCons.Util.to_bytes(env.subst_target_source('${'+v+'}', SUBST_SIG, target, source))) else: raise Exception("WE SHOULD NEVER GET HERE result should be bytearray not:%s"%type(result)) - # result.append(SCons.Util.to_bytes(env.subst_target_source('${'+v+'}', SCons.Subst.SUBST_SIG, target, source))) - + # result.append(SCons.Util.to_bytes(env.subst_target_source('${'+v+'}', SUBST_SIG, target, source))) - if isinstance(result, (bytes,bytearray)): + if isinstance(result, (bytes, bytearray)): return result - else: - raise Exception("WE SHOULD NEVER GET HERE - #2 result should be bytearray not:%s" % type(result)) - # return b''.join(result) + + raise Exception("WE SHOULD NEVER GET HERE - #2 result should be bytearray not:%s" % type(result)) def __add__(self, other): return _actionAppend(self, other) @@ -788,7 +781,7 @@ def get_default_ENV(env): return env['ENV'] except KeyError: if not default_ENV: - import SCons.Environment + import SCons.Environment # pylint: disable=import-outside-toplevel,redefined-outer-name # This is a hideously expensive way to get a default execution # environment. What it really should do is run the platform # setup to get the default ENV. Fortunately, it's incredibly @@ -815,42 +808,47 @@ def _resolve_shell_env(env, target, source): shell_gens = iter(shell_gen) except TypeError: raise SCons.Errors.UserError("SHELL_ENV_GENERATORS must be iteratable.") - else: - ENV = ENV.copy() - for generator in shell_gens: - ENV = generator(env, target, source, ENV) - if not isinstance(ENV, dict): - raise SCons.Errors.UserError(f"SHELL_ENV_GENERATORS function: {generator} must return a dict.") - return ENV + ENV = ENV.copy() + for generator in shell_gens: + ENV = generator(env, target, source, ENV) + if not isinstance(ENV, dict): + raise SCons.Errors.UserError(f"SHELL_ENV_GENERATORS function: {generator} must return a dict.") -def scons_subproc_run( - scons_env, *args, error: str = None, **kwargs -) -> subprocess.CompletedProcess: - """Run a command with arguments using an SCons execution environment. + return ENV - Does an underlyng call to :func:`subprocess.run` to run a command and - returns a :class:`subprocess.CompletedProcess` instance with the results. - Use when an external command needs to run in an "SCons context" - - that is, with a crafted execution environment, rather than the user's - existing environment, particularly when you need to collect output - back. Typical case: run a tool's "version" option to find out which - version you have installed. - If supplied, the ``env`` keyword argument passes an - execution environment to process into appropriate form before it is - supplied to :mod:`subprocess`; if omitted, *scons_env* is used to derive - a suitable default. The other keyword arguments are passed through, - except that SCons legacy ``error` keyword is remapped to - the subprocess ``check` keyword. The caller is responsible for - setting up the desired arguments for :func:`subprocess.run`. +def scons_subproc_run(scons_env, *args, **kwargs) -> subprocess.CompletedProcess: + """Run an external command using an SCons execution environment. + + SCons normally runs external build commands using :mod:`subprocess`, + but does not harvest any output from such commands. This function + is a thin wrapper around :func:`subprocess.run` allowing running + a command in an SCons context (i.e. uses an "execution environment" + rather than the user's existing environment), and provides the ability + to return any output in a :class:`subprocess.CompletedProcess` + instance (this must be selected by setting ``stdout`` and/or + ``stderr`` to ``PIPE``, or setting ``capture_output=True`` - see + Keyword Arguments). Typical use case is to run a tool's "version" + option to find out the installed version. + + If supplied, the ``env`` keyword argument provides an execution + environment to process into appropriate form before it is supplied + to :mod:`subprocess`; if omitted, *scons_env* is used to derive a + suitable default. The other keyword arguments are passed through, + except that the SCons legacy ``error`` keyword is remapped to the + subprocess ``check`` keyword; if both are omitted ``check=False`` + will be passed. The caller is responsible for setting up the desired + arguments for :func:`subprocess.run`. This function retains the legacy behavior of returning something - vaguely usable even in the face of complete failure: it synthesizes - a :class:`~subprocess.CompletedProcess` instance in this case. + vaguely usable even in the face of complete failure, unless + ``check=True`` (in which case an error is allowed to be raised): + it synthesizes a :class:`~subprocess.CompletedProcess` instance in + this case. A subset of interesting keyword arguments follows; see the Python - documentation of :mod:`subprocess` for a complete list. + documentation of :mod:`subprocess` for the complete list. Keyword Arguments: stdout: (and *stderr*, *stdin*) if set to :const:`subprocess.PIPE`. @@ -858,12 +856,11 @@ def scons_subproc_run( the subprocess; the default ``None`` does no redirection (i.e. output or errors may go to the console or log file, but is not captured); if set to :const:`subprocess.DEVNULL` - they are explicitly thrown away. ``capture_output`` is a + they are explicitly thrown away. ``capture_output=True`` is a synonym for setting both ``stdout`` and ``stderr`` to :const:`~subprocess.PIPE`. text: open *stdin*, *stdout*, *stderr* in text mode. Default - is binary mode. ``universal_newlines`` is a synonym - - note ``text`` is not understood before Python 3.7. + is binary mode. ``universal_newlines`` is a synonym. encoding: specifies an encoding. Changes to text mode. errors: specified error handling. Changes to text mode. input: a byte sequence to be passed to *stdin*, unless text @@ -877,54 +874,48 @@ def scons_subproc_run( .. versionadded:: 4.6 """ # Figure out the execution environment to use - ENV = kwargs.get('env', None) - if ENV is None: - ENV = get_default_ENV(scons_env) - kwargs['env'] = SCons.Util.sanitize_shell_env(ENV) - - # backwards-compat with _subproc: accept 'error', map it to - # ``check`` and remove, since it would not be recognized by run() - check = kwargs.get('check') - if check and error: + env = kwargs.get('env', None) + if env is None: + env = get_default_ENV(scons_env) + kwargs['env'] = SCons.Util.sanitize_shell_env(env) + + # Backwards-compat with _subproc: accept 'error', map to 'check', + # and remove, since subprocess.run does not recognize. + # 'error' isn't True/False, it takes a string value (see _subproc) + error = kwargs.get('error') + if error and 'check' in kwargs: raise ValueError('error and check arguments may not both be used.') - if check is None: - check = False # always supply some value, to pacify checkers (pylint). + check = kwargs.get('check', False) # always set a value for 'check' if error is not None: if error == 'raise': check = True del kwargs['error'] kwargs['check'] = check - # Ensure that the ENV values are all strings: - new_env = {} - for key, value in ENV.items(): - if is_List(value): - # If the value is a list, then we assume it is a path list, - # because that's a pretty common list-like value to stick - # in an environment variable: - value = SCons.Util.flatten_sequence(value) - new_env[key] = os.pathsep.join(str(v) for v in value) - else: - # If it's not a list type, "convert" it to str. This is - # harmless if it's already a string, but gets us the right - # thing for Dir and File instances and will produce something - # reasonable for just about everything else: - new_env[key] = str(value) - kwargs['env'] = new_env - - # Some tools/tests expect no failures for things like missing files - # unless raise/check is set. If set, let subprocess.run go ahead and - # kill things, else catch and construct a dummy CompletedProcess instance. + # TODO: Python version-compat stuff: remap/remove too-new args if needed + if 'text' in kwargs and sys.version_info[:3] < (3, 7): + kwargs['universal_newlines'] = kwargs.pop('text') + + if 'capture_output' in kwargs and sys.version_info[:3] < (3, 7): + capture_output = kwargs.pop('capture_output') + if capture_output: + kwargs['stdout'] = kwargs['stderr'] = PIPE + + # Most SCons tools/tests expect not to fail on things like missing files. + # check=True (or error="raise") means we're okay to take an exception; + # else we catch the likely exception and construct a dummy + # CompletedProcess instance. + # Note pylint can't see we always include 'check' in kwargs: suppress. if check: - cp = subprocess.run(*args, **kwargs) + cp = subprocess.run(*args, **kwargs) # pylint: disable=subprocess-run-check else: try: - cp = subprocess.run(*args, **kwargs) + cp = subprocess.run(*args, **kwargs) # pylint: disable=subprocess-run-check except OSError as exc: argline = ' '.join(*args) - cp = subprocess.CompletedProcess(argline, 1) - cp.stdout = "" - cp.stderr = "" + cp = subprocess.CompletedProcess( + args=argline, returncode=1, stdout="", stderr="" + ) return cp @@ -1044,7 +1035,6 @@ def strfunction(self, target, source, env, executor=None, overrides: bool=False) if self.cmdstr is None: return None if self.cmdstr is not _null: - from SCons.Subst import SUBST_RAW if executor: c = env.subst(self.cmdstr, SUBST_RAW, executor=executor, overrides=overrides) else: @@ -1077,12 +1067,11 @@ def execute(self, target, source, env, executor=None): spawn = env['SPAWN'] except KeyError: raise SCons.Errors.UserError('Missing SPAWN construction variable.') - else: - if is_String(spawn): - spawn = env.subst(spawn, raw=1, conv=lambda x: x) - escape = env.get('ESCAPE', lambda x: x) + if is_String(spawn): + spawn = env.subst(spawn, raw=1, conv=lambda x: x) + escape = env.get('ESCAPE', lambda x: x) ENV = _resolve_shell_env(env, target, source) # Ensure that the ENV values are all strings: @@ -1125,7 +1114,6 @@ def get_presig(self, target, source, env, executor=None): This strips $(-$) and everything in between the string, since those parts don't affect signatures. """ - from SCons.Subst import SUBST_SIG cmd = self.cmd_list if is_List(cmd): cmd = ' '.join(map(str, cmd)) @@ -1133,8 +1121,7 @@ def get_presig(self, target, source, env, executor=None): cmd = str(cmd) if executor: return env.subst_target_source(cmd, SUBST_SIG, executor=executor) - else: - return env.subst_target_source(cmd, SUBST_SIG, target, source) + return env.subst_target_source(cmd, SUBST_SIG, target, source) def get_implicit_deps(self, target, source, env, executor=None): """Return the implicit dependencies of this action's command line.""" @@ -1154,17 +1141,15 @@ def get_implicit_deps(self, target, source, env, executor=None): # An integer value greater than 1 specifies the number of entries # to scan. "all" means to scan all. return self._get_implicit_deps_heavyweight(target, source, env, executor, icd_int) - else: - # Everything else (usually 1 or True) means that we want - # lightweight dependency scanning. - return self._get_implicit_deps_lightweight(target, source, env, executor) + # Everything else (usually 1 or True) means that we want + # lightweight dependency scanning. + return self._get_implicit_deps_lightweight(target, source, env, executor) def _get_implicit_deps_lightweight(self, target, source, env, executor): """ Lightweight dependency scanning involves only scanning the first entry in an action string, even if it contains &&. """ - from SCons.Subst import SUBST_SIG if executor: cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor) else: @@ -1202,7 +1187,6 @@ def _get_implicit_deps_heavyweight(self, target, source, env, executor, # Avoid circular and duplicate dependencies by not providing source, # target, or executor to subst_list. This causes references to # $SOURCES, $TARGETS, and all related variables to disappear. - from SCons.Subst import SUBST_SIG cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, conv=lambda x: x) res = [] @@ -1281,7 +1265,7 @@ def __str__(self) -> str: def batch_key(self, env, target, source): return self._generate(target, source, env, 1).batch_key(env, target, source) - def genstring(self, target, source, env, executor=None): + def genstring(self, target, source, env, executor=None) -> str: return self._generate(target, source, env, 1, executor).genstring(target, source, env) def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, @@ -1409,7 +1393,6 @@ def strfunction(self, target, source, env, executor=None): if self.cmdstr is None: return None if self.cmdstr is not _null: - from SCons.Subst import SUBST_RAW if executor: c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) else: @@ -1456,9 +1439,7 @@ def execute(self, target, source, env, executor=None): rsources = list(map(rfile, source)) try: result = self.execfunction(target=target, source=rsources, env=env) - except KeyboardInterrupt as e: - raise - except SystemExit as e: + except (KeyboardInterrupt, SystemExit): raise except Exception as e: result = e @@ -1466,8 +1447,8 @@ def execute(self, target, source, env, executor=None): if result: result = SCons.Errors.convert_to_BuildError(result, exc_info) - result.node=target - result.action=self + result.node = target + result.action = self try: result.command=self.strfunction(target, source, env, executor) except TypeError: @@ -1480,7 +1461,7 @@ def execute(self, target, source, env, executor=None): # some codes do not check the return value of Actions and I do # not have the time to modify them at this point. if (exc_info[1] and - not isinstance(exc_info[1],EnvironmentError)): + not isinstance(exc_info[1], EnvironmentError)): raise result return result @@ -1514,7 +1495,7 @@ def list_of_actions(x): self.varlist = () self.targets = '$TARGETS' - def genstring(self, target, source, env): + def genstring(self, target, source, env, executor=None) -> str: return '\n'.join([a.genstring(target, source, env) for a in self.list]) def __str__(self) -> str: @@ -1601,8 +1582,9 @@ def subst(self, s, target, source, env): # was called by using this hard-coded value as a special return. if s == '$__env__': return env - elif is_String(s): + if is_String(s): return env.subst(s, 1, target, source) + return self.parent.convert(s) def subst_args(self, target, source, env): diff --git a/SCons/ActionTests.py b/SCons/ActionTests.py index 7239f4f5ef..7d99a2560b 100644 --- a/SCons/ActionTests.py +++ b/SCons/ActionTests.py @@ -41,11 +41,13 @@ def __call__(self) -> None: import sys import types import unittest +from unittest import mock from subprocess import PIPE import SCons.Action import SCons.Environment import SCons.Errors +from SCons.Action import scons_subproc_run import TestCmd @@ -2329,7 +2331,7 @@ def test_code_contents(self) -> None: def test_uncaught_exception_bubbles(self): """Test that scons_subproc_run bubbles uncaught exceptions""" try: - cp = SCons.Action.scons_subproc_run(Environment(), None, stdout=PIPE) + cp = scons_subproc_run(Environment(), None, stdout=PIPE) except EnvironmentError: pass except Exception: @@ -2338,6 +2340,56 @@ def test_uncaught_exception_bubbles(self): raise Exception("expected a non-EnvironmentError exception") + + def mock_subprocess_run(*args, **kwargs): + """Replacement subprocess.run: return kwargs for checking.""" + kwargs.pop("env") # the value of env isn't interesting here + return kwargs + + @mock.patch("subprocess.run", mock_subprocess_run) + def test_scons_subproc_run(self): + """Test the argument remapping options.""" + # set phony Python versions to trigger the logic in scons_subproc_run: + # any version greater than 3.6, really + save_info, sys.version_info = sys.version_info, (3, 11, 1) + env = Environment() + self.assertEqual(scons_subproc_run(env), {"check": False}) + with self.subTest(): + self.assertEqual( + scons_subproc_run(env, error="raise"), + {"check": True} + ) + with self.subTest(): + self.assertEqual( + scons_subproc_run(env, capture_output=True), + {"capture_output": True, "check": False}, + ) + with self.subTest(): + self.assertEqual( + scons_subproc_run(env, text=True), + {"text": True, "check": False}, + ) + + # 3.6: + sys.version_info = (3, 6, 2) + with self.subTest(): + self.assertEqual( + scons_subproc_run(env, capture_output=True), + {"check": False, "stdout": PIPE, "stderr": PIPE}, + ) + with self.subTest(): + self.assertEqual( + scons_subproc_run(env, text=True), + {"check": False, "universal_newlines": True}, + ) + with self.subTest(): + self.assertEqual( + scons_subproc_run(env, universal_newlines=True), + {"universal_newlines": True, "check": False}, + ) + sys.version_info = save_info + + if __name__ == "__main__": unittest.main() diff --git a/SCons/Conftest.py b/SCons/Conftest.py index 6af5e78893..79ede99689 100644 --- a/SCons/Conftest.py +++ b/SCons/Conftest.py @@ -231,17 +231,22 @@ def _check_empty_program(context, comp, text, language, use_shared: bool = False return context.CompileProg(text, suffix) -def CheckFunc(context, function_name, header = None, language = None): +def CheckFunc(context, function_name, header = None, language = None, funcargs = None): """ Configure check for a function "function_name". "language" should be "C" or "C++" and is used to select the compiler. Default is "C". Optional "header" can be defined to define a function prototype, include a header file or anything else that comes before main(). + Optional "funcargs" can be defined to define an argument list for the + generated function invocation. Sets HAVE_function_name in context.havedict according to the result. Note that this uses the current value of compiler and linker flags, make sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. Returns an empty string for success, an error message for failure. + + .. versionchanged:: 4.7.0 + The ``funcargs`` parameter was added. """ # Remarks from autoconf: @@ -274,6 +279,9 @@ def CheckFunc(context, function_name, header = None, language = None): context.Display("Cannot check for %s(): %s\n" % (function_name, msg)) return msg + if not funcargs: + funcargs = '' + text = """ %(include)s #include @@ -287,14 +295,15 @@ def CheckFunc(context, function_name, header = None, language = None): #if defined (__stub_%(name)s) || defined (__stub___%(name)s) #error "%(name)s has a GNU stub, cannot check" #else - %(name)s(); + %(name)s(%(args)s); #endif return 0; } """ % { 'name': function_name, 'include': includetext, - 'hdr': header } + 'hdr': header, + 'args': funcargs} context.Display("Checking for %s function %s()... " % (lang, function_name)) ret = context.BuildProg(text, suffix) diff --git a/SCons/Debug.py b/SCons/Debug.py index 615eb4fe41..9397fdeda9 100644 --- a/SCons/Debug.py +++ b/SCons/Debug.py @@ -41,6 +41,9 @@ track_instances = False # List of currently tracked classes tracked_classes = {} +# Global variable that gets set to 'True' by the Main script +# when SConscript call tracing should be enabled. +sconscript_trace = False def logInstanceCreation(instance, name=None) -> None: if name is None: diff --git a/SCons/Defaults.py b/SCons/Defaults.py index cabadcc679..32e9da3f25 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 @@ -455,16 +455,35 @@ def _concat_ixes(prefix, items_iter, suffix, env): return result -def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): +def _stripixes( + prefix: str, + items, + suffix: str, + stripprefixes: List[str], + stripsuffixes: List[str], + env, + literal_prefix: str = "", + c: Callable[[list], list] = None, +) -> list: + """Returns a list with text added to items after first stripping them. + + A companion to :func:`_concat_ixes`, used by tools (like the GNU + linker) that need to turn something like ``libfoo.a`` into ``-lfoo``. + *stripprefixes* and *stripsuffixes* are stripped from *items*. + Calls function *c* to postprocess the result. + + Args: + prefix: string to prepend to elements + items: string or iterable to transform + suffix: string to append to elements + stripprefixes: prefix string(s) to strip from elements + stripsuffixes: suffix string(s) to strip from elements + env: construction environment for variable interpolation + c: optional function to perform a transformation on the list. + The default is `None`, which will select :func:`_concat_ixes`. """ - This is a wrapper around _concat()/_concat_ixes() that checks for - the existence of prefixes or suffixes on list items and strips them - where it finds them. This is used by tools (like the GNU linker) - that need to turn something like 'libfoo.a' into '-lfoo'. - """ - - if not itms: - return itms + if not items: + return items if not callable(c): env_c = env['_concat'] @@ -480,8 +499,16 @@ def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): stripprefixes = list(map(env.subst, flatten(stripprefixes))) stripsuffixes = list(map(env.subst, flatten(stripsuffixes))) + # This is a little funky: if literal_prefix is the same as os.pathsep + # (e.g. both ':'), the normal conversion to a PathList will drop the + # literal_prefix prefix. Tell it not to split in that case, which *should* + # be okay because if we come through here, we're normally processing + # library names and won't have strings like "path:secondpath:thirdpath" + # which is why PathList() otherwise wants to split strings. + do_split = not literal_prefix == os.pathsep + stripped = [] - for l in SCons.PathList.PathList(itms).subst_path(env, None, None): + for l in SCons.PathList.PathList(items, do_split).subst_path(env, None, None): if isinstance(l, SCons.Node.FS.File): stripped.append(l) continue @@ -489,6 +516,10 @@ def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): if not is_String(l): l = str(l) + if literal_prefix and l.startswith(literal_prefix): + stripped.append(l) + continue + for stripprefix in stripprefixes: lsp = len(stripprefix) if l[:lsp] == stripprefix: diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml index b892f2ef9f..f1ba3860d3 100644 --- a/SCons/Defaults.xml +++ b/SCons/Defaults.xml @@ -250,7 +250,7 @@ as the result will be non-portable and the directories will not be searched by the dependency scanner. &cv-CPPPATH; should be a list of path strings, or a single string, not a pathname list joined by -Python's os.sep. +Python's os.pathsep. @@ -473,7 +473,7 @@ when the &cv-link-_LIBDIRFLAGS; variable is automatically generated. An automatically-generated construction variable containing the linker command-line options for specifying libraries to be linked with the resulting target. -The value of &cv-link-_LIBFLAGS; is created +The value of &cv-_LIBFLAGS; is created by respectively prepending and appending &cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; to each filename in &cv-link-LIBS;. @@ -510,7 +510,7 @@ The list of directories that will be searched for libraries specified by the &cv-link-LIBS; &consvar;. &cv-LIBPATH; should be a list of path strings, or a single string, not a pathname list joined by -Python's os.sep. +Python's os.pathsep. +For each &Builder; call that causes linking with libraries, +&SCons; will add the libraries in the setting of &cv-LIBS; +in effect at that moment to the dependecy graph +as dependencies of the target being generated. + + + +The library list will transformed to command line +arguments through the automatically-generated +&cv-link-_LIBFLAGS; &consvar; which is constructed by respectively prepending and appending the values of the -&cv-LIBLINKPREFIX; and &cv-LIBLINKSUFFIX; &consvars; -to each library name in &cv-LIBS;. -Library name strings should not include a -path component, instead the compiler will be -directed to look for libraries in the paths -specified by &cv-link-LIBPATH;. +&cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; &consvars; +to each library name. -Any command lines you define that need -the &cv-LIBS; library list should -include &cv-_LIBFLAGS;: +Any command lines you define yourself that need +the libraries from &cv-LIBS; should include &cv-_LIBFLAGS; +(as well as &cv-link-_LIBDIRFLAGS;) +rather than &cv-LIBS;. +For example: env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE") + + + + -If you add a -File -object to the -&cv-LIBS; -list, the name of that file will be added to -&cv-_LIBFLAGS;, -and thus to the link line, as-is, without -&cv-LIBLINKPREFIX; -or -&cv-LIBLINKSUFFIX;. -For example: +If the linker supports command line syntax directing +that the argument specifying a library should be +searched for literally (without modification), +&cv-LIBLITERALPREFIX; can be set to that indicator. +For example, the GNU linker follows this rule: + +-l:foo searches the library path +for a filename called foo, +without converting it to +libfoo.so or +libfoo.a. + +If &cv-LIBLITERALPREFIX; is set, +&SCons; will not transform a string-valued entry in +&cv-link-LIBS; that starts with that string. +The entry will still be surrounded with +&cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; +on the command line. +This is useful, for example, +in directing that a static library +be used when both a static and dynamic library are available +and linker policy is to prefer dynamic libraries. +Compared to the example in &cv-link-LIBS;, - -env.Append(LIBS=File('/tmp/mylib.so')) +env.Append(LIBS=":libmylib.a") - -In all cases, scons will add dependencies from the executable program to -all the libraries in this list. +will let the linker select that specific (static) +library name if found in the library search path. +This differs from using a +File object +to specify the static library, +as the latter bypasses the library search path entirely. diff --git a/SCons/Environment.py b/SCons/Environment.py index 6327d86206..64d38b0768 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -37,6 +37,7 @@ import shlex from collections import UserDict, deque from subprocess import PIPE, DEVNULL +from typing import Optional import SCons.Action import SCons.Builder @@ -679,7 +680,7 @@ def gvars(self): def lvars(self): return {} - def subst(self, string, raw: int=0, target=None, source=None, conv=None, executor=None, overrides: bool=False): + def subst(self, string, raw: int=0, target=None, source=None, conv=None, executor=None, overrides: Optional[dict] = None): """Recursively interpolates construction variables from the Environment into the specified string, returning the expanded result. Construction variables are specified by a $ prefix @@ -705,9 +706,11 @@ def subst_kw(self, kw, raw: int=0, target=None, source=None): nkw[k] = v return nkw - def subst_list(self, string, raw: int=0, target=None, source=None, conv=None, executor=None, overrides: bool=False): - """Calls through to SCons.Subst.scons_subst_list(). See - the documentation for that function.""" + def subst_list(self, string, raw: int=0, target=None, source=None, conv=None, executor=None, overrides: Optional[dict] = None): + """Calls through to SCons.Subst.scons_subst_list(). + + See the documentation for that function. + """ gvars = self.gvars() lvars = self.lvars() lvars['__env__'] = self @@ -716,9 +719,10 @@ def subst_list(self, string, raw: int=0, target=None, source=None, conv=None, ex return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv, overrides=overrides) def subst_path(self, path, target=None, source=None): - """Substitute a path list, turning EntryProxies into Nodes - and leaving Nodes (and other objects) as-is.""" + """Substitute a path list. + Turns EntryProxies into Nodes, leaving Nodes (and other objects) as-is. + """ if not is_List(path): path = [path] diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index 2f96679793..c40439ae07 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -1489,7 +1489,8 @@ def copy2(self, src, dst) -> None: SCons.CacheDir.CacheDir.copy_from_cache = save_copy_from_cache SCons.CacheDir.CacheDir.copy_to_cache = save_copy_to_cache - def test_concat(self) -> None: + # function is in Defaults.py, tested here to use TestEnvironment + def test__concat(self) -> None: """Test _concat()""" e1 = self.TestEnvironment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b']) s = e1.subst @@ -1507,7 +1508,8 @@ def test_concat(self) -> None: assert x == '$( preasuf prebsuf $)', x - def test_concat_nested(self) -> None: + # function is in Defaults.py, tested here to use TestEnvironment + def test__concat_nested(self) -> None: """Test _concat() on a nested substitution strings.""" e = self.TestEnvironment(PRE='pre', SUF='suf', L1=['a', 'b'], @@ -1522,6 +1524,49 @@ def test_concat_nested(self) -> None: x = e.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)') assert x == 'preasuf prebsuf precsuf predsuf precsuf predsuf', x + + # function is in Defaults.py, tested here to use TestEnvironment + def test__stripixes(self) -> None: + """Test _stripixes()""" + # LIBPREFIXES and LIBSUFFIXES are stripped, except if an entry + # begins with LIBLITERALPREFIX. Check this with and without that + # argument being passed, and whether or not LIBLITERALPREFIX is + # explicitly set. + e = self.TestEnvironment( + PRE='pre', + SUF='suf', + LIST=['xxx-a', 'b.yyy', 'zzxxx-c.yyy'], + LIBPREFIXES=['xxx-'], + LIBSUFFIXES=['.yyy'], + ) + + # e['LIBLITERALPREFIX'] = '' + with self.subTest(): + x = e.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__, LIBLITERALPREFIX)} $)') + self.assertEqual('preasuf prebsuf prezzxxx-csuf', x) + + with self.subTest(): + x = e.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__)} $)') + self.assertEqual('preasuf prebsuf prezzxxx-csuf', x) + + # add it to the env: + e['LIBLITERALPREFIX'] = 'zz' + + with self.subTest(): + x = e.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__, LIBLITERALPREFIX)} $)') + self.assertEqual('preasuf prebsuf prezzxxx-c.yyysuf', x) + + with self.subTest(): + x = e.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__)} $)') + self.assertEqual('preasuf prebsuf prezzxxx-csuf', x) + + # And special case: LIBLITERALPREFIX is the same as os.pathsep: + e['LIBLITERALPREFIX'] = os.pathsep + with self.subTest(): + x = e.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__, LIBLITERALPREFIX)} $)') + self.assertEqual('preasuf prebsuf prezzxxx-csuf', x) + + def test_gvars(self) -> None: """Test the Environment gvars() method""" env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z') diff --git a/SCons/PathList.py b/SCons/PathList.py index dab8b2ce41..33ac7e58be 100644 --- a/SCons/PathList.py +++ b/SCons/PathList.py @@ -64,10 +64,9 @@ def node_conv(obj): return result class _PathList: - """ - An actual PathList object. - """ - def __init__(self, pathlist) -> None: + """An actual PathList object.""" + + def __init__(self, pathlist, split=True) -> None: """ Initializes a PathList object, canonicalizing the input and pre-processing it for quicker substitution later. @@ -94,7 +93,10 @@ def __init__(self, pathlist) -> None: over and over for each target. """ if SCons.Util.is_String(pathlist): - pathlist = pathlist.split(os.pathsep) + if split: + pathlist = pathlist.split(os.pathsep) + else: # no splitting, but still need a list + pathlist = [pathlist] elif not SCons.Util.is_Sequence(pathlist): pathlist = [pathlist] @@ -141,8 +143,7 @@ def subst_path(self, env, target, source): class PathListCache: - """ - A class to handle caching of PathList lookups. + """A class to handle caching of PathList lookups. This class gets instantiated once and then deleted from the namespace, so it's used as a Singleton (although we don't enforce that in the @@ -161,7 +162,7 @@ class PathListCache: The main type of duplication we're trying to catch will come from looking up the same path list from two different clones of the same construction environment. That is, given - + env2 = env1.Clone() both env1 and env2 will have the same CPPPATH value, and we can @@ -189,7 +190,7 @@ def _PathList_key(self, pathlist): return pathlist @SCons.Memoize.CountDictCall(_PathList_key) - def PathList(self, pathlist): + def PathList(self, pathlist, split=True): """ Returns the cached _PathList object for the specified pathlist, creating and caching a new object as necessary. @@ -206,7 +207,7 @@ def PathList(self, pathlist): except KeyError: pass - result = _PathList(pathlist) + result = _PathList(pathlist, split) memo_dict[pathlist] = result diff --git a/SCons/Platform/Platform.xml b/SCons/Platform/Platform.xml index c449fa50da..dc9ed795d4 100644 --- a/SCons/Platform/Platform.xml +++ b/SCons/Platform/Platform.xml @@ -49,7 +49,8 @@ to reflect the names of the libraries they create. -A list of all legal prefixes for library file names. +A list of all legal prefixes for library file names +on the current platform. When searching for library dependencies, SCons will look for files with these prefixes, the base library name, @@ -75,6 +76,7 @@ to reflect the names of the libraries they create. A list of all legal suffixes for library file names. +on the current platform. When searching for library dependencies, SCons will look for files with prefixes from the &cv-link-LIBPREFIXES; list, the base library name, @@ -129,7 +131,7 @@ else: platform argument to &f-link-Environment;). - Should be considered immutable. + Should be considered immutable. &cv-HOST_OS; is not currently used by &SCons;, but the option is reserved to do so in future @@ -177,7 +179,7 @@ else: and x86_64 for 64-bit hosts. - Should be considered immutable. + Should be considered immutable. &cv-HOST_ARCH; is not currently used by other platforms, but the option is reserved to do so in future @@ -305,7 +307,7 @@ an alternate command line so the invoked tool will make use of the contents of the temporary file. If you need to replace the default tempfile object, the callable should take into account the settings of -&cv-link-MAXLINELENGTH;, +&cv-link-MAXLINELENGTH;, &cv-link-TEMPFILEPREFIX;, &cv-link-TEMPFILESUFFIX;, &cv-link-TEMPFILEARGJOIN;, @@ -367,7 +369,7 @@ The directory to create the long-lines temporary file in. -The default argument escape function is +The default argument escape function is SCons.Subst.quote_spaces. If you need to apply extra operations on a command argument (to fix Windows slashes, normalize paths, etc.) diff --git a/SCons/Platform/cygwin.py b/SCons/Platform/cygwin.py index c62a668b37..2353763d7c 100644 --- a/SCons/Platform/cygwin.py +++ b/SCons/Platform/cygwin.py @@ -47,8 +47,9 @@ def generate(env) -> None: env['PROGSUFFIX'] = '.exe' env['SHLIBPREFIX'] = '' env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX', '$IMPLIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX', '$IMPLIBSUFFIX' ] + env['LIBPREFIXES'] = ['$LIBPREFIX', '$SHLIBPREFIX', '$IMPLIBPREFIX'] + env['LIBSUFFIXES'] = ['$LIBSUFFIX', '$SHLIBSUFFIX', '$IMPLIBSUFFIX'] + env['LIBLITERAPPREFIX'] = ':' env['TEMPFILE'] = TempFileMunge env['TEMPFILEPREFIX'] = '@' env['MAXLINELENGTH'] = 2048 diff --git a/SCons/Platform/os2.py b/SCons/Platform/os2.py index 7394aa8995..72bb034024 100644 --- a/SCons/Platform/os2.py +++ b/SCons/Platform/os2.py @@ -43,8 +43,9 @@ def generate(env) -> None: env['LIBSUFFIX'] = '.lib' env['SHLIBPREFIX'] = '' env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = '$LIBPREFIX' - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['LIBPREFIXES'] = ['$LIBPREFIX'] + env['LIBSUFFIXES'] = ['$LIBSUFFIX', '$SHLIBSUFFIX'] + env['LIBLITERAPPREFIX'] = '' env['HOST_OS'] = 'os2' env['HOST_ARCH'] = win32.get_architecture().arch diff --git a/SCons/Platform/posix.py b/SCons/Platform/posix.py index 55b00b4db1..b655b77d51 100644 --- a/SCons/Platform/posix.py +++ b/SCons/Platform/posix.py @@ -93,8 +93,9 @@ def generate(env) -> None: env['LIBSUFFIX'] = '.a' env['SHLIBPREFIX'] = '$LIBPREFIX' env['SHLIBSUFFIX'] = '.so' - env['LIBPREFIXES'] = [ '$LIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['LIBPREFIXES'] = ['$LIBPREFIX'] + env['LIBSUFFIXES'] = ['$LIBSUFFIX', '$SHLIBSUFFIX'] + env['LIBLITERALPREFIX'] = '' env['HOST_OS'] = 'posix' env['HOST_ARCH'] = platform.machine() env['PSPAWN'] = pspawn diff --git a/SCons/Platform/win32.py b/SCons/Platform/win32.py index 160cb988e7..b145823616 100644 --- a/SCons/Platform/win32.py +++ b/SCons/Platform/win32.py @@ -81,9 +81,8 @@ def win_api_copyfile(src,dst) -> None: # This locked version of spawnve works around a Windows # MSVCRT bug, because its spawnve is not thread-safe. # Without this, python can randomly crash while using -jN. - # See the python bug at http://bugs.python.org/issue6476 - # and SCons issue at - # https://github.com/SCons/scons/issues/2449 + # See the python bug at https://github.com/python/cpython/issues/50725 + # and SCons issue at https://github.com/SCons/scons/issues/2449 def spawnve(mode, file, args, env): spawn_lock.acquire() try: @@ -421,8 +420,9 @@ def generate(env): env['LIBSUFFIX'] = '.lib' env['SHLIBPREFIX'] = '' env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = [ '$LIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] + env['LIBPREFIXES'] = ['$LIBPREFIX'] + env['LIBSUFFIXES'] = ['$LIBSUFFIX'] + env['LIBLITERALPREFIX'] = '' env['PSPAWN'] = piped_spawn env['SPAWN'] = spawn env['SHELL'] = cmd_interp diff --git a/SCons/SConf.py b/SCons/SConf.py index e522c8bb7a..53666e63ee 100644 --- a/SCons/SConf.py +++ b/SCons/SConf.py @@ -1002,8 +1002,8 @@ def SConf(*args, **kw): return SCons.Util.Null() -def CheckFunc(context, function_name, header = None, language = None) -> bool: - res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language) +def CheckFunc(context, function_name, header = None, language = None, funcargs = None) -> bool: + res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language, funcargs = funcargs) context.did_show_result = 1 return not res diff --git a/SCons/SConfTests.py b/SCons/SConfTests.py index 2903ba675e..08ef25e79d 100644 --- a/SCons/SConfTests.py +++ b/SCons/SConfTests.py @@ -671,11 +671,26 @@ def test_CheckFunc(self) -> None: log_file=self.test.workpath('config.log')) try: - # CheckFunc() + # look for function using default heading r = sconf.CheckFunc('strcpy') assert r, "did not find strcpy" + # no default heading, supply dummy signature r = sconf.CheckFunc('strcpy', '/* header */ char strcpy();') assert r, "did not find strcpy" + # ... supply complete signature, and function args + r = sconf.CheckFunc('strcpy', header='/* header */ char *strcpy(char *dest, char *src);', funcargs='"", ""') + # ... supply standard header for prototype, and function args + assert r, "did not find strcpy" + r = sconf.CheckFunc('strcpy', header='#include ', funcargs='"", ""') + # also try in C++ mode + cpp_header = """\ +#ifdef __cplusplus +extern "C" +#endif +char *strcpy(char *dest, char *src); +""" + r = sconf.CheckFunc('strcpy', header=cpp_header, funcargs='"", ""', language="C++") + assert r, "did not find strcpy" r = sconf.CheckFunc('hopefullynofunction') assert not r, "unexpectedly found hopefullynofunction" diff --git a/SCons/Script/Interactive.py b/SCons/Script/Interactive.py index aa390cb53a..ec58c0f79d 100644 --- a/SCons/Script/Interactive.py +++ b/SCons/Script/Interactive.py @@ -346,7 +346,7 @@ def do_shell(self, argv) -> None: argv = os.environ[self.shell_variable] try: # Per "[Python-Dev] subprocess insufficiently platform-independent?" - # http://mail.python.org/pipermail/python-dev/2008-August/081979.html "+ + # https://mail.python.org/pipermail/python-dev/2008-August/081979.html "+ # Doing the right thing with an argument list currently # requires different shell= values on Windows and Linux. p = subprocess.Popen(argv, shell=(sys.platform=='win32')) diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 9735df2613..c29fb381c9 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -725,6 +725,8 @@ def _set_debug_values(options) -> None: SCons.Node.print_duplicate = True if "json" in debug_values: ENABLE_JSON = True + if "sconscript" in debug_values: + SCons.Debug.sconscript_trace = True def _create_path(plist): path = '.' diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index e799716c57..b74353eb9c 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -752,7 +752,7 @@ def opt_invalid_rm(group, value, msg): debug_options = ["count", "duplicate", "explain", "findlibs", "includes", "memoizer", "memory", "objects", "pdb", "prepare", "presub", "stacktrace", - "time", "action-timestamps", "json"] + "time", "action-timestamps", "json", "sconscript"] def opt_debug(option, opt, value__, parser, debug_options=debug_options, diff --git a/SCons/Script/SConscript.py b/SCons/Script/SConscript.py index 539fa75dc1..85070ab053 100644 --- a/SCons/Script/SConscript.py +++ b/SCons/Script/SConscript.py @@ -274,9 +274,16 @@ def _SConscript(fs, *files, **kw): scriptdata = _file_.read() scriptname = _file_.name _file_.close() + if SCons.Debug.sconscript_trace: + print("scons: Entering "+str(scriptname)) exec(compile(scriptdata, scriptname, 'exec'), call_stack[-1].globals) + if SCons.Debug.sconscript_trace: + print("scons: Exiting "+str(scriptname)) except SConscriptReturn: - pass + if SCons.Debug.sconscript_trace: + print("scons: Exiting "+str(scriptname)) + else: + pass finally: if Main.print_time: elapsed = time.perf_counter() - start_time diff --git a/SCons/Subst.py b/SCons/Subst.py index 4046ca6b53..b04ebe50cd 100644 --- a/SCons/Subst.py +++ b/SCons/Subst.py @@ -26,6 +26,7 @@ import collections import re from inspect import signature, Parameter +from typing import Optional import SCons.Errors from SCons.Util import is_String, is_Sequence @@ -448,11 +449,12 @@ def substitute(self, args, lvars): This serves as a wrapper for splitting up a string into separate tokens. """ + def sub_match(match): + return self.conv(self.expand(match.group(1), lvars)) + if is_String(args) and not isinstance(args, CmdStringHolder): args = str(args) # In case it's a UserString. try: - def sub_match(match): - return self.conv(self.expand(match.group(1), lvars)) result = _dollar_exps.sub(sub_match, args) except TypeError: # If the internal conversion routine doesn't return @@ -805,7 +807,7 @@ def _remove_list(list): _space_sep = re.compile(r'[\t ]+(?![^{]*})') -def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None, overrides: bool=False): +def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None, overrides: Optional[dict] = None): """Expand a string or list containing construction variable substitutions. @@ -887,7 +889,7 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={ return result -def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None,overrides: bool=False): +def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None, overrides: Optional[dict] = None): """Substitute construction variables in a string (or list or other object) and separate the arguments into a command list. diff --git a/SCons/Tool/JavaCommon.py b/SCons/Tool/JavaCommon.py index 0722f008cf..31695c21b2 100644 --- a/SCons/Tool/JavaCommon.py +++ b/SCons/Tool/JavaCommon.py @@ -124,6 +124,7 @@ def __init__(self, version=default_java_version) -> None: '18.0', '19.0', '20.0', + '21.0', ): msg = "Java version %s not supported" % version raise NotImplementedError(msg) @@ -251,6 +252,7 @@ def addAnonClass(self) -> None: '18.0', '19.0', '20.0', + '21.0', ): self.stackAnonClassBrackets.append(self.brackets) className = [] diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index 29d6f246f2..7c0f1fe6ff 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -29,8 +29,6 @@ namedtuple, ) -from . import Util - from .Exceptions import ( MSVCInternalError, ) @@ -319,6 +317,7 @@ def verify(): + from . import Util from .. import vc for msvc_version in vc._VCVER: if msvc_version not in MSVC_VERSION_SUFFIX: diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py index 970b4d4412..b5b72c42fc 100644 --- a/SCons/Tool/MSCommon/MSVC/Registry.py +++ b/SCons/Tool/MSCommon/MSVC/Registry.py @@ -62,7 +62,7 @@ def registry_query_path(key, val, suffix, expand: bool=True): extval = val + '\\' + suffix if suffix else val qpath = read_value(key, extval, expand=expand) if qpath and os.path.exists(qpath): - qpath = Util.process_path(qpath) + qpath = Util.normalize_path(qpath) else: qpath = None return (qpath, key, val, extval) @@ -81,7 +81,7 @@ def microsoft_query_paths(suffix, usrval=None, expand: bool=True): extval = val + '\\' + suffix if suffix else val qpath = read_value(key, extval, expand=expand) if qpath and os.path.exists(qpath): - qpath = Util.process_path(qpath) + qpath = Util.normalize_path(qpath) if qpath not in paths: paths.append(qpath) records.append((qpath, key, val, extval, usrval)) diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index d41ff7d9f6..44b112546b 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -26,16 +26,25 @@ """ import os +import pathlib import re from collections import ( namedtuple, ) +from ..common import debug + from . import Config # path utilities +# windows drive specification (e.g., 'C:') +_RE_DRIVESPEC = re.compile(r'^[A-Za-z][:]$', re.IGNORECASE) + +# windows path separators +_OS_PATH_SEPS = (os.path.sep, os.path.altsep) if os.path.altsep else (os.path.sep,) + def listdir_dirs(p): """ Return a list of tuples for each subdirectory of the given directory path. @@ -57,22 +66,92 @@ def listdir_dirs(p): dirs.append((dir_name, dir_path)) return dirs -def process_path(p): +def resolve_path(p, ignore_drivespec=True): """ - Normalize a system path + Make path absolute resolving any symlinks Args: p: str system path + ignore_drivespec: bool + ignore drive specifications when True Returns: - str: normalized system path + str: absolute path with symlinks resolved """ + if p: + + if ignore_drivespec and _RE_DRIVESPEC.match(p): + # don't attempt to resolve drive specification (e.g., C:) + pass + else: + # both abspath and resolve necessary for an unqualified file name + # on a mapped network drive in order to return a mapped drive letter + # path rather than a UNC path. + p = os.path.abspath(p) + try: + p = str(pathlib.Path(p).resolve()) + except OSError as e: + debug( + 'caught exception: path=%s, exception=%s(%s)', + repr(p), type(e).__name__, repr(str(e)) + ) + + return p + +def normalize_path( + p, + strip=True, + preserve_trailing=False, + expand=False, + realpath=True, + ignore_drivespec=True, +): + """ + Normalize path + + Args: + p: str + system path + strip: bool + remove leading and trailing whitespace when True + preserve_trailing: bool + preserve trailing path separator when True + expand: bool + apply expanduser and expandvars when True + realpath: bool + make the path absolute resolving any symlinks when True + ignore_drivespec: bool + ignore drive specifications for realpath when True + + Returns: + str: normalized path + + """ + + if p and strip: + p = p.strip() + + if p: + + trailing = bool(preserve_trailing and p.endswith(_OS_PATH_SEPS)) + + if expand: + p = os.path.expanduser(p) + p = os.path.expandvars(p) + p = os.path.normpath(p) - p = os.path.realpath(p) + + if realpath: + p = resolve_path(p, ignore_drivespec=ignore_drivespec) + p = os.path.normcase(p) + + if trailing: + p += os.path.sep + return p # msvc version and msvc toolset version regexes diff --git a/SCons/Tool/MSCommon/MSVC/UtilTests.py b/SCons/Tool/MSCommon/MSVC/UtilTests.py index 36e08f5eb1..86ea58d875 100644 --- a/SCons/Tool/MSCommon/MSVC/UtilTests.py +++ b/SCons/Tool/MSCommon/MSVC/UtilTests.py @@ -28,14 +28,46 @@ import unittest import os import re +import sys +import pathlib from SCons.Tool.MSCommon.MSVC import Config from SCons.Tool.MSCommon.MSVC import Util from SCons.Tool.MSCommon.MSVC import WinSDK +def resolve(p): + p = os.path.abspath(p) + p = str(pathlib.Path(p).resolve()) + return p + +def normalize(*comps): + p = os.path.join(*comps) + p = os.path.normpath(p) + p = os.path.normcase(p) + return p + class Data: - UTIL_PARENT_DIR = os.path.join(os.path.dirname(Util.__file__), os.pardir) + IS_WINDOWS = sys.platform == 'win32' + + CWD = os.getcwd() + + UTIL_MODULE = os.path.dirname(Util.__file__) + UTIL_MODULE_PARENT = os.path.join(UTIL_MODULE, os.pardir) + + HOME = pathlib.Path.home() + HOMEDRIVE = HOME.drive + HOMEPATH = str(HOME) + + REALPATH_CWD = resolve(CWD) + + REALPATH_UTIL_MODULE = resolve(UTIL_MODULE) + REALPATH_UTIL_MODULE_PARENT = resolve(UTIL_MODULE_PARENT) + + REALPATH_HOMEPATH = resolve(HOMEPATH) + REALPATH_HOMEPATH_PARENT = resolve(os.path.join(HOMEPATH, os.pardir)) + REALPATH_HOMEDRIVE = resolve(HOMEDRIVE) + REALPATH_HOMEDRIVE_CWD = resolve(HOMEDRIVE) class UtilTests(unittest.TestCase): @@ -43,21 +75,72 @@ def test_listdir_dirs(self) -> None: func = Util.listdir_dirs for dirname, expect in [ (None, False), ('', False), ('doesnotexist.xyz.abc', False), - (Data.UTIL_PARENT_DIR, True), + (Data.UTIL_MODULE_PARENT, True), ]: dirs = func(dirname) self.assertTrue((len(dirs) > 0) == expect, "{}({}): {}".format( func.__name__, repr(dirname), 'list is empty' if expect else 'list is not empty' )) - def test_process_path(self) -> None: - func = Util.process_path - for p, expect in [ - (None, True), ('', True), - ('doesnotexist.xyz.abc', False), (Data.UTIL_PARENT_DIR, False), - ]: - rval = func(p) - self.assertTrue((p == rval) == expect, "{}({}): {}".format( + def test_resolve_path(self) -> None: + func = Util.resolve_path + # default kwargs: + # ignore_drivespec=True + test_list = [ + (Data.UTIL_MODULE, Data.REALPATH_UTIL_MODULE, {}), + (os.path.join(Data.UTIL_MODULE, os.pardir), Data.REALPATH_UTIL_MODULE_PARENT, {}), + (Data.HOMEPATH, Data.REALPATH_HOMEPATH, {}), + (os.path.join(Data.HOMEPATH, os.pardir), Data.REALPATH_HOMEPATH_PARENT, {}), + ] + if Data.IS_WINDOWS: + test_list.extend([ + (Data.HOMEDRIVE, Data.HOMEDRIVE, {}), + (Data.HOMEDRIVE, Data.HOMEDRIVE, {'ignore_drivespec': True}), + (Data.HOMEDRIVE, Data.REALPATH_HOMEDRIVE_CWD, {'ignore_drivespec': False}), + ]) + for p, expect, kwargs in test_list: + rval = func(p, **kwargs) + # print(repr(p), repr(expect), repr(rval)) + self.assertTrue(rval == expect, "{}({}): {}".format( + func.__name__, repr(p), repr(rval) + )) + + def test_normalize_path(self) -> None: + func = Util.normalize_path + # default kwargs: + # strip=True + # preserve_trailing=False + # expand=False + # realpath=True + # ignore_drivespec=True + test_list = [ + (Data.UTIL_MODULE, normalize(Data.REALPATH_UTIL_MODULE), {}), + (os.path.join(Data.UTIL_MODULE, os.pardir), normalize(Data.REALPATH_UTIL_MODULE_PARENT), {}), + (None, None, {}), + ('', '', {'realpath': False}), + ('', '', {'realpath': True}), + ('DoesNotExist.xyz.abc', normalize('DoesNotExist.xyz.abc'), {'realpath': False}), + ('DoesNotExist.xyz.abc', normalize(Data.REALPATH_CWD, 'DoesNotExist.xyz.abc'), {'realpath': True}), + (' DoesNotExist.xyz.abc ', normalize(Data.REALPATH_CWD, 'DoesNotExist.xyz.abc'), {'realpath': True}), + (' ~ ', '~', {'realpath': False, 'expand': False}), + (' ~ ', normalize(Data.REALPATH_HOMEPATH), {'realpath': True, 'expand': True}), + ] + if Data.IS_WINDOWS: + test_list.extend([ + ('DoesNotExist.xyz.abc/', normalize('DoesNotExist.xyz.abc') + os.path.sep, {'realpath': False, 'preserve_trailing': True}), + (' DoesNotExist.xyz.abc\\ ', normalize('DoesNotExist.xyz.abc') + os.path.sep, {'realpath': False, 'preserve_trailing': True}), + (' ~/ ', normalize(Data.REALPATH_HOMEPATH) + os.path.sep, {'realpath': True, 'expand': True, 'preserve_trailing': True}), + (' ~\\ ', normalize(Data.REALPATH_HOMEPATH) + os.path.sep, {'realpath': True, 'expand': True, 'preserve_trailing': True}), + (' ~/ ', normalize(Data.REALPATH_CWD, '~') + os.path.sep, {'realpath': True, 'expand': False, 'preserve_trailing': True}), + (' ~\\ ', normalize(Data.REALPATH_CWD, '~') + os.path.sep, {'realpath': True, 'expand': False, 'preserve_trailing': True}), + (Data.HOMEDRIVE, normalize(Data.HOMEDRIVE), {}), + (Data.HOMEDRIVE, normalize(Data.HOMEDRIVE), {'ignore_drivespec': True}), + (Data.HOMEDRIVE, normalize(Data.REALPATH_HOMEDRIVE_CWD), {'ignore_drivespec': False}), + ]) + for p, expect, kwargs in test_list: + rval = func(p, **kwargs) + # print(repr(p), repr(expect), repr(rval)) + self.assertTrue(rval == expect, "{}({}): {}".format( func.__name__, repr(p), repr(rval) )) diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py index 39617b16cc..7115d505ee 100644 --- a/SCons/Tool/MSCommon/MSVC/WinSDK.py +++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py @@ -83,7 +83,7 @@ def _sdk_10_layout(version): if not version_nbr.startswith(folder_prefix): continue - sdk_inc_path = Util.process_path(os.path.join(version_nbr_path, 'um')) + sdk_inc_path = Util.normalize_path(os.path.join(version_nbr_path, 'um')) if not os.path.exists(sdk_inc_path): continue @@ -127,7 +127,7 @@ def _sdk_81_layout(version): # msvc does not check for existence of root or other files - sdk_inc_path = Util.process_path(os.path.join(sdk_root, r'include\um')) + sdk_inc_path = Util.normalize_path(os.path.join(sdk_root, r'include\um')) if not os.path.exists(sdk_inc_path): continue diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index f8816c4f0c..2b8c67b213 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -75,32 +75,86 @@ def filter(self, record) -> bool: record.relfilename = relfilename return True - # Log format looks like: - # 00109ms:MSCommon/vc.py:find_vc_pdir#447: VC found '14.3' [file] - # debug: 00109ms:MSCommon/vc.py:find_vc_pdir#447: VC found '14.3' [stdout] - log_format=( - '%(relativeCreated)05dms' - ':%(relfilename)s' - ':%(funcName)s' - '#%(lineno)s' - ': %(message)s' - ) + class _CustomFormatter(logging.Formatter): + + # Log format looks like: + # 00109ms:MSCommon/vc.py:find_vc_pdir#447: VC found '14.3' [file] + # debug: 00109ms:MSCommon/vc.py:find_vc_pdir#447: VC found '14.3' [stdout] + + log_format=( + '%(relativeCreated)05dms' + ':%(relfilename)s' + ':%(funcName)s' + '#%(lineno)s' + ': %(message)s' + ) + + log_format_classname=( + '%(relativeCreated)05dms' + ':%(relfilename)s' + ':%(classname)s' + '.%(funcName)s' + '#%(lineno)s' + ': %(message)s' + ) + + def __init__(self, log_prefix): + super().__init__() + if log_prefix: + self.log_format = log_prefix + self.log_format + self.log_format_classname = log_prefix + self.log_format_classname + log_record = logging.LogRecord( + '', # name (str) + 0, # level (int) + '', # pathname (str) + 0, # lineno (int) + None, # msg (Any) + {}, # args (tuple | dict[str, Any]) + None # exc_info (tuple[type[BaseException], BaseException, types.TracebackType] | None) + ) + self.default_attrs = set(log_record.__dict__.keys()) + self.default_attrs.add('relfilename') + + def format(self, record): + extras = set(record.__dict__.keys()) - self.default_attrs + if 'classname' in extras: + log_format = self.log_format_classname + else: + log_format = self.log_format + formatter = logging.Formatter(log_format) + return formatter.format(record) + if LOGFILE == '-': - log_format = 'debug: ' + log_format + log_prefix = 'debug: ' log_handler = logging.StreamHandler(sys.stdout) else: + log_prefix = '' log_handler = logging.FileHandler(filename=LOGFILE) - log_formatter = logging.Formatter(log_format) + log_formatter = _CustomFormatter(log_prefix) log_handler.setFormatter(log_formatter) logger = logging.getLogger(name=__name__) logger.setLevel(level=logging.DEBUG) logger.addHandler(log_handler) logger.addFilter(_Debug_Filter()) debug = logger.debug + + def debug_extra(cls=None): + if cls: + extra = {'classname': cls.__qualname__} + else: + extra = None + return extra + + DEBUG_ENABLED = True + else: - def debug(x, *args): + def debug(x, *args, **kwargs): + return None + + def debug_extra(*args, **kwargs): return None + DEBUG_ENABLED = False # SCONS_CACHE_MSVC_CONFIG is public, and is documented. CONFIG_CACHE = os.environ.get('SCONS_CACHE_MSVC_CONFIG', '') diff --git a/SCons/Tool/MSCommon/sdk.py b/SCons/Tool/MSCommon/sdk.py index 8499dc8a4b..c6600f3a5b 100644 --- a/SCons/Tool/MSCommon/sdk.py +++ b/SCons/Tool/MSCommon/sdk.py @@ -45,10 +45,9 @@ # seem to be any sane registry key, so the precise location is hardcoded. # # For versions below 2003R1, it seems the PSDK is included with Visual Studio? -# -# Also, per the following: -# http://benjamin.smedbergs.us/blog/tag/atl/ # VC++ Professional comes with the SDK, VC++ Express does not. +# +# Of course, all this changed again after Express was phased out (2005). # Location of the SDK (checked for 6.1 only) _CURINSTALLED_SDK_HKEY_ROOT = \ diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index a92ceb7cd8..5b94747e56 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -2190,7 +2190,7 @@ def msvc_setup_env(env): seen_path = False for k, v in d.items(): - if not seen_path and k.upper() == 'PATH': + if not seen_path and k == 'PATH': seen_path = True found_cl_path = SCons.Util.WhereIs('cl', v) found_cl_envpath = SCons.Util.WhereIs('cl', env['ENV'].get(k, [])) @@ -2201,16 +2201,15 @@ def msvc_setup_env(env): # final check to issue a warning if the requested compiler is not present if not found_cl_path: + warn_msg = "Could not find requested MSVC compiler 'cl'." if CONFIG_CACHE: - propose = f"SCONS_CACHE_MSVC_CONFIG caching enabled, remove cache file {CONFIG_CACHE} if out of date." + warn_msg += f" SCONS_CACHE_MSVC_CONFIG caching enabled, remove cache file {CONFIG_CACHE} if out of date." else: - propose = "It may need to be installed separately with Visual Studio." - warn_msg = "Could not find requested MSVC compiler 'cl'." + warn_msg += " It may need to be installed separately with Visual Studio." if found_cl_envpath: warn_msg += " A 'cl' was found on the scons ENV path which may be erroneous." - warn_msg += " %s" - debug(warn_msg, propose) - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg % propose) + debug(warn_msg) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) def msvc_exists(env=None, version=None): vcs = get_installed_vcs(env) diff --git a/SCons/Tool/compilation_db.py b/SCons/Tool/compilation_db.py index 14c6ef59c7..e17b5dc661 100644 --- a/SCons/Tool/compilation_db.py +++ b/SCons/Tool/compilation_db.py @@ -1,12 +1,5 @@ -""" -Implements the ability for SCons to emit a compilation database for the MongoDB project. See -http://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation -database is, and why you might want one. The only user visible entry point here is -'env.CompilationDatabase'. This method takes an optional 'target' to name the file that -should hold the compilation database, otherwise, the file defaults to compile_commands.json, -which is the name that most clang tools search for by default. -""" - +# MIT License +# # Copyright 2020 MongoDB Inc. # # Permission is hereby granted, free of charge, to any person obtaining @@ -27,7 +20,17 @@ # 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. -# + +"""Compilation Database + +Implements the ability for SCons to emit a compilation database for a +project. See https://clang.llvm.org/docs/JSONCompilationDatabase.html +for details on what a compilation database is, and why you might want one. +The only user visible entry point here is ``env.CompilationDatabase``. +This method takes an optional *target* to name the file that should hold +the compilation database, otherwise, the file defaults to +``compile_commands.json``, the name that most clang tools search for by default. +""" import json import itertools diff --git a/SCons/Tool/cyglink.py b/SCons/Tool/cyglink.py index 80caf0b87a..6a5ca99cf3 100644 --- a/SCons/Tool/cyglink.py +++ b/SCons/Tool/cyglink.py @@ -1,6 +1,29 @@ +# 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. + """SCons.Tool.cyglink -Customization of gnulink for Cygwin (http://www.cygwin.com/) +Customization of gnulink for Cygwin (https://www.cygwin.com/) There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() diff --git a/SCons/Tool/dmd.py b/SCons/Tool/dmd.py index 13e9e7e446..2ac84d0cf2 100644 --- a/SCons/Tool/dmd.py +++ b/SCons/Tool/dmd.py @@ -24,7 +24,7 @@ """SCons.Tool.dmd Tool-specific initialization for the Digital Mars D compiler. -(http://digitalmars.com/d) +(https://digitalmars.com/d) Originally coded by Andy Friesen (andy@ikagames.com) 15 November 2003 @@ -138,7 +138,7 @@ def generate(env) -> None: env['DLIBLINKPREFIX'] = '' if env['PLATFORM'] == 'win32' else '-L-l' env['DLIBLINKSUFFIX'] = '.lib' if env['PLATFORM'] == 'win32' else '' - env['_DLIBFLAGS'] = '${_stripixes(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' + env['_DLIBFLAGS'] = '${_stripixes(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__, LIBLITERALPREFIX)}' env['DLIBDIRPREFIX'] = '-L-L' env['DLIBDIRSUFFIX'] = '' diff --git a/SCons/Tool/docbook/__init__.py b/SCons/Tool/docbook/__init__.py index b682cdfa37..3adb3148bb 100644 --- a/SCons/Tool/docbook/__init__.py +++ b/SCons/Tool/docbook/__init__.py @@ -155,8 +155,8 @@ def __create_output_dir(base_dir) -> None: xsltproc_com_priority = ['xsltproc', 'saxon', 'saxon-xslt', 'xalan'] # TODO: Set minimum version of saxon-xslt to be 8.x (lower than this only supports xslt 1.0. -# see: http://saxon.sourceforge.net/saxon6.5.5/ -# see: http://saxon.sourceforge.net/ +# see: https://saxon.sourceforge.net/saxon6.5.5/ +# see: https://saxon.sourceforge.net/ xsltproc_com = {'xsltproc' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE', 'saxon' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE $DOCBOOK_XSLTPROCPARAMS', # Note if saxon-xslt is version 5.5 the proper arguments are: (swap order of docbook_xsl and source) diff --git a/SCons/Tool/gnulink.py b/SCons/Tool/gnulink.py index 159aa972cb..c636c11d23 100644 --- a/SCons/Tool/gnulink.py +++ b/SCons/Tool/gnulink.py @@ -52,6 +52,9 @@ def generate(env) -> None: env['RPATHSUFFIX'] = '' env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + env['LIBLITERALPREFIX'] = ':' + + def exists(env): # TODO: sync with link.smart_link() to choose a linker diff --git a/SCons/Tool/ldc.py b/SCons/Tool/ldc.py index bd0767e7f5..6ee022aa34 100644 --- a/SCons/Tool/ldc.py +++ b/SCons/Tool/ldc.py @@ -73,7 +73,7 @@ def generate(env) -> None: 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['_DFLAGS'] = '${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)}' env['SHDC'] = '$DC' @@ -96,10 +96,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'] = '' @@ -115,7 +115,7 @@ def generate(env) -> None: env['DLIBLINKPREFIX'] = '' if env['PLATFORM'] == 'win32' else '-L-l' env['DLIBLINKSUFFIX'] = '.lib' if env['PLATFORM'] == 'win32' else '' # env['_DLIBFLAGS'] = '${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs, TARGET, SOURCE)}' - env['_DLIBFLAGS'] = '${_stripixes(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' + env['_DLIBFLAGS'] = '${_stripixes(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__, LIBLITERALPREFIX)}' env['DLIBDIRPREFIX'] = '-L-L' env['DLIBDIRSUFFIX'] = '' diff --git a/SCons/Tool/link.py b/SCons/Tool/link.py index e879ae8623..cd8b2f88b5 100644 --- a/SCons/Tool/link.py +++ b/SCons/Tool/link.py @@ -55,7 +55,7 @@ def generate(env) -> None: env['LINKCOM'] = '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['LIBDIRPREFIX'] = '-L' env['LIBDIRSUFFIX'] = '' - env['_LIBFLAGS'] = '${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' + env['_LIBFLAGS'] = '${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__, LIBLITERALPREFIX)}' env['LIBLINKPREFIX'] = '-l' env['LIBLINKSUFFIX'] = '' diff --git a/SCons/Tool/mingw.py b/SCons/Tool/mingw.py index 0119444a71..824f20daaa 100644 --- a/SCons/Tool/mingw.py +++ b/SCons/Tool/mingw.py @@ -23,7 +23,7 @@ """SCons.Tool.gcc -Tool-specific initialization for MinGW (http://www.mingw.org/) +Tool-specific initialization for MinGW (https://www.mingw.org/) There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 8beebf9a2d..0772e5d3bc 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -94,10 +94,9 @@ Sets &consvars; for the Microsoft Visual C/C++ compiler. Builds a Microsoft Visual C++ precompiled header. Calling this builder -returns a list of two targets: the PCH as the first element, and the object -file as the second element. Normally the object file is ignored. -This builder is only -provided when Microsoft Visual C++ is being used as the compiler. +returns a list of two target nodes: the PCH as the first element, +and the object file as the second element. +Normally the object file is ignored. The &b-PCH; builder is generally used in conjunction with the &cv-link-PCH; &consvar; to force object files to use the precompiled header: @@ -106,6 +105,42 @@ the precompiled header: env['PCH'] = env.PCH('StdAfx.cpp')[0] + + + +This builder is specific to the PCH implementation +in Microsoft Visual C++. +Other compiler chains also implement precompiled header support, +but &b-PCH; does not work with them at this time. +As a result, the builder is only generated into the +construction environment when +Microsoft Visual C++ is being used as the compiler. + + +The builder only works correctly in a C++ project. +The Microsoft implementation distinguishes between +precompiled headers from C and C++. +Use of the builder will cause the PCH generation to happen with a flag +that tells cl.exe all of the +files are C++ files; if that PCH file is then supplied when +compiling a C source file, +cl.exe will fail the build with +a compatibility violation. + + +If possible, arrange the project so that a +C++ source file passed to the &b-PCH; builder +is not also included in the list of sources +to be otherwise compiled in the project. +&SCons; will correctly track that file in the dependency tree +as a result of the &b-PCH; call, +and (for MSVC 11.0 and greater) automatically add the +corresponding object file to the link line. +If the source list is automatically generated, +for example using the &f-link-Glob; function, +it may be necessary to remove that file from the list. + + @@ -154,7 +189,7 @@ when the &cv-link-PDB; &consvar; is set. -The Visual C++ compiler option that SCons uses by default +The Visual C++ compiler option that &SCons; uses by default to generate PDB information is . This works correctly with parallel () builds because it embeds the debug information in the intermediate object files, @@ -192,12 +227,12 @@ env['CCPDBFLAGS'] = '/Zi /Fd${TARGET}.pdb' When set to any true value, -specifies that SCons should batch +specifies that &SCons; should batch compilation of object files when calling the Microsoft Visual C/C++ compiler. All compilations of source files from the same source directory that generate target files in a same output directory -and were configured in SCons using the same &consenv; +and were configured in &SCons; using the same &consenv; will be built in a single call to the compiler. Only source files that have changed since their object files were built will be passed to each compiler invocation @@ -213,17 +248,19 @@ will be compiled separately. -The Microsoft Visual C++ precompiled header that will be used when compiling -object files. This variable is ignored by tools other than Microsoft Visual C++. +A node for the Microsoft Visual C++ precompiled header that will be +used when compiling object files. +This variable is ignored by tools other than Microsoft Visual C++. When this variable is -defined SCons will add options to the compiler command line to +defined, &SCons; will add options to the compiler command line to cause it to use the precompiled header, and will also set up the dependencies for the PCH file. -Example: +Examples: env['PCH'] = File('StdAfx.pch') +env['PCH'] = env.PCH('pch.cc')[0] @@ -242,7 +279,7 @@ builder to generated a precompiled header. The string displayed when generating a precompiled header. -If this is not set, then &cv-link-PCHCOM; (the command line) is displayed. +If not set, then &cv-link-PCHCOM; (the command line) is displayed. @@ -356,7 +393,7 @@ when the &cv-link-RCINCFLAGS; variable is expanded. Sets the preferred version of Microsoft Visual C/C++ to use. If the specified version is unavailable (not installed, or not discoverable), tool initialization will fail. -If &cv-MSVC_VERSION; is not set, SCons will (by default) select the +If &cv-MSVC_VERSION; is not set, &SCons; will (by default) select the latest version of Visual C/C++ installed on your system. @@ -809,10 +846,10 @@ Specify the location of vswhere.exe. It provides full information about installations of 2017 and later editions. With the argument, vswhere.exe can detect installations of the 2010 through 2015 editions with limited data returned. -If VSWHERE is set, SCons will use that location. +If VSWHERE is set, &SCons; will use that location. - Otherwise SCons will look in the following locations and set VSWHERE to the path of the first vswhere.exe + Otherwise &SCons; will look in the following locations and set VSWHERE to the path of the first vswhere.exe located. diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index 16e422dace..12974489a4 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -145,6 +145,8 @@ def msvs_parse_version(s): # the MSVS Project file invoke SCons the same way that scons.bat does, # which works regardless of how we were invoked. def getExecScriptMain(env, xml=None): + if 'SCONS_HOME' not in env: + env['SCONS_HOME'] = os.environ.get('SCONS_HOME') scons_home = env.get('SCONS_HOME') if not scons_home and 'SCONS_LIB_DIR' in os.environ: scons_home = os.environ['SCONS_LIB_DIR'] @@ -2109,7 +2111,9 @@ def generate(env) -> None: env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}' env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}' - env['SCONS_HOME'] = os.environ.get('SCONS_HOME') + + if 'SCONS_HOME' not in env: + env['SCONS_HOME'] = os.environ.get('SCONS_HOME') def exists(env): return msvc_setup_env_tool(env, tool=tool_name) diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index bfa48f5ebe..a0640e6340 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -70,22 +70,22 @@ def __init__(self, initlist=None, allowedElems=None) -> None: self.allowedElems = sorted(allowedElems) def __cmp__(self, other): - raise NotImplementedError + return NotImplemented def __eq__(self, other): - raise NotImplementedError + return NotImplemented def __ge__(self, other): - raise NotImplementedError + return NotImplemented def __gt__(self, other): - raise NotImplementedError + return NotImplemented def __le__(self, other): - raise NotImplementedError + return NotImplemented def __lt__(self, other): - raise NotImplementedError + return NotImplemented def __str__(self) -> str: if not len(self): diff --git a/SCons/__init__.py b/SCons/__init__.py index f0f241de88..7a7d3ef794 100644 --- a/SCons/__init__.py +++ b/SCons/__init__.py @@ -1,9 +1,9 @@ -__version__="4.5.2" +__version__="4.6.1" __copyright__="Copyright (c) 2001 - 2023 The SCons Foundation" __developer__="bdbaddog" -__date__="Sun, 04 Jun 2023 15:36:48 -0700" +__date__="Sun, 19 Nov 2023 17:54:10 -0700" __buildsys__="M1Dog2021" -__revision__="b3744e8862927899e3d0ebcb41297f9b4c142c63" -__build__="b3744e8862927899e3d0ebcb41297f9b4c142c63" +__revision__="70bcde6f38478a85a552ee624baaa2beb7b2bb92" +__build__="70bcde6f38478a85a552ee624baaa2beb7b2bb92" # make sure compatibility is always in place -import SCons.compat # noqa \ No newline at end of file +import SCons.compat # noqa \ No newline at end of file diff --git a/SCons/cpp.py b/SCons/cpp.py index c9c9a70f06..97aba8cc34 100644 --- a/SCons/cpp.py +++ b/SCons/cpp.py @@ -561,8 +561,7 @@ def do_include(self, t) -> None: [('scons_current_file', self.current_file)] self.tuples[:] = new_tuples + self.tuples - # Date: Tue, 22 Nov 2005 20:26:09 -0500 - # From: Stefan Seefeld + # From: Stefan Seefeld (22 Nov 2005) # # By the way, #include_next is not the same as #include. The difference # being that #include_next starts its search in the path following the @@ -570,10 +569,12 @@ def do_include(self, t) -> None: # include paths are ['/foo', '/bar'], and you are looking at a header # '/foo/baz.h', it might issue an '#include_next ' which would # correctly resolve to '/bar/baz.h' (if that exists), but *not* see - # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html - # for more reasoning. + # '/foo/baz.h' again. See + # https://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html for more notes. # - # I have no idea in what context 'import' might be used. + # I have no idea in what context #import might be used. + # Update: possibly these notes? + # https://github.com/MicrosoftDocs/cpp-docs/blob/main/docs/preprocessor/hash-import-directive-cpp.md # XXX is #include_next really the same as #include ? do_include_next = do_include diff --git a/SConstruct b/SConstruct index b414532704..98e1bd9462 100644 --- a/SConstruct +++ b/SConstruct @@ -36,7 +36,7 @@ copyright_years = strftime('2001 - %Y') # This gets inserted into the man pages to reflect the month of release. month_year = strftime('%B %Y') project = 'scons' -default_version = '4.5.3' +default_version = '4.6.1' copyright = f"Copyright (c) {copyright_years} The SCons Foundation" # We let the presence or absence of various utilities determine whether diff --git a/doc/generated/builders.gen b/doc/generated/builders.gen index deccae72a6..25254b2a36 100644 --- a/doc/generated/builders.gen +++ b/doc/generated/builders.gen @@ -27,10 +27,12 @@ Example: # builds foo.c -env.CFile(target = 'foo.c', source = 'foo.l') +env.CFile(target='foo.c', source='foo.l') + # builds bar.c -env.CFile(target = 'bar', source = 'bar.y') +env.CFile(target='bar', source='bar.y') + @@ -124,10 +126,12 @@ Example: # builds foo.cc -env.CXXFile(target = 'foo.cc', source = 'foo.ll') +env.CXXFile(target='foo.cc', source='foo.ll') + # builds bar.cc -env.CXXFile(target = 'bar', source = 'bar.yy') +env.CXXFile(target='bar', source='bar.yy') + @@ -1535,10 +1539,9 @@ the top directory of the project. Builds a Microsoft Visual C++ precompiled header. Calling this builder -returns a list of two targets: the PCH as the first element, and the object -file as the second element. Normally the object file is ignored. -This builder is only -provided when Microsoft Visual C++ is being used as the compiler. +returns a list of two target nodes: the PCH as the first element, +and the object file as the second element. +Normally the object file is ignored. The &b-PCH; builder is generally used in conjunction with the &cv-link-PCH; &consvar; to force object files to use the precompiled header: @@ -1547,6 +1550,42 @@ the precompiled header: env['PCH'] = env.PCH('StdAfx.cpp')[0] + + + +This builder is specific to the PCH implementation +in Microsoft Visual C++. +Other compiler chains also implement precompiled header support, +but &b-PCH; does not work with them at this time. +As a result, the builder is only generated into the +construction environment when +Microsoft Visual C++ is being used as the compiler. + + +The builder only works correctly in a C++ project. +The Microsoft implementation distinguishes between +precompiled headers from C and C++. +Use of the builder will cause the PCH generation to happen with a flag +that tells cl.exe all of the +files are C++ files; if that PCH file is then supplied when +compiling a C source file, +cl.exe will fail the build with +a compatibility violation. + + +If possible, arrange the project so that a +C++ source file passed to the &b-PCH; builder +is not also included in the list of sources +to be otherwise compiled in the project. +&SCons; will correctly track that file in the dependency tree +as a result of the &b-PCH; call, +and (for MSVC 11.0 and greater) automatically add the +corresponding object file to the link line. +If the source list is automatically generated, +for example using the &f-link-Glob; function, +it may be necessary to remove that file from the list. + + diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml index 422f37d59d..940377d242 100644 --- a/doc/generated/examples/caching_ex-random_1.xml +++ b/doc/generated/examples/caching_ex-random_1.xml @@ -1,8 +1,8 @@ % scons -Q -cc -o f4.o -c f4.c cc -o f2.o -c f2.c -cc -o f1.o -c f1.c -cc -o f5.o -c f5.c cc -o f3.o -c f3.c +cc -o f5.o -c f5.c +cc -o f1.o -c f1.c +cc -o f4.o -c f4.c cc -o prog f1.o f2.o f3.o f4.o f5.o diff --git a/doc/generated/examples/commandline_EnumVariable_3.xml b/doc/generated/examples/commandline_EnumVariable_3.xml index 154c041d2a..6c03598287 100644 --- a/doc/generated/examples/commandline_EnumVariable_3.xml +++ b/doc/generated/examples/commandline_EnumVariable_3.xml @@ -4,5 +4,5 @@ COLOR: Set background color (red|green|blue) default: red actual: red -Use scons -H for help about command-line options. +Use scons -H for help about SCons built-in command-line options. diff --git a/doc/generated/examples/commandline_Variables_Help_1.xml b/doc/generated/examples/commandline_Variables_Help_1.xml index 1bbf2fb675..005c014a9a 100644 --- a/doc/generated/examples/commandline_Variables_Help_1.xml +++ b/doc/generated/examples/commandline_Variables_Help_1.xml @@ -4,5 +4,5 @@ RELEASE: Set to 1 to build for release default: 0 actual: 0 -Use scons -H for help about command-line options. +Use scons -H for help about SCons built-in command-line options. diff --git a/doc/generated/examples/output_ex1_1.xml b/doc/generated/examples/output_ex1_1.xml index 1558b0118b..5a3123d918 100644 --- a/doc/generated/examples/output_ex1_1.xml +++ b/doc/generated/examples/output_ex1_1.xml @@ -5,5 +5,5 @@ scons: done reading SConscript files. Type: 'scons program' to build the production program, 'scons debug' to build the debug version. -Use scons -H for help about command-line options. +Use scons -H for help about SCons built-in command-line options. diff --git a/doc/generated/examples/output_ex2_1.xml b/doc/generated/examples/output_ex2_1.xml index d832a522f2..b3b3fdb24f 100644 --- a/doc/generated/examples/output_ex2_1.xml +++ b/doc/generated/examples/output_ex2_1.xml @@ -6,5 +6,5 @@ Type: 'scons program' to build the production program. Type: 'scons windebug' to build the Windows debug version. -Use scons -H for help about command-line options. +Use scons -H for help about SCons built-in command-line options. diff --git a/doc/generated/examples/output_ex2_2.xml b/doc/generated/examples/output_ex2_2.xml index 4cbccc1345..528caddcab 100644 --- a/doc/generated/examples/output_ex2_2.xml +++ b/doc/generated/examples/output_ex2_2.xml @@ -4,5 +4,5 @@ scons: done reading SConscript files. Type: 'scons program' to build the production program. -Use scons -H for help about command-line options. +Use scons -H for help about SCons built-in command-line options. diff --git a/doc/generated/examples/troubleshoot_Dump_1.xml b/doc/generated/examples/troubleshoot_Dump_1.xml index 6a0fa822be..54d1288d2d 100644 --- a/doc/generated/examples/troubleshoot_Dump_1.xml +++ b/doc/generated/examples/troubleshoot_Dump_1.xml @@ -36,6 +36,7 @@ scons: Reading SConscript files ... 'IDLSUFFIXES': ['.idl', '.IDL'], 'INSTALL': <function copyFunc at 0x700000>, 'INSTALLVERSIONEDLIB': <function copyFuncVersionedLib at 0x700000>, + 'LIBLITERALPREFIX': '', 'LIBPREFIX': 'lib', 'LIBPREFIXES': ['$LIBPREFIX'], 'LIBSUFFIX': '.a', diff --git a/doc/generated/examples/troubleshoot_Dump_2.xml b/doc/generated/examples/troubleshoot_Dump_2.xml index c952602f4e..a29772a2b8 100644 --- a/doc/generated/examples/troubleshoot_Dump_2.xml +++ b/doc/generated/examples/troubleshoot_Dump_2.xml @@ -61,6 +61,7 @@ scons: Reading SConscript files ... 'INSTALL': <function copyFunc at 0x700000>, 'INSTALLVERSIONEDLIB': <function copyFuncVersionedLib at 0x700000>, 'LEXUNISTD': ['--nounistd'], + 'LIBLITERALPREFIX': '', 'LIBPREFIX': '', 'LIBPREFIXES': ['$LIBPREFIX'], 'LIBSUFFIX': '.lib', diff --git a/doc/generated/examples/troubleshoot_explain1_3.xml b/doc/generated/examples/troubleshoot_explain1_3.xml index a0aec9ff91..e658d89fd2 100644 --- a/doc/generated/examples/troubleshoot_explain1_3.xml +++ b/doc/generated/examples/troubleshoot_explain1_3.xml @@ -2,5 +2,5 @@ cp file.in file.oout scons: warning: Cannot find target file.out after building -File "/Users/bdbaddog/devel/scons/git/scons-2/scripts/scons.py", line 97, in <module> +File "/Users/bdbaddog/devel/scons/git/as_scons/scripts/scons.py", line 97, in <module> diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index 90d263da12..2068b55943 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -328,17 +328,32 @@ file into an object file. Alias(alias, [source, [action]]) env.Alias(alias, [source, [action]]) -Creates a phony target (or targets) that -can be used as references to zero or more other targets, -as specified by the optional source -parameter. -alias and -source +Creates an alias target that +can be used as a reference to zero or more other targets, +specified by the optional source parameter. +Aliases provide a way to give a shorter or more descriptive +name to specific targets, +and to group multiple targets under a single name. +The alias name, or an Alias Node object, +may be used as a dependency of any other target, +including another alias. + + + +alias and source may each be a string or Node object, or a list of strings or Node objects; if Nodes are used for alias they must be Alias nodes. +If source is omitted, +the alias is created but has no reference; +if selected for building this will result in a +Nothing to be done. message. +An empty alias can be used to define the alias +in a visible place in the project; +it can later be appended to in a subsidiary SConscript file +with the actual target(s) to refer to. The optional action parameter specifies an action or list of actions @@ -347,20 +362,18 @@ whenever the any of the alias targets are out-of-date. -Returns a list of Alias Node objects representing the alias(es), -which exist outside of any physical file system. +&f-Alias; can be called for an existing alias, +which appends the alias +and/or action arguments +to the existing lists for that alias. -The alias name, or an Alias Node object, -may be used as a dependency of any other target, -including another alias. -&f-Alias; -can be called multiple times for the same -alias to add additional targets to the alias, -or additional actions to the list for this alias. -Aliases are global even if set through -the construction environment method. +Returns a list of Alias Node objects representing the alias(es), +which exist outside of any physical file system. +The alias name space is separate from the name space for +tangible targets; to avoid confusion do not reuse +target names as alias names. @@ -794,15 +807,6 @@ of disables derived file caching. - -When specifying a -custom_class which should be a class type which is a subclass of -SCons.CacheDir.CacheDir, SCons will -internally invoke this class to use for performing caching operations. -This argument is optional and if left to default None, will use the -default SCons.CacheDir.CacheDir class. - - Calling the environment method &f-link-env-CacheDir; @@ -817,6 +821,18 @@ that do not set up environment-specific caching by calling &f-env-CacheDir;. + +Caching behavior can be configured by passing a specialized cache +class as the optional custom_class parameter. +This class must be a subclass of +SCons.CacheDir.CacheDir. +&SCons; will internally invoke the custom class for performing +caching operations. +If the parameter is omitted or set to +None, &SCons; will use the default +SCons.CacheDir.CacheDir class. + + When derived-file caching is being used and @@ -842,15 +858,14 @@ identified by its &buildsig;, for future use. The Retrieved `file' from cache messages are useful for human consumption, -but less so when comparing log files between +but less useful when comparing log files between &scons; runs which will show differences that are noisy and not actually significant. To disable, use the option. -With this option, &scons; -will print the action that would -have been used to build the file without -considering cache retrieval. +With this option, &scons; changes printing +to always show the action that would +have been used to build the file without caching. @@ -858,10 +873,10 @@ Derived-file caching may be disabled for any invocation of &scons; by giving the -command line option. -Cache updating may be disabled, leaving cache +command line option; +cache updating may be disabled, leaving cache fetching enabled, by giving the -. + option. @@ -871,7 +886,7 @@ option is used, &scons; will place a copy of all -derived files in the cache, +derived files into the cache, even if they already existed and were not built by this invocation. This is useful to populate a cache @@ -896,7 +911,7 @@ predict or prohibitively large. Note that (at this time) &SCons; provides no facilities for managing the derived-file cache. It is up to the developer -to arrange for cache pruning, expiry, etc. if needed. +to arrange for cache pruning, expiry, access control, etc. if needed. @@ -1447,26 +1462,39 @@ env.Default(hello) DefaultEnvironment([**kwargs]) -Instantiates and returns the default &consenv; object. -The &defenv; is used internally by SCons -in order to execute many of the global functions in this list +Instantiates and returns the global &consenv; object. +This environment is used internally by SCons +when it executes many of the global functions listed in this section (that is, those not called as methods of a specific &consenv;). +The &defenv; is a singleton: +the keyword arguments are used only on the first call; +on subsequent calls the already-constructed object is returned +and any keyword arguments are silently ignored. +The &defenv; can still be modified after instantiation +in the same way as any other &consenv;. +The &defenv; is independent: +modifying it has no effect on any other &consenv; +constructed by an &f-link-Environment; or &f-link-Clone; call. + + + It is not mandatory to call &f-DefaultEnvironment;: -the &defenv; will be instantiated automatically when the -build phase begins if the function has not been called, +the &defenv; is instantiated automatically when the +build phase begins if this function has not been called; however calling it explicitly gives the opportunity to affect and examine the contents of the &defenv;. +Instantiation happens even if no build instructions +appar to use it, as there are internal uses. +If there are no uses in the project &SConscript; files, +a small performance gain may be seen by calling +&f-DefaultEnvironment; with an empty tools list, +thus avoiding that part of the initialization cost. +This is mainly of interest in testing when &scons; is +launched repeatedly in a short time period: - -The &defenv; is a singleton, so the keyword -arguments affect it only on the first call, on subsequent -calls the already-constructed object is returned and -any keyword arguments are silently ignored. -The &defenv; can be modified after instantiation -in the same way as any &consenv;. -Modifying the &defenv; has no effect on the &consenv; -constructed by an &f-link-Environment; or &f-link-Clone; call. - + +DefaultEnvironment(tools=[]) + @@ -1805,25 +1833,45 @@ is used if no value is specified. Export([vars...], [key=value...]) env.Export([vars...], [key=value...]) -Exports variables from the current -SConscript file to a global collection where they can be -imported by other SConscript files. +Exports variables for sharing with other SConscript files. +The variables are added to a global collection where +they can be imported by other SConscript files. vars may be one or more -strings representing variable names to be exported. -If a string contains whitespace, it is split into -separate strings, as if multiple string arguments -had been given. A vars argument -may also be a dictionary, which can be used to map variables -to different names when exported. -Keyword arguments can be used to provide names and their values. +strings, or a list of strings. If any string +contains whitespace, it is split automatically +into individual strings. Each string must +match the name of a variable that is in scope +during evaluation of the current SConscript file, +or an exception is raised. + + + +A vars argument +may also be a dictionary or +individual keyword arguments; +in accordance with &Python; syntax rules, +keyword arguments must come after any +non-keyword arguments. +The dictionary/keyword form can be used +to map the local name of a variable to +a different name to be used for imports. +See the Examples for an illustration of the syntax. &f-Export; calls are cumulative. Specifying a previously -exported variable will overwrite the earlier value. +exported variable will replace the previous value in the collection. Both local variables and global variables can be exported. + +To use an exported variable, an SConscript must +call &f-link-Import; to bring it into its own scope. +Importing creates an additional reference to the object that +was originally exported, so if that object is mutable, +changes made will be visible to other users of that object. + + Examples: @@ -1850,10 +1898,10 @@ Export({"debug": env}) Note that the &f-link-SConscript; -function supports an &exports; -argument that allows exporting a variable or -set of variables to a specific SConscript file or files. -See the description below. +function also supports an &exports; +argument that allows exporting one or more variables +to the SConscript files invoked by that call (only). +See the description of that function for details. @@ -2657,28 +2705,41 @@ sources = Glob("*.cpp", exclude=["os_*_specific_*.cpp"]) \ - Help(text, append=False) - env.Help(text, append=False) + Help(text, append=False, local_only=False) + env.Help(text, append=False, local_only=False) -Specifies a local help message to be printed if the - -argument is given to -&scons;. -Subsequent calls to -&f-Help; -append text to the previously -defined local help text. +Adds text to the help message shown when +&scons; is called with the + or +argument. -For the first call to &f-Help; only, +On the first call to &f-Help;, if append is False -(the default) -any local help message generated through -&f-link-AddOption; calls is replaced. +(the default), any existing help text is discarded. +The default help text is the help for the &scons; +command itself plus help collected from any +project-local &f-link-AddOption; calls. +This is the help printed if &f-Help; has never been called. If append is True, text is appended to the existing help text. +If local_only is also True +(the default is False), +the project-local help from &f-AddOption; calls is preserved +in the help message but the &scons; command help is not. + + +Subsequent calls to +&f-Help; ignore the keyword arguments +append and +local_only +and always append to the existing help text. + + +Changed in 4.6.0: added local_only. + @@ -2724,23 +2785,31 @@ env.Ignore('bar', 'bar/foobar.obj') Import(vars...) env.Import(vars...) -Imports variables into the current SConscript file. +Imports variables into the scope of the current SConscript file. vars must be strings representing names of variables which have been previously exported either by the &f-link-Export; function or by the -&exports; argument to -&f-link-SConscript;. -Variables exported by -&f-SConscript; +&exports; argument to the +&f-link-SConscript; function. +Variables exported by the +&f-SConscript; call take precedence. Multiple variable names can be passed to &f-Import; -as separate arguments or as words in a space-separated string. +as separate arguments, as a list of strings, +or as words in a space-separated string. The wildcard "*" can be used to import all available variables. + +If the imported variable is mutable, +changes made locally will be reflected in the object the +variable is bound to. This allows subsidiary SConscript files +to contribute to building up, for example, a &consenv;. + + Examples: @@ -3623,12 +3692,12 @@ for a complete explanation of the arguments and behavior. - SConscript(scripts, [exports, variant_dir, duplicate, must_exist]) - env.SConscript(scripts, [exports, variant_dir, duplicate, must_exist]) + SConscript(scriptnames, [exports, variant_dir, duplicate, must_exist]) + env.SConscript(scriptnames, [exports, variant_dir, duplicate, must_exist]) SConscript(dirs=subdirs, [name=scriptname, exports, variant_dir, duplicate, must_exist]) env.SConscript(dirs=subdirs, [name=scriptname, exports, variant_dir, duplicate, must_exist]) -Executes one or more subsidiary SConscript (configuration) files. +Executes subsidiary SConscript (build configuration) file(s). There are two ways to call the &f-SConscript; function. @@ -3636,30 +3705,31 @@ There are two ways to call the The first calling style is to supply one or more SConscript file names -as the first (positional) argument. -A single script may be specified as a string; -multiple scripts must be specified as a list of strings -(either explicitly or as created by -a function like -&f-link-Split;). +as the first positional argument, +which can be a string or a list of strings. +If there is a second positional argument, +it is treated as if the +exports +keyword argument had been given (see below). Examples: SConscript('SConscript') # run SConscript in the current directory SConscript('src/SConscript') # run SConscript in the src directory SConscript(['src/SConscript', 'doc/SConscript']) +SConscript(Split('src/SConscript doc/SConscript')) config = SConscript('MyConfig.py') -The other calling style is to omit the positional argument naming -scripts and instead specify a list of directory names using the +The second calling style is to omit the positional argument naming +the script and instead specify directory names using the dirs keyword argument. +The value can be a string or list of strings. In this case, &scons; -will -execute a subsidiary configuration file named -&SConscript; +will execute a subsidiary configuration file named +&SConscript; (by default) in each of the specified directories. You may specify a name other than &SConscript; @@ -3679,22 +3749,29 @@ SConscript(dirs=['sub1', 'sub2'], name='MySConscript') The optional exports -keyword argument provides a string or list of strings representing -variable names, or a dictionary of named values, to export. -For the first calling style only, a second positional argument -will be interpreted as exports; the -second calling style must use the keyword argument form -for exports. +keyword argument specifies variables to make available +for use by the called SConscripts, +which are evaluated in an isolated context +and otherwise do not have access to local variables +from the calling SConscript. +The value may be a string or list of strings representing +variable names, or a dictionary mapping local names to +the names they can be imported by. +For the first (scriptnames) calling style, +a second positional argument will also be interpreted as +exports; +the second (directory) calling style accepts no +positional arguments and must use the keyword form. These variables are locally exported only to the called -SConscript file(s) -and do not affect the global pool of variables managed by the +SConscript file(s), and take precedence over any same-named +variables in the global pool managed by the &f-link-Export; function. The subsidiary SConscript files must use the &f-link-Import; -function to import the variables. +function to import the variables into their local scope. Examples: @@ -3798,15 +3875,18 @@ TODO??? SConscript('build/SConscript', src_dir='src') If the optional must_exist -is True, -causes an exception to be raised if a requested -SConscript file is not found. The current default is -False, -causing only a warning to be emitted, but this default is deprecated -(since 3.1). -For scripts which truly intend to be optional, transition to -explicitly supplying -must_exist=False to the &f-SConscript; call. +is True (the default), +an exception is raised if a requested +SConscript file is not found. +To allow missing scripts to be silently ignored +(the default behavior prior to &SCons; version 3.1), +pass +must_exist=False in the &f-SConscript; call. + + + +Changed in 4.6.0: must_exist +now defaults to True. diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen index c3b4cfc51b..3e1b0dcf6c 100644 --- a/doc/generated/tools.gen +++ b/doc/generated/tools.gen @@ -1078,7 +1078,7 @@ Sets construction variables for the TeX formatter and typesetter. textfile -Set &consvars; for the &b-Textfile; and &b-Substfile; builders. +Set &consvars; for the &b-link-Textfile; and &b-link-Substfile; builders. Sets: &cv-link-FILE_ENCODING;, &cv-link-LINESEPARATOR;, &cv-link-SUBSTFILEPREFIX;, &cv-link-SUBSTFILESUFFIX;, &cv-link-TEXTFILEPREFIX;, &cv-link-TEXTFILESUFFIX;.Uses: &cv-link-SUBST_DICT;. @@ -1104,9 +1104,9 @@ provides &b-POTUpdate; builder to make PO yacc -Sets construction variables for the &yacc; parse generator. +Sets construction variables for the &yacc; parser generator. -Sets: &cv-link-YACC;, &cv-link-YACCCOM;, &cv-link-YACCFLAGS;, &cv-link-YACCHFILESUFFIX;, &cv-link-YACCHXXFILESUFFIX;, &cv-link-YACCVCGFILESUFFIX;.Uses: &cv-link-YACCCOMSTR;, &cv-link-YACCFLAGS;, &cv-link-YACC_GRAPH_FILE;, &cv-link-YACC_HEADER_FILE;. +Sets: &cv-link-YACC;, &cv-link-YACCCOM;, &cv-link-YACCFLAGS;, &cv-link-YACCHFILESUFFIX;, &cv-link-YACCHXXFILESUFFIX;, &cv-link-YACCVCGFILESUFFIX;, &cv-link-YACC_GRAPH_FILE_SUFFIX;.Uses: &cv-link-YACCCOMSTR;, &cv-link-YACCFLAGS;, &cv-link-YACC_GRAPH_FILE;, &cv-link-YACC_HEADER_FILE;. zip diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 86395a70e1..621853d5f9 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -375,8 +375,8 @@ env['BUILDERS']['NewBuilder'] = bld The class type that SCons should use when instantiating a -new &f-link-CacheDir; for the given environment. It must be -a subclass of the SCons.CacheDir.CacheDir class. +new &f-link-CacheDir; in this &consenv;. Must be +a subclass of the SCons.CacheDir.CacheDir class. @@ -466,7 +466,7 @@ when the &cv-link-PDB; &consvar; is set. -The Visual C++ compiler option that SCons uses by default +The Visual C++ compiler option that &SCons; uses by default to generate PDB information is . This works correctly with parallel () builds because it embeds the debug information in the intermediate object files, @@ -895,23 +895,23 @@ 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 and the directories will not be searched by the dependency scanner. &cv-CPPPATH; should be a list of path strings, or a single string, not a pathname list joined by -Python's os.sep. +Python's os.pathsep. 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: @@ -1182,6 +1182,42 @@ General options that are passed to the D compiler. DFLAGSUFFIX. + + + + + DI_FILE_DIR + + +Path where .di files will be generated + + + + + + DI_FILE_DIR_PREFIX + + +Prefix to send the di path argument to compiler + + + + + + DI_FILE_DIR_SUFFFIX + + +Suffix to send the di path argument to compiler + + + + + + DI_FILE_SUFFIX + + +Suffix of d include files default is .di + @@ -2706,8 +2742,11 @@ target being built. FILE_ENCODING -File encoding used for files written by &b-link-Textfile; and &b-link-Substfile;. Set to "utf-8" by default. -Added in version 4.5.0. +File encoding used for files written by &b-link-Textfile; and &b-link-Substfile;. +Set to "utf-8" by default. + + +New in version 4.5.0. @@ -3183,7 +3222,7 @@ is -dNOPAUSE -dBATCH -sDEVICE=pdfwrite and x86_64 for 64-bit hosts. - Should be considered immutable. + Should be considered immutable. &cv-HOST_ARCH; is not currently used by other platforms, but the option is reserved to do so in future @@ -3201,7 +3240,7 @@ is -dNOPAUSE -dBATCH -sDEVICE=pdfwrite platform argument to &f-link-Environment;). - Should be considered immutable. + Should be considered immutable. &cv-HOST_OS; is not currently used by &SCons;, but the option is reserved to do so in future @@ -4204,7 +4243,7 @@ general information on specifying emitters. An automatically-generated construction variable containing the linker command-line options for specifying libraries to be linked with the resulting target. -The value of &cv-link-_LIBFLAGS; is created +The value of &cv-_LIBFLAGS; is created by respectively prepending and appending &cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; to each filename in &cv-link-LIBS;. @@ -4233,6 +4272,48 @@ This will be appended to each library in the &cv-link-LIBS; construction variable when the &cv-link-_LIBFLAGS; variable is automatically generated. + + + + + LIBLITERALPREFIX + + +If the linker supports command line syntax directing +that the argument specifying a library should be +searched for literally (without modification), +&cv-LIBLITERALPREFIX; can be set to that indicator. +For example, the GNU linker follows this rule: + +-l:foo searches the library path +for a filename called foo, +without converting it to +libfoo.so or +libfoo.a. + +If &cv-LIBLITERALPREFIX; is set, +&SCons; will not transform a string-valued entry in +&cv-link-LIBS; that starts with that string. +The entry will still be surrounded with +&cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; +on the command line. +This is useful, for example, +in directing that a static library +be used when both a static and dynamic library are available +and linker policy is to prefer dynamic libraries. +Compared to the example in &cv-link-LIBS;, + + +env.Append(LIBS=":libmylib.a") + + +will let the linker select that specific (static) +library name if found in the library search path. +This differs from using a +File object +to specify the static library, +as the latter bypasses the library search path entirely. + @@ -4244,7 +4325,7 @@ The list of directories that will be searched for libraries specified by the &cv-link-LIBS; &consvar;. &cv-LIBPATH; should be a list of path strings, or a single string, not a pathname list joined by -Python's os.sep. +Python's os.pathsep. +For each &Builder; call that causes linking with libraries, +&SCons; will add the libraries in the setting of &cv-LIBS; +in effect at that moment to the dependecy graph +as dependencies of the target being generated. + + +The library list will transformed to command line +arguments through the automatically-generated +&cv-link-_LIBFLAGS; &consvar; +which is constructed by +respectively prepending and appending the values of the +&cv-link-LIBLINKPREFIX; and &cv-link-LIBLINKSUFFIX; &consvars; +to each library name. + + + +Any command lines you define yourself that need +the libraries from &cv-LIBS; should include &cv-_LIBFLAGS; +(as well as &cv-link-_LIBDIRFLAGS;) +rather than &cv-LIBS;. +For example: + + + +env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE") + @@ -4413,6 +4523,7 @@ to reflect the names of the libraries they create. A list of all legal suffixes for library file names. +on the current platform. When searching for library dependencies, SCons will look for files with prefixes from the &cv-link-LIBPREFIXES; list, the base library name, @@ -4852,12 +4963,12 @@ and When set to any true value, -specifies that SCons should batch +specifies that &SCons; should batch compilation of object files when calling the Microsoft Visual C/C++ compiler. All compilations of source files from the same source directory that generate target files in a same output directory -and were configured in SCons using the same &consenv; +and were configured in &SCons; using the same &consenv; will be built in a single call to the compiler. Only source files that have changed since their object files were built will be passed to each compiler invocation @@ -5850,7 +5961,7 @@ The burden is on the user to ensure the requisite UWP libraries are installed. Sets the preferred version of Microsoft Visual C/C++ to use. If the specified version is unavailable (not installed, or not discoverable), tool initialization will fail. -If &cv-MSVC_VERSION; is not set, SCons will (by default) select the +If &cv-MSVC_VERSION; is not set, &SCons; will (by default) select the latest version of Visual C/C++ installed on your system. @@ -6774,17 +6885,19 @@ not the underlying project code itself. PCH -The Microsoft Visual C++ precompiled header that will be used when compiling -object files. This variable is ignored by tools other than Microsoft Visual C++. +A node for the Microsoft Visual C++ precompiled header that will be +used when compiling object files. +This variable is ignored by tools other than Microsoft Visual C++. When this variable is -defined SCons will add options to the compiler command line to +defined, &SCons; will add options to the compiler command line to cause it to use the precompiled header, and will also set up the dependencies for the PCH file. -Example: +Examples: env['PCH'] = File('StdAfx.pch') +env['PCH'] = env.PCH('pch.cc')[0] @@ -6805,7 +6918,7 @@ builder to generated a precompiled header. The string displayed when generating a precompiled header. -If this is not set, then &cv-link-PCHCOM; (the command line) is displayed. +If not set, then &cv-link-PCHCOM; (the command line) is displayed. @@ -9488,7 +9601,7 @@ an alternate command line so the invoked tool will make use of the contents of the temporary file. If you need to replace the default tempfile object, the callable should take into account the settings of -&cv-link-MAXLINELENGTH;, +&cv-link-MAXLINELENGTH;, &cv-link-TEMPFILEPREFIX;, &cv-link-TEMPFILESUFFIX;, &cv-link-TEMPFILEARGJOIN;, @@ -9503,7 +9616,7 @@ and TEMPFILEARGESCFUNC -The default argument escape function is +The default argument escape function is SCons.Subst.quote_spaces. If you need to apply extra operations on a command argument (to fix Windows slashes, normalize paths, etc.) @@ -9732,10 +9845,10 @@ Specify the location of vswhere.exe. It provides full information about installations of 2017 and later editions. With the argument, vswhere.exe can detect installations of the 2010 through 2015 editions with limited data returned. -If VSWHERE is set, SCons will use that location. +If VSWHERE is set, &SCons; will use that location. - Otherwise SCons will look in the following locations and set VSWHERE to the path of the first vswhere.exe + Otherwise &SCons; will look in the following locations and set VSWHERE to the path of the first vswhere.exe located. @@ -10146,7 +10259,9 @@ to disable debug package generation. To enable debug package generation, include this variable set either to None, or to a custom list that does not include the default line. -Added in version 3.1. + + +New in version 3.1. @@ -10502,6 +10617,30 @@ Will be emitted as a command-line option. Use this in preference to including in &cv-link-YACCFLAGS; directly. +New in version 4.4.0. + + + + + YACC_GRAPH_FILE_SUFFIX + + +Previously specified by &cv-link-YACCVCGFILESUFFIX;. + + +The suffix of the file +containing a graph of the grammar automaton +when the option +(or without an option-argument) +is used in &cv-link-YACCFLAGS;. +Note that setting this variable informs &SCons; +how to construct the graph filename for tracking purposes, +it does not affect the actual generated filename. +Various yacc tools have emitted various formats +at different times. +Set this to match what your parser generator produces. + +New in version 4.X.Y. @@ -10514,6 +10653,7 @@ Will be emitted as a command-line option. Use this in preference to including in &cv-link-YACCFLAGS; directly. +New in version 4.4.0. @@ -10554,12 +10694,19 @@ and adds those to the target list. -If a option is present, +If the option is present in &cv-YACCFLAGS; &scons; assumes that the call will also create a header file with the suffix defined by &cv-link-YACCHFILESUFFIX; if the yacc source file ends in a .y suffix, or a file with the suffix defined by &cv-link-YACCHXXFILESUFFIX; if the yacc source file ends in a .yy suffix. +The header will have the same base name as the requested target. +This is only correct if the executable is bison +(or win_bison). +If using Berkeley yacc (byacc), +y.tab.h is always written - +avoid the in this case and +use &cv-link-YACC_HEADER_FILE; instead. @@ -10576,22 +10723,31 @@ with the suffix .output. Also recognized are GNU &bison; options - and its deprecated synonym -, + +(and its deprecated synonym ), which is similar to -but the output filename is named by the option argument; -and , +but gives the option to explicitly name the output header file +through an option argument; +and , which is similar to -but the output filename is named by the option argument. +but gives the option to explicitly name the output graph file +through an option argument. +The file suffixes described for + and above +are not applied if these are used in the option=argument form. Note that files specified by and may not be properly handled -by &SCons; in all situations. Consider using -&cv-link-YACC_HEADER_FILE; and &cv-link-YACC_GRAPH_FILE; instead. +by &SCons; in all situations, and using those in &cv-YACCFLAGS; +should be considered legacy support only. +Consider using &cv-link-YACC_HEADER_FILE; +and &cv-link-YACC_GRAPH_FILE; instead +if the files need to be explicitly named +(new in version 4.4.0). @@ -10602,14 +10758,13 @@ by &SCons; in all situations. Consider using The suffix of the C header file generated by the parser generator -when the - -option is used. -Note that setting this variable does not cause -the parser generator to generate a header -file with the specified suffix, -it exists to allow you to specify -what suffix the parser generator will use of its own accord. +when the option +(or without an option-argument) +is used in &cv-link-YACCFLAGS;. +Note that setting this variable informs &SCons; +how to construct the header filename for tracking purposes, +it does not affect the actual generated filename. +Set this to match what your parser generator produces. The default value is .h. @@ -10622,22 +10777,14 @@ The default value is The suffix of the C++ header file generated by the parser generator -when the - -option is used. -Note that setting this variable does not cause -the parser generator to generate a header -file with the specified suffix, -it exists to allow you to specify -what suffix the parser generator will use of its own accord. -The default value is -.hpp, -except on Mac OS X, -where the default is -${TARGET.suffix}.h. -because the default &bison; parser generator just -appends .h -to the name of the generated C++ file. +when the option +(or without an option-argument) +is used in &cv-link-YACCFLAGS;. +Note that setting this variable informs &SCons; +how to construct the header filename for tracking purposes, +it does not affect the actual generated filename. +Set this to match what your parser generator produces. +The default value is .hpp. @@ -10646,18 +10793,14 @@ to the name of the generated C++ file. YACCVCGFILESUFFIX -The suffix of the file -containing the VCG grammar automaton definition -when the - -option is used. -Note that setting this variable does not cause -the parser generator to generate a VCG -file with the specified suffix, -it exists to allow you to specify -what suffix the parser generator will use of its own accord. -The default value is -.vcg. +Obsoleted. Use &cv-link-YACC_GRAPH_FILE_SUFFIX; instead. +The value is used only if &cv-YACC_GRAPH_FILE_SUFFIX; is not set. +The default value is .gv. + + +Changed in version 4.X.Y: deprecated. The default value +changed from .vcg (&bison; stopped generating +.vcg output with version 2.4, in 2006). diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod index c3787ea8e6..3b3e81eb5a 100644 --- a/doc/generated/variables.mod +++ b/doc/generated/variables.mod @@ -286,6 +286,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $_LIBFLAGS"> $LIBLINKPREFIX"> $LIBLINKSUFFIX"> +$LIBLITERALPREFIX"> $LIBPATH"> $LIBPREFIX"> $LIBPREFIXES"> @@ -967,6 +968,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $_LIBFLAGS"> $LIBLINKPREFIX"> $LIBLINKSUFFIX"> +$LIBLITERALPREFIX"> $LIBPATH"> $LIBPREFIX"> $LIBPREFIXES"> diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 6a888cbc9c..22f93f5f79 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -1117,6 +1117,12 @@ since multiple build commands and intervening SCons processing should take place in parallel.) + + + + sconscript + +Enables output indicating entering and exiting each SConscript file. @@ -3948,7 +3954,7 @@ Returns a boolean indicating success or failure. - context.CheckFunc(function_name, [header, language]) + context.CheckFunc(function_name, [header, language, funcargs]) Checks if function_name is usable in the context's local environment, using the compiler @@ -3976,17 +3982,18 @@ char function_name(void); -Note: if header is supplied, -it should not -include the standard header file that declares -function_name, -and it should include a -dummy prototype similar to the default case. -Compilers reject builds where a function call does -not match the declared prototype as happens -if the "real" header is included, -and modern compilers are now rejecting -implicit function declarations. +If header is supplied, it should not include +the standard header file that declares function_name and it +should include a dummy prototype similar to the default case. If +this is not possible, the optional funcargs argument can be used +to specify a string containing an argument list with the same number and type of +arguments as the prototype. The arguments can simply be constant values of the correct +type. Modern C/C++ compilers reject implicit function declarations and may also reject +function calls whose arguments are not type compatible with the prototype. + + + +Changed in version 4.7.0: added the funcargs. Returns a boolean indicating success or failure. diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py index bf492f0063..8bfdc419e5 100644 --- a/doc/sphinx/conf.py +++ b/doc/sphinx/conf.py @@ -98,8 +98,9 @@ # The full version, including alpha/beta/rc tags: release = __version__ # The short X.Y version. -major, minor, _ = __version__.split('.') -version = '.'.join([major, minor]) +version_parts = __version__.split('.') +major, minor, patch = version_parts[0:3] +version = '.'.join([major, minor,patch]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/user/README b/doc/user/README index f94632d942..1e0bd13aa3 100644 --- a/doc/user/README +++ b/doc/user/README @@ -1,11 +1,12 @@ -# __COPYRIGHT__ +# SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org) +# SPDX-License-Identifier: MIT When adding a new file, add it to main.xml and MANIFEST. To build the .xml files from the .in files: scons -D BUILDDOC=1 foo.xml To build the whole PDF doc from this dir, for testing: - scons -D ../../build/doc/PDF/scons-user.pdf + scons -D ../../build/doc/PDF/scons-user.pdf Writing examples: here's a simple template. diff --git a/doc/user/SConstruct b/doc/user/SConstruct index cfd85cf28e..b64482b776 100644 --- a/doc/user/SConstruct +++ b/doc/user/SConstruct @@ -1,57 +1,36 @@ +# SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org) +# SPDX-License-Identifier: MIT # # SConstruct file for building SCons documentation. # -# -# __COPYRIGHT__ -# -# 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. - import os -env = Environment(ENV={'PATH' : os.environ['PATH']}, - tools=['docbook','gs','zip'], - toolpath=['../../SCons/Tool'], - # DOCBOOK_XSLTPROCFLAGS="--stringparam fop.extensions 1", - DOCBOOK_DEFAULT_XSL_HTML='html.xsl', - DOCBOOK_DEFAULT_XSL_HTMLCHUNKED='chtml.xsl', - DOCBOOK_DEFAULT_XSL_PDF='pdf.xsl') +env = Environment( + ENV={'PATH': os.environ['PATH']}, + tools=['docbook', 'gs', 'zip'], + toolpath=['../../SCons/Tool'], + # DOCBOOK_XSLTPROCFLAGS="--stringparam fop.extensions 1", + DOCBOOK_DEFAULT_XSL_HTML='html.xsl', + DOCBOOK_DEFAULT_XSL_HTMLCHUNKED='chtml.xsl', + DOCBOOK_DEFAULT_XSL_PDF='pdf.xsl', +) has_pdf = False -if (env.WhereIs('fop') or - env.WhereIs('xep')): +if env.WhereIs('fop') or env.WhereIs('xep'): has_pdf = True # # UserGuide for SCons # env.DocbookXInclude('scons_xi.xml', 'main.xml') -env.DocbookXslt('scons_ex.xml', 'scons_xi.xml', - xsl='../xslt/xinclude_examples.xslt') +env.DocbookXslt('scons_ex.xml', 'scons_xi.xml', xsl='../xslt/xinclude_examples.xslt') env.DocbookXInclude('scons_exi.xml', 'scons_ex.xml') -env.DocbookXslt('scons_db.xml', 'scons_exi.xml', - xsl='../xslt/to_docbook.xslt') -env.DocbookHtml('index.html','scons_db.xml') +env.DocbookXslt('scons_db.xml', 'scons_exi.xml', xsl='../xslt/to_docbook.xslt') +env.DocbookHtml('index.html', 'scons_db.xml') env.DocbookHtmlChunked('index.html', 'scons_db.xml', base_dir='scons-user/') if has_pdf: - env.DocbookPdf('scons-user.pdf','scons_db.xml') + env.DocbookPdf('scons-user.pdf', 'scons_db.xml') has_gs = False if env.WhereIs('gs'): @@ -61,7 +40,10 @@ if env.WhereIs('gs'): # Create the EPUB format # if has_gs and has_pdf: - jpg = env.Gs('OEBPS/cover.jpg','scons-user.pdf', - GSFLAGS='-dNOPAUSE -dBATCH -sDEVICE=jpeg -dFirstPage=1 -dLastPage=1 -dJPEGQ=100 -r72x72 -q') + jpg = env.Gs( + 'OEBPS/cover.jpg', + 'scons-user.pdf', + GSFLAGS='-dNOPAUSE -dBATCH -sDEVICE=jpeg -dFirstPage=1 -dLastPage=1 -dJPEGQ=100 -r72x72 -q', + ) epub = env.DocbookEpub('scons-user.epub', 'scons_db.xml', xsl='epub.xsl') env.Depends(epub, jpg) diff --git a/doc/user/actions.xml b/doc/user/actions.xml index c980f9cf3f..d357eb6190 100644 --- a/doc/user/actions.xml +++ b/doc/user/actions.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -11,7 +17,7 @@ %tools-mod; %variables-mod; - + ]> &SCons; Actions - - %scons; - + %builders-mod; @@ -17,7 +17,7 @@ Copyright The SCons Foundation %tools-mod; %variables-mod; - + ]> + + + %scons; - + %builders-mod; @@ -11,7 +17,7 @@ %tools-mod; %variables-mod; - + ]> Alias Targets - - We've already seen how you can use the &Alias; diff --git a/doc/user/ant.xml b/doc/user/ant.xml index e829d0e855..17f44fc52d 100644 --- a/doc/user/ant.xml +++ b/doc/user/ant.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -11,7 +17,7 @@ %tools-mod; %variables-mod; - + ]> Converting From Ant - - XXX diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml index 3f6982c968..f2b7241f3f 100644 --- a/doc/user/build-install.xml +++ b/doc/user/build-install.xml @@ -1,8 +1,8 @@ + + + + %scons; - + %builders-mod; @@ -11,7 +17,7 @@ %tools-mod; %variables-mod; - + ]> Built-In Builders - - &SCons; provides the ability to build a lot of different diff --git a/doc/user/builders-commands.xml b/doc/user/builders-commands.xml index 7d47daef5b..ba01e0c239 100644 --- a/doc/user/builders-commands.xml +++ b/doc/user/builders-commands.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -20,31 +26,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Not Writing a Builder: the &Command; Builder - - + %scons; @@ -20,34 +26,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Extending &SCons;: Writing Your Own Builders - - - + Although &SCons; provides many useful methods for building common software products diff --git a/doc/user/builders.xml b/doc/user/builders.xml index 9fd83d70b9..9383424b4a 100644 --- a/doc/user/builders.xml +++ b/doc/user/builders.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Builders - - This appendix contains descriptions of all of the diff --git a/doc/user/caching.xml b/doc/user/caching.xml index 8cca510ef7..b79cd6259b 100644 --- a/doc/user/caching.xml +++ b/doc/user/caching.xml @@ -1,8 +1,8 @@ + - + + + + %scons; @@ -20,34 +26,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Controlling a Build From the Command Line - - - + &SCons; provides a number of ways for you as the writer of the &SConscript; files diff --git a/doc/user/depends.xml b/doc/user/depends.xml index 62b6d911d3..961edb2c9b 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -20,34 +26,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Dependencies - - - + So far we've seen how &SCons; handles one-time builds. But one of the main functions of a build tool like &SCons; @@ -125,7 +104,7 @@ int main() { printf("Hello, world!\n"); } - By default, &SCons; + By default, &SCons; uses a cryptographic hash of the file's contents, not the file's modification time, to decide whether a file has changed. diff --git a/doc/user/environments.xml b/doc/user/environments.xml index 7118f21bff..b4f86892c7 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -20,31 +26,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Environments - - + + + + %scons; - + %builders-mod; @@ -20,31 +26,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Errors - - XXX diff --git a/doc/user/example.xml b/doc/user/example.xml index a4e3b99115..18783ded8e 100644 --- a/doc/user/example.xml +++ b/doc/user/example.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -11,7 +17,7 @@ %tools-mod; %variables-mod; - + ]> Complex &SCons; Example - - XXX diff --git a/doc/user/external.xml b/doc/user/external.xml index ceeece084d..46314c4cfe 100644 --- a/doc/user/external.xml +++ b/doc/user/external.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -20,33 +26,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Using SCons with other build tools - - Sometimes a project needs to interact with other projects diff --git a/doc/user/factories.xml b/doc/user/factories.xml index 362b6f0c88..905b959325 100644 --- a/doc/user/factories.xml +++ b/doc/user/factories.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -20,31 +26,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Platform-Independent File System Manipulation - - &SCons; provides a number of platform-independent functions, @@ -183,7 +164,7 @@ touch $* The &Copy; factory has a third optional argument which controls how symlinks are copied. - + @@ -404,7 +385,7 @@ SConscript('S') For example, if we need to process a file in a temporary directory in which the processing tool - will create other files that we don't care about, + will create other files that we don't care about, you could use: diff --git a/doc/user/file-removal.xml b/doc/user/file-removal.xml index 6eb688d249..4f93b2066b 100644 --- a/doc/user/file-removal.xml +++ b/doc/user/file-removal.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -11,7 +17,7 @@ %tools-mod; %variables-mod; - + ]> Controlling Removal of Targets - - There are two occasions when &SCons; will, diff --git a/doc/user/functions.xml b/doc/user/functions.xml index 8044310306..9185f4adb8 100644 --- a/doc/user/functions.xml +++ b/doc/user/functions.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Functions and Environment Methods - - This appendix contains descriptions of all of the diff --git a/doc/user/gettext.xml b/doc/user/gettext.xml index 331ec33c81..b9ebbbda7c 100644 --- a/doc/user/gettext.xml +++ b/doc/user/gettext.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -11,7 +17,7 @@ %tools-mod; %variables-mod; - + ]> Internationalization and localization with gettext - - The &t-link-gettext; toolset supports internationalization and localization of SCons-based projects. Builders provided by &t-link-gettext; automatize @@ -76,7 +57,7 @@
Simple project - Let's start with a very simple project, the "Hello world" program + Let's start with a very simple project, the "Hello world" program for example @@ -145,7 +126,7 @@ int main(int argc, char* argv[]) translate the message at runtime. - + Now we shall instruct SCons how to generate and maintain translation files. For that, use the &b-link-Translate; builder and &b-link-MOFiles; builder. The first one takes source files, extracts internationalized @@ -158,7 +139,7 @@ int main(int argc, char* argv[]) called locale. - The completed + The completed SConstruct is as follows: diff --git a/doc/user/hierarchy.xml b/doc/user/hierarchy.xml index 17874d0349..c501e53cb0 100644 --- a/doc/user/hierarchy.xml +++ b/doc/user/hierarchy.xml @@ -1,8 +1,8 @@ + print for debugging, or write a Python function that wants to evaluate a path. - You can force &SCons; to evaluate a top-relative path + You can force &SCons; to evaluate a top-relative path and produce a string that can be used by &Python; code by creating a Node object from it: diff --git a/doc/user/html.xsl b/doc/user/html.xsl index fa3d915bf3..6def44b593 100644 --- a/doc/user/html.xsl +++ b/doc/user/html.xsl @@ -1,28 +1,10 @@ - + diff --git a/doc/user/install.xml b/doc/user/install.xml index d5ea4d8455..b1ae1f6cbd 100644 --- a/doc/user/install.xml +++ b/doc/user/install.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -20,31 +26,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Installing Files in Other Directories: the &Install; Builder - - Once a program is built, diff --git a/doc/user/java.xml b/doc/user/java.xml index 4f1beef5c0..1a51621ba3 100644 --- a/doc/user/java.xml +++ b/doc/user/java.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -19,13 +25,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Java Builds - - - + So far, we've been using examples of building C and C++ programs diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml index 8a30cdfbfb..56d4f49f8a 100644 --- a/doc/user/less-simple.xml +++ b/doc/user/less-simple.xml @@ -1,9 +1,10 @@ + + %scons; diff --git a/doc/user/libraries.xml b/doc/user/libraries.xml index d7983c90ce..67e8a52a62 100644 --- a/doc/user/libraries.xml +++ b/doc/user/libraries.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Building and Linking with Libraries - - It's often useful to organize large software projects @@ -269,7 +250,7 @@ void f3() { printf("f3.c\n"); } You link libraries with a program by specifying the libraries in the &cv-link-LIBS; construction variable, and by specifying the directory in which - the library will be found in the + the library will be found in the &cv-link-LIBPATH; construction variable: diff --git a/doc/user/main.xml b/doc/user/main.xml index de549a8bcb..ba18279d1a 100644 --- a/doc/user/main.xml +++ b/doc/user/main.xml @@ -1,29 +1,8 @@ - The SCons Development Team - Released: Mon, 21 Mar 2023 12:25:39 -0400 + Released: Mon, 19 Nov 2023 17:52:53 -0700 2004 - 2023 diff --git a/doc/user/make.xml b/doc/user/make.xml index 7df8f6a35d..f7ca40e758 100644 --- a/doc/user/make.xml +++ b/doc/user/make.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -10,7 +16,7 @@ %tools-mod; - %variables-mod; + %variables-mod; ]> Converting From Make - - + %scons; @@ -19,34 +25,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Merging Options into the Environment: the &MergeFlags; Function - - - + &SCons; &consenvs; have a &f-link-MergeFlags; method that merges values from a passed-in argument into the &consenv;. diff --git a/doc/user/misc.xml b/doc/user/misc.xml index b1e150786b..ef2e4ca1d6 100644 --- a/doc/user/misc.xml +++ b/doc/user/misc.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Miscellaneous Functionality - - &SCons; supports a lot of additional functionality diff --git a/doc/user/nodes.xml b/doc/user/nodes.xml index 66b37cf28e..83514ec5f1 100644 --- a/doc/user/nodes.xml +++ b/doc/user/nodes.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -19,34 +25,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Node Objects - - - + Internally, &SCons; represents all of the files and directories it knows about as &Nodes;. diff --git a/doc/user/output.xml b/doc/user/output.xml index 7a63540516..ebcf06eef9 100644 --- a/doc/user/output.xml +++ b/doc/user/output.xml @@ -1,8 +1,8 @@ + + + + %scons; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Merging Options While Creating Environment: the <parameter>parse_flags</parameter> Parameter - - It is also possible to merge &consvar; values from arguments diff --git a/doc/user/parseconfig.xml b/doc/user/parseconfig.xml index fc9a889e36..c7fcdd63bd 100644 --- a/doc/user/parseconfig.xml +++ b/doc/user/parseconfig.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -19,34 +25,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Finding Installed Library Information: the &ParseConfig; Function - - - + Configuring the right options to build programs to work with libraries--especially shared libraries--that are available diff --git a/doc/user/parseflags.xml b/doc/user/parseflags.xml index a1ab7af8f2..a007f9b573 100644 --- a/doc/user/parseflags.xml +++ b/doc/user/parseflags.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -19,34 +25,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Separating Compile Arguments into their Variables: the &ParseFlags; Function - - - + &SCons; has a bewildering array of &consvars; for different types of options when building programs. diff --git a/doc/user/pdf.xsl b/doc/user/pdf.xsl index c975f0e809..a6281889e7 100644 --- a/doc/user/pdf.xsl +++ b/doc/user/pdf.xsl @@ -1,27 +1,8 @@ - + + + %scons; @@ -19,34 +25,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Preface - - - + Thank you for taking the time to read about &SCons;. &SCons; is a modern diff --git a/doc/user/python.xml b/doc/user/python.xml index cac61a1841..dc10a7b484 100644 --- a/doc/user/python.xml +++ b/doc/user/python.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -20,31 +26,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Python overview - - This section will provide a brief overview of @@ -155,7 +136,7 @@ while: statements look like break statements look like - + continue statements look like diff --git a/doc/user/repositories.xml b/doc/user/repositories.xml index 77d75d3f34..261ec0a05f 100644 --- a/doc/user/repositories.xml +++ b/doc/user/repositories.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Building From Code Repositories - - Often, a software project will have @@ -540,7 +521,7 @@ int f2() { printf("file2.c\n"); } - + (Note that this is safe even if the &SConstruct; file lists /usr/repository1 as a repository, because &SCons; will remove the current build directory diff --git a/doc/user/run.xml b/doc/user/run.xml index 3025afb84e..acedd4b293 100644 --- a/doc/user/run.xml +++ b/doc/user/run.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> How to Run &SCons; - - + %scons; - + %builders-mod; @@ -19,33 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Extending &SCons;: Writing Your Own Scanners - - + %scons; @@ -19,34 +25,7 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Multi-Platform Configuration (&Autoconf; Functionality) - - - + &SCons; has integrated support for build configuration similar in style to GNU &Autoconf;, but designed to be diff --git a/doc/user/scons_title.xsl b/doc/user/scons_title.xsl index 2eb1293e97..634cb2fee6 100644 --- a/doc/user/scons_title.xsl +++ b/doc/user/scons_title.xsl @@ -1,31 +1,13 @@ - - @@ -385,14 +367,14 @@ - + - + @@ -436,7 +418,7 @@ - + @@ -471,7 +453,7 @@ - + @@ -513,7 +495,7 @@ - + @@ -4702,7 +4684,7 @@ - + @@ -5585,7 +5567,7 @@ page-position="first"/> - @@ -5607,7 +5589,7 @@ page-position="first"/> - @@ -5619,7 +5601,7 @@ - + @@ -5627,7 +5609,7 @@ page-position="first"/> - @@ -5647,7 +5629,7 @@ page-position="first"/> - @@ -5672,7 +5654,7 @@ - @@ -5788,7 +5770,7 @@ - + @@ -5800,7 +5782,7 @@ - @@ -6003,22 +5985,22 @@ - - - - @@ -6042,7 +6024,7 @@ - @@ -6055,7 +6037,7 @@ - @@ -6063,7 +6045,7 @@ - diff --git a/doc/user/separate.xml b/doc/user/separate.xml index 0485a55e51..897a126e36 100644 --- a/doc/user/separate.xml +++ b/doc/user/separate.xml @@ -1,8 +1,8 @@ + + + + %scons; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Declaring Additional Outputs: the &f-SideEffect; Function - - Sometimes the way an action is defined causes effects on files @@ -157,7 +138,7 @@ f1 = env.Command( Unfortunately, the tool which sets up the &b-Program; builder for the MSVC compiler chain does not come prebuilt with an understanding of the details of the .ilk - example - that the target list would need to change + example - that the target list would need to change in the presence of that specific option flag. Unlike the trivial example above where we could simply tell the &Command; builder there were two targets of the action, modifying the diff --git a/doc/user/simple.xml b/doc/user/simple.xml index 6f27b4f13c..3fa8cea72d 100644 --- a/doc/user/simple.xml +++ b/doc/user/simple.xml @@ -1,8 +1,8 @@ + + + + %scons; - + %builders-mod; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Handling Common Tasks - - There is a common set of simple tasks that many build configurations rely on as they become more complex. Most build tools have special @@ -122,14 +103,14 @@ env.Append(CPPPATH = "#") env.Append(BUILDERS = {'Copy1' : Builder(action = 'cat < $SOURCE > $TARGET', suffix='.h', src_suffix='.bar')}) -env.Copy1('test.bar') # produces test.h from test.bar. +env.Copy1('test.bar') # produces test.h from test.bar. env.Program('app','main.cpp') # indirectly depends on test.bar ## Source file example env.Append(BUILDERS = {'Copy2' : Builder(action = 'cat < $SOURCE > $TARGET', suffix='.cpp', src_suffix='.bar2')}) -foo = env.Copy2('foo.bar2') # produces foo.cpp from foo.bar2. +foo = env.Copy2('foo.bar2') # produces foo.cpp from foo.bar2. env.Program('app2',['main2.cpp'] + foo) # compiles main2.cpp and foo.cpp into app2. @@ -163,7 +144,7 @@ produces this: scons -Q - + diff --git a/doc/user/tools.xml b/doc/user/tools.xml index 5aa88ea002..ac46bab582 100644 --- a/doc/user/tools.xml +++ b/doc/user/tools.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Tools - - This appendix contains descriptions of all of the diff --git a/doc/user/troubleshoot.xml b/doc/user/troubleshoot.xml index 24020abb5e..7a13b64da7 100644 --- a/doc/user/troubleshoot.xml +++ b/doc/user/troubleshoot.xml @@ -1,4 +1,10 @@ + + + %scons; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Troubleshooting - - The experience of configuring any diff --git a/doc/user/variables.xml b/doc/user/variables.xml index 0677450b74..27e3323a55 100644 --- a/doc/user/variables.xml +++ b/doc/user/variables.xml @@ -1,8 +1,14 @@ + + + %scons; - + %builders-mod; @@ -19,31 +25,6 @@ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd"> Construction Variables - - This appendix contains descriptions of all of the diff --git a/doc/user/variants.xml b/doc/user/variants.xml index 5096745e35..498d82c775 100644 --- a/doc/user/variants.xml +++ b/doc/user/variants.xml @@ -1,14 +1,15 @@ - %scons; - + %builders-mod; diff --git a/setup.cfg b/setup.cfg index b6d7a5e567..40e24e2985 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,10 +45,6 @@ classifiers = [options] zip_safe = False python_requires = >=3.6 -install_requires = setuptools -setup_requires = - setuptools - build include_package_data = True packages = find: diff --git a/test/AS/as-live.py b/test/AS/as-live.py index 676b5375ee..4a21fcf6f0 100644 --- a/test/AS/as-live.py +++ b/test/AS/as-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 correct use of the live 'as' assembler. @@ -37,24 +36,22 @@ test = TestSCons.TestSCons() - - if not test.detect('AS', 'as'): test.skip_test("as not found; skipping test\n") x86 = (sys.platform == 'win32' or sys.platform.find('linux') != -1) - if not x86: test.skip_test("skipping as test on non-x86 platform '%s'\n" % sys.platform) namelbl = "name" -testccc = """ccc = aaa.Clone(CPPPATH=['.']) -ccc.Program(target = 'ccc', source = ['ccc.S', 'ccc_main.c']) +testccc = """\ +ccc = aaa.Clone(CPPPATH=['.']) +ccc.Program(target='ccc', source=['ccc.S', 'ccc_main.c']) """ if sys.platform == "win32": namelbl = "_name" testccc = "" - + test.write("wrapper.py", """\ import subprocess import sys @@ -66,30 +63,34 @@ test.write('SConstruct', """\ aaa = Environment() -aaa.Program(target = 'aaa', source = ['aaa.s', 'aaa_main.c']) -bbb = aaa.Clone(AS = r'%(_python_)s wrapper.py ' + WhereIs('as')) -bbb.Program(target = 'bbb', source = ['bbb.s', 'bbb_main.c']) +aaa.Program(target='aaa', source=['aaa.s', 'aaa_main.c']) +bbb = aaa.Clone(AS=r'%(_python_)s wrapper.py ' + WhereIs('as')) +bbb.Program(target='bbb', source=['bbb.s', 'bbb_main.c']) %(testccc)s """ % locals()) -test.write('aaa.s', -""" .file "aaa.s" -.data -.align 4 -.globl %(namelbl)s +test.write('aaa.s', """ + .file "aaa.s" + .data + .align 4 + .globl %(namelbl)s %(namelbl)s: - .ascii "aaa.s" - .byte 0 + .ascii "aaa.s" + .byte 0 + .ident "handcrafted test assembly" + .section .note.GNU-stack,"",@progbits """ % locals()) test.write('bbb.s', """\ -.file "bbb.s" -.data -.align 4 -.globl %(namelbl)s + .file "bbb.s" + .data + .align 4 + .globl %(namelbl)s %(namelbl)s: - .ascii "bbb.s" - .byte 0 + .ascii "bbb.s" + .byte 0 + .ident "handcrafted test assembly" + .section .note.GNU-stack,"",@progbits """ % locals()) test.write('ccc.h', """\ @@ -98,13 +99,15 @@ test.write('ccc.S', """\ #include -.file STRING -.data -.align 4 -.globl name + .file STRING + .data + .align 4 + .globl name name: - .ascii STRING - .byte 0 + .ascii STRING + .byte 0 + .ident "handcrafted test assembly" + .section .note.GNU-stack,"",@progbits """) test.write('aaa_main.c', r""" @@ -171,13 +174,13 @@ if sys.platform != "win32": test.run(program = test.workpath('ccc'), stdout = "ccc_main.c ccc.S\n") - + test.must_match('wrapper.out', "wrapper.py: bbb.s\n") - + test.write("ccc.h", """\ #define STRING "ccc.S 2" """) - + test.run() test.run(program = test.workpath('ccc'), stdout = "ccc_main.c ccc.S 2\n") diff --git a/test/Configure/config-h.py b/test/Configure/config-h.py index e5df6258f1..aca18e42ac 100644 --- a/test/Configure/config-h.py +++ b/test/Configure/config-h.py @@ -41,7 +41,7 @@ import os env.AppendENVPath('PATH', os.environ['PATH']) conf = Configure(env, config_h = 'config.h') -r1 = conf.CheckFunc('printf') +r1 = conf.CheckFunc('printf', header='#include ', funcargs='""') r2 = conf.CheckFunc('noFunctionCall') r3 = conf.CheckFunc('memmove') r4 = conf.CheckType('int') diff --git a/test/Docbook/basic/man/image/refdb.xml b/test/Docbook/basic/man/image/refdb.xml index de5f94e351..502798d6bf 100644 --- a/test/Docbook/basic/man/image/refdb.xml +++ b/test/Docbook/basic/man/image/refdb.xml @@ -65,13 +65,13 @@ See also RefDB (7), refdbd (1) - refdbctl (1). + refdbctl (1). RefDB manual (local copy) <prefix>/share/doc/refdb-<version>/refdb-manual/index.html - RefDB manual (web) <http://refdb.sourceforge.net/manual/index.html> + RefDB manual (web) <https://refdb.sourceforge.net/manual/index.html> - RefDB on the web <http://refdb.sourceforge.net/> + RefDB on the web <https://refdb.sourceforge.net/> diff --git a/test/LINK/VersionedLib-j2.py b/test/LINK/VersionedLib-j2.py index 0cde91c56d..8f84a1a18e 100644 --- a/test/LINK/VersionedLib-j2.py +++ b/test/LINK/VersionedLib-j2.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,14 +22,12 @@ # 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__" """ Ensure that SharedLibrary builder works with SHLIBVERSION and -j2. This is regression test for: -http://article.gmane.org/gmane.comp.programming.tools.scons.user/27049 +https://article.gmane.org/gmane.comp.programming.tools.scons.user/27049 +(historical - dead link) """ import TestSCons diff --git a/test/LINK/VersionedLib-subdir.py b/test/LINK/VersionedLib-subdir.py index 66fef63adb..53ae5d0c9c 100644 --- a/test/LINK/VersionedLib-subdir.py +++ b/test/LINK/VersionedLib-subdir.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,16 +22,14 @@ # 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__" """ Ensure that SharedLibrary builder with SHLIBVERSION='0.1.2' can build its target -in a subdirectory containing .so.0.1.2 in name. +in a subdirectory containing .so.0.1.2 in name. This is regression test for issue mentioned in: -http://thread.gmane.org/gmane.comp.programming.tools.scons.user/27081 +https://thread.gmane.org/gmane.comp.programming.tools.scons.user/27081 +(historical - dead link) """ import TestSCons diff --git a/test/Libs/LIBLITERALPREFIX.py b/test/Libs/LIBLITERALPREFIX.py new file mode 100644 index 0000000000..ebd32b6ae5 --- /dev/null +++ b/test/Libs/LIBLITERALPREFIX.py @@ -0,0 +1,101 @@ +#!/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. + +""" +Test LIBLITERALPREFIX behavior. + +Build a static library and shared library of the same name, +and make sure the requested syntax selects the static one. +Depends on a live compiler to build libraries and executable, +and actually runs the executable. +""" + +import os +import sys + +import TestSCons +from TestSCons import lib_, _lib + +test = TestSCons.TestSCons() + +if sys.platform == 'win32': + import SCons.Tool.MSCommon as msc + if msc.msvc_exists(): + test.skip_test("Functionality not available for msvc, skipping test.\n") + +test.write('SConstruct', """\ +LIBLITERALPREFIX = ":" +env = Environment(LIBLITERALPREFIX=LIBLITERALPREFIX, LIBPATH=".") +lib = env.Library(target='foo', source='foo.c') +shlib = env.SharedLibrary(target='foo', source='shfoo.c') +env.Program(target='prog', source=['prog.c'], LIBS=f"{LIBLITERALPREFIX}libfoo.a") +""") + +test.write('shfoo.c', r""" +#include + +void +foo(void) +{ + printf("shared libfoo\n"); +} +""") + +test.write('foo.c', r""" +#include + +void +foo(void) +{ + printf("static libfoo\n"); +} +""") + +test.write('prog.c', r""" +#include + +void foo(void); +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + foo(); + printf("prog.c\n"); + return 0; +} +""") + +test.run(arguments='.', stderr=TestSCons.noisy_ar, match=TestSCons.match_re_dotall) +test.fail_test(not os.path.exists(test.workpath(f"{lib_}foo{_lib}"))) + +test.run(program=test.workpath('prog'), stdout="static libfoo\nprog.c\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Libs/LIBPREFIXES.py b/test/Libs/LIBPREFIXES.py index e496ca4baa..2bf3c9e205 100644 --- a/test/Libs/LIBPREFIXES.py +++ b/test/Libs/LIBPREFIXES.py @@ -23,6 +23,13 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test LIBPREFIXES. + +Depends on a live compiler to build library and executable, +and actually runs the executable. +""" + import os import sys @@ -36,11 +43,10 @@ test = TestSCons.TestSCons() -test.write('SConstruct', """ -env = Environment(LIBPREFIX = 'xxx-', - LIBPREFIXES = ['xxx-']) -lib = env.Library(target = 'foo', source = 'foo.c') -env.Program(target = 'prog', source = ['prog.c', lib]) +test.write('SConstruct', """\ +env = Environment(LIBPREFIX='xxx-', LIBPREFIXES=['xxx-']) +lib = env.Library(target='foo', source='foo.c') +env.Program(target='prog', source=['prog.c', lib]) """) test.write('foo.c', r""" @@ -67,13 +73,10 @@ } """) -test.run(arguments = '.', - stderr=TestSCons.noisy_ar, - match=TestSCons.match_re_dotall) - +test.run(arguments='.', stderr=TestSCons.noisy_ar, match=TestSCons.match_re_dotall) test.fail_test(not os.path.exists(test.workpath('xxx-foo' + _lib))) -test.run(program = test.workpath('prog'), stdout = "foo.c\nprog.c\n") +test.run(program=test.workpath('prog'), stdout="foo.c\nprog.c\n") test.pass_test() diff --git a/test/Libs/LIBSUFFIXES.py b/test/Libs/LIBSUFFIXES.py index 13baeab9ff..06e4506ac4 100644 --- a/test/Libs/LIBSUFFIXES.py +++ b/test/Libs/LIBSUFFIXES.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,29 +22,31 @@ # 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__" +""" +Test LIBSUFFIXES. + +Depends on a live compiler to build library and executable, +and actually runs the executable. +""" import os import sys + import TestSCons +from TestSCons import lib_ if sys.platform == 'win32': - lib_ = '' import SCons.Tool.MSCommon as msc if not msc.msvc_exists(): lib_ = 'lib' -else: - lib_ = 'lib' test = TestSCons.TestSCons() -test.write('SConstruct', """ -env = Environment(LIBSUFFIX = '.xxx', - LIBSUFFIXES = ['.xxx']) -lib = env.Library(target = 'foo', source = 'foo.c') -env.Program(target = 'prog', source = ['prog.c', lib]) +test.write('SConstruct', """\ +env = Environment(LIBSUFFIX='.xxx', LIBSUFFIXES=['.xxx']) +lib = env.Library(target='foo', source='foo.c') +env.Program(target='prog', source=['prog.c', lib]) """) test.write('foo.c', r""" @@ -69,13 +73,10 @@ } """) -test.run(arguments = '.', - stderr=TestSCons.noisy_ar, - match=TestSCons.match_re_dotall) - +test.run(arguments='.', stderr=TestSCons.noisy_ar, match=TestSCons.match_re_dotall) test.fail_test(not os.path.exists(test.workpath(lib_ + 'foo.xxx'))) -test.run(program = test.workpath('prog'), stdout = "foo.c\nprog.c\n") +test.run(program=test.workpath('prog'), stdout="foo.c\nprog.c\n") test.pass_test() diff --git a/test/Libs/Library.py b/test/Libs/Library.py index 50fe68bf7d..04ba37dff1 100644 --- a/test/Libs/Library.py +++ b/test/Libs/Library.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,7 @@ # 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__" import TestSCons diff --git a/test/MSVC/PCH-source.py b/test/MSVC/PCH-source.py index 75f6245c8c..84a39bd78b 100644 --- a/test/MSVC/PCH-source.py +++ b/test/MSVC/PCH-source.py @@ -27,7 +27,7 @@ Test use of pre-compiled headers when the source .cpp file shows up in both the env.PCH() and the env.Program() source list. -Issue 2505: http://github.com/SCons/scons/issues/2505 +Issue 2505: https://github.com/SCons/scons/issues/2505 """ import TestSCons diff --git a/test/MSVS/vs-10.0-exec.py b/test/MSVS/vs-10.0-exec.py index df13454a06..cf140a26eb 100644 --- a/test/MSVS/vs-10.0-exec.py +++ b/test/MSVS/vs-10.0-exec.py @@ -90,6 +90,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-10.0Exp-exec.py b/test/MSVS/vs-10.0Exp-exec.py index 1c3b56185a..87ddf29cea 100644 --- a/test/MSVS/vs-10.0Exp-exec.py +++ b/test/MSVS/vs-10.0Exp-exec.py @@ -91,6 +91,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-11.0-exec.py b/test/MSVS/vs-11.0-exec.py index 82868418a5..27dd79460e 100644 --- a/test/MSVS/vs-11.0-exec.py +++ b/test/MSVS/vs-11.0-exec.py @@ -91,6 +91,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-11.0Exp-exec.py b/test/MSVS/vs-11.0Exp-exec.py index e7cffada28..385a50dfcf 100644 --- a/test/MSVS/vs-11.0Exp-exec.py +++ b/test/MSVS/vs-11.0Exp-exec.py @@ -91,6 +91,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-14.0-exec.py b/test/MSVS/vs-14.0-exec.py index 7e919ba830..96e3389692 100644 --- a/test/MSVS/vs-14.0-exec.py +++ b/test/MSVS/vs-14.0-exec.py @@ -92,6 +92,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-14.0Exp-exec.py b/test/MSVS/vs-14.0Exp-exec.py index 5d2a5859cf..d27d4d5ed4 100644 --- a/test/MSVS/vs-14.0Exp-exec.py +++ b/test/MSVS/vs-14.0Exp-exec.py @@ -55,10 +55,14 @@ test.run(arguments = '-n -q -Q -f -', stdin = """\ env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s') -sconsEnv = repr(env['ENV']) -print("os.environ.update(" + sconsEnv + ")") +if env.WhereIs('cl'): + print("os.environ.update(%%s)" %% repr(env['ENV'])) """ % locals()) +if test.stdout() == "": + msg = "Visual Studio %s missing cl.exe; skipping test.\n" % msvs_version + test.skip_test(msg) + exec(test.stdout()) @@ -88,6 +92,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-14.1-exec.py b/test/MSVS/vs-14.1-exec.py index 2accaafab6..0a9250923f 100644 --- a/test/MSVS/vs-14.1-exec.py +++ b/test/MSVS/vs-14.1-exec.py @@ -92,6 +92,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-14.2-exec.py b/test/MSVS/vs-14.2-exec.py index 5a3079d075..a95982b85c 100644 --- a/test/MSVS/vs-14.2-exec.py +++ b/test/MSVS/vs-14.2-exec.py @@ -92,6 +92,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-14.3-exec.py b/test/MSVS/vs-14.3-exec.py index 840b4324d5..ef9dc33de5 100644 --- a/test/MSVS/vs-14.3-exec.py +++ b/test/MSVS/vs-14.3-exec.py @@ -92,6 +92,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-6.0-exec.py b/test/MSVS/vs-6.0-exec.py index ab708726fd..7cffb48373 100644 --- a/test/MSVS/vs-6.0-exec.py +++ b/test/MSVS/vs-6.0-exec.py @@ -90,9 +90,11 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.run(chdir='sub dir', program=[test.get_msvs_executable(msvs_version)], - arguments=['Test.dsp', '/MAKE', 'foo - Win32 Release']) + arguments=['foo.dsp', '/MAKE', 'foo - Win32 Release']) test.run(program=test.workpath('sub dir', 'foo'), stdout="foo.c\n") diff --git a/test/MSVS/vs-7.0-exec.py b/test/MSVS/vs-7.0-exec.py index 3c41aa56a3..7e81d4e70c 100644 --- a/test/MSVS/vs-7.0-exec.py +++ b/test/MSVS/vs-7.0-exec.py @@ -90,6 +90,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcproj')) test.run(chdir='sub dir', diff --git a/test/MSVS/vs-7.1-exec.py b/test/MSVS/vs-7.1-exec.py index f66b92d793..e7f4203f79 100644 --- a/test/MSVS/vs-7.1-exec.py +++ b/test/MSVS/vs-7.1-exec.py @@ -90,6 +90,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcproj')) test.run(chdir='sub dir', diff --git a/test/MSVS/vs-8.0-exec.py b/test/MSVS/vs-8.0-exec.py index 91e99dd5a0..3a3de05295 100644 --- a/test/MSVS/vs-8.0-exec.py +++ b/test/MSVS/vs-8.0-exec.py @@ -91,6 +91,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-8.0Exp-exec.py b/test/MSVS/vs-8.0Exp-exec.py index 77cae646e1..a3a83ada8b 100644 --- a/test/MSVS/vs-8.0Exp-exec.py +++ b/test/MSVS/vs-8.0Exp-exec.py @@ -91,6 +91,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-9.0-exec.py b/test/MSVS/vs-9.0-exec.py index 26a115df34..e112f7a20f 100644 --- a/test/MSVS/vs-9.0-exec.py +++ b/test/MSVS/vs-9.0-exec.py @@ -91,6 +91,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcproj')) import SCons.Platform.win32 diff --git a/test/MSVS/vs-9.0Exp-exec.py b/test/MSVS/vs-9.0Exp-exec.py index 0c274ba58b..cc95ba9715 100644 --- a/test/MSVS/vs-9.0Exp-exec.py +++ b/test/MSVS/vs-9.0Exp-exec.py @@ -91,6 +91,8 @@ test.run(chdir='sub dir', arguments='.') +test.unlink_files('sub dir', ['foo.exe', 'foo.obj', '.sconsign.dblite']) + test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcproj')) import SCons.Platform.win32 diff --git a/test/SWIG/SWIG.py b/test/SWIG/SWIG.py index 625b5f1be8..5858952241 100644 --- a/test/SWIG/SWIG.py +++ b/test/SWIG/SWIG.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 the swig tool generates file names that we expect. @@ -55,7 +54,7 @@ print("") print("Configured options: +pcre") print("") - print("Please see http://www.swig.org for reporting bugs " + print("Please see https://www.swig.org for reporting bugs " "and further information") sys.exit(0) diff --git a/test/TEX/generated_files.py b/test/TEX/generated_files.py index 418d99b7cd..f5d0aaa365 100644 --- a/test/TEX/generated_files.py +++ b/test/TEX/generated_files.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__" r""" Test creation of a Tex document with generated tex files @@ -87,7 +86,7 @@ month = {March}, publisher = {Morgan Kaufmann}, title = {The Art of Multiprocessor Programming}, - url = {http://www.worldcat.org/isbn/0123705916}, + url = {https://www.worldcat.org/isbn/0123705916}, year = {2008} } @@ -106,7 +105,7 @@ keywords = {books, model\_checking}, publisher = {The MIT Press}, title = {Principles of Model Checking}, - url = {http://www.worldcat.org/isbn/026202649X}, + url = {https://www.worldcat.org/isbn/026202649X}, year = {2008} } diff --git a/test/TEX/variant_dir.py b/test/TEX/variant_dir.py index 4280f5986e..1db3768937 100644 --- a/test/TEX/variant_dir.py +++ b/test/TEX/variant_dir.py @@ -160,7 +160,7 @@ test.write(['docs', 'test.bib'], """\ %% This BibTeX bibliography file was created using BibDesk. -%% http://bibdesk.sourceforge.net/ +%% https://bibdesk.sourceforge.net/ %% Created for Rob Managan at 2006-11-15 12:53:16 -0800 diff --git a/test/TEX/variant_dir_bibunit.py b/test/TEX/variant_dir_bibunit.py index 2a669ba524..0d286300ac 100644 --- a/test/TEX/variant_dir_bibunit.py +++ b/test/TEX/variant_dir_bibunit.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__" r""" Test creation of a fully-featured TeX document (with bibunit @@ -151,7 +150,7 @@ test.write(['src', 'units.bib'], """\ %% This BibTeX bibliography file was created using BibDesk. -%% http://bibdesk.sourceforge.net/ +%% https://bibdesk.sourceforge.net/ @techreport{gnu:1998, Author = {A. N. Author}, diff --git a/test/TEX/variant_dir_dup0.py b/test/TEX/variant_dir_dup0.py index ea6b51e098..cc2e0f635b 100644 --- a/test/TEX/variant_dir_dup0.py +++ b/test/TEX/variant_dir_dup0.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__" r""" Test creation of a fully-featured TeX document (with bibliography @@ -160,7 +159,7 @@ test.write(['docs', 'test.bib'], """\ %% This BibTeX bibliography file was created using BibDesk. -%% http://bibdesk.sourceforge.net/ +%% https://bibdesk.sourceforge.net/ %% Saved with string encoding Western (ASCII) diff --git a/test/TEX/variant_dir_style_dup0.py b/test/TEX/variant_dir_style_dup0.py index 430b7924bf..c12e7bb631 100644 --- a/test/TEX/variant_dir_style_dup0.py +++ b/test/TEX/variant_dir_style_dup0.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__" r""" Test creation of a fully-featured TeX document (with bibliography @@ -87,7 +86,7 @@ test.write(['docs', 'test.bib'], r""" % This BibTeX bibliography file was created using BibDesk. -% http://bibdesk.sourceforge.net/ +% https://bibdesk.sourceforge.net/ @techreport{AnAuthor:2006fk, Author = {A. N. Author}, diff --git a/test/option/debug-sconscript.py b/test/option/debug-sconscript.py new file mode 100644 index 0000000000..337a2c4e72 --- /dev/null +++ b/test/option/debug-sconscript.py @@ -0,0 +1,72 @@ +#!/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. + +""" +Test the --debug=sconscript option +""" + +import os +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +print("SConstruct") +""") + +wpath = test.workpath() + +test.run(arguments=".") +unexpect = [ + 'scons: Entering %s%sSConstruct' % (wpath, os.sep), + 'scons: Exiting %s%sSConstruct' % (wpath, os.sep) +] +test.must_not_contain_any_line(test.stdout(), unexpect) + +test.run(arguments="--debug=sconscript .") +expect = [ + 'scons: Entering %s%sSConstruct' % (wpath, os.sep), + 'scons: Exiting %s%sSConstruct' % (wpath, os.sep) +] +test.must_contain_all_lines(test.stdout(), expect) + +# Ensure that reutrns with stop are handled properly + +test.write('SConstruct', """\ +foo = "bar" +Return("foo", stop=True) +print("SConstruct") +""") + +test.run(arguments="--debug=sconscript .") +test.must_contain_all_lines(test.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/packaging/ipkg.py b/test/packaging/ipkg.py index 99c31e534b..2c7ebcf825 100644 --- a/test/packaging/ipkg.py +++ b/test/packaging/ipkg.py @@ -65,7 +65,7 @@ When you modify this example, be sure to change the Package, Version, Maintainer, Depends, and Description fields.''', - SOURCE_URL = 'http://gnu.org/foo-0.0.tar.gz', + SOURCE_URL = 'https://gnu.org/foo-0.0.tar.gz', X_IPK_SECTION = 'extras', X_IPK_PRIORITY = 'optional', ARCHITECTURE = 'arm', @@ -102,7 +102,7 @@ Package: foo Priority: optional Section: extras -Source: http://gnu.org/foo-0.0.tar.gz +Source: https://gnu.org/foo-0.0.tar.gz Version: 0.0 Architecture: arm Maintainer: Familiar User diff --git a/test/packaging/option--package-type.py b/test/packaging/option--package-type.py index 715dc96b8e..089c05d570 100644 --- a/test/packaging/option--package-type.py +++ b/test/packaging/option--package-type.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__" """ Test the --package-type option. @@ -65,7 +64,7 @@ X_RPM_INSTALL = r'%(_python_)s %(scons)s --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', DESCRIPTION = 'this should be really long', source = [ prog ], - SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz', + SOURCE_URL = 'https://foo.org/foo-1.2.3.tar.gz', ARCHITECTURE = 'noarch' ) """ % locals()) diff --git a/test/packaging/rpm/cleanup.py b/test/packaging/rpm/cleanup.py index 7483750b95..178a13d8da 100644 --- a/test/packaging/rpm/cleanup.py +++ b/test/packaging/rpm/cleanup.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__" """ Assert that files created by the RPM packager will be removed by 'scons -c'. @@ -71,7 +70,7 @@ X_RPM_INSTALL = r'%(_python_)s %(scons)s --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', DESCRIPTION = 'this should be really really long', source = [ prog ], - SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + SOURCE_URL = 'https://foo.org/foo-1.2.3.tar.gz' ) env.Alias( 'install', prog ) diff --git a/test/packaging/rpm/explicit-target.py b/test/packaging/rpm/explicit-target.py index 66f5e4a289..95684ca55c 100644 --- a/test/packaging/rpm/explicit-target.py +++ b/test/packaging/rpm/explicit-target.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__" """ Test the ability to create a rpm package from a explicit target name. @@ -72,7 +71,7 @@ DESCRIPTION = 'this should be really really long', source = [ prog ], target = "my_rpm_package.rpm", - SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + SOURCE_URL = 'https://foo.org/foo-1.2.3.tar.gz' ) """ % locals()) diff --git a/test/packaging/rpm/internationalization.py b/test/packaging/rpm/internationalization.py index 612360708d..28f0b450be 100644 --- a/test/packaging/rpm/internationalization.py +++ b/test/packaging/rpm/internationalization.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__" """ Test the ability to handle internationalized package and file meta-data. @@ -78,7 +77,7 @@ DESCRIPTION_de = 'das sollte wirklich lang sein', DESCRIPTION_fr = 'ceci devrait être vraiment long', source = [ prog ], - SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + SOURCE_URL = 'https://foo.org/foo-1.2.3.tar.gz' ) env.Alias ( 'install', prog ) @@ -156,7 +155,7 @@ DESCRIPTION_de = 'das sollte wirklich lang sein', DESCRIPTION_fr = 'ceci devrait être vraiment long', source = [ prog, man_pages ], - SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz', + SOURCE_URL = 'https://foo.org/foo-1.2.3.tar.gz', ) env.Alias ( 'install', [ prog, man_pages ] ) diff --git a/test/packaging/rpm/multipackage.py b/test/packaging/rpm/multipackage.py index 4a29a4087d..99a94f2c0f 100644 --- a/test/packaging/rpm/multipackage.py +++ b/test/packaging/rpm/multipackage.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__" """ Test the ability to create more than rpm file with different package root @@ -70,7 +69,7 @@ X_RPM_INSTALL = r'%(_python_)s %(scons)s --tree=all --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', DESCRIPTION = 'this should be really really long', source = [ prog ], - SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + SOURCE_URL = 'https://foo.org/foo-1.2.3.tar.gz' ) env.Package( NAME = 'foo2', diff --git a/test/packaging/rpm/package.py b/test/packaging/rpm/package.py index 0ef5fad07d..04a8ff0aa9 100644 --- a/test/packaging/rpm/package.py +++ b/test/packaging/rpm/package.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__" """ Test the ability to create a really simple rpm package. @@ -69,7 +68,7 @@ X_RPM_INSTALL = r'%(_python_)s %(scons)s --tree=all --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', DESCRIPTION = 'this should be really really long', source = [ prog ], - SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + SOURCE_URL = 'https://foo.org/foo-1.2.3.tar.gz' ) env.Alias( 'install', prog ) diff --git a/test/packaging/rpm/tagging.py b/test/packaging/rpm/tagging.py index f02a51b053..a888a37cca 100644 --- a/test/packaging/rpm/tagging.py +++ b/test/packaging/rpm/tagging.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__" """ Test the ability to add file tags @@ -75,7 +74,7 @@ X_RPM_INSTALL = r'%(_python_)s %(scons)s --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"', DESCRIPTION = 'this should be really really long', source = [ prog_install ], - SOURCE_URL = 'http://foo.org/foo-1.2.3.tar.gz' + SOURCE_URL = 'https://foo.org/foo-1.2.3.tar.gz' ) """ % locals()) diff --git a/test/rebuild-generated.py b/test/rebuild-generated.py index 91b4e1e7c3..1b4292f878 100644 --- a/test/rebuild-generated.py +++ b/test/rebuild-generated.py @@ -26,7 +26,8 @@ """ Test case for the bug report: "[ 1088979 ] Unnecessary rebuild with generated header file" -(). +(). +(historical - dead link) Unnecessary rebuild with generated header file Scons rebuilds some nodes when invoked twice. The diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 48238a023a..98094c875e 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -802,7 +802,7 @@ def where_is(file, path=None, pathext=None): # From Josiah Carlson, # ASPN : Python Cookbook : Module to allow Asynchronous subprocess use on Windows and Posix platforms -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554 +# https://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554 if sys.platform == 'win32': # and subprocess.mswindows: try: @@ -1872,6 +1872,31 @@ def unlink(self, file) -> None: file = self.canonicalize(file) os.unlink(file) + def unlink_files(self, dirpath, files): + """Unlinks a list of file names from the specified directory. + + The directory path may be a list, in which case the elements are + concatenated with the os.path.join() method. + + A file name may be a list, in which case the elements are + concatenated with the os.path.join() method. + + The directory path and file name are concatenated with the + os.path.join() method. The resulting file path is assumed to be + under the temporary working directory unless it is an absolute path + name. An attempt to unlink the resulting file is made only when the + file exists otherwise the file path is ignored. + """ + if is_List(dirpath): + dirpath = os.path.join(*dirpath) + for file in files: + if is_List(file): + file = os.path.join(*file) + filepath = os.path.join(dirpath, file) + filepath = self.canonicalize(filepath) + if os.path.exists(filepath): + self.unlink(filepath) + def verbose_set(self, verbose) -> None: """Sets the verbose level.""" self.verbose = verbose diff --git a/testing/framework/TestCmdTests.py b/testing/framework/TestCmdTests.py index dc752ba4e8..1331f9db4d 100644 --- a/testing/framework/TestCmdTests.py +++ b/testing/framework/TestCmdTests.py @@ -1601,10 +1601,10 @@ def test_rmdir(self): try: test.rmdir(['no', 'such', 'dir']) - except EnvironmentError: + except FileNotFoundError: pass else: - raise Exception("did not catch expected SConsEnvironmentError") + raise Exception("did not catch expected FileNotFoundError") test.subdir(['sub'], ['sub', 'dir'], @@ -1616,19 +1616,19 @@ def test_rmdir(self): try: test.rmdir(['sub']) - except EnvironmentError: + except OSError: pass else: - raise Exception("did not catch expected SConsEnvironmentError") + raise Exception("did not catch expected OSError") assert os.path.isdir(s_d_o), f"{s_d_o} is gone?" try: test.rmdir(['sub']) - except EnvironmentError: + except OSError: pass else: - raise Exception("did not catch expected SConsEnvironmentError") + raise Exception("did not catch expected OSError") assert os.path.isdir(s_d_o), f"{s_d_o} is gone?" @@ -1647,7 +1647,6 @@ def test_rmdir(self): assert not os.path.exists(s), f"{s} exists?" - class run_TestCase(TestCmdTestCase): def test_run(self) -> None: """Test run()""" @@ -2994,6 +2993,103 @@ def test_unlink(self): os.chmod(wdir_file5, 0o600) +class unlink_files_TestCase(TestCmdTestCase): + def test_unlink_files(self): + """Test unlink_files()""" + test = TestCmd.TestCmd(workdir = '', subdir = 'foo') + wdir_file1 = os.path.join(test.workdir, 'file1') + wdir_file2 = os.path.join(test.workdir, 'file2') + wdir_foo_file3a = os.path.join(test.workdir, 'foo', 'file3a') + wdir_foo_file3b = os.path.join(test.workdir, 'foo', 'file3b') + wdir_foo_file3c = os.path.join(test.workdir, 'foo', 'file3c') + wdir_foo_file3d = os.path.join(test.workdir, 'foo', 'file3d') + wdir_foo_file4a = os.path.join(test.workdir, 'foo', 'file4a') + wdir_foo_file4b = os.path.join(test.workdir, 'foo', 'file4b') + wdir_foo_file4c = os.path.join(test.workdir, 'foo', 'file4c') + wdir_foo_file4d = os.path.join(test.workdir, 'foo', 'file4d') + wdir_file5 = os.path.join(test.workdir, 'file5') + + with open(wdir_file1, 'w') as f: + f.write("") + with open(wdir_file2, 'w') as f: + f.write("") + with open(wdir_foo_file3a, 'w') as f: + f.write("") + with open(wdir_foo_file3b, 'w') as f: + f.write("") + with open(wdir_foo_file3c, 'w') as f: + f.write("") + with open(wdir_foo_file3d, 'w') as f: + f.write("") + with open(wdir_foo_file4a, 'w') as f: + f.write("") + with open(wdir_foo_file4b, 'w') as f: + f.write("") + with open(wdir_foo_file4c, 'w') as f: + f.write("") + with open(wdir_foo_file4d, 'w') as f: + f.write("") + with open(wdir_file5, 'w') as f: + f.write("") + + test.unlink_files('', [ + 'no_file_a', + 'no_file_b', + ]) + + test.unlink_files('', [ + 'file1', + 'file2', + ]) + assert not os.path.exists(wdir_file1) + assert not os.path.exists(wdir_file2) + + test.unlink_files('foo', [ + 'file3a', + 'file3b', + ]) + assert not os.path.exists(wdir_foo_file3a) + assert not os.path.exists(wdir_foo_file3b) + + test.unlink_files(['foo'], [ + 'file3c', + 'file3d', + ]) + assert not os.path.exists(wdir_foo_file3c) + assert not os.path.exists(wdir_foo_file3d) + + test.unlink_files('', [ + ['foo', 'file4a'], + ['foo', 'file4b'], + ]) + assert not os.path.exists(wdir_foo_file4a) + assert not os.path.exists(wdir_foo_file4b) + + test.unlink_files([''], [ + ['foo', 'file4c'], + ['foo', 'file4d'], + ]) + assert not os.path.exists(wdir_foo_file4c) + assert not os.path.exists(wdir_foo_file4d) + + # Make it so we can't unlink file5. + # For UNIX, remove write permission from the dir and the file. + # For Windows, open the file. + os.chmod(test.workdir, 0o500) + os.chmod(wdir_file5, 0o400) + with open(wdir_file5, 'r'): + try: + try: + test.unlink_files('', ['file5']) + except OSError: # expect "Permission denied" + pass + except: + raise + finally: + os.chmod(test.workdir, 0o700) + os.chmod(wdir_file5, 0o600) + + class touch_TestCase(TestCmdTestCase): def test_touch(self) -> None: """Test touch()""" diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 39a4245dbc..b6e34905cc 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -55,7 +55,7 @@ # here provides some independent verification that what we packaged # conforms to what we expect. -default_version = '4.5.3ayyyymmdd' +default_version = '4.7.0ayyyymmdd' # TODO: these need to be hand-edited when there are changes python_version_unsupported = (3, 6, 0) diff --git a/testing/framework/TestUnit/taprunner.py b/testing/framework/TestUnit/taprunner.py index 6f8cb00510..b52c762a82 100644 --- a/testing/framework/TestUnit/taprunner.py +++ b/testing/framework/TestUnit/taprunner.py @@ -1,6 +1,6 @@ """ Format unittest results in Test Anything Protocol (TAP). -http://testanything.org/tap-version-13-specification.html +https://testanything.org/tap-version-13-specification.html Public domain work by: anatoly techtonik From caa65390a5e16603386578eddbe2cf377d61ef8f Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 7 Jan 2024 09:57:59 -0500 Subject: [PATCH 014/386] Add additional registry keys for 12.0Exp and 11.0Exp detection (VS roots). --- SCons/Tool/MSCommon/vc.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 5b94747e56..d28b6af3c4 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -788,21 +788,25 @@ def _skip_sendtelemetry(env): # VS2015 and earlier: configure registry queries to probe for installed VC editions _VCVER_TO_PRODUCT_DIR = { '14.0': [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir') + ], '14.0Exp': [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\WDExpress\14.0\Setup\VS\ProductDir'), # vs root - (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir'), # not populated? - (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], # kind detection + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\WDExpress\14.0\Setup\VS\ProductDir'), + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir'), + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir') + ], '12.0': [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\12.0\Setup\VC\ProductDir'), ], '12.0Exp': [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\WDExpress\12.0\Setup\VS\ProductDir'), (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\12.0\Setup\VC\ProductDir'), ], '11.0': [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'), ], '11.0Exp': [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\WDExpress\11.0\Setup\VS\ProductDir'), (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\11.0\Setup\VC\ProductDir'), ], '10.0': [ @@ -1415,8 +1419,8 @@ def find_vc_pdir_registry(msvc_version): # Visual C++ for Python registry key is VS installdir (root) not VC productdir is_vsroot = True is_vcforpython = True - elif msvc_version == '14.0Exp' and key.lower().endswith('\\14.0\\setup\\vs\\productdir'): - # 2015Exp VS productdir (root) not VC productdir + elif msvc_version in ('14.0Exp', '12.0Exp', '11.0Exp') and key.lower().endswith('\\setup\\vs\\productdir'): + # Visual Studio 2015/2013/2012 Express is VS productdir (root) not VC productdir is_vsroot = True if is_vsroot: From bc1ee034f818b7f454c6c78bb3b7842e08ac361b Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 7 Jan 2024 10:09:21 -0500 Subject: [PATCH 015/386] Remove unused cache dictionary. --- SCons/Tool/MSCommon/MSVC/Kind.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Kind.py b/SCons/Tool/MSCommon/MSVC/Kind.py index 8b254a23b4..d91e6a29fb 100644 --- a/SCons/Tool/MSCommon/MSVC/Kind.py +++ b/SCons/Tool/MSCommon/MSVC/Kind.py @@ -656,7 +656,6 @@ def msvc_version_uwp_is_supported(msvc_version, target_arch=None, env=None): # reset cache def reset() -> None: - global _cache_installed_vcs global _cache_vcver_kind_map global _cache_pdir_vswhere_kind global _cache_pdir_registry_kind @@ -664,7 +663,6 @@ def reset() -> None: debug('') - _cache_installed_vcs = None _cache_vcver_kind_map = {} _cache_pdir_vswhere_kind = {} _cache_pdir_registry_kind = {} From 5038ac5e11edc5704b647de7a06af1200012f3e7 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 7 Jan 2024 10:22:50 -0500 Subject: [PATCH 016/386] Add additional vswhere locations and remove vswhere command-line stub. Add vswhere roots for: * winget * scoop --- SCons/Tool/MSCommon/vc.py | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index d28b6af3c4..51c83e80d5 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -56,8 +56,6 @@ import SCons.Warnings from SCons.Tool import find_program_path -# import SCons.Script - from . import common from .common import CONFIG_CACHE, debug from .sdk import get_installed_sdks @@ -920,6 +918,9 @@ def msvc_version_to_maj_min(msvc_version): os.path.expandvars(r"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer"), os.path.expandvars(r"%ProgramFiles%\Microsoft Visual Studio\Installer"), os.path.expandvars(r"%ChocolateyInstall%\bin"), + os.path.expandvars(r"%LOCALAPPDATA%\Microsoft\WinGet\Links"), + os.path.expanduser(r"~\scoop\shims"), + os.path.expandvars(r"%SCOOP%\shims"), ]] # normalize user-specified vswhere paths @@ -964,27 +965,6 @@ def _vswhere_user_path(pval): return vswhere_path -# normalized user-specified command-line vswhere path - -# TODO: stub for command-line specification of vswhere -_vswhere_path_cmdline = None - -def _msvc_cmdline_vswhere(): - global _vswhere_path_cmdline - - if _vswhere_path_cmdline == UNDEFINED: - - vswhere_path = None - vswhere_user = None - - if vswhere_user: - vswhere_path = _vswhere_user_path(vswhere_user) - - _vswhere_path_cmdline = vswhere_path - debug('vswhere_path=%s', vswhere_path) - - return _vswhere_path_cmdline - # normalized default vswhere path _vswhere_paths_processed = [ @@ -1018,10 +998,6 @@ def msvc_find_vswhere(): # For bug 3542: also accommodate not being on C: drive. # NB: this gets called from testsuite on non-Windows platforms. # Whether that makes sense or not, don't break it for those. - vswhere_path = _msvc_cmdline_vswhere() - if vswhere_path: - return - vswhere_path = None for pf in VSWHERE_PATHS: if os.path.exists(pf): @@ -1078,10 +1054,6 @@ def _filter_vswhere_paths(env): if vswhere_environ and vswhere_environ not in _VSWhere.seen_vswhere: vswhere_paths.append(vswhere_environ) - vswhere_cmdline = _msvc_cmdline_vswhere() - if vswhere_cmdline and vswhere_cmdline not in _VSWhere.seen_vswhere: - vswhere_paths.append(vswhere_cmdline) - vswhere_default = _msvc_default_vswhere() if vswhere_default and vswhere_default not in _VSWhere.seen_vswhere: vswhere_paths.append(vswhere_default) From 7859a407b5c4f6e6ff89b7da63cd258b0bec1930 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 7 Jan 2024 10:46:26 -0500 Subject: [PATCH 017/386] Replace removed process_path function call with normalize_path. --- SCons/Tool/MSCommon/vc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 51c83e80d5..c464715413 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -946,7 +946,7 @@ def _vswhere_user_path(pval): else: - # vswhere_norm = MSVC.Util.process_path(pval) + # TODO: vswhere_norm = MSVC.Util.normalize_path(pval) vswhere_norm = os.path.normcase(os.path.normpath(pval)) tail = os.path.split(vswhere_norm)[-1] @@ -968,7 +968,7 @@ def _vswhere_user_path(pval): # normalized default vswhere path _vswhere_paths_processed = [ - MSVC.Util.process_path(pval) + MSVC.Util.normalize_path(pval) for pval in VSWHERE_PATHS if os.path.exists(pval) ] @@ -1156,7 +1156,7 @@ def _update_vswhere_msvc_map(env): if not os.path.exists(vc_path): continue - vc_root = MSVC.Util.process_path(vc_path) + vc_root = MSVC.Util.normalize_path(vc_path) if vc_root in _VSWhere.seen_root: continue _VSWhere.seen_root.add(vc_root) From 981d74aa31021e46aa752fd0a66f47a9e203e983 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 29 Feb 2024 07:41:08 -0500 Subject: [PATCH 018/386] Test suite update and bug fixes. Changes: * Bug fix for not found toolset version in vcTests.py * Re-work sdk list tests in vcTests.py when run on machine that may not have sdks installed (no msvc or older msvc). Potential future problem lurking as the gap between supported sdk versions grows with future releases. * Update the tolerance for precompiled header "fast enough" in msvc.py. This test, more than others, has a tendency to fail when run in a virtual machine on Windows. * Explicity run "foo.exe" instead of "foo" in vs-14.3-exec.py. A recent change in VS2022 appears to create a "foo" folder which causes the test to fail. --- SCons/Tool/MSCommon/vcTests.py | 25 ++++++++++++++++++++----- test/MSVC/msvc.py | 2 +- test/MSVS/vs-14.3-exec.py | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index 8c22ba37e9..da360b3717 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -215,7 +215,14 @@ def runTest(self) -> None: class Data: - HAVE_MSVC = True if MSCommon.vc.msvc_default_version() else False + DEFAULT_VERSION = MSCommon.vc.msvc_default_version() + + if DEFAULT_VERSION: + HAVE_MSVC = True + DEFAULT_VERSION_DEF = MSCommon.msvc_version_components(DEFAULT_VERSION) + else: + HAVE_MSVC = False + DEFAULT_VERSION_DEF = None INSTALLED_VCS_COMPONENTS = MSCommon.vc.get_installed_vcs_components() @@ -230,11 +237,11 @@ def _msvc_toolset_notfound_list(cls, toolset_seen, toolset_list): if len(comps) != 3: continue # full versions only + ival = int(comps[-1]) nloop = 0 while nloop < 10: - ival = int(comps[-1]) if ival == 0: - ival = 1000000 + ival = 100000 ival -= 1 version = '{}.{}.{:05d}'.format(comps[0], comps[1], ival) if version not in toolset_seen: @@ -310,10 +317,18 @@ def test_valid_vcver(self) -> None: version_def = MSCommon.msvc_version_components(symbol) for msvc_uwp_app in (True, False): sdk_list = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) - if Data.HAVE_MSVC and version_def.msvc_vernum >= 14.0: + if version_def.msvc_vernum < 14.0: + # version < VS2015 + # does not accept sdk version argument + self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol))) + elif Data.HAVE_MSVC and Data.DEFAULT_VERSION_DEF.msvc_vernum >= 14.0: + # version >= VS2015 and default_version >= VS2015 + # no guarantee test is valid: compatible sdks may not be installed as version gap grows self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(symbol))) else: - self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol))) + # version >= VS2015 and default_version < VS2015 + # skip test: version is not installed; compatible windows sdks may not be installed + pass def test_valid_vcver_toolsets(self) -> None: for symbol in MSCommon.vc._VCVER: diff --git a/test/MSVC/msvc.py b/test/MSVC/msvc.py index 6bc36759a2..8d018f82bf 100644 --- a/test/MSVC/msvc.py +++ b/test/MSVC/msvc.py @@ -116,7 +116,7 @@ # TODO: Reevaluate if having this part of the test makes sense any longer # using precompiled headers should be faster -limit = slow*0.90 +limit = slow if fast >= limit: print("Using precompiled headers was not fast enough:") print("slow.obj: %.3fs" % slow) diff --git a/test/MSVS/vs-14.3-exec.py b/test/MSVS/vs-14.3-exec.py index ef9dc33de5..dc4dc7e0e7 100644 --- a/test/MSVS/vs-14.3-exec.py +++ b/test/MSVS/vs-14.3-exec.py @@ -104,7 +104,7 @@ program=[test.get_msvs_executable(msvs_version)], arguments=['foo.sln', '/build', 'Release']) -test.run(program=test.workpath('sub dir', 'foo'), stdout="foo.c\n") +test.run(program=test.workpath('sub dir', 'foo.exe'), stdout="foo.c\n") test.validate_msvs_file(test.workpath('sub dir', 'foo.vcxproj.user')) From ea5baf8bae87b0d7900ea717a4ac5bdd477359f9 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 3 Mar 2024 07:52:23 -0500 Subject: [PATCH 019/386] Clear VisualStudio internal cache and installed visual studios data structures when vswhere finds new msvc roots. --- CHANGES.txt | 17 +++++++++-------- RELEASE.txt | 12 +++++++----- SCons/Tool/MSCommon/vc.py | 25 +++++++++++++++++-------- SCons/Tool/MSCommon/vs.py | 38 +++++++++++++++++++++++++++++++++++--- 4 files changed, 68 insertions(+), 24 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f11e002e87..a55f9ed323 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -80,13 +80,14 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER of the results from all vswhere executables). There is a single invocation for each vswhere executable that processes the output for all installations. Prior implementations called the vswhere executable for each supported msvc version. - - Previously, the installed vcs list was constructed once and cached at runtime. If - a vswhere executable was specified via the construction variable VSWHERE and found - additional msvc installations, the new installations were not reflected in the - installed vcs list. During runtime, if a user-specified vswhere executable finds - new msvc installations, internal runtime caches are cleared and the installed vcs - list is reconstructed. - + - Previously, the installed vcs and visual studios lists were constructed once and + cached at runtime. If a vswhere executable was specified via the construction + variable VSWHERE and found additional msvc installations, the new installations + were not reflected in the installed vcs and visual studios lists. Now, when a + user-specified vswhere executable finds new msvc installations, internal runtime + caches are cleared and the installed vcs and visual studios lists are reconstructed + on their next retrieval. + From Thaddeus Crews: - Implemented SCons.Util.sctyping as a safe means of hinting complex types. Currently only implemented for `Executor` as a proof-of-concept. @@ -1404,7 +1405,7 @@ RELEASE 3.1.2 - Mon, 17 Dec 2019 02:06:27 +0000 in the case where a child process is spawned while a Python action has a file open. Original author: Ryan Beasley. - Added memoization support for calls to Environment.Value() in order to - improve performance of repeated calls. + improve performance of repeated calls. From Jason Kenny diff --git a/RELEASE.txt b/RELEASE.txt index 99aa0fa1df..7a8706d1e9 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -70,11 +70,13 @@ FIXES msiexec /i VCForPython27.msi ALLUSERS=1 When installed for all users, Visual Studio 2008 (9.0) Visual C++ For Python is now correctly detected. -- MSVC: The installed vcs list was constructed and cached during the initial - invocation. If a vswhere executable was specified via the construction variable - VSWHERE and found additional msvc installations, the new installations were not - reflected in the installed vcs list. Now, when a user-specified vswhere - executable finds new msvc installations, the installed vcs list is reconstructed. +- MSVC: The installed vcs and visual studios lists were constructed and cached + during their initial invocations. If a vswhere executable was specified via the + construction variable VSWHERE and found additional msvc installations, the new + installations were not reflected in the cached installed vcs and visual studios + lists. Now, when a user-specified vswhere executable finds new msvc installations, + the installed vcs and visual studios lists are cleared and reconstructed on their + next retrieval. - On Windows platform, when collecting command output (Configure checks), make sure decoding of bytes doesn't fail. - Documentation indicated that both Pseudo() and env.Pseudo() were usable, diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index c464715413..7857a7a41f 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1008,7 +1008,8 @@ def msvc_find_vswhere(): class _VSWhere: - reset_funcs = [] + reset_funcs_list = [] + reset_funcs_seen = set() @classmethod def reset(cls): @@ -1036,15 +1037,20 @@ def msvc_instances_default_order(a, b): @classmethod def register_reset_func(cls, func): - cls.reset_funcs.append(func) + if func and func not in cls.reset_funcs_seen: + cls.reset_funcs_seen.add(func) + cls.reset_funcs_list.append(func) @classmethod def call_reset_funcs(cls): - for func in cls.reset_funcs: + for func in cls.reset_funcs_list: func() _VSWhere.reset() +def vswhere_register_reset_func(func): + _VSWhere.register_reset_func(func) + def _filter_vswhere_paths(env): vswhere_paths = [] @@ -1117,7 +1123,7 @@ def _vswhere_query_json_output(vswhere_exe, vswhere_args): 'vc_component_suffix', ]) -def _update_vswhere_msvc_map(env): +def _vswhere_update_msvc_map(env): vswhere_paths = _filter_vswhere_paths(env) if not vswhere_paths: @@ -1241,6 +1247,9 @@ def _update_vswhere_msvc_map(env): return _VSWhere.msvc_map +def vswhere_update_msvc(env): + _vswhere_update_msvc_map(env) + _cache_pdir_vswhere_queries = {} def _reset_pdir_vswhere_queries(): @@ -1267,7 +1276,7 @@ def find_vc_pdir_vswhere(msvc_version, env=None): """ global _cache_pdir_vswhere_queries - msvc_map = _update_vswhere_msvc_map(env) + msvc_map = _vswhere_update_msvc_map(env) if not msvc_map: return None @@ -1751,9 +1760,9 @@ def _check_files_exist_in_vc_dir(env, vc_dir, msvc_version) -> bool: def get_installed_vcs(env=None): global __INSTALLED_VCS_RUN - # the installed vcs cache is cleared - # if new vc roots are discovered - _update_vswhere_msvc_map(env) + debug('') + # the installed vcs cache is cleared if new vc roots are discovered + _vswhere_update_msvc_map(env) if __INSTALLED_VCS_RUN is not None: return __INSTALLED_VCS_RUN diff --git a/SCons/Tool/MSCommon/vs.py b/SCons/Tool/MSCommon/vs.py index ef4f13cdfb..afe2f743ef 100644 --- a/SCons/Tool/MSCommon/vs.py +++ b/SCons/Tool/MSCommon/vs.py @@ -28,7 +28,6 @@ import os import SCons.Errors -import SCons.Tool.MSCommon.vc import SCons.Util from .common import ( @@ -40,6 +39,14 @@ read_reg, ) +from .vc import ( + find_vc_pdir, + get_msvc_version_numeric, + reset_installed_vcs, + vswhere_register_reset_func, + vswhere_update_msvc, +) + class VisualStudio: """ @@ -48,6 +55,7 @@ class VisualStudio: """ def __init__(self, version, **kw) -> None: self.version = version + self.vernum = float(get_msvc_version_numeric(version)) kw['vc_version'] = kw.get('vc_version', version) kw['sdk_version'] = kw.get('sdk_version', version) self.__dict__.update(kw) @@ -66,7 +74,7 @@ def find_batch_file(self): return batch_file def find_vs_dir_by_vc(self, env): - dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version, env) + dir = find_vc_pdir(self.vc_version, env) if not dir: debug('no installed VC %s', self.vc_version) return None @@ -411,8 +419,12 @@ def reset(self) -> None: ), ] +SupportedVSWhereList = [] SupportedVSMap = {} for vs in SupportedVSList: + if vs.vernum >= 14.1: + # VS2017 and later detected via vswhere.exe + SupportedVSWhereList.append(vs) SupportedVSMap[vs.version] = vs @@ -428,6 +440,12 @@ def reset(self) -> None: def get_installed_visual_studios(env=None): global InstalledVSList global InstalledVSMap + debug('') + # Calling vswhere_update_msvc may cause installed caches to be cleared. + # The installed vcs and visual studios are cleared when new vc roots + # are discovered which causes the InstalledVSList and InstalledVSMap + # variables to be set to None. + vswhere_update_msvc(env) if InstalledVSList is None: InstalledVSList = [] InstalledVSMap = {} @@ -442,6 +460,7 @@ def get_installed_visual_studios(env=None): def reset_installed_visual_studios() -> None: global InstalledVSList global InstalledVSMap + debug('') InstalledVSList = None InstalledVSMap = None for vs in SupportedVSList: @@ -449,7 +468,20 @@ def reset_installed_visual_studios() -> None: # Need to clear installed VC's as well as they are used in finding # installed VS's - SCons.Tool.MSCommon.vc.reset_installed_vcs() + reset_installed_vcs() + +def _reset_installed_vswhere_visual_studios(): + # vswhere found new roots: reset VS2017 and later + global InstalledVSList + global InstalledVSMap + debug('') + InstalledVSList = None + InstalledVSMap = None + for vs in SupportedVSWhereList: + vs.reset() + +# register vswhere callback function +vswhere_register_reset_func(_reset_installed_vswhere_visual_studios) # We may be asked to update multiple construction environments with From 1fab5957fc547c6ce6475e29613e892dd73afb6e Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 12 Mar 2024 07:45:07 -0400 Subject: [PATCH 020/386] Detection changes for VS IDE editions and VS2008 (develop and vcforpython). Changes: * An express installation of the IDE binary is used when no other IDE edition is detected. * A full development edition (e.g., Professional) of VS2008 is elected before a Visual C++ For Python edition. * Update detection order in README.rst and remove hard tabs. * Minor fixes lingering from original VSWHERE implementation where all call chains were not updated when the environment argument was added. --- CHANGES.txt | 7 ++- RELEASE.txt | 7 ++- SCons/Tool/MSCommon/README.rst | 72 +++++++++++++++--------------- SCons/Tool/MSCommon/sdk.py | 2 +- SCons/Tool/MSCommon/vc.py | 46 +++++++++---------- SCons/Tool/MSCommon/vs.py | 33 ++++++++++---- SCons/Tool/msvsTests.py | 4 +- test/MSVC/query_vcbat.py | 2 +- testing/framework/TestSConsMSVS.py | 6 +-- 9 files changed, 102 insertions(+), 77 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 359211bc34..6d79040e71 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -43,7 +43,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER method so that the generated function argument list matches the function's prototype when including a header file. - For msvc version specifications without an 'Exp' suffix, an express installation - is used when no other edition is detected for the msvc version. + is used when no other edition is detected for the msvc version. Similarly, an + express installation of the IDE binary is used when no other IDE edition is + detected. - VS2015 Express (14.0Exp) does not support the sdk version argument. VS2015 Express does not support the store argument for target architectures other than x86. Script argument validation now takes into account these restrictions. @@ -72,6 +74,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER (i.e., msiexec /i VCForPython27.msi ALLUSERS=1), the registry keys are written to HKEY_LOCAL_MACHINE rather than HKEY_CURRENT_USER. An entry was added to query the Visual C++ For Python keys in HKLM following the HKCU query, if necessary. + - For VS2008, a full development edition (e.g., Professional) is now selected before + a Visual C++ For Python edition. Prior to this change, Visual C++ For Python was + selected before a full development edition when both editions are installed. - The registry detection of VS2015 (14.0), and earlier, is now cached at runtime and is only evaluated once for each msvc version. - The vswhere detection of VS2017 (14.1), and later, is now cached at runtime and is diff --git a/RELEASE.txt b/RELEASE.txt index 7a8706d1e9..65114e6c33 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -38,7 +38,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY installation is used when no other edition is detected for the msvc version. This was the behavior for Visual Studio 2008 (9.0) through Visual Studio 2015 (14.0). This behavior was extended to Visual Studio 2017 (14.1) and Visual - Studio 2008 (8.0). + Studio 2008 (8.0). An express installation of the IDE binary is used when no + other IDE edition is detected. FIXES ----- @@ -70,6 +71,10 @@ FIXES msiexec /i VCForPython27.msi ALLUSERS=1 When installed for all users, Visual Studio 2008 (9.0) Visual C++ For Python is now correctly detected. +- MSVC: For Visual Studio 2008 (9.0), a full development edition (e.g., Professional) + is now selected before a Visual C++ For Python edition. Prior to this change, + Visual C++ For Python was selected before a full development edition when both + editions are installed. - MSVC: The installed vcs and visual studios lists were constructed and cached during their initial invocations. If a vswhere executable was specified via the construction variable VSWHERE and found additional msvc installations, the new diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index bc57ca43b0..4a7acc59b4 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -36,7 +36,7 @@ installation is used only when no other installation is detected. ======= ======= ======================================================== Product VCVer Priority ======= ======= ======================================================== -VS2022 14.3 Enterprise, Professional, Community, BuildTools +VS2022 14.3 Enterprise, Professional, Community, BuildTools ------- ------- -------------------------------------------------------- VS2019 14.2 Enterprise, Professional, Community, BuildTools ------- ------- -------------------------------------------------------- @@ -44,15 +44,15 @@ VS2017 14.1 Enterprise, Professional, Community, BuildTools, Express ------- ------- -------------------------------------------------------- VS2017 14.1Exp Express ------- ------- -------------------------------------------------------- -VS2015 14.0 [Develop, BuildTools, CmdLine], Express +VS2015 14.0 [Develop, BuildTools, CmdLine], Express ------- ------- -------------------------------------------------------- VS2015 14.0Exp Express ------- ------- -------------------------------------------------------- -VS2013 12.0 Develop, Express +VS2013 12.0 Develop, Express ------- ------- -------------------------------------------------------- VS2013 12.0Exp Express ------- ------- -------------------------------------------------------- -VS2012 11.0 Develop, Express +VS2012 11.0 Develop, Express ------- ------- -------------------------------------------------------- VS2012 11.0Exp Express ------- ------- -------------------------------------------------------- @@ -60,7 +60,7 @@ VS2010 10.0 Develop, Express ------- ------- -------------------------------------------------------- VS2010 10.0Exp Express ------- ------- -------------------------------------------------------- -VS2008 9.0 VCForPython, Develop, Express +VS2008 9.0 Develop, VCForPython, Express ------- ------- -------------------------------------------------------- VS2008 9.0Exp Express ------- ------- -------------------------------------------------------- @@ -105,20 +105,20 @@ and/or linker build failures. The VS2015 BuildTools ``vcvarsall.bat`` batch file dispatches to the stand-alone buildtools batch file under certain circumstances. A fragment from the vcvarsall batch file is: :: - if exist "%~dp0..\common7\IDE\devenv.exe" goto setup_VS - if exist "%~dp0..\common7\IDE\wdexpress.exe" goto setup_VS - if exist "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" goto setup_buildsku + if exist "%~dp0..\common7\IDE\devenv.exe" goto setup_VS + if exist "%~dp0..\common7\IDE\wdexpress.exe" goto setup_VS + if exist "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" goto setup_buildsku - :setup_VS + :setup_VS - ... + ... - :setup_buildsku - if not exist "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" goto usage - set CurrentDir=%CD% - call "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" %1 %2 - cd /d %CurrentDir% - goto :eof + :setup_buildsku + if not exist "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" goto usage + set CurrentDir=%CD% + call "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" %1 %2 + cd /d %CurrentDir% + goto :eof VS2015 Express -------------- @@ -136,19 +136,19 @@ architecture. The generated ``store`` library paths include directories that do The store library paths appear in two places in the ``vcvarsx86_amd64`` batch file: :: - :setstorelib - @if exist "%VCINSTALLDIR%LIB\amd64\store" set LIB=%VCINSTALLDIR%LIB\amd64\store;%LIB% - ... - :setstorelibpath - @if exist "%VCINSTALLDIR%LIB\amd64\store" set LIBPATH=%VCINSTALLDIR%LIB\amd64\store;%LIBPATH% + :setstorelib + @if exist "%VCINSTALLDIR%LIB\amd64\store" set LIB=%VCINSTALLDIR%LIB\amd64\store;%LIB% + ... + :setstorelibpath + @if exist "%VCINSTALLDIR%LIB\amd64\store" set LIBPATH=%VCINSTALLDIR%LIB\amd64\store;%LIBPATH% The correct store library paths would be: :: - :setstorelib - @if exist "%VCINSTALLDIR%LIB\store\amd64" set LIB=%VCINSTALLDIR%LIB\store\amd64;%LIB% - ... - :setstorelibpath - @if exist "%VCINSTALLDIR%LIB\store\amd64" set LIBPATH=%VCINSTALLDIR%LIB\store\amd64;%LIBPATH% + :setstorelib + @if exist "%VCINSTALLDIR%LIB\store\amd64" set LIB=%VCINSTALLDIR%LIB\store\amd64;%LIB% + ... + :setstorelibpath + @if exist "%VCINSTALLDIR%LIB\store\amd64" set LIBPATH=%VCINSTALLDIR%LIB\store\amd64;%LIBPATH% arm Target Architecture ^^^^^^^^^^^^^^^^^^^^^^^ @@ -158,19 +158,19 @@ architecture. The generated ``store`` library paths include directories that do The store library paths appear in two places in the ``vcvarsx86_arm`` batch file: :: - :setstorelib - @if exist "%VCINSTALLDIR%LIB\ARM\store" set LIB=%VCINSTALLDIR%LIB\ARM\store;%LIB% - ... - :setstorelibpath - @if exist "%VCINSTALLDIR%LIB\ARM\store" set LIBPATH=%VCINSTALLDIR%LIB\ARM\store;%LIBPATH% + :setstorelib + @if exist "%VCINSTALLDIR%LIB\ARM\store" set LIB=%VCINSTALLDIR%LIB\ARM\store;%LIB% + ... + :setstorelibpath + @if exist "%VCINSTALLDIR%LIB\ARM\store" set LIBPATH=%VCINSTALLDIR%LIB\ARM\store;%LIBPATH% The correct store library paths would be file: :: - :setstorelib - @if exist "%VCINSTALLDIR%LIB\store\ARM" set LIB=%VCINSTALLDIR%LIB\store\ARM;%LIB% - ... - :setstorelibpath - @if exist "%VCINSTALLDIR%LIB\store\ARM" set LIBPATH=%VCINSTALLDIR%LIB\store\ARM;%LIBPATH% + :setstorelib + @if exist "%VCINSTALLDIR%LIB\store\ARM" set LIB=%VCINSTALLDIR%LIB\store\ARM;%LIB% + ... + :setstorelibpath + @if exist "%VCINSTALLDIR%LIB\store\ARM" set LIBPATH=%VCINSTALLDIR%LIB\store\ARM;%LIBPATH% Known Issues diff --git a/SCons/Tool/MSCommon/sdk.py b/SCons/Tool/MSCommon/sdk.py index c6600f3a5b..a06dfacfc6 100644 --- a/SCons/Tool/MSCommon/sdk.py +++ b/SCons/Tool/MSCommon/sdk.py @@ -372,7 +372,7 @@ def mssdk_setup_env(env): return msvs_version = env.subst(msvs_version) from . import vs - msvs = vs.get_vs_by_version(msvs_version) + msvs = vs.get_vs_by_version(msvs_version, env) debug('mssdk_setup_env:msvs is :%s', msvs) if not msvs: debug('mssdk_setup_env: no VS version detected, bailingout:%s', msvs) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 7857a7a41f..fcc1ba80c7 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -103,7 +103,7 @@ class BatchFileExecutionError(VisualCException): # MSVC 9.0 preferred query order: # True: VCForPython, VisualStudio # False: VisualStudio, VCForPython -_VC90_Prefer_VCForPython = True +_VC90_Prefer_VCForPython = False # Dict to 'canonalize' the arch _ARCH_TO_CANONICAL = { @@ -923,6 +923,21 @@ def msvc_version_to_maj_min(msvc_version): os.path.expandvars(r"%SCOOP%\shims"), ]] +def msvc_find_vswhere(): + """ Find the location of vswhere """ + # For bug 3333: support default location of vswhere for both + # 64 and 32 bit windows installs. + # For bug 3542: also accommodate not being on C: drive. + # NB: this gets called from testsuite on non-Windows platforms. + # Whether that makes sense or not, don't break it for those. + vswhere_path = None + for pf in VSWHERE_PATHS: + if os.path.exists(pf): + vswhere_path = pf + break + + return vswhere_path + # normalize user-specified vswhere paths _cache_user_vswhere_paths = {} @@ -965,13 +980,7 @@ def _vswhere_user_path(pval): return vswhere_path -# normalized default vswhere path - -_vswhere_paths_processed = [ - MSVC.Util.normalize_path(pval) - for pval in VSWHERE_PATHS - if os.path.exists(pval) -] +# normalize default vswhere path _vswhere_path_default = UNDEFINED @@ -981,6 +990,12 @@ def _msvc_default_vswhere(): if _vswhere_path_default == UNDEFINED: + _vswhere_paths_processed = [ + MSVC.Util.normalize_path(pval) + for pval in VSWHERE_PATHS + if os.path.exists(pval) + ] + if _vswhere_paths_processed: vswhere_path = _vswhere_paths_processed[0] else: @@ -991,21 +1006,6 @@ def _msvc_default_vswhere(): return _vswhere_path_default -def msvc_find_vswhere(): - """ Find the location of vswhere """ - # For bug 3333: support default location of vswhere for both - # 64 and 32 bit windows installs. - # For bug 3542: also accommodate not being on C: drive. - # NB: this gets called from testsuite on non-Windows platforms. - # Whether that makes sense or not, don't break it for those. - vswhere_path = None - for pf in VSWHERE_PATHS: - if os.path.exists(pf): - vswhere_path = pf - break - - return vswhere_path - class _VSWhere: reset_funcs_list = [] diff --git a/SCons/Tool/MSCommon/vs.py b/SCons/Tool/MSCommon/vs.py index afe2f743ef..d40ac426ff 100644 --- a/SCons/Tool/MSCommon/vs.py +++ b/SCons/Tool/MSCommon/vs.py @@ -48,6 +48,12 @@ ) +# Visual Studio express version policy when unqualified version is not installed: +# True: use express version for unqualified version (e.g., use 12.0Exp for 12.0) +# False: do not use express version for unqualified version +_VSEXPRESS_USE_VERSTR = True + + class VisualStudio: """ An abstract base class for trying to find installed versions of @@ -55,7 +61,9 @@ class VisualStudio: """ def __init__(self, version, **kw) -> None: self.version = version - self.vernum = float(get_msvc_version_numeric(version)) + self.verstr = get_msvc_version_numeric(version) + self.vernum = float(self.verstr) + self.is_express = True if self.verstr != self.version else False kw['vc_version'] = kw.get('vc_version', version) kw['sdk_version'] = kw.get('sdk_version', version) self.__dict__.update(kw) @@ -454,9 +462,17 @@ def get_installed_visual_studios(env=None): if vs.get_executable(env): debug('found VS %s', vs.version) InstalledVSList.append(vs) + if vs.is_express and vs.verstr not in InstalledVSMap: + if _VSEXPRESS_USE_VERSTR: + InstalledVSMap[vs.verstr] = vs InstalledVSMap[vs.version] = vs return InstalledVSList +def _get_installed_vss(env=None): + get_installed_visual_studios(env) + versions = list(InstalledVSMap.keys()) + return versions + def reset_installed_visual_studios() -> None: global InstalledVSList global InstalledVSMap @@ -519,7 +535,7 @@ def _reset_installed_vswhere_visual_studios(): def msvs_exists(env=None) -> bool: return len(get_installed_visual_studios(env)) > 0 -def get_vs_by_version(msvs): +def get_vs_by_version(msvs, env=None): global InstalledVSMap global SupportedVSMap @@ -527,7 +543,7 @@ def get_vs_by_version(msvs): if msvs not in SupportedVSMap: msg = "Visual Studio version %s is not supported" % repr(msvs) raise SCons.Errors.UserError(msg) - get_installed_visual_studios() + get_installed_visual_studios(env) vs = InstalledVSMap.get(msvs) debug('InstalledVSMap:%s', InstalledVSMap) debug('found vs:%s', vs) @@ -556,7 +572,7 @@ def get_default_version(env): """ if 'MSVS' not in env or not SCons.Util.is_Dict(env['MSVS']): # get all versions, and remember them for speed later - versions = [vs.version for vs in get_installed_visual_studios()] + versions = _get_installed_vss(env) env['MSVS'] = {'VERSIONS' : versions} else: versions = env['MSVS'].get('VERSIONS', []) @@ -602,7 +618,7 @@ def merge_default_version(env) -> None: # TODO: refers to versions and arch which aren't defined; called nowhere. Drop? def msvs_setup_env(env) -> None: - msvs = get_vs_by_version(version) + msvs = get_vs_by_version(version, env) if msvs is None: return batfilename = msvs.get_batch_file() @@ -614,7 +630,7 @@ def msvs_setup_env(env) -> None: vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE') - msvs_list = get_installed_visual_studios() + msvs_list = get_installed_visual_studios(env) vscommonvarnames = [vs.common_tools_var for vs in msvs_list] save_ENV = env['ENV'] nenv = normalize_env(env['ENV'], @@ -629,11 +645,10 @@ def msvs_setup_env(env) -> None: for k, v in vars.items(): env.PrependENVPath(k, v, delete_existing=1) -def query_versions(): +def query_versions(env=None): """Query the system to get available versions of VS. A version is considered when a batfile is found.""" - msvs_list = get_installed_visual_studios() - versions = [msvs.version for msvs in msvs_list] + versions = _get_installed_vss(env) return versions # Local Variables: diff --git a/SCons/Tool/msvsTests.py b/SCons/Tool/msvsTests.py index 1266055494..63d91ce2fa 100644 --- a/SCons/Tool/msvsTests.py +++ b/SCons/Tool/msvsTests.py @@ -635,9 +635,9 @@ def _TODO_test_merge_default_version(self) -> None: """Test the merge_default_version() function""" pass - def test_query_versions(self) -> None: + def test_query_versions(self, env=None) -> None: """Test retrieval of the list of visual studio versions""" - v1 = query_versions() + v1 = query_versions(env) assert not v1 or str(v1[0]) == self.highest_version, \ (v1, self.highest_version) assert len(v1) == self.number_of_versions, v1 diff --git a/test/MSVC/query_vcbat.py b/test/MSVC/query_vcbat.py index 6e30706b89..28bbd02dc3 100644 --- a/test/MSVC/query_vcbat.py +++ b/test/MSVC/query_vcbat.py @@ -51,7 +51,7 @@ # print k, v #MergeMSVSBatFile(env, 9.0) #print(env['ENV']['PATH']) -print(query_versions()) +print(query_versions(env=None)) """) test.run(stderr=None) diff --git a/testing/framework/TestSConsMSVS.py b/testing/framework/TestSConsMSVS.py index 8c34372b85..91aa329d66 100644 --- a/testing/framework/TestSConsMSVS.py +++ b/testing/framework/TestSConsMSVS.py @@ -680,7 +680,7 @@ def msvs_versions(self): import SCons import SCons.Tool.MSCommon print("self.scons_version =%%s"%%repr(SCons.__%s__)) -print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions())) +print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions(env=None))) """ % 'version' self.run(arguments = '-n -q -Q -f -', stdin = input) @@ -738,13 +738,13 @@ def msvs_substitute(self, input, msvs_ver, result = result.replace('\n', sln_sccinfo) return result - def get_msvs_executable(self, version): + def get_msvs_executable(self, version, env=None): """Returns a full path to the executable (MSDEV or devenv) for the specified version of Visual Studio. """ from SCons.Tool.MSCommon import get_vs_by_version - msvs = get_vs_by_version(version) + msvs = get_vs_by_version(version, env) if not msvs: return None return msvs.get_executable() From 3b95dd6919dec1a13e26e7bef62a02157108c350 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 23 Mar 2024 10:21:47 -0400 Subject: [PATCH 021/386] Refactor vswhere executable support and detection. vswhere changes: * Split the current vswhere paths list into two lists: * the visual studio installer locations, * the package manager locations. * Add additional user vswhere priority groups: * before the vs installer locations [high priority], * after the vs installer locations [default priority], and * after the package manager locations [low priority]. * Adjust tests for vswhere path list changes accordingly. * Add vswhere location functions to: * register user vswhere executable paths by priority, * get the currently configured vswhere executable location, * freeze the vswhere executable location used for detection. * Assign the environment VSWHERE construction variable in Tool/MSCommon/vc.py. * Remove the assignment of the environment VSWHERE construction variable in Tool/msvc.py * Update the test suite function signature for finding the msvc product directory with vswhere. * Freeze the vswhere executable in the following functions before the cache is evaluated which will raise an exception if the vswhere executable has changed since the initial detection (if necessary): * MSCommon.vc.get_installed_visual_studios, * MSCommon.vc.find_vc_pdir, * MSCommon.vc.msvc_setup_env, * MSCommon.vs.get_installed_visual_studios --- CHANGES.txt | 17 +- RELEASE.txt | 17 +- SCons/Tool/MSCommon/MSVC/Util.py | 9 + SCons/Tool/MSCommon/__init__.py | 5 + SCons/Tool/MSCommon/vc.py | 770 ++++++++++++------ SCons/Tool/MSCommon/vcTests.py | 16 +- SCons/Tool/MSCommon/vs.py | 23 +- SCons/Tool/msvc.py | 3 - SCons/Tool/msvsTests.py | 9 +- test/MSVC/VSWHERE-fixture/SConstruct | 25 +- test/MSVC/VSWHERE.py | 10 + test/fixture/no_msvc/no_msvcs_sconstruct.py | 4 +- ...s_sconstruct_msvc_query_toolset_version.py | 4 +- .../no_msvcs_sconstruct_msvc_sdk_versions.py | 4 +- ..._msvcs_sconstruct_msvc_toolset_versions.py | 4 +- .../no_msvc/no_msvcs_sconstruct_tools.py | 4 +- .../no_msvc/no_msvcs_sconstruct_version.py | 4 +- 17 files changed, 608 insertions(+), 320 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6d79040e71..9733e1ec55 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -85,13 +85,16 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER of the results from all vswhere executables). There is a single invocation for each vswhere executable that processes the output for all installations. Prior implementations called the vswhere executable for each supported msvc version. - - Previously, the installed vcs and visual studios lists were constructed once and - cached at runtime. If a vswhere executable was specified via the construction - variable VSWHERE and found additional msvc installations, the new installations - were not reflected in the installed vcs and visual studios lists. Now, when a - user-specified vswhere executable finds new msvc installations, internal runtime - caches are cleared and the installed vcs and visual studios lists are reconstructed - on their next retrieval. + - The vswhere executable is frozen upon initial detection. Specifying a different + vswhere executable via the construction variable VSWHERE after the initial + detection now results in an exception. Multiple bugs in the implementation of + specifying a vswhere executable via the construction variable VSWHERE have been + fixed. Previously, when a user specified vswhere executable detects new msvc + installations after the initial detection, the internal msvc installation cache + and the default msvc version based on the initial detection are no longer valid. + For example, when no vswhere executable is found for the initial detection + and then later an environment is constructed with a user specified vswhere + executable that detects new msvc installations. From Thaddeus Crews: - Implemented SCons.Util.sctyping as a safe means of hinting complex types. Currently diff --git a/RELEASE.txt b/RELEASE.txt index 65114e6c33..5ca5d06fdb 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -75,13 +75,16 @@ FIXES is now selected before a Visual C++ For Python edition. Prior to this change, Visual C++ For Python was selected before a full development edition when both editions are installed. -- MSVC: The installed vcs and visual studios lists were constructed and cached - during their initial invocations. If a vswhere executable was specified via the - construction variable VSWHERE and found additional msvc installations, the new - installations were not reflected in the cached installed vcs and visual studios - lists. Now, when a user-specified vswhere executable finds new msvc installations, - the installed vcs and visual studios lists are cleared and reconstructed on their - next retrieval. +- The vswhere executable is frozen upon initial detection. Specifying a different + vswhere executable via the construction variable VSWHERE after the initial + detection now results in an exception. Multiple bugs in the implementation of + specifying a vswhere executable via the construction variable VSWHERE have been + fixed. Previously, when a user specified vswhere executable detects new msvc + installations after the initial detection, the internal msvc installation cache + and the default msvc version based on the initial detection are no longer valid. + For example, when no vswhere executable is found for the initial detection + and then later an environment is constructed with a user specified vswhere + executable that detects new msvc installations. - On Windows platform, when collecting command output (Configure checks), make sure decoding of bytes doesn't fail. - Documentation indicated that both Pseudo() and env.Pseudo() were usable, diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index 44b112546b..f6178e2886 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -37,6 +37,15 @@ from . import Config + +# call _initialize method upon class definition completion + +class AutoInitialize: + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + if hasattr(cls, '_initialize') and callable(getattr(cls, '_initialize', None)): + cls._initialize() + # path utilities # windows drive specification (e.g., 'C:') diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index c3078ac630..4d7b8bcb3a 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -45,6 +45,9 @@ msvc_toolset_versions, msvc_toolset_versions_spectre, msvc_query_version_toolset, + vswhere_register_executable, + vswhere_get_executable, + vswhere_freeze_executable, ) from SCons.Tool.MSCommon.vs import ( # noqa: F401 @@ -78,7 +81,9 @@ MSVCUnsupportedHostArch, MSVCUnsupportedTargetArch, MSVCScriptNotFound, + MSVCUseScriptError, MSVCUseSettingsError, + VSWhereUserError, ) from .MSVC.Util import ( # noqa: F401 diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index fcc1ba80c7..126d2588ce 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -51,13 +51,17 @@ ) import json from functools import cmp_to_key +import enum import SCons.Util import SCons.Warnings from SCons.Tool import find_program_path from . import common -from .common import CONFIG_CACHE, debug +from .common import ( + CONFIG_CACHE, + debug, +) from .sdk import get_installed_sdks from . import MSVC @@ -81,9 +85,15 @@ class MSVCUnsupportedTargetArch(VisualCException): class MSVCScriptNotFound(MSVCUserError): pass +class MSVCUseScriptError(MSVCUserError): + pass + class MSVCUseSettingsError(MSVCUserError): pass +class VSWhereUserError(MSVCUserError): + pass + # internal exceptions class UnsupportedVersion(VisualCException): @@ -914,113 +924,330 @@ def msvc_version_to_maj_min(msvc_version): raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) from None -VSWHERE_PATHS = [os.path.join(p,'vswhere.exe') for p in [ +_VSWHERE_EXEGROUP_MSVS = [os.path.join(p, _VSWHERE_EXE) for p in [ + # For bug 3333: support default location of vswhere for both + # 64 and 32 bit windows installs. + # For bug 3542: also accommodate not being on C: drive. os.path.expandvars(r"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer"), os.path.expandvars(r"%ProgramFiles%\Microsoft Visual Studio\Installer"), +]] + +_VSWHERE_EXEGROUP_PKGMGR = [os.path.join(p, _VSWHERE_EXE) for p in [ os.path.expandvars(r"%ChocolateyInstall%\bin"), os.path.expandvars(r"%LOCALAPPDATA%\Microsoft\WinGet\Links"), os.path.expanduser(r"~\scoop\shims"), os.path.expandvars(r"%SCOOP%\shims"), ]] -def msvc_find_vswhere(): - """ Find the location of vswhere """ - # For bug 3333: support default location of vswhere for both - # 64 and 32 bit windows installs. - # For bug 3542: also accommodate not being on C: drive. - # NB: this gets called from testsuite on non-Windows platforms. - # Whether that makes sense or not, don't break it for those. - vswhere_path = None - for pf in VSWHERE_PATHS: - if os.path.exists(pf): - vswhere_path = pf - break +_VSWhereBinary = namedtuple('_VSWhereBinary', [ + 'vswhere_exe', + 'vswhere_norm', +]) - return vswhere_path +class VSWhereBinary(_VSWhereBinary, MSVC.Util.AutoInitialize): + + _UNDEFINED_VSWHERE_BINARY = None -# normalize user-specified vswhere paths + _cache_vswhere_paths = {} -_cache_user_vswhere_paths = {} + @classmethod + def _initialize(cls): + vswhere_binary = cls( + vswhere_exe=None, + vswhere_norm=None, + ) + cls._UNDEFINED_VSWHERE_BINARY = vswhere_binary + for symbol in (None, ''): + cls._cache_vswhere_paths[symbol] = vswhere_binary -def _vswhere_user_path(pval): - global _cache_user_vswhere_path + @classmethod + def factory(cls, vswhere_exe): - rval = _cache_user_vswhere_paths.get(pval, UNDEFINED) - if rval != UNDEFINED: - debug('vswhere_path=%s', rval) - return rval + if not vswhere_exe: + return cls._UNDEFINED_VSWHERE_BINARY - vswhere_path = None - if pval: + vswhere_binary = cls._cache_vswhere_paths.get(vswhere_exe) + if vswhere_binary: + return vswhere_binary - if not os.path.exists(pval): + vswhere_norm = MSVC.Util.normalize_path(vswhere_exe) - warn_msg = f'vswhere executable path not found: {pval!r}' - SCons.Warnings.warn(MSVC.Warnings.VSWherePathWarning, warn_msg) - debug(warn_msg) + vswhere_binary = cls._cache_vswhere_paths.get(vswhere_norm) + if vswhere_binary: + return vswhere_binary - else: + vswhere_binary = cls( + vswhere_exe=vswhere_exe, + vswhere_norm=vswhere_exe, + ) - # TODO: vswhere_norm = MSVC.Util.normalize_path(pval) - vswhere_norm = os.path.normcase(os.path.normpath(pval)) + cls._cache_vswhere_paths[vswhere_exe] = vswhere_binary + cls._cache_vswhere_paths[vswhere_norm] = vswhere_binary - tail = os.path.split(vswhere_norm)[-1] - if tail != _VSWHERE_EXE: + return vswhere_binary - warn_msg = f'unsupported vswhere executable (expected {_VSWHERE_EXE!r}, found {tail!r}): {pval!r}' - SCons.Warnings.warn(MSVC.Warnings.VSWherePathWarning, warn_msg) - debug(warn_msg) +class _VSWhereExecutable(MSVC.Util.AutoInitialize): - else: + debug_extra = None - vswhere_path = vswhere_norm - debug('vswhere_path=%s', vswhere_path) + class _VSWhereUserPriority(enum.IntEnum): + HIGH = 0 # before msvs locations + DEFAULT = enum.auto() # after msvs locations and before pkgmgr locations + LOW = enum.auto() # after pkgmgr locations - _cache_user_vswhere_paths[pval] = vswhere_path + priority_default = _VSWhereUserPriority.DEFAULT + priority_map = {} + priority_symbols = [] + priority_indmap = {} - return vswhere_path + for e in _VSWhereUserPriority: + for symbol in (e.name.lower(), e.name.lower().capitalize(), e.name): + priority_map[symbol] = e.value + priority_symbols.append(symbol) + priority_indmap[e.value] = e.name + + UNDEFINED_VSWHERE_BINARY = VSWhereBinary.factory(None) + + @classmethod + def reset(cls): + + cls._vswhere_exegroups = None + cls._vswhere_exegroups_user = None + cls._vswhere_exegroup_msvs = None + cls._vswhere_exegroup_pkgmgr = None + + cls.vswhere_frozen_flag = False + cls.vswhere_frozen_binary = None + + @classmethod + def _initialize(cls): + cls.debug_extra = common.debug_extra(cls) + cls.reset() + + @classmethod + def _vswhere_exegroup_binaries(cls, vswhere_exe_list, label): + vswhere_binaries = [] + for vswhere_exe in vswhere_exe_list: + if not os.path.exists(vswhere_exe): + continue + vswhere_binary = VSWhereBinary.factory(vswhere_exe) + vswhere_binaries.append(vswhere_binary) + debug( + "insert exegroup=%s, vswhere_binary=%s", + label, vswhere_binary, extra=cls.debug_extra + ) + return vswhere_binaries + + @classmethod + def _get_vswhere_exegroups(cls): + + if cls._vswhere_exegroups is None: + + cls._vswhere_exegroup_msvs = cls._vswhere_exegroup_binaries(_VSWHERE_EXEGROUP_MSVS, 'MSVS') + cls._vswhere_exegroup_pkgmgr = cls._vswhere_exegroup_binaries(_VSWHERE_EXEGROUP_PKGMGR, 'PKGMGR') + + cls._vswhere_exegroups_user = [ + [] for e in cls._VSWhereUserPriority + ] -# normalize default vswhere path + vswhere_exegroups = [ + cls._vswhere_exegroups_user[cls._VSWhereUserPriority.HIGH], + cls._vswhere_exegroup_msvs, + cls._vswhere_exegroups_user[cls._VSWhereUserPriority.DEFAULT], + cls._vswhere_exegroup_pkgmgr, + cls._vswhere_exegroups_user[cls._VSWhereUserPriority.LOW], + ] -_vswhere_path_default = UNDEFINED + cls._vswhere_exegroups = vswhere_exegroups -def _msvc_default_vswhere(): - global _vswhere_paths_processed - global _vswhere_path_default + return cls._vswhere_exegroups - if _vswhere_path_default == UNDEFINED: + @classmethod + def _get_vswhere_exegroups_user(cls): + + if cls._vswhere_exegroups_user is None: + cls._get_vswhere_exegroups() - _vswhere_paths_processed = [ - MSVC.Util.normalize_path(pval) - for pval in VSWHERE_PATHS - if os.path.exists(pval) - ] + return cls._vswhere_exegroups_user + + @classmethod + def _vswhere_exegroup_binary(cls, exegroup): + # first vswhere binary in group or UNDEFINED_VSWHERE_BINARY + vswhere_binary = exegroup[0] if exegroup else cls.UNDEFINED_VSWHERE_BINARY + return vswhere_binary - if _vswhere_paths_processed: - vswhere_path = _vswhere_paths_processed[0] + @classmethod + def _vswhere_current_binary(cls): + # first vswhere binary in priority order or UNDEFINED_VSWHERE_BINARY + vswhere_binary = cls.UNDEFINED_VSWHERE_BINARY + vswhere_exegroups = cls._get_vswhere_exegroups() + for exegroup in vswhere_exegroups: + binary = cls._vswhere_exegroup_binary(exegroup) + if binary == cls.UNDEFINED_VSWHERE_BINARY: + continue + vswhere_binary = binary + break + return vswhere_binary + + @classmethod + def _vswhere_all_executables(cls): + # unique vswhere executables in priority order + vswhere_exe_list = [] + vswhere_norm_set = set() + vswhere_exegroups = cls._get_vswhere_exegroups() + for exegroup in vswhere_exegroups: + for vswhere_binary in exegroup: + if vswhere_binary.vswhere_norm in vswhere_norm_set: + continue + vswhere_norm_set.add(vswhere_binary.vswhere_norm) + vswhere_exe_list.append(vswhere_binary.vswhere_exe) + return vswhere_exe_list + + @classmethod + def freeze_vswhere_binary(cls): + if not cls.vswhere_frozen_flag: + cls.vswhere_frozen_flag = True + cls.vswhere_frozen_binary = cls._vswhere_current_binary() + debug("freeze=%s", cls.vswhere_frozen_binary, extra=cls.debug_extra) + return cls.vswhere_frozen_binary + + @classmethod + def freeze_vswhere_executable(cls): + vswhere_binary = cls.freeze_vswhere_binary() + vswhere_exe = vswhere_binary.vswhere_exe + return vswhere_exe + + @classmethod + def get_vswhere_executable(cls): + if cls.vswhere_frozen_flag: + vswhere_binary = cls.vswhere_frozen_binary else: - vswhere_path = None + vswhere_binary = cls._vswhere_current_binary() + vswhere_exe = vswhere_binary.vswhere_exe + debug("vswhere_exe=%s", repr(vswhere_exe), extra=cls.debug_extra) + return vswhere_exe + + @classmethod + def _vswhere_priority_group(cls, priority): + if not priority: + group = cls.priority_default + else: + group = cls.priority_map.get(priority) + if group is None: + msg = f'Value specified for vswhere executable priority is not supported: {priority!r}:\n' \ + f' Valid values are: {cls.priority_symbols}' + debug(f'VSWhereUserError: {msg}', extra=cls.debug_extra) + raise VSWhereUserError(msg) + return group + + @classmethod + def register_vswhere_executable(cls, vswhere_exe, priority=None) -> bool: - _vswhere_path_default = vswhere_path - debug('vswhere_path=%s', vswhere_path) + vswhere_binary = cls.UNDEFINED_VSWHERE_BINARY - return _vswhere_path_default + if not vswhere_exe: + # ignore: None or empty + return vswhere_binary -class _VSWhere: + if not os.path.exists(vswhere_exe): + msg = f'Specified vswhere executable not found: {vswhere_exe!r}.' + debug(f'VSWhereUserError: {msg}', extra=cls.debug_extra) + raise VSWhereUserError(msg) - reset_funcs_list = [] - reset_funcs_seen = set() + group = cls._vswhere_priority_group(priority) + + vswhere_binary = VSWhereBinary.factory(vswhere_exe) + + if cls.vswhere_frozen_flag: + + if vswhere_binary.vswhere_norm == cls.vswhere_frozen_binary.vswhere_norm: + # ignore: user executable == frozen executable + return vswhere_binary + + msg = 'A different vswhere execuable cannot be requested after initial detetection:\n' \ + f' initial vswhere executable: {cls.vswhere_frozen_binary.vswhere_exe!r}\n' \ + f' request vswhere executable: {vswhere_binary.vswhere_exe!r}' + + debug(f'VSWhereUserError: {msg}', extra=cls.debug_extra) + raise VSWhereUserError(msg) + + vswhere_exegroups_user = cls._get_vswhere_exegroups_user() + + exegroup = vswhere_exegroups_user[group] + group_binary = cls._vswhere_exegroup_binary(exegroup) + + if vswhere_binary.vswhere_norm == group_binary.vswhere_norm: + # ignore: user executable == exegroup[0] executable + return vswhere_binary + + exegroup.insert(0, vswhere_binary) + debug( + "insert exegroup=user[%s], vswhere_binary=%s", + cls.priority_indmap[group], vswhere_binary, extra=cls.debug_extra + ) + + return vswhere_binary @classmethod - def reset(cls): + def vswhere_freeze_executable(cls, vswhere_exe): + vswhere_binary = cls.register_vswhere_executable(vswhere_exe, priority='high') + frozen_binary = cls.freeze_vswhere_binary() + return frozen_binary, vswhere_binary - cls.seen_vswhere = set() - cls.seen_root = set() + @classmethod + def vswhere_freeze_env(cls, env): + + if env is None: + # no environment, no VSWHERE + vswhere_exe = None + write_vswhere = False + elif 'VSWHERE' not in env or not env['VSWHERE']: + # environment, VSWHERE undefined + vswhere_exe = None + write_vswhere = True + else: + # environment, VSWHERE defined + vswhere_exe = env.subst('$VSWHERE') + write_vswhere = False - cls.vswhere_executables = [] + frozen_binary, vswhere_binary = cls.vswhere_freeze_executable(vswhere_exe) - cls.msvc_instances = [] - cls.msvc_map = {} + if write_vswhere and frozen_binary.vswhere_norm != vswhere_binary.vswhere_norm: + env['VSWHERE'] = frozen_binary.vswhere_exe + debug( + "env['VSWHERE']=%s", + repr(frozen_binary.vswhere_exe), extra=cls.debug_extra + ) + + return frozen_binary, vswhere_binary + +# external use +vswhere_register_executable = _VSWhereExecutable.register_vswhere_executable +vswhere_get_executable = _VSWhereExecutable.get_vswhere_executable +vswhere_freeze_executable = _VSWhereExecutable.freeze_vswhere_executable + +# internal use +vswhere_freeze_env = _VSWhereExecutable.vswhere_freeze_env + +def msvc_find_vswhere(): + """ Find the location of vswhere """ + # NB: this gets called from testsuite on non-Windows platforms. + # Whether that makes sense or not, don't break it for those. + vswhere_path = _VSWhereExecutable.get_vswhere_executable() + return vswhere_path + +_MSVCInstance = namedtuple('_MSVCInstance', [ + 'vc_path', + 'vc_version', + 'vc_version_numeric', + 'vc_version_scons', + 'vc_release', + 'vc_component_id', + 'vc_component_rank', + 'vc_component_suffix', +]) + +class MSVCInstance(_MSVCInstance): @staticmethod def msvc_instances_default_order(a, b): @@ -1035,237 +1262,231 @@ def msvc_instances_default_order(a, b): return 1 if a.vc_component_rank < b.vc_component_rank else -1 return 0 - @classmethod - def register_reset_func(cls, func): - if func and func not in cls.reset_funcs_seen: - cls.reset_funcs_seen.add(func) - cls.reset_funcs_list.append(func) +class _VSWhere(MSVC.Util.AutoInitialize): + + debug_extra = None @classmethod - def call_reset_funcs(cls): - for func in cls.reset_funcs_list: - func() + def reset(cls): -_VSWhere.reset() + cls.seen_vswhere = set() + cls.seen_root = set() -def vswhere_register_reset_func(func): - _VSWhere.register_reset_func(func) + cls.vswhere_executables = [] -def _filter_vswhere_paths(env): + cls.msvc_instances = [] + cls.msvc_map = {} - vswhere_paths = [] + @classmethod + def _initialize(cls): + cls.debug_extra = common.debug_extra(cls) + cls.reset() - if env and 'VSWHERE' in env: - vswhere_environ = _vswhere_user_path(env.subst('$VSWHERE')) - if vswhere_environ and vswhere_environ not in _VSWhere.seen_vswhere: - vswhere_paths.append(vswhere_environ) + @classmethod + def _new_roots_discovered(cls): + # TODO(JCB) should not happen due to freezing vswhere.exe + # need sanity check? + pass - vswhere_default = _msvc_default_vswhere() - if vswhere_default and vswhere_default not in _VSWhere.seen_vswhere: - vswhere_paths.append(vswhere_default) + @classmethod + def _filter_vswhere_paths(cls, vswhere_exe): - debug('vswhere_paths=%s', vswhere_paths) - return vswhere_paths + vswhere_paths = [] -def _vswhere_query_json_output(vswhere_exe, vswhere_args): + if vswhere_exe and vswhere_exe not in cls.seen_vswhere: + vswhere_paths.append(vswhere_exe) - vswhere_json = None + return vswhere_paths - once = True - while once: - once = False - # using break for single exit (unless exception) + @classmethod + def _vswhere_query_json_output(cls, vswhere_exe, vswhere_args): - vswhere_cmd = [vswhere_exe] + vswhere_args + ['-format', 'json', '-utf8'] - debug("running: %s", vswhere_cmd) + vswhere_json = None - try: - cp = subprocess.run(vswhere_cmd, stdout=PIPE, stderr=PIPE, check=True) - except OSError as e: - errmsg = str(e) - debug("%s: %s", type(e).__name__, errmsg) - break - except Exception as e: - errmsg = str(e) - debug("%s: %s", type(e).__name__, errmsg) - raise + once = True + while once: + once = False + # using break for single exit (unless exception) - if not cp.stdout: - debug("no vswhere information returned") - break + vswhere_cmd = [vswhere_exe] + vswhere_args + ['-format', 'json', '-utf8'] + debug("running: %s", vswhere_cmd, extra=cls.debug_extra) - vswhere_output = cp.stdout.decode('utf8', errors='replace') - if not vswhere_output: - debug("no vswhere information output") - break + try: + cp = subprocess.run(vswhere_cmd, stdout=PIPE, stderr=PIPE, check=True) + except OSError as e: + errmsg = str(e) + debug("%s: %s", type(e).__name__, errmsg, extra=cls.debug_extra) + break + except Exception as e: + errmsg = str(e) + debug("%s: %s", type(e).__name__, errmsg, extra=cls.debug_extra) + raise - try: - vswhere_output_json = json.loads(vswhere_output) - except json.decoder.JSONDecodeError: - debug("json decode exception loading vswhere output") - break + if not cp.stdout: + debug("no vswhere information returned", extra=cls.debug_extra) + break - vswhere_json = vswhere_output_json - break + vswhere_output = cp.stdout.decode('utf8', errors='replace') + if not vswhere_output: + debug("no vswhere information output", extra=cls.debug_extra) + break - debug('vswhere_json=%s, vswhere_exe=%s', bool(vswhere_json), repr(vswhere_exe)) + try: + vswhere_output_json = json.loads(vswhere_output) + except json.decoder.JSONDecodeError: + debug("json decode exception loading vswhere output", extra=cls.debug_extra) + break - return vswhere_json + vswhere_json = vswhere_output_json + break -MSVC_INSTANCE = namedtuple('MSVCInstance', [ - 'vc_path', - 'vc_version', - 'vc_version_numeric', - 'vc_version_scons', - 'vc_release', - 'vc_component_id', - 'vc_component_rank', - 'vc_component_suffix', -]) + debug( + 'vswhere_json=%s, vswhere_exe=%s', + bool(vswhere_json), repr(vswhere_exe), extra=cls.debug_extra + ) -def _vswhere_update_msvc_map(env): + return vswhere_json - vswhere_paths = _filter_vswhere_paths(env) - if not vswhere_paths: - debug('new_roots=False, msvc_instances=%s', len(_VSWhere.msvc_instances)) - return _VSWhere.msvc_map + @classmethod + def vswhere_update_msvc_map(cls, vswhere_exe): - n_instances = len(_VSWhere.msvc_instances) + frozen_binary, vswhere_binary = _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe) - for vswhere_exe in vswhere_paths: + vswhere_exe = frozen_binary.vswhere_exe - if vswhere_exe in _VSWhere.seen_vswhere: - continue + vswhere_paths = cls._filter_vswhere_paths(vswhere_exe) + if not vswhere_paths: + return cls.msvc_map - _VSWhere.seen_vswhere.add(vswhere_exe) - _VSWhere.vswhere_executables.append(vswhere_exe) + n_instances = len(cls.msvc_instances) - debug('vswhere_exe=%s', repr(vswhere_exe)) + for vswhere_exe in vswhere_paths: - vswhere_json = _vswhere_query_json_output( - vswhere_exe, - ['-all', '-products', '*'] - ) + if vswhere_exe in cls.seen_vswhere: + continue - if not vswhere_json: - continue + cls.seen_vswhere.add(vswhere_exe) + cls.vswhere_executables.append(vswhere_exe) - for instance in vswhere_json: + debug('vswhere_exe=%s', repr(vswhere_exe), extra=cls.debug_extra) - #print(json.dumps(instance, indent=4, sort_keys=True)) + vswhere_json = cls._vswhere_query_json_output( + vswhere_exe, + ['-all', '-products', '*'] + ) - installation_path = instance.get('installationPath') - if not installation_path or not os.path.exists(installation_path): + if not vswhere_json: continue - vc_path = os.path.join(installation_path, 'VC') - if not os.path.exists(vc_path): - continue + for instance in vswhere_json: - vc_root = MSVC.Util.normalize_path(vc_path) - if vc_root in _VSWhere.seen_root: - continue - _VSWhere.seen_root.add(vc_root) + #print(json.dumps(instance, indent=4, sort_keys=True)) - installation_version = instance.get('installationVersion') - if not installation_version: - continue + installation_path = instance.get('installationPath') + if not installation_path or not os.path.exists(installation_path): + continue - vs_major = installation_version.split('.')[0] - if not vs_major in _VSWHERE_VSMAJOR_TO_VCVERSION: - debug('ignore vs_major: %s', vs_major) - continue + vc_path = os.path.join(installation_path, 'VC') + if not os.path.exists(vc_path): + continue - vc_version = _VSWHERE_VSMAJOR_TO_VCVERSION[vs_major] + vc_root = MSVC.Util.normalize_path(vc_path) + if vc_root in cls.seen_root: + continue + cls.seen_root.add(vc_root) - product_id = instance.get('productId') - if not product_id: - continue + installation_version = instance.get('installationVersion') + if not installation_version: + continue - component_id = product_id.split('.')[-1] - if component_id not in _VSWHERE_COMPONENTID_CANDIDATES: - debug('ignore component_id: %s', component_id) - continue + vs_major = installation_version.split('.')[0] + if not vs_major in _VSWHERE_VSMAJOR_TO_VCVERSION: + debug('ignore vs_major: %s', vs_major, extra=cls.debug_extra) + continue - component_rank = _VSWHERE_COMPONENTID_RANKING.get(component_id,0) - if component_rank == 0: - raise MSVCInternalError(f'unknown component_rank for component_id: {component_id!r}') + vc_version = _VSWHERE_VSMAJOR_TO_VCVERSION[vs_major] - scons_suffix = _VSWHERE_COMPONENTID_SCONS_SUFFIX[component_id] + product_id = instance.get('productId') + if not product_id: + continue - if scons_suffix: - vc_version_scons = vc_version + scons_suffix - else: - vc_version_scons = vc_version - - is_prerelease = True if instance.get('isPrerelease', False) else False - is_release = False if is_prerelease else True - - msvc_instance = MSVC_INSTANCE( - vc_path = vc_path, - vc_version = vc_version, - vc_version_numeric = float(vc_version), - vc_version_scons = vc_version_scons, - vc_release = is_release, - vc_component_id = component_id, - vc_component_rank = component_rank, - vc_component_suffix = component_suffix, - ) + component_id = product_id.split('.')[-1] + if component_id not in _VSWHERE_COMPONENTID_CANDIDATES: + debug('ignore component_id: %s', component_id, extra=cls.debug_extra) + continue - _VSWhere.msvc_instances.append(msvc_instance) + component_rank = _VSWHERE_COMPONENTID_RANKING.get(component_id,0) + if component_rank == 0: + raise MSVCInternalError(f'unknown component_rank for component_id: {component_id!r}') - new_roots = bool(len(_VSWhere.msvc_instances) > n_instances) - if new_roots: + scons_suffix = _VSWHERE_COMPONENTID_SCONS_SUFFIX[component_id] - _VSWhere.msvc_instances = sorted( - _VSWhere.msvc_instances, - key=cmp_to_key(_VSWhere.msvc_instances_default_order) - ) + if scons_suffix: + vc_version_scons = vc_version + scons_suffix + else: + vc_version_scons = vc_version + + is_prerelease = True if instance.get('isPrerelease', False) else False + is_release = False if is_prerelease else True + + msvc_instance = MSVCInstance( + vc_path = vc_path, + vc_version = vc_version, + vc_version_numeric = float(vc_version), + vc_version_scons = vc_version_scons, + vc_release = is_release, + vc_component_id = component_id, + vc_component_rank = component_rank, + vc_component_suffix = component_suffix, + ) - _VSWhere.msvc_map = {} + cls.msvc_instances.append(msvc_instance) - for msvc_instance in _VSWhere.msvc_instances: + new_roots = bool(len(cls.msvc_instances) > n_instances) + if new_roots: - debug( - 'msvc instance: msvc_version=%s, is_release=%s, component_id=%s, vc_path=%s', - repr(msvc_instance.vc_version_scons), msvc_instance.vc_release, - repr(msvc_instance.vc_component_id), repr(msvc_instance.vc_path) + cls.msvc_instances = sorted( + cls.msvc_instances, + key=cmp_to_key(MSVCInstance.msvc_instances_default_order) ) - key = (msvc_instance.vc_version_scons, msvc_instance.vc_release) - _VSWhere.msvc_map.setdefault(key,[]).append(msvc_instance) + cls.msvc_map = {} - if msvc_instance.vc_version_scons == msvc_instance.vc_version: - continue + for msvc_instance in cls.msvc_instances: - key = (msvc_instance.vc_version, msvc_instance.vc_release) - _VSWhere.msvc_map.setdefault(key,[]).append(msvc_instance) + debug( + 'msvc instance: msvc_version=%s, is_release=%s, component_id=%s, vc_path=%s', + repr(msvc_instance.vc_version_scons), msvc_instance.vc_release, + repr(msvc_instance.vc_component_id), repr(msvc_instance.vc_path), + extra=cls.debug_extra + ) - _VSWhere.call_reset_funcs() + key = (msvc_instance.vc_version_scons, msvc_instance.vc_release) + cls.msvc_map.setdefault(key,[]).append(msvc_instance) - debug('new_roots=%s, msvc_instances=%s', new_roots, len(_VSWhere.msvc_instances)) + if msvc_instance.vc_version_scons == msvc_instance.vc_version: + continue - return _VSWhere.msvc_map + key = (msvc_instance.vc_version, msvc_instance.vc_release) + cls.msvc_map.setdefault(key,[]).append(msvc_instance) -def vswhere_update_msvc(env): - _vswhere_update_msvc_map(env) + cls._new_roots_discovered() -_cache_pdir_vswhere_queries = {} + debug( + 'new_roots=%s, msvc_instances=%s', + new_roots, len(cls.msvc_instances), extra=cls.debug_extra + ) -def _reset_pdir_vswhere_queries(): - global _cache_pdir_vswhere_queries - _cache_pdir_vswhere_queries = {} - debug('reset _cache_pdir_vswhere_queries') + return cls.msvc_map -# register pdir vswhere cache reset function with vswhere state manager -_VSWhere.register_reset_func(_reset_pdir_vswhere_queries) +_cache_pdir_vswhere_queries = {} -def find_vc_pdir_vswhere(msvc_version, env=None): +def _find_vc_pdir_vswhere(msvc_version, vswhere_exe): """ Find the MSVC product directory using the vswhere program. Args: msvc_version: MSVC version to search for - env: optional to look up VSWHERE variable + vswhere_exe: vswhere executable or None Returns: MSVC install dir or None @@ -1276,7 +1497,7 @@ def find_vc_pdir_vswhere(msvc_version, env=None): """ global _cache_pdir_vswhere_queries - msvc_map = _vswhere_update_msvc_map(env) + msvc_map = _VSWhere.vswhere_update_msvc_map(vswhere_exe) if not msvc_map: return None @@ -1335,7 +1556,7 @@ def find_vc_pdir_vswhere(msvc_version, env=None): _cache_pdir_registry_queries = {} -def find_vc_pdir_registry(msvc_version): +def _find_vc_pdir_registry(msvc_version): """ Find the MSVC product directory using the registry. Args: @@ -1436,17 +1657,15 @@ def find_vc_pdir_registry(msvc_version): return pdir -def find_vc_pdir(msvc_version, env=None): +def _find_vc_pdir(msvc_version, vswhere_exe): """Find the MSVC product directory for the given version. - Use find_vc_pdir_vsvwhere for msvc versions 14.1 and later. - Use find_vc_pdir_registry for msvc versions 14.0 and earlier. - Args: msvc_version: str msvc version (major.minor, e.g. 10.0) - env: - optional to look up VSWHERE variable + + vswhere_exe: str + vswhere executable or None Returns: str: Path found in registry, or None @@ -1460,14 +1679,14 @@ def find_vc_pdir(msvc_version, env=None): if msvc_version in _VSWHERE_SUPPORTED_VCVER: - pdir = find_vc_pdir_vswhere(msvc_version, env) + pdir = _find_vc_pdir_vswhere(msvc_version, vswhere_exe) if pdir: debug('VC found: %s, dir=%s', repr(msvc_version), repr(pdir)) return pdir elif msvc_version in _VCVER_TO_PRODUCT_DIR: - pdir = find_vc_pdir_registry(msvc_version) + pdir = _find_vc_pdir_registry(msvc_version) if pdir: debug('VC found: %s, dir=%s', repr(msvc_version), repr(pdir)) return pdir @@ -1480,16 +1699,40 @@ def find_vc_pdir(msvc_version, env=None): debug('no VC found for version %s', repr(msvc_version)) return None +def find_vc_pdir(msvc_version, env=None): + """Find the MSVC product directory for the given version. + + Args: + msvc_version: str + msvc version (major.minor, e.g. 10.0) + env: + optional to look up VSWHERE variable + + Returns: + str: Path found in registry, or None + + Raises: + UnsupportedVersion: if the version is not known by this file. + + UnsupportedVersion inherits from VisualCException. + + """ + + frozen_binary, _ = _VSWhereExecutable.vswhere_freeze_env(env) + + pdir = _find_vc_pdir(msvc_version, frozen_binary.vswhere_exe) + debug('pdir=%s', repr(pdir)) + + return pdir + # register find_vc_pdir function with Kind routines # in case of unknown msvc_version (just in case) MSVC.Kind.register_msvc_version_pdir_func(find_vc_pdir) def _reset_vc_pdir(): debug('reset pdir caches') - global _cache_user_vswhere_path global _cache_pdir_vswhere_queries global _cache_pdir_registry_queries - _cache_user_vswhere_paths = {} _cache_pdir_vswhere_queries = {} _cache_pdir_registry_queries = {} @@ -1593,9 +1836,6 @@ def _reset_installed_vcs(): debug('reset __INSTALLED_VCS_RUN') __INSTALLED_VCS_RUN = None -# register vcs cache reset function with vswhere state manager -_VSWhere.register_reset_func(_reset_installed_vcs) - _VC_TOOLS_VERSION_FILE_PATH = ['Auxiliary', 'Build', 'Microsoft.VCToolsVersion.default.txt'] _VC_TOOLS_VERSION_FILE = os.sep.join(_VC_TOOLS_VERSION_FILE_PATH) @@ -1760,9 +2000,7 @@ def _check_files_exist_in_vc_dir(env, vc_dir, msvc_version) -> bool: def get_installed_vcs(env=None): global __INSTALLED_VCS_RUN - debug('') - # the installed vcs cache is cleared if new vc roots are discovered - _vswhere_update_msvc_map(env) + _VSWhereExecutable.vswhere_freeze_env(env) if __INSTALLED_VCS_RUN is not None: return __INSTALLED_VCS_RUN @@ -1788,8 +2026,8 @@ def get_installed_vcs(env=None): debug('no compiler found %s', ver) else: debug('return None for ver %s', ver) - except (MSVCUnsupportedTargetArch, MSVCUnsupportedHostArch): - # Allow this exception to propagate further as it should cause + except (MSVCUnsupportedTargetArch, MSVCUnsupportedHostArch, VSWhereUserError): + # Allow these exceptions to propagate further as it should cause # SCons to exit with an error code raise except VisualCException as e: @@ -1807,6 +2045,7 @@ def reset_installed_vcs() -> None: """Make it try again to find VC. This is just for the tests.""" _reset_installed_vcs() _reset_vc_pdir() + _VSWhereExecutable.reset() _VSWhere.reset() MSVC._reset() @@ -2132,6 +2371,9 @@ def get_use_script_use_settings(env): def msvc_setup_env(env): debug('called') + + _VSWhereExecutable.vswhere_freeze_env(env) + version = get_default_version(env) if version is None: if not msvc_setup_env_user(env): @@ -2268,8 +2510,11 @@ def msvc_sdk_versions(version=None, msvc_uwp_app: bool=False): rval = MSVC.WinSDK.get_msvc_sdk_version_list(version, msvc_uwp_app) return rval -def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False): - debug('msvc_version=%s, full=%s, sxs=%s', repr(msvc_version), repr(full), repr(sxs)) +def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False, vswhere_exe=None): + debug( + 'msvc_version=%s, full=%s, sxs=%s, vswhere_exe=%s', + repr(msvc_version), repr(full), repr(sxs), repr(vswhere_exe) + ) rval = [] @@ -2284,7 +2529,7 @@ def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False): msg = f'Unsupported msvc version {msvc_version!r}' raise MSVCArgumentError(msg) - vc_dir = find_vc_pdir(msvc_version) + vc_dir = _find_vc_pdir(msvc_version, vswhere_exe) if not vc_dir: debug('VC folder not found for version %s', repr(msvc_version)) return rval @@ -2292,8 +2537,8 @@ def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False): rval = MSVC.ScriptArguments._msvc_toolset_versions_internal(msvc_version, vc_dir, full=full, sxs=sxs) return rval -def msvc_toolset_versions_spectre(msvc_version=None): - debug('msvc_version=%s', repr(msvc_version)) +def msvc_toolset_versions_spectre(msvc_version=None, vswhere_exe=None): + debug('msvc_version=%s, vswhere_exe=%s', repr(msvc_version), repr(vswhere_exe)) rval = [] @@ -2308,7 +2553,7 @@ def msvc_toolset_versions_spectre(msvc_version=None): msg = f'Unsupported msvc version {msvc_version!r}' raise MSVCArgumentError(msg) - vc_dir = find_vc_pdir(msvc_version) + vc_dir = _find_vc_pdir(msvc_version, vswhere_exe) if not vc_dir: debug('VC folder not found for version %s', repr(msvc_version)) return rval @@ -2316,7 +2561,7 @@ def msvc_toolset_versions_spectre(msvc_version=None): rval = MSVC.ScriptArguments._msvc_toolset_versions_spectre_internal(msvc_version, vc_dir) return rval -def msvc_query_version_toolset(version=None, prefer_newest: bool=True): +def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_exe=None): """ Returns an msvc version and a toolset version given a version specification. @@ -2353,6 +2598,9 @@ def msvc_query_version_toolset(version=None, prefer_newest: bool=True): the native Visual Studio instance is not detected, prefer newer Visual Studio instances. + vswhere_exe: str + A vswhere executable location or None. + Returns: tuple: A tuple containing the msvc version and the msvc toolset version. The msvc toolset version may be None. @@ -2427,7 +2675,7 @@ def msvc_query_version_toolset(version=None, prefer_newest: bool=True): continue seen_msvc_version.add(query_msvc_version) - vc_dir = find_vc_pdir(query_msvc_version) + vc_dir = _find_vc_pdir(query_msvc_version, vswhere_exe) if not vc_dir: continue diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index da360b3717..3b30a570a0 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -57,12 +57,22 @@ def testDefaults(self) -> None: Verify that msvc_find_vswhere() find's files in the specified paths """ # import pdb; pdb.set_trace() - vswhere_dirs = [os.path.splitdrive(p)[1] for p in SCons.Tool.MSCommon.vc.VSWHERE_PATHS] + test_all_dirs = [] + base_dir = test.workpath('fake_vswhere') + + vswhere_dirs = [os.path.splitdrive(p)[1] for p in SCons.Tool.MSCommon.vc._VSWHERE_EXEGROUP_MSVS] + test_vswhere_dirs = [os.path.join(base_dir,d[1:]) for d in vswhere_dirs] + SCons.Tool.MSCommon.vc._VSWHERE_EXEGROUP_MSVS = test_vswhere_dirs + test_all_dirs += test_vswhere_dirs + + vswhere_dirs = [os.path.splitdrive(p)[1] for p in SCons.Tool.MSCommon.vc._VSWHERE_EXEGROUP_PKGMGR] test_vswhere_dirs = [os.path.join(base_dir,d[1:]) for d in vswhere_dirs] + SCons.Tool.MSCommon.vc._VSWHERE_EXEGROUP_PKGMGR = test_vswhere_dirs + test_all_dirs += test_vswhere_dirs - SCons.Tool.MSCommon.vc.VSWHERE_PATHS = test_vswhere_dirs - for vsw in test_vswhere_dirs: + for vsw in test_all_dirs: + SCons.Tool.MSCommon.vc._VSWhereExecutable.reset() VswhereTestCase._createVSWhere(vsw) find_path = SCons.Tool.MSCommon.vc.msvc_find_vswhere() self.assertTrue(vsw == find_path, "Didn't find vswhere in %s found in %s" % (vsw, find_path)) diff --git a/SCons/Tool/MSCommon/vs.py b/SCons/Tool/MSCommon/vs.py index d40ac426ff..1f14376251 100644 --- a/SCons/Tool/MSCommon/vs.py +++ b/SCons/Tool/MSCommon/vs.py @@ -43,8 +43,7 @@ find_vc_pdir, get_msvc_version_numeric, reset_installed_vcs, - vswhere_register_reset_func, - vswhere_update_msvc, + vswhere_freeze_env, ) @@ -448,12 +447,7 @@ def reset(self) -> None: def get_installed_visual_studios(env=None): global InstalledVSList global InstalledVSMap - debug('') - # Calling vswhere_update_msvc may cause installed caches to be cleared. - # The installed vcs and visual studios are cleared when new vc roots - # are discovered which causes the InstalledVSList and InstalledVSMap - # variables to be set to None. - vswhere_update_msvc(env) + vswhere_freeze_env(env) if InstalledVSList is None: InstalledVSList = [] InstalledVSMap = {} @@ -486,19 +480,6 @@ def reset_installed_visual_studios() -> None: # installed VS's reset_installed_vcs() -def _reset_installed_vswhere_visual_studios(): - # vswhere found new roots: reset VS2017 and later - global InstalledVSList - global InstalledVSMap - debug('') - InstalledVSList = None - InstalledVSMap = None - for vs in SupportedVSWhereList: - vs.reset() - -# register vswhere callback function -vswhere_register_reset_func(_reset_installed_vswhere_visual_studios) - # We may be asked to update multiple construction environments with # SDK information. When doing this, we check on-disk for whether diff --git a/SCons/Tool/msvc.py b/SCons/Tool/msvc.py index 6afa171c97..b823752b50 100644 --- a/SCons/Tool/msvc.py +++ b/SCons/Tool/msvc.py @@ -296,9 +296,6 @@ def generate(env) -> None: # without it for lex generation env["LEXUNISTD"] = SCons.Util.CLVar("--nounistd") - # Get user specified vswhere location or locate. - env['VSWHERE'] = env.get('VSWHERE', msvc_find_vswhere()) - # Set-up ms tools paths msvc_setup_env_once(env, tool=tool_name) diff --git a/SCons/Tool/msvsTests.py b/SCons/Tool/msvsTests.py index 63d91ce2fa..7893cafdcc 100644 --- a/SCons/Tool/msvsTests.py +++ b/SCons/Tool/msvsTests.py @@ -420,6 +420,11 @@ def get(self, name, value=None): def Dir(self, name): return self.fs.Dir(name) + def subst(self, key): + if key[0] == '$': + key = key[1:] + return self[key] + class RegKey: """key class for storing an 'open' registry key""" @@ -579,7 +584,7 @@ def DummyQueryValue(key, value): def DummyExists(path) -> bool: return True -def DummyVsWhere(msvc_version, env): +def DummyVsWhere(msvc_version, vswhere_exe): # not testing versions with vswhere, so return none return None @@ -947,7 +952,7 @@ class msvsEmptyTestCase(msvsTestCase): SCons.Util.RegEnumKey = DummyEnumKey SCons.Util.RegEnumValue = DummyEnumValue SCons.Util.RegQueryValueEx = DummyQueryValue - SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere + SCons.Tool.MSCommon.vc._find_vc_pdir_vswhere = DummyVsWhere os.path.exists = DummyExists # make sure all files exist :-) os.path.isfile = DummyExists # make sure all files are files :-) diff --git a/test/MSVC/VSWHERE-fixture/SConstruct b/test/MSVC/VSWHERE-fixture/SConstruct index 74eea18c16..0346f802e0 100644 --- a/test/MSVC/VSWHERE-fixture/SConstruct +++ b/test/MSVC/VSWHERE-fixture/SConstruct @@ -5,12 +5,16 @@ import os import os.path -from SCons.Tool.MSCommon.vc import VSWHERE_PATHS +import SCons.Tool.MSCommon as mscommon +import SCons.Tool.MSCommon.vc as vc # Dump out expected paths -for vw_path in VSWHERE_PATHS: - print("VSWHERE_PATH=%s" % vw_path) - +for exegroup in ( + vc._VSWHERE_EXEGROUP_MSVS, + vc._VSWHERE_EXEGROUP_PKGMGR +): + for vw_path in exegroup: + print("VSWHERE_PATH=%s" % vw_path) # Allow normal detection logic to find vswhere.exe DefaultEnvironment(tools=[]) @@ -21,6 +25,19 @@ print("VSWHERE-detect=%s" % env1['VSWHERE']) v_local = os.path.join(os.getcwd(), 'vswhere.exe') Execute(Copy(os.path.join(os.getcwd(), 'vswhere.exe'), env1['VSWHERE'])) +# Reset vswhere executable manager +# A vswhere.exe not equal to the vswhere.exe for initial detection is an error +vc._VSWhereExecutable.reset() + # With VSWHERE set to copied vswhere.exe (see above), find vswhere.exe env = Environment(VSWHERE=v_local) print("VSWHERE-env=%s" % env['VSWHERE']) + +# Reset vswhere executable manager +# A vswhere.exe not equal to the vswhere.exe for initial detection is an error +vc._VSWhereExecutable.reset() + +# With VSWHERE set to copied vswhere.exe (see above), find vswhere.exe +mscommon.vswhere_register_executable(v_local, priority='high') +env = Environment() +print("VSWHERE-util=%s" % env['VSWHERE']) diff --git a/test/MSVC/VSWHERE.py b/test/MSVC/VSWHERE.py index e50e42a26e..b38ae5f2da 100644 --- a/test/MSVC/VSWHERE.py +++ b/test/MSVC/VSWHERE.py @@ -62,11 +62,14 @@ detected_path = l.strip().split('=')[-1] elif 'VSWHERE-env' in l: env_path = l.strip().split('=')[-1] + elif 'VSWHERE-util' in l: + util_path = l.strip().split('=')[-1] # Debug code # print("VPP:%s" % default_locations) # print("V-D:%s" % detected_path) # print("V-E:%s" % env_path) +# print("V-U:%s" % util_path) test.fail_test( @@ -87,6 +90,12 @@ message='VSWHERE not\n\t%s\n\t but\n\t%s' % (expected_env_path, env_path), ) +expected_util_path = os.path.join(test.workdir, 'vswhere.exe') +test.fail_test( + util_path != expected_env_path, + message='VSWHERE not\n\t%s\n\t but\n\t%s' % (expected_util_path, util_path), +) + test.pass_test() # here for reference, unused @@ -99,6 +108,7 @@ VSWHERE-detect=C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe Copy("C:\Users\Bill\AppData\Local\Temp\testcmd.11256.1ae1_as5\vswhere.exe", "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe") VSWHERE-env=C:\Users\Bill\AppData\Local\Temp\testcmd.11256.1ae1_as5\vswhere.exe +VSWHERE-util=C:\Users\Bill\AppData\Local\Temp\testcmd.11256.1ae1_as5\vswhere.exe scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct.py b/test/fixture/no_msvc/no_msvcs_sconstruct.py index 1aed9ecc1c..8dba65acba 100644 --- a/test/fixture/no_msvc/no_msvcs_sconstruct.py +++ b/test/fixture/no_msvc/no_msvcs_sconstruct.py @@ -7,7 +7,7 @@ DefaultEnvironment(tools=[]) -def DummyVsWhere(msvc_version, env): +def DummyVsWhere(msvc_version, vswhere_exe): # not testing versions with vswhere, so return none return None @@ -16,6 +16,6 @@ def DummyVsWhere(msvc_version, env): (SCons.Util.HKEY_LOCAL_MACHINE, r'') ] -SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere +SCons.Tool.MSCommon.vc._find_vc_pdir_vswhere = DummyVsWhere env = SCons.Environment.Environment() print('MSVC_VERSION=' + str(env.get('MSVC_VERSION'))) diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py index fc5558b54a..9fe9440321 100644 --- a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py @@ -7,7 +7,7 @@ DefaultEnvironment(tools=[]) -def DummyVsWhere(msvc_version, env): +def DummyVsWhere(msvc_version, vswhere_exe): # not testing versions with vswhere, so return none return None @@ -16,6 +16,6 @@ def DummyVsWhere(msvc_version, env): (SCons.Util.HKEY_LOCAL_MACHINE, r'') ] -SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere +SCons.Tool.MSCommon.vc._find_vc_pdir_vswhere = DummyVsWhere msvc_version, msvc_toolset_version = SCons.Tool.MSCommon.msvc_query_version_toolset() print(f'msvc_version={msvc_version!r}, msvc_toolset_version={msvc_toolset_version!r}') diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py index 6e7562d390..1880b8bf7a 100644 --- a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py @@ -7,7 +7,7 @@ DefaultEnvironment(tools=[]) -def DummyVsWhere(msvc_version, env): +def DummyVsWhere(msvc_version, vswhere_exe): # not testing versions with vswhere, so return none return None @@ -16,6 +16,6 @@ def DummyVsWhere(msvc_version, env): (SCons.Util.HKEY_LOCAL_MACHINE, r'') ] -SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere +SCons.Tool.MSCommon.vc._find_vc_pdir_vswhere = DummyVsWhere sdk_version_list = SCons.Tool.MSCommon.msvc_sdk_versions() print('sdk_version_list=' + repr(sdk_version_list)) diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py index c2208cd393..35a45c3b62 100644 --- a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py @@ -7,7 +7,7 @@ DefaultEnvironment(tools=[]) -def DummyVsWhere(msvc_version, env): +def DummyVsWhere(msvc_version, vswhere_exe): # not testing versions with vswhere, so return none return None @@ -16,6 +16,6 @@ def DummyVsWhere(msvc_version, env): (SCons.Util.HKEY_LOCAL_MACHINE, r'') ] -SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere +SCons.Tool.MSCommon.vc._find_vc_pdir_vswhere = DummyVsWhere toolset_version_list = SCons.Tool.MSCommon.msvc_toolset_versions() print('toolset_version_list=' + repr(toolset_version_list)) diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_tools.py b/test/fixture/no_msvc/no_msvcs_sconstruct_tools.py index 2f2f07aedc..baac01bb2c 100644 --- a/test/fixture/no_msvc/no_msvcs_sconstruct_tools.py +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_tools.py @@ -7,7 +7,7 @@ DefaultEnvironment(tools=[]) -def DummyVsWhere(msvc_version, env): +def DummyVsWhere(msvc_version, vswhere_exe): # not testing versions with vswhere, so return none return None @@ -16,6 +16,6 @@ def DummyVsWhere(msvc_version, env): (SCons.Util.HKEY_LOCAL_MACHINE, r'') ] -SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere +SCons.Tool.MSCommon.vc._find_vc_pdir_vswhere = DummyVsWhere env = SCons.Environment.Environment(tools=['myignoredefaultmsvctool']) diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_version.py b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py index bd81688e35..3bf15d35e9 100644 --- a/test/fixture/no_msvc/no_msvcs_sconstruct_version.py +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py @@ -7,7 +7,7 @@ DefaultEnvironment(tools=[]) -def DummyVsWhere(msvc_version, env): +def DummyVsWhere(msvc_version, vswhere_exe): # not testing versions with vswhere, so return none return None @@ -16,6 +16,6 @@ def DummyVsWhere(msvc_version, env): (SCons.Util.HKEY_LOCAL_MACHINE, r'') ] -SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere +SCons.Tool.MSCommon.vc._find_vc_pdir_vswhere = DummyVsWhere SCons.Tool.MSCommon.msvc_set_notfound_policy('error') env = SCons.Environment.Environment(MSVC_VERSION='14.3') From 7dd8caac555cc97ae46e245be1801b4ffd2e8b8a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 23 Mar 2024 10:57:31 -0400 Subject: [PATCH 022/386] Remove orphaned warning and update CHANGES.txt --- CHANGES.txt | 9 +++------ SCons/Tool/MSCommon/MSVC/Warnings.py | 3 --- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index dca9d509de..e4f6a92ba1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -47,12 +47,6 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER selected before a full development edition when both editions are installed. - The registry detection of VS2015 (14.0), and earlier, is now cached at runtime and is only evaluated once for each msvc version. - - The vswhere detection of VS2017 (14.1), and later, is now cached at runtime and is - only evaluated once for each vswhere executable. The detected msvc instances for - all vswhere executables are merged (i.e., detected msvc instances are the union - of the results from all vswhere executables). There is a single invocation for - each vswhere executable that processes the output for all installations. Prior - implementations called the vswhere executable for each supported msvc version. - The vswhere executable is frozen upon initial detection. Specifying a different vswhere executable via the construction variable VSWHERE after the initial detection now results in an exception. Multiple bugs in the implementation of @@ -63,6 +57,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER For example, when no vswhere executable is found for the initial detection and then later an environment is constructed with a user specified vswhere executable that detects new msvc installations. + - The vswhere detection of VS2017 (14.1), and later, is now cached at runtime and is + only evaluated once using a single vswhere invocation for all installations. + Previously, the vswhere executable was invoked for each supported msvc version. From Mats Wichmann: diff --git a/SCons/Tool/MSCommon/MSVC/Warnings.py b/SCons/Tool/MSCommon/MSVC/Warnings.py index 902de299a6..cab5145a99 100644 --- a/SCons/Tool/MSCommon/MSVC/Warnings.py +++ b/SCons/Tool/MSCommon/MSVC/Warnings.py @@ -30,9 +30,6 @@ class VisualCWarning(SCons.Warnings.WarningOnByDefault): pass -class VSWherePathWarning(VisualCWarning): - pass - class MSVCScriptExecutionWarning(VisualCWarning): pass From 9714abc7704897e782770011dae14aa2c5add38a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 24 Mar 2024 07:53:42 -0400 Subject: [PATCH 023/386] Update documentation. --- CHANGES.txt | 5 +++- RELEASE.txt | 8 +++++++ SCons/Tool/msvc.xml | 57 ++++++++++++++++++++++++++------------------- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e4f6a92ba1..99e1938954 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -58,8 +58,11 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER and then later an environment is constructed with a user specified vswhere executable that detects new msvc installations. - The vswhere detection of VS2017 (14.1), and later, is now cached at runtime and is - only evaluated once using a single vswhere invocation for all installations. + only evaluated once using a single vswhere invocation for all msvc versions. Previously, the vswhere executable was invoked for each supported msvc version. + - The vswhere executable locations for the WinGet and Scoop package managers were + added to the default vswhere executable search list after the Chocolatey + installation location. From Mats Wichmann: diff --git a/RELEASE.txt b/RELEASE.txt index e549ec76d2..4a10bf7d27 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -35,6 +35,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY (14.0). This behavior was extended to Visual Studio 2017 (14.1) and Visual Studio 2008 (8.0). An express installation of the IDE binary is used when no other IDE edition is detected. +- The vswhere executable locations for the WinGet and Scoop package managers were + added to the default vswhere executable search list after the Chocolatey + installation location. + FIXES ----- @@ -79,6 +83,10 @@ IMPROVEMENTS - MSVC: Visual Studio 2015 BuildTools (14.0) does not support the sdk version argument and does not support the store argument. Script argument validation now takes into account these restrictions. +- MSVC: The registry detection of VS2015 (14.0), and earlier, is now cached at runtime + and is only evaluated once for each msvc version. +- MSVC: The vswhere detection of VS2017 (14.1), and later, is now cached at runtime and + is only evaluated once using a single vswhere invocation for all msvc versions. PACKAGING --------- diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index e6cbca6841..2b8f8a4f13 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -400,24 +400,25 @@ latest version of &MSVC; installed on your system. The valid values for &cv-MSVC_VERSION; represent major versions of the compiler, except that versions ending in Exp -refer to "Express" or "Express for Desktop" Visual Studio editions, -which require distinct entries because they use a different -filesystem layout and have feature limitations compared to -the full version. +refer to "Express" or "Express for Desktop" Visual Studio editions. Values that do not look like a valid compiler version string are not supported. -To have the desired effect, &cv-MSVC_VERSION; must be set by the -time compiler discovery takes place. -If the default tools list -or an explicit tools list including &t-link-msvc; is used, -discovery takes place as the &consenv; is created, -so passing it as an argument in the the &f-link-Environment; call +To have the desired effect, &cv-MSVC_VERSION; must be set before an msvc &f-link-Tool; +(e.g., &t-link-msvc;) or an msvc-dependent &f-link-Tool; (e.g., &t-link-midl;) is loaded +into the &consenv;. + + + +If the default tools list or an explicit tools list is used that includes an +msvc &f-link-Tool; (e.g., &t-link-msvc;) or an msvc-dependent &f-link-Tool; (e.g., &t-link-midl;); +the &cv-MSVC_VERSION; is resolved when the &consenv; is created. +In this case, passing &cv-MSVC_VERSION; as an argument in the &f-link-Environment; call is the effective solution. -Otherwise, &cv-MSVC_VERSION; must be set before the first msvc tool is -loaded into the environment. +Otherwise, &cv-MSVC_VERSION; must be set before the first msvc &f-link-Tool; or +msvc-dependent &f-link-Tool; is loaded into the environment. See the manpage section "Construction Environments" for an example. @@ -475,10 +476,10 @@ Visual Studio "14.1Exp" - 14.1 - 1910 + 14.1 or 14.1x + 191x Visual Studio 2017 Express - 15.0 + 15.x "14.0" @@ -864,34 +865,42 @@ Specify the location of vswhere.exe. The vswhere.exe executable is distributed with Microsoft Visual Studio and Build - Tools since the 2017 edition, but is also available standalone. + Tools since the 2017 edition, but is also available as a standalone installation. It provides full information about installations of 2017 and later editions. With the argument, vswhere.exe can detect installations of the 2010 through 2015 editions with limited data returned. -If VSWHERE is set, &SCons; will use that location. - Otherwise &SCons; will look in the following locations and set VSWHERE to the path of the first vswhere.exe -located. + If &cv-VSWHERE; is set to a vswhere.exe location, &SCons; will use that location. + When &cv-VSWHERE; is undefined, &SCons; will look in the following locations and set &cv-VSWHERE; to the path + of the first vswhere.exe located: %ProgramFiles(x86)%\Microsoft Visual Studio\Installer %ProgramFiles%\Microsoft Visual Studio\Installer %ChocolateyInstall%\bin +%LOCALAPPDATA%\Microsoft\WinGet\Links +~\scoop\shims +%SCOOP%\shims - Note that VSWHERE must be set at the same time or prior to any of &t-link-msvc;, &t-link-msvs; , and/or &t-link-mslink; &f-link-Tool; being initialized. + Note that &cv-VSWHERE; must be set prior to the initial &MSVC; compiler discovery. + For example, &cv-VSWHERE; must be set at the same time or before the first msvc &f-link-Tool; + (e.g., &t-link-msvc;) or msvc-dependent &f-link-Tool; (e.g., &t-link-midl;) is initialized. + - Either set it as follows + + Either set it as follows: env = Environment(VSWHERE='c:/my/path/to/vswhere') -or if your &consenv; is created specifying an empty tools list -(or a list of tools which omits all of default, msvs, msvc, and mslink), -and also before &f-link-env-Tool; is called to ininitialize any of those tools: +Or, if your &consenv; is created specifying: (a) an empty tools list, or (b) +a list of tools which omits all of default, msvc (e.g., &t-link-msvc;), and +msvc-dependent tools (e.g., &t-link-midl;); and before &f-link-env-Tool; +is called to initialize any of those tools: env = Environment(tools=[]) From 1dacd31dd618640cc7cdb003114bd05f4149cfa3 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 24 Mar 2024 16:20:59 -0400 Subject: [PATCH 024/386] Rework vswhere executable filter/seen handling. --- SCons/Tool/MSCommon/MSVC/Kind.py | 1 - SCons/Tool/MSCommon/vc.py | 150 ++++++++++++++----------------- SCons/Tool/MSCommon/vs.py | 4 - 3 files changed, 69 insertions(+), 86 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Kind.py b/SCons/Tool/MSCommon/MSVC/Kind.py index d91e6a29fb..47d4a25d9f 100644 --- a/SCons/Tool/MSCommon/MSVC/Kind.py +++ b/SCons/Tool/MSCommon/MSVC/Kind.py @@ -177,7 +177,6 @@ def msvc_version_register_kind(msvc_version, kind_t) -> None: global _cache_vcver_kind_map if kind_t is None: kind_t = _VCVER_DETECT_KIND_UNKNOWN - # print('register_kind: msvc_version=%s, kind=%s' % (repr(msvc_version), repr(VCVER_KIND_STR[kind_t.kind]))) debug('msvc_version=%s, kind=%s', repr(msvc_version), repr(VCVER_KIND_STR[kind_t.kind])) _cache_vcver_kind_map[msvc_version] = kind_t diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 126d2588ce..e5ca7d8e06 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1272,8 +1272,6 @@ def reset(cls): cls.seen_vswhere = set() cls.seen_root = set() - cls.vswhere_executables = [] - cls.msvc_instances = [] cls.msvc_map = {} @@ -1283,20 +1281,20 @@ def _initialize(cls): cls.reset() @classmethod - def _new_roots_discovered(cls): - # TODO(JCB) should not happen due to freezing vswhere.exe - # need sanity check? - pass + def _filter_vswhere_binary(cls, vswhere_binary): - @classmethod - def _filter_vswhere_paths(cls, vswhere_exe): + vswhere_norm = None - vswhere_paths = [] + if vswhere_binary.vswhere_norm not in cls.seen_vswhere: + cls.seen_vswhere.add(vswhere_binary.vswhere_norm) + vswhere_norm = vswhere_binary.vswhere_norm - if vswhere_exe and vswhere_exe not in cls.seen_vswhere: - vswhere_paths.append(vswhere_exe) + return vswhere_norm - return vswhere_paths + @classmethod + def _new_roots_discovered(cls): + if len(cls.seen_vswhere) > 1: + raise MSVCInternalError(f'vswhere discovered new msvc installations after initial detection') @classmethod def _vswhere_query_json_output(cls, vswhere_exe, vswhere_args): @@ -1352,95 +1350,85 @@ def vswhere_update_msvc_map(cls, vswhere_exe): frozen_binary, vswhere_binary = _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe) - vswhere_exe = frozen_binary.vswhere_exe - - vswhere_paths = cls._filter_vswhere_paths(vswhere_exe) - if not vswhere_paths: + vswhere_norm = cls._filter_vswhere_binary(frozen_binary) + if not vswhere_norm: return cls.msvc_map - n_instances = len(cls.msvc_instances) + debug('vswhere_norm=%s', repr(vswhere_norm), extra=cls.debug_extra) - for vswhere_exe in vswhere_paths: + vswhere_json = cls._vswhere_query_json_output( + vswhere_norm, + ['-all', '-products', '*'] + ) - if vswhere_exe in cls.seen_vswhere: - continue + if not vswhere_json: + return cls.msvc_map - cls.seen_vswhere.add(vswhere_exe) - cls.vswhere_executables.append(vswhere_exe) + n_instances = len(cls.msvc_instances) - debug('vswhere_exe=%s', repr(vswhere_exe), extra=cls.debug_extra) + for instance in vswhere_json: - vswhere_json = cls._vswhere_query_json_output( - vswhere_exe, - ['-all', '-products', '*'] - ) + #print(json.dumps(instance, indent=4, sort_keys=True)) - if not vswhere_json: + installation_path = instance.get('installationPath') + if not installation_path or not os.path.exists(installation_path): continue - for instance in vswhere_json: - - #print(json.dumps(instance, indent=4, sort_keys=True)) - - installation_path = instance.get('installationPath') - if not installation_path or not os.path.exists(installation_path): - continue - - vc_path = os.path.join(installation_path, 'VC') - if not os.path.exists(vc_path): - continue + vc_path = os.path.join(installation_path, 'VC') + if not os.path.exists(vc_path): + continue - vc_root = MSVC.Util.normalize_path(vc_path) - if vc_root in cls.seen_root: - continue - cls.seen_root.add(vc_root) + vc_root = MSVC.Util.normalize_path(vc_path) + if vc_root in cls.seen_root: + continue + cls.seen_root.add(vc_root) - installation_version = instance.get('installationVersion') - if not installation_version: - continue + installation_version = instance.get('installationVersion') + if not installation_version: + continue - vs_major = installation_version.split('.')[0] - if not vs_major in _VSWHERE_VSMAJOR_TO_VCVERSION: - debug('ignore vs_major: %s', vs_major, extra=cls.debug_extra) - continue + vs_major = installation_version.split('.')[0] + if not vs_major in _VSWHERE_VSMAJOR_TO_VCVERSION: + debug('ignore vs_major: %s', vs_major, extra=cls.debug_extra) + continue - vc_version = _VSWHERE_VSMAJOR_TO_VCVERSION[vs_major] + vc_version = _VSWHERE_VSMAJOR_TO_VCVERSION[vs_major] - product_id = instance.get('productId') - if not product_id: - continue + product_id = instance.get('productId') + if not product_id: + continue - component_id = product_id.split('.')[-1] - if component_id not in _VSWHERE_COMPONENTID_CANDIDATES: - debug('ignore component_id: %s', component_id, extra=cls.debug_extra) - continue + component_id = product_id.split('.')[-1] + if component_id not in _VSWHERE_COMPONENTID_CANDIDATES: + debug('ignore component_id: %s', component_id, extra=cls.debug_extra) + continue - component_rank = _VSWHERE_COMPONENTID_RANKING.get(component_id,0) - if component_rank == 0: - raise MSVCInternalError(f'unknown component_rank for component_id: {component_id!r}') + component_rank = _VSWHERE_COMPONENTID_RANKING.get(component_id,0) + if component_rank == 0: + raise MSVCInternalError(f'unknown component_rank for component_id: {component_id!r}') - scons_suffix = _VSWHERE_COMPONENTID_SCONS_SUFFIX[component_id] + scons_suffix = _VSWHERE_COMPONENTID_SCONS_SUFFIX[component_id] - if scons_suffix: - vc_version_scons = vc_version + scons_suffix - else: - vc_version_scons = vc_version - - is_prerelease = True if instance.get('isPrerelease', False) else False - is_release = False if is_prerelease else True - - msvc_instance = MSVCInstance( - vc_path = vc_path, - vc_version = vc_version, - vc_version_numeric = float(vc_version), - vc_version_scons = vc_version_scons, - vc_release = is_release, - vc_component_id = component_id, - vc_component_rank = component_rank, - vc_component_suffix = component_suffix, - ) + if scons_suffix: + vc_version_scons = vc_version + scons_suffix + else: + vc_version_scons = vc_version + + is_prerelease = True if instance.get('isPrerelease', False) else False + is_release = False if is_prerelease else True + + msvc_instance = MSVCInstance( + vc_path = vc_path, + vc_version = vc_version, + vc_version_numeric = float(vc_version), + vc_version_scons = vc_version_scons, + vc_release = is_release, + vc_component_id = component_id, + vc_component_rank = component_rank, + vc_component_suffix = component_suffix, + ) - cls.msvc_instances.append(msvc_instance) + cls.msvc_instances.append(msvc_instance) new_roots = bool(len(cls.msvc_instances) > n_instances) if new_roots: diff --git a/SCons/Tool/MSCommon/vs.py b/SCons/Tool/MSCommon/vs.py index 1f14376251..34fb670b5f 100644 --- a/SCons/Tool/MSCommon/vs.py +++ b/SCons/Tool/MSCommon/vs.py @@ -426,12 +426,8 @@ def reset(self) -> None: ), ] -SupportedVSWhereList = [] SupportedVSMap = {} for vs in SupportedVSList: - if vs.vernum >= 14.1: - # VS2017 and later detected via vswhere.exe - SupportedVSWhereList.append(vs) SupportedVSMap[vs.version] = vs From 41308408334e4865ba8db77f96e964d8d5bad271 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 25 Mar 2024 06:50:54 -0400 Subject: [PATCH 025/386] Update MSCommon/README.rst formatting. --- SCons/Tool/MSCommon/README.rst | 225 +++++++++++++++------------------ 1 file changed, 99 insertions(+), 126 deletions(-) diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index 4a7acc59b4..4ff8d7f913 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -33,55 +33,37 @@ MSVC Detection Priority For msvc version specifications without an 'Exp' suffix, an express installation is used only when no other installation is detected. -======= ======= ======================================================== -Product VCVer Priority -======= ======= ======================================================== -VS2022 14.3 Enterprise, Professional, Community, BuildTools -------- ------- -------------------------------------------------------- -VS2019 14.2 Enterprise, Professional, Community, BuildTools -------- ------- -------------------------------------------------------- -VS2017 14.1 Enterprise, Professional, Community, BuildTools, Express -------- ------- -------------------------------------------------------- -VS2017 14.1Exp Express -------- ------- -------------------------------------------------------- -VS2015 14.0 [Develop, BuildTools, CmdLine], Express -------- ------- -------------------------------------------------------- -VS2015 14.0Exp Express -------- ------- -------------------------------------------------------- -VS2013 12.0 Develop, Express -------- ------- -------------------------------------------------------- -VS2013 12.0Exp Express -------- ------- -------------------------------------------------------- -VS2012 11.0 Develop, Express -------- ------- -------------------------------------------------------- -VS2012 11.0Exp Express -------- ------- -------------------------------------------------------- -VS2010 10.0 Develop, Express -------- ------- -------------------------------------------------------- -VS2010 10.0Exp Express -------- ------- -------------------------------------------------------- -VS2008 9.0 Develop, VCForPython, Express -------- ------- -------------------------------------------------------- -VS2008 9.0Exp Express -------- ------- -------------------------------------------------------- -VS2005 8.0 Develop, Express -------- ------- -------------------------------------------------------- -VS2005 8.0Exp Express -------- ------- -------------------------------------------------------- -VS2003 7.1 Develop -------- ------- -------------------------------------------------------- -VS2002 7.0 Develop -------- ------- -------------------------------------------------------- -VS6.0 6.0 Develop -======= ======= ======================================================== +======= ======= ======================================================== +Product VCVer Priority +======= ======= ======================================================== +VS2022 14.3 Enterprise, Professional, Community, BuildTools +VS2019 14.2 Enterprise, Professional, Community, BuildTools +VS2017 14.1 Enterprise, Professional, Community, BuildTools, Express +VS2017 14.1Exp Express +VS2015 14.0 [Develop, BuildTools, CmdLine], Express +VS2015 14.0Exp Express +VS2013 12.0 Develop, Express +VS2013 12.0Exp Express +VS2012 11.0 Develop, Express +VS2012 11.0Exp Express +VS2010 10.0 Develop, Express +VS2010 10.0Exp Express +VS2008 9.0 Develop, VCForPython, Express +VS2008 9.0Exp Express +VS2005 8.0 Develop, Express +VS2005 8.0Exp Express +VS2003 7.1 Develop +VS2002 7.0 Develop +VS6.0 6.0 Develop +======= ======= ======================================================== Legend: Develop - devenv.com (or msdev.com) is detected. + devenv.com or msdev.com is detected. Express - WDExpress.exe (or VCExpress.exe) is detected. + WDExpress.exe or VCExpress.exe is detected. BuildTools [VS2015] The vcvarsall batch file dispatches to the buildtools batch file. @@ -104,7 +86,9 @@ and/or linker build failures. The VS2015 BuildTools ``vcvarsall.bat`` batch file dispatches to the stand-alone buildtools batch file under certain circumstances. A fragment from the vcvarsall batch file is: + :: + if exist "%~dp0..\common7\IDE\devenv.exe" goto setup_VS if exist "%~dp0..\common7\IDE\wdexpress.exe" goto setup_VS if exist "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" goto setup_buildsku @@ -135,7 +119,9 @@ As installed, VS2015 Express does not support the ``store`` argument for the ``a architecture. The generated ``store`` library paths include directories that do not exist. The store library paths appear in two places in the ``vcvarsx86_amd64`` batch file: + :: + :setstorelib @if exist "%VCINSTALLDIR%LIB\amd64\store" set LIB=%VCINSTALLDIR%LIB\amd64\store;%LIB% ... @@ -143,7 +129,9 @@ The store library paths appear in two places in the ``vcvarsx86_amd64`` batch fi @if exist "%VCINSTALLDIR%LIB\amd64\store" set LIBPATH=%VCINSTALLDIR%LIB\amd64\store;%LIBPATH% The correct store library paths would be: + :: + :setstorelib @if exist "%VCINSTALLDIR%LIB\store\amd64" set LIB=%VCINSTALLDIR%LIB\store\amd64;%LIB% ... @@ -157,7 +145,9 @@ As installed, VS2015 Express does not support the ``store`` argument for the ``a architecture. The generated ``store`` library paths include directories that do not exist. The store library paths appear in two places in the ``vcvarsx86_arm`` batch file: + :: + :setstorelib @if exist "%VCINSTALLDIR%LIB\ARM\store" set LIB=%VCINSTALLDIR%LIB\ARM\store;%LIB% ... @@ -165,7 +155,9 @@ The store library paths appear in two places in the ``vcvarsx86_arm`` batch file @if exist "%VCINSTALLDIR%LIB\ARM\store" set LIBPATH=%VCINSTALLDIR%LIB\ARM\store;%LIBPATH% The correct store library paths would be file: + :: + :setstorelib @if exist "%VCINSTALLDIR%LIB\store\ARM" set LIB=%VCINSTALLDIR%LIB\store\ARM;%LIB% ... @@ -208,6 +200,7 @@ returns the msvc version and the msvc toolset version for the corresponding vers This is a proxy for using the toolset version for selection until that functionality can be added. Example usage: + :: for version in [ @@ -240,6 +233,7 @@ Example usage: print('{}Query: {} version={}, prefer_newest={}'.format(newline, msg, version, prefer_newest)) Example output fragment + :: Build: _build003 {'MSVC_VERSION': '14.3', 'MSVC_TOOLSET_VERSION': '14.29.30133'} @@ -267,6 +261,7 @@ added to the batch file argument list. This is intended to make the cache more updates that may change the default toolset version and/or the default SDK version. Example usage: + :: @echo Enabling scons cache ... @@ -289,6 +284,7 @@ Enabling warnings to be produced for detected msvc batch file errors may provide for build failures. Refer to the documentation for details. Change the default policy: + :: from SCons.Tool.MSCommon import msvc_set_scripterror_policy @@ -296,6 +292,7 @@ Change the default policy: msvc_set_scripterror_policy('Warning') Specify the policy per-environment: + :: env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True, MSVC_SCRIPTERROR_POLICY='Warning') @@ -321,6 +318,7 @@ On occasion, the raw vswhere output may prove useful especially if there are sus detection of installed msvc instances. Windows command-line sample invocations: + :: @rem 64-Bit Windows @@ -338,17 +336,14 @@ Batch File Arguments Supported MSVC batch file arguments by product: -======= ======= ====== ======= ======= -Product UWP SDK Toolset Spectre -======= ======= ====== ======= ======= -VS2022 X X X X -------- ------- ------ ------- ------- -VS2019 X X X X -------- ------- ------ ------- ------- -VS2017 X X X X -------- ------- ------ ------- ------- -VS2015 X [1]_ X [2]_ -======= ======= ====== ======= ======= +======== ======= ======= ======== ======= +Product UWP SDK Toolset Spectre +======== ======= ======= ======== ======= +VS2022 X X X X +VS2019 X X X X +VS2017 X X X X +VS2015 X [1]_ X [2]_ +======== ======= ======= ======== ======= .. [1] The BuildTools edition does not support the ``store`` argument. The Express edition supports the ``store`` argument for the ``x86`` target only. @@ -356,17 +351,14 @@ VS2015 X [1]_ X [2]_ Supported MSVC batch file arguments in SCons: -======== ====================================== =================================================== -Argument Construction Variable Script Argument Equivalent -======== ====================================== =================================================== -UWP ``MSVC_UWP_APP=True`` ``MSVC_SCRIPT_ARGS='store'`` --------- -------------------------------------- --------------------------------------------------- -SDK ``MSVC_SDK_VERSION='10.0.20348.0'`` ``MSVC_SCRIPT_ARGS='10.0.20348.0'`` --------- -------------------------------------- --------------------------------------------------- -Toolset ``MSVC_TOOLSET_VERSION='14.31.31103'`` ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.31.31103'`` --------- -------------------------------------- --------------------------------------------------- -Spectre ``MSVC_SPECTRE_LIBS=True`` ``MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre'`` -======== ====================================== =================================================== +======== ====================================== =================================================== +Argument Construction Variable Script Argument Equivalent +======== ====================================== =================================================== +UWP ``MSVC_UWP_APP=True`` ``MSVC_SCRIPT_ARGS='store'`` +SDK ``MSVC_SDK_VERSION='10.0.20348.0'`` ``MSVC_SCRIPT_ARGS='10.0.20348.0'`` +Toolset ``MSVC_TOOLSET_VERSION='14.31.31103'`` ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.31.31103'`` +Spectre ``MSVC_SPECTRE_LIBS=True`` ``MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre'`` +======== ====================================== =================================================== **MSVC_SCRIPT_ARGS contents are not validated. Utilizing script arguments that have construction variable equivalents is discouraged and may lead to difficult to diagnose build errors.** @@ -398,6 +390,7 @@ that the msvc batch files would return. When using ``MSVC_SCRIPT_ARGS``, the toolset specification should be omitted entirely. Local installation and summary test results: + :: VS2022\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt @@ -407,6 +400,7 @@ Local installation and summary test results: 14.32.31326 Toolset version summary: + :: 14.31.31103 Environment() @@ -422,6 +416,7 @@ Toolset version summary: 14.32.31326 Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.32']) VS2022\\Common7\\Tools\\vsdevcmd\\ext\\vcvars.bat usage fragment: + :: @echo -vcvars_ver=version : Version of VC++ Toolset to select @@ -443,6 +438,7 @@ VS2022\\Common7\\Tools\\vsdevcmd\\ext\\vcvars.bat usage fragment: @echo SxS toolset to [VSInstallDir]\VC\MSVC\Tools\ directory. VS2022 batch file fragment to determine the default toolset version: + :: @REM Add MSVC @@ -469,13 +465,12 @@ Visual Studio Version Notes SDK Versions ------------ -==== ================= -SDK Format -==== ================= -10.0 10.0.XXXXX.Y [*]_ ----- ----------------- -8.1 8.1 -==== ================= +==== ================= +SDK Format +==== ================= +10.0 10.0.XXXXX.Y [*]_ +8.1 8.1 +==== ================= .. [*] The Windows 10 SDK version number is 10.0.20348.0 and earlier. @@ -484,64 +479,42 @@ SDK Format BuildTools Versions ------------------- -========== ===== ===== ======== -BuildTools VCVER CLVER MSVCRT -========== ===== ===== ======== -v143 14.3 19.3 140/ucrt ----------- ----- ----- -------- -v142 14.2 19.2 140/ucrt ----------- ----- ----- -------- -v141 14.1 19.1 140/ucrt ----------- ----- ----- -------- -v140 14.0 19.0 140/ucrt ----------- ----- ----- -------- -v120 12.0 18.0 120 ----------- ----- ----- -------- -v110 11.0 17.0 110 ----------- ----- ----- -------- -v100 10.0 16.0 100 ----------- ----- ----- -------- -v90 9.0 15.0 90 ----------- ----- ----- -------- -v80 8.0 14.0 80 ----------- ----- ----- -------- -v71 7.1 13.1 71 ----------- ----- ----- -------- -v70 7.0 13.0 70 ----------- ----- ----- -------- -v60 6.0 12.0 60 -========== ===== ===== ======== +========== ===== ===== ======== +BuildTools VCVER CLVER MSVCRT +========== ===== ===== ======== +v143 14.3 19.3 140/ucrt +v142 14.2 19.2 140/ucrt +v141 14.1 19.1 140/ucrt +v140 14.0 19.0 140/ucrt +v120 12.0 18.0 120 +v110 11.0 17.0 110 +v100 10.0 16.0 100 +v90 9.0 15.0 90 +v80 8.0 14.0 80 +v71 7.1 13.1 71 +v70 7.0 13.0 70 +v60 6.0 12.0 60 +========== ===== ===== ======== Product Versions ---------------- -======== ===== ========= ====================== -Product VSVER SDK BuildTools -======== ===== ========= ====================== -2022 17.0 10.0, 8.1 v143, v142, v141, v140 --------- ----- --------- ---------------------- -2019 16.0 10.0, 8.1 v142, v141, v140 --------- ----- --------- ---------------------- -2017 15.0 10.0, 8.1 v141, v140 --------- ----- --------- ---------------------- -2015 14.0 10.0, 8.1 v140 --------- ----- --------- ---------------------- -2013 12.0 v120 --------- ----- --------- ---------------------- -2012 11.0 v110 --------- ----- --------- ---------------------- -2010 10.0 v100 --------- ----- --------- ---------------------- -2008 9.0 v90 --------- ----- --------- ---------------------- -2005 8.0 v80 --------- ----- --------- ---------------------- -2003.NET 7.1 v71 --------- ----- --------- ---------------------- -2002.NET 7.0 v70 --------- ----- --------- ---------------------- -6.0 6.0 v60 -======== ===== ========= ====================== +========= ====== ========== ====================== +Product VSVER SDK BuildTools +========= ====== ========== ====================== +2022 17.0 10.0, 8.1 v143, v142, v141, v140 +2019 16.0 10.0, 8.1 v142, v141, v140 +2017 15.0 10.0, 8.1 v141, v140 +2015 14.0 10.0, 8.1 v140 +2013 12.0 v120 +2012 11.0 v110 +2010 10.0 v100 +2008 9.0 v90 +2005 8.0 v80 +2003.NET 7.1 v71 +2002.NET 7.0 v70 +6.0 6.0 v60 +========= ====== ========== ====================== SCons Implementation Notes From f747386f833aca843201e9241ba0f83c5ee24b46 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 25 Mar 2024 08:15:56 -0400 Subject: [PATCH 026/386] Convert simple tables to grid tables in MSCommon/README.rst --- SCons/Tool/MSCommon/README.rst | 202 ++++++++++++++++++++------------- 1 file changed, 125 insertions(+), 77 deletions(-) diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index 4ff8d7f913..bcac466759 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -33,29 +33,47 @@ MSVC Detection Priority For msvc version specifications without an 'Exp' suffix, an express installation is used only when no other installation is detected. -======= ======= ======================================================== -Product VCVer Priority -======= ======= ======================================================== -VS2022 14.3 Enterprise, Professional, Community, BuildTools -VS2019 14.2 Enterprise, Professional, Community, BuildTools -VS2017 14.1 Enterprise, Professional, Community, BuildTools, Express -VS2017 14.1Exp Express -VS2015 14.0 [Develop, BuildTools, CmdLine], Express -VS2015 14.0Exp Express -VS2013 12.0 Develop, Express -VS2013 12.0Exp Express -VS2012 11.0 Develop, Express -VS2012 11.0Exp Express -VS2010 10.0 Develop, Express -VS2010 10.0Exp Express -VS2008 9.0 Develop, VCForPython, Express -VS2008 9.0Exp Express -VS2005 8.0 Develop, Express -VS2005 8.0Exp Express -VS2003 7.1 Develop -VS2002 7.0 Develop -VS6.0 6.0 Develop -======= ======= ======================================================== ++---------+---------+----------------------------------------------------------+ +| Product | VCVer | Priority | ++=========+=========+==========================================================+ +| VS2022 | 14.3 | Enterprise, Professional, Community, BuildTools | ++---------+---------+----------------------------------------------------------+ +| VS2019 | 14.2 | Enterprise, Professional, Community, BuildTools | ++---------+---------+----------------------------------------------------------+ +| VS2017 | 14.1 | Enterprise, Professional, Community, BuildTools, Express | ++---------+---------+----------------------------------------------------------+ +| VS2017 | 14.1Exp | Express | ++---------+---------+----------------------------------------------------------+ +| VS2015 | 14.0 | [Develop, BuildTools, CmdLine], Express | ++---------+---------+----------------------------------------------------------+ +| VS2015 | 14.0Exp | Express | ++---------+---------+----------------------------------------------------------+ +| VS2013 | 12.0 | Develop, Express | ++---------+---------+----------------------------------------------------------+ +| VS2013 | 12.0Exp | Express | ++---------+---------+----------------------------------------------------------+ +| VS2012 | 11.0 | Develop, Express | ++---------+---------+----------------------------------------------------------+ +| VS2012 | 11.0Exp | Express | ++---------+---------+----------------------------------------------------------+ +| VS2010 | 10.0 | Develop, Express | ++---------+---------+----------------------------------------------------------+ +| VS2010 | 10.0Exp | Express | ++---------+---------+----------------------------------------------------------+ +| VS2008 | 9.0 | Develop, VCForPython, Express | ++---------+---------+----------------------------------------------------------+ +| VS2008 | 9.0Exp | Express | ++---------+---------+----------------------------------------------------------+ +| VS2005 | 8.0 | Develop, Express | ++---------+---------+----------------------------------------------------------+ +| VS2005 | 8.0Exp | Express | ++---------+---------+----------------------------------------------------------+ +| VS2003 | 7.1 | Develop | ++---------+---------+----------------------------------------------------------+ +| VS2002 | 7.0 | Develop | ++---------+---------+----------------------------------------------------------+ +| VS6.0 | 6.0 | Develop | ++---------+---------+----------------------------------------------------------+ Legend: @@ -336,14 +354,18 @@ Batch File Arguments Supported MSVC batch file arguments by product: -======== ======= ======= ======== ======= -Product UWP SDK Toolset Spectre -======== ======= ======= ======== ======= -VS2022 X X X X -VS2019 X X X X -VS2017 X X X X -VS2015 X [1]_ X [2]_ -======== ======= ======= ======== ======= ++---------+---------+--------+---------+---------+ +| Product | UWP | SDK | Toolset | Spectre | ++=========+=========+========+=========+=========+ +| VS2022 | X | X | X | X | ++---------+---------+--------+---------+---------+ +| VS2019 | X | X | X | X | ++---------+---------+--------+---------+---------+ +| VS2017 | X | X | X | X | ++---------+---------+--------+---------+---------+ +| VS2015 | X [1]_ | X [2]_ | | | ++---------+---------+--------+---------+---------+ + .. [1] The BuildTools edition does not support the ``store`` argument. The Express edition supports the ``store`` argument for the ``x86`` target only. @@ -351,14 +373,17 @@ VS2015 X [1]_ X [2]_ Supported MSVC batch file arguments in SCons: -======== ====================================== =================================================== -Argument Construction Variable Script Argument Equivalent -======== ====================================== =================================================== -UWP ``MSVC_UWP_APP=True`` ``MSVC_SCRIPT_ARGS='store'`` -SDK ``MSVC_SDK_VERSION='10.0.20348.0'`` ``MSVC_SCRIPT_ARGS='10.0.20348.0'`` -Toolset ``MSVC_TOOLSET_VERSION='14.31.31103'`` ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.31.31103'`` -Spectre ``MSVC_SPECTRE_LIBS=True`` ``MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre'`` -======== ====================================== =================================================== ++----------+----------------------------------------+-----------------------------------------------------+ +| Argument | Construction Variable | Script Argument Equivalent | ++==========+========================================+=====================================================+ +| UWP | ``MSVC_UWP_APP=True`` | ``MSVC_SCRIPT_ARGS='store'`` | ++----------+----------------------------------------+-----------------------------------------------------+ +| SDK | ``MSVC_SDK_VERSION='10.0.20348.0'`` | ``MSVC_SCRIPT_ARGS='10.0.20348.0'`` | ++----------+----------------------------------------+-----------------------------------------------------+ +| Toolset | ``MSVC_TOOLSET_VERSION='14.31.31103'`` | ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.31.31103'`` | ++----------+----------------------------------------+-----------------------------------------------------+ +| Spectre | ``MSVC_SPECTRE_LIBS=True`` | ``MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre'`` | ++----------+----------------------------------------+-----------------------------------------------------+ **MSVC_SCRIPT_ARGS contents are not validated. Utilizing script arguments that have construction variable equivalents is discouraged and may lead to difficult to diagnose build errors.** @@ -465,12 +490,13 @@ Visual Studio Version Notes SDK Versions ------------ -==== ================= -SDK Format -==== ================= -10.0 10.0.XXXXX.Y [*]_ -8.1 8.1 -==== ================= ++------+-------------------+ +| SDK | Format | ++======+===================+ +| 10.0 | 10.0.XXXXX.Y [*]_ | ++------+-------------------+ +| 8.1 | 8.1 | ++------+-------------------+ .. [*] The Windows 10 SDK version number is 10.0.20348.0 and earlier. @@ -479,42 +505,64 @@ SDK Format BuildTools Versions ------------------- -========== ===== ===== ======== -BuildTools VCVER CLVER MSVCRT -========== ===== ===== ======== -v143 14.3 19.3 140/ucrt -v142 14.2 19.2 140/ucrt -v141 14.1 19.1 140/ucrt -v140 14.0 19.0 140/ucrt -v120 12.0 18.0 120 -v110 11.0 17.0 110 -v100 10.0 16.0 100 -v90 9.0 15.0 90 -v80 8.0 14.0 80 -v71 7.1 13.1 71 -v70 7.0 13.0 70 -v60 6.0 12.0 60 -========== ===== ===== ======== ++------------+-------+-------+----------+ +| BuildTools | VCVER | CLVER | MSVCRT | ++============+=======+=======+==========+ +| v143 | 14.3 | 19.3 | 140/ucrt | ++------------+-------+-------+----------+ +| v142 | 14.2 | 19.2 | 140/ucrt | ++------------+-------+-------+----------+ +| v141 | 14.1 | 19.1 | 140/ucrt | ++------------+-------+-------+----------+ +| v140 | 14.0 | 19.0 | 140/ucrt | ++------------+-------+-------+----------+ +| v120 | 12.0 | 18.0 | 120 | ++------------+-------+-------+----------+ +| v110 | 11.0 | 17.0 | 110 | ++------------+-------+-------+----------+ +| v100 | 10.0 | 16.0 | 100 | ++------------+-------+-------+----------+ +| v90 | 9.0 | 15.0 | 90 | ++------------+-------+-------+----------+ +| v80 | 8.0 | 14.0 | 80 | ++------------+-------+-------+----------+ +| v71 | 7.1 | 13.1 | 71 | ++------------+-------+-------+----------+ +| v70 | 7.0 | 13.0 | 70 | ++------------+-------+-------+----------+ +| v60 | 6.0 | 12.0 | 60 | ++------------+-------+-------+----------+ Product Versions ---------------- -========= ====== ========== ====================== -Product VSVER SDK BuildTools -========= ====== ========== ====================== -2022 17.0 10.0, 8.1 v143, v142, v141, v140 -2019 16.0 10.0, 8.1 v142, v141, v140 -2017 15.0 10.0, 8.1 v141, v140 -2015 14.0 10.0, 8.1 v140 -2013 12.0 v120 -2012 11.0 v110 -2010 10.0 v100 -2008 9.0 v90 -2005 8.0 v80 -2003.NET 7.1 v71 -2002.NET 7.0 v70 -6.0 6.0 v60 -========= ====== ========== ====================== ++----------+-------+-----------+------------------------+ +| Product | VSVER | SDK | BuildTools | ++==========+=======+===========+========================+ +| 2022 | 17.0 | 10.0, 8.1 | v143, v142, v141, v140 | ++----------+-------+-----------+------------------------+ +| 2019 | 16.0 | 10.0, 8.1 | v142, v141, v140 | ++----------+-------+-----------+------------------------+ +| 2017 | 15.0 | 10.0, 8.1 | v141, v140 | ++----------+-------+-----------+------------------------+ +| 2015 | 14.0 | 10.0, 8.1 | v140 | ++----------+-------+-----------+------------------------+ +| 2013 | 12.0 | | v120 | ++----------+-------+-----------+------------------------+ +| 2012 | 11.0 | | v110 | ++----------+-------+-----------+------------------------+ +| 2010 | 10.0 | | v100 | ++----------+-------+-----------+------------------------+ +| 2008 | 9.0 | | v90 | ++----------+-------+-----------+------------------------+ +| 2005 | 8.0 | | v80 | ++----------+-------+-----------+------------------------+ +| 2003.NET | 7.1 | | v71 | ++----------+-------+-----------+------------------------+ +| 2002.NET | 7.0 | | v70 | ++----------+-------+-----------+------------------------+ +| 6.0 | 6.0 | | v60 | ++----------+-------+-----------+------------------------+ SCons Implementation Notes From 15206e4b5ca1cfeb49c9c00e23033bfac42759e6 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 25 Mar 2024 08:37:18 -0400 Subject: [PATCH 027/386] Change rst footnotes to numbered lists (github rendering places at end of document). --- SCons/Tool/MSCommon/README.rst | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index bcac466759..28511494bc 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -363,13 +363,14 @@ Supported MSVC batch file arguments by product: +---------+---------+--------+---------+---------+ | VS2017 | X | X | X | X | +---------+---------+--------+---------+---------+ -| VS2015 | X [1]_ | X [2]_ | | | +| VS2015 | X [1] | X [2] | | | +---------+---------+--------+---------+---------+ +Notes: -.. [1] The BuildTools edition does not support the ``store`` argument. The Express edition - supports the ``store`` argument for the ``x86`` target only. -.. [2] The ``sdk version`` argument is not supported in the BuildTools and Express editions. +1) The BuildTools edition does not support the ``store`` argument. The Express edition + supports the ``store`` argument for the ``x86`` target only. +2) The ``sdk version`` argument is not supported in the BuildTools and Express editions. Supported MSVC batch file arguments in SCons: @@ -493,14 +494,16 @@ SDK Versions +------+-------------------+ | SDK | Format | +======+===================+ -| 10.0 | 10.0.XXXXX.Y [*]_ | +| 10.0 | 10.0.XXXXX.Y [1] | +------+-------------------+ | 8.1 | 8.1 | +------+-------------------+ -.. [*] The Windows 10 SDK version number is 10.0.20348.0 and earlier. +Notes: - The Windows 11 SDK version number is 10.0.22000.194 and later. +1) The Windows 10 SDK version number is 10.0.20348.0 and earlier. + + The Windows 11 SDK version number is 10.0.22000.194 and later. BuildTools Versions ------------------- From 56e1769b4d9e29d004d52c824f7d91dcb119812f Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:53:35 -0400 Subject: [PATCH 028/386] Update vswhere function api and update MSCommon/README.rst Changes: * add vswhere_exe argument to msvc_quert_version_toolset function in MSCommon/README.rst * fix: pass normalized path for vswhere_norm in vswhere binary named tuple constructor * add freeze argument for vswhere_register_executable --- SCons/Tool/MSCommon/README.rst | 12 +++++++----- SCons/Tool/MSCommon/vc.py | 33 +++++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index 28511494bc..5a72885c00 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -207,13 +207,15 @@ The following issues are known to exist: Experimental Features ===================== -msvc_query_version_toolset(version=None, prefer_newest=True) ------------------------------------------------------------- +msvc_query_version_toolset(version=None, prefer_newest=True, vswhere_exe=None) +------------------------------------------------------------------------------ The experimental function ``msvc_query_version_toolset`` was added to ``MSCommon/vc.py`` -and is available via the ``SCons.Tool.MSCommon`` namespace. This function takes a version -specification or a toolset version specification and a product preference as arguments and -returns the msvc version and the msvc toolset version for the corresponding version specification. +and is available via the ``SCons.Tool.MSCommon`` namespace. + +This function takes a version specification or a toolset version specification, an optional product +preference, and an optional vswhere executable location as arguments and returns the msvc version and +the msvc toolset version for the corresponding version specification. This is a proxy for using the toolset version for selection until that functionality can be added. diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index e5ca7d8e06..0c80363f3a 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -978,7 +978,7 @@ def factory(cls, vswhere_exe): vswhere_binary = cls( vswhere_exe=vswhere_exe, - vswhere_norm=vswhere_exe, + vswhere_norm=vswhere_norm, ) cls._cache_vswhere_paths[vswhere_exe] = vswhere_binary @@ -1103,6 +1103,11 @@ def _vswhere_all_executables(cls): vswhere_exe_list.append(vswhere_binary.vswhere_exe) return vswhere_exe_list + @classmethod + def is_frozen(cls) -> bool: + rval = bool(cls.vswhere_frozen_flag) + return rval + @classmethod def freeze_vswhere_binary(cls): if not cls.vswhere_frozen_flag: @@ -1141,7 +1146,7 @@ def _vswhere_priority_group(cls, priority): return group @classmethod - def register_vswhere_executable(cls, vswhere_exe, priority=None) -> bool: + def register_vswhere_executable(cls, vswhere_exe, priority=None): vswhere_binary = cls.UNDEFINED_VSWHERE_BINARY @@ -1222,11 +1227,27 @@ def vswhere_freeze_env(cls, env): return frozen_binary, vswhere_binary # external use -vswhere_register_executable = _VSWhereExecutable.register_vswhere_executable -vswhere_get_executable = _VSWhereExecutable.get_vswhere_executable -vswhere_freeze_executable = _VSWhereExecutable.freeze_vswhere_executable -# internal use +def vswhere_register_executable(vswhere_exe, priority=None, freeze=False): + debug('register vswhere_exe=%s, priority=%s, freeze=%s', repr(vswhere_exe), repr(priority), repr(freeze)) + _VSWhereExecutable.register_vswhere_executable(vswhere_exe, priority=priority) + if freeze: + _VSWhereExecutable.freeze_vswhere_executable() + rval = _VSWhereExecutable.get_vswhere_executable() + debug('current vswhere_exe=%s, is_frozen=%s', repr(rval), _VSWhereExecutable.is_frozen()) + return vswhere_exe + +def vswhere_get_executable(): + debug('') + vswhere_exe = _VSWhereExecutable.get_vswhere_executable() + return vswhere_exe + +def vswhere_freeze_executable(): + debug('') + vswhere_exe = _VSWhereExecutable.freeze_vswhere_executable() + return vswhere_exe + +# internal use only vswhere_freeze_env = _VSWhereExecutable.vswhere_freeze_env def msvc_find_vswhere(): From e8a541f1948857fc51b7ffc538d8c5b062703200 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 May 2024 05:32:16 -0400 Subject: [PATCH 029/386] Simplify check for VSWHERE undefined, None, or empty. --- SCons/Tool/MSCommon/vc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 0c80363f3a..8a2fd8e026 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1203,11 +1203,11 @@ def vswhere_freeze_executable(cls, vswhere_exe): def vswhere_freeze_env(cls, env): if env is None: - # no environment, no VSWHERE + # no environment, VSWHERE undefined vswhere_exe = None write_vswhere = False - elif 'VSWHERE' not in env or not env['VSWHERE']: - # environment, VSWHERE undefined + elif not env.get('VSWHERE'): + # environment, VSWHERE undefined/none/empty vswhere_exe = None write_vswhere = True else: From 1a7acf0eebd39e3236368bd28f155f7834d3c304 Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Sun, 26 May 2024 12:11:04 -0500 Subject: [PATCH 030/386] Migrate `setup.cfg` logic to `pyproject.toml` --- CHANGES.txt | 1 + RELEASE.txt | 3 +- SConstruct | 4 +-- pyproject.toml | 69 ++++++++++++++++++++++++++++++++++++++++-- setup.cfg | 81 -------------------------------------------------- 5 files changed, 71 insertions(+), 87 deletions(-) delete mode 100644 setup.cfg diff --git a/CHANGES.txt b/CHANGES.txt index 3fe676ab14..cbcb821b7b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Thaddeus Crews: - GetSConsVersion() to grab the latest SCons version without needing to access SCons internals. + - Migrate setup.cfg logic to pyproject.toml; remove setup.cfg. From Raymond Li: - Fix issue #3935: OSErrors are now no longer hidden during execution of diff --git a/RELEASE.txt b/RELEASE.txt index 92e31ab72d..a8d121ef36 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -63,7 +63,8 @@ IMPROVEMENTS PACKAGING --------- -- List changes in the way SCons is packaged and/or released +- setup.cfg logic now handled via pyproject.toml; consequently, setup.cfg + was removed. DOCUMENTATION diff --git a/SConstruct b/SConstruct index 41b1d355be..04bde62850 100644 --- a/SConstruct +++ b/SConstruct @@ -205,7 +205,7 @@ wheel = env.Command( '$DISTDIR/SCons-${VERSION}-py3-none-any.whl', '$DISTDIR/SCons-${VERSION}.tar.gz', ], - source=['setup.cfg', 'setup.py', 'SCons/__init__.py'] + man_pages, + source=['pyproject.toml', 'setup.py', 'SCons/__init__.py'] + man_pages, action='$PYTHON -m build --outdir $DISTDIR', ) env.Alias("wheel", wheel[0]) @@ -215,7 +215,7 @@ env.Alias("tar-gz", wheel[1]) # and it deletes its isolated env so we can't just zip that one up. zip_file = env.Command( target='$DISTDIR/SCons-${VERSION}.zip', - source=['setup.cfg', 'setup.py', 'SCons/__init__.py'] + man_pages, + source=['pyproject.toml', 'setup.py', 'SCons/__init__.py'] + man_pages, action='$PYTHON setup.py sdist --format=zip', ) env.Alias("zip", zip_file) diff --git a/pyproject.toml b/pyproject.toml index 548ae2d65a..8d755dcb2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,12 +2,75 @@ build-backend = "setuptools.build_meta" requires = ["setuptools"] +[project] +name = "SCons" +description = "Open Source next-generation build tool." +requires-python = ">=3.6" +license = { text = "MIT" } +readme = { file = "README-package.rst", content-type = "text/x-rst" } +authors = [{ name = "William Deegan", email = "bill@baddogconsulting.com" }] +dynamic = ["version"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Topic :: Software Development :: Build Tools", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Operating System :: Unix", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", +] + +[project.urls] +Homepage = "https://www.scons.org/" +Documentation = "https://scons.org/documentation.html" +Twitter = "https://twitter.com/SConsProject" +GitHub = "https://github.com/SCons/scons" +Bug-Tracker = "https://github.com/SCons/scons/issues" +Discord = "https://discord.gg/pejaFYrD9n" +"Mailing lists" = "https://scons.org/lists.html" + +[project.scripts] +scons = "SCons.Script.Main:main" +sconsign = "SCons.Utilities.sconsign:main" +scons-configure-cache = "SCons.Utilities.ConfigureCache:main" + +[tool.setuptools] +zip-safe = false +include-package-data = true +license-files = ["LICENSE"] + +[tool.setuptools.packages.find] +exclude = ["template"] +namespaces = false + +[tool.setuptools.package-data] +"*" = ["*.txt", "*.rst", "*.1"] +"scons.tool.docbook" = ["*.*"] + +[tool.distutils.sdist] +dist-dir = "build/dist" + +[tool.distutils.bdist_wheel] +dist-dir = "build/dist" + # for black and mypy, set the lowest Python version supported [tool.black] quiet = true target-version = ['py36'] skip-string-normalization = true -[mypy] -python_version = 3.6 - +[tool.mypy] +python_version = "3.6" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 7c6af765cb..0000000000 --- a/setup.cfg +++ /dev/null @@ -1,81 +0,0 @@ -[metadata] -name = SCons -license = MIT -author = William Deegan -author_email =bill@baddogconsulting.com -long_description = file: README-package.rst -long_description_content_type = text/x-rst -description = Open Source next-generation build tool. -group = Development/Tools -license_file = LICENSE - - -url = https://www.scons.org/ -project_urls = - Documentation = https://scons.org/documentation.html - Twitter = https://twitter.com/SConsProject - GitHub = https://github.com/SCons/scons - Bug-Tracker = https://github.com/SCons/scons/issues - Discord = https://discord.gg/pejaFYrD9n - Mailing lists = https://scons.org/lists.html - - -classifiers = - Development Status :: 5 - Production/Stable - Topic :: Software Development :: Build Tools - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Programming Language :: Python :: 3.13 - Environment :: Console - Intended Audience :: Developers - License :: OSI Approved :: MIT License - Operating System :: POSIX :: Linux - Operating System :: Unix - Operating System :: MacOS - Operating System :: Microsoft :: Windows - - -[options] -zip_safe = False -python_requires = >=3.6 -include_package_data = True -packages = find: - - -[options.packages.find] -;include=SCons.* -exclude=template - -[options.entry_points] -console_scripts = - scons = SCons.Script.Main:main - sconsign = SCons.Utilities.sconsign:main - scons-configure-cache = SCons.Utilities.ConfigureCache:main - - -[options.package_data] -* = *.txt, *.rst, *.1 -SCons.Tool.docbook = *.* - - -[options.data_files] -. = scons.1 - scons-time.1 - sconsign.1 - -[sdist] -dist_dir=build/dist - -[bdist_wheel] - ; We're now py3 only -;universal=true -dist_dir=build/dist - From 44bd8b4da325ffb04ce806586b9639366050f1d7 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 26 May 2024 12:11:31 -0600 Subject: [PATCH 031/386] Fix two scons-time tests on GH Windows runner Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 +++ testing/framework/TestSCons_time.py | 7 ++++++- windows_ci_skip.txt | 2 -- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3fe676ab14..473858c98d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -71,6 +71,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER tweaked the Util package doc build so it's structured more like the other packages (a missed part of the transition when it was split). - Updated manpage description of Command "builder" and function. + - Framework for scons-time tests adjusted so a path with a long username + Windows has squashed doesn't get re-expanded. Fixes a problem seen + on GitHub Windows runner which uses a name "runneradmin". RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/testing/framework/TestSCons_time.py b/testing/framework/TestSCons_time.py index d2f061a9a5..6ee4b10e27 100644 --- a/testing/framework/TestSCons_time.py +++ b/testing/framework/TestSCons_time.py @@ -40,6 +40,7 @@ from TestCommon import * from TestCommon import __all__ +from TestCmd import IS_WINDOWS # some of the scons_time tests may need regex-based matching: from TestSCons import search_re, search_re_in_list @@ -236,7 +237,11 @@ def tempdir_re(self, *args): except AttributeError: pass else: - tempdir = realpath(tempdir) + # Don't realpath on Windows, tempdir could contain 8+3 path + # E.g. username on GitHub runner is "runneradmin" -> "RUNNER~1" + # We don't want to convert that back! + if not IS_WINDOWS: + tempdir = realpath(tempdir) args = (tempdir, 'scons-time-',) + args x = os.path.join(*args) diff --git a/windows_ci_skip.txt b/windows_ci_skip.txt index 24a2b6c8a8..a26444d877 100644 --- a/windows_ci_skip.txt +++ b/windows_ci_skip.txt @@ -33,8 +33,6 @@ test/packaging/msi/package.py test/packaging/tar/xz_packaging.py test/scons-time/run/config/python.py test/scons-time/run/option/python.py -test/scons-time/run/option/quiet.py -test/scons-time/run/option/verbose.py test/sconsign/script/no-SConsignFile.py test/sconsign/script/SConsignFile.py test/sconsign/script/Signatures.py From 40e6f1d24682cab5bf76df375ebd16551d0942be Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 27 May 2024 09:38:49 -0400 Subject: [PATCH 032/386] Fix issue #4543: Add support for msvc toolset versions 14.4x for msvc buildtools v143. Changes: * Add msvc build series data structure. * Change msvc build tools data structure to contain list of build series. * Update script argument validation and tests. --- CHANGES.txt | 3 + RELEASE.txt | 2 + SCons/Tool/MSCommon/MSVC/Config.py | 135 ++++++++++++----- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 135 ++++++++++------- .../MSCommon/MSVC/ScriptArgumentsTests.py | 25 +-- SCons/Tool/MSCommon/MSVC/Util.py | 15 +- SCons/Tool/MSCommon/README.rst | 142 +++++++++++------- SCons/Tool/MSCommon/vc.py | 2 +- test/MSVC/MSVC_SDK_VERSION.py | 4 +- test/MSVC/MSVC_TOOLSET_VERSION.py | 59 ++++++-- 10 files changed, 347 insertions(+), 175 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 51dbf28e53..82663a7e6e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -63,6 +63,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - The vswhere executable locations for the WinGet and Scoop package managers were added to the default vswhere executable search list after the Chocolatey installation location. + - Fix issue #4543: add support for msvc toolset versions 14.4X installed as the + latest msvc toolset versions for msvc buildtools v143. The v143 msvc buildtools + may contain msvc toolset versions from 14.30 to 14.4X. From Thaddeus Crews: - GetSConsVersion() to grab the latest SCons version without needing to diff --git a/RELEASE.txt b/RELEASE.txt index 7d9f82574f..f4c9e8581e 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -90,6 +90,8 @@ FIXES For example, when no vswhere executable is found for the initial detection and then later an environment is constructed with a user specified vswhere executable that detects new msvc installations. +- MSVC: Visual Studio 2022 v143 BuildTools now supports msvc toolset versions from + 14.30 to 14.4X. Fixes Issue #4543. IMPROVEMENTS ------------ diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index 7c0f1fe6ff..a04db4904e 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -118,15 +118,67 @@ for vc_runtime_alias in vc_runtime_alias_list: MSVC_RUNTIME_EXTERNAL[vc_runtime_alias] = vc_runtime_def -MSVC_BUILDTOOLS_DEFINITION = namedtuple('MSVCBuildtools', [ - 'vc_buildtools', - 'vc_buildtools_numeric', +MSVC_BUILDSERIES_DEFINITION = namedtuple('MSVCBuildSeries', [ + 'vc_buildseries', + 'vc_buildseries_numeric', 'vc_version', 'vc_version_numeric', 'cl_version', 'cl_version_numeric', +]) + +MSVC_BUILDSERIES_DEFINITION_LIST = [] + +MSVC_BUILDSERIES_INTERNAL = {} +MSVC_BUILDSERIES_EXTERNAL = {} + +VC_BUILDTOOLS_MAP = {} + +VC_VERSION_MAP = {} +CL_VERSION_MAP = {} + +for (vc_buildseries, vc_version, cl_version) in [ + ('144', '14.4', '19.4'), + ('143', '14.3', '19.3'), + ('142', '14.2', '19.2'), + ('141', '14.1', '19.1'), + ('140', '14.0', '19.0'), + ('120', '12.0', '18.0'), + ('110', '11.0', '17.0'), + ('100', '10.0', '16.0'), + ('90', '9.0', '15.0'), + ('80', '8.0', '14.0'), + ('71', '7.1', '13.1'), + ('70', '7.0', '13.0'), + ('60', '6.0', '12.0'), +]: + + vc_buildseries_def = MSVC_BUILDSERIES_DEFINITION( + vc_buildseries=vc_buildseries, + vc_buildseries_numeric=int(vc_buildseries), + vc_version=vc_version, + vc_version_numeric=float(vc_version), + cl_version=cl_version, + cl_version_numeric=float(cl_version), + ) + + MSVC_BUILDSERIES_DEFINITION_LIST.append(vc_buildseries_def) + + MSVC_BUILDSERIES_INTERNAL[vc_buildseries] = vc_buildseries_def + MSVC_BUILDSERIES_EXTERNAL[vc_buildseries] = vc_buildseries_def + MSVC_BUILDSERIES_EXTERNAL[vc_version] = vc_buildseries_def + + VC_VERSION_MAP[vc_version] = vc_buildseries_def + CL_VERSION_MAP[cl_version] = vc_buildseries_def + +MSVC_BUILDTOOLS_DEFINITION = namedtuple('MSVCBuildtools', [ + 'vc_buildtools', + 'vc_buildtools_numeric', + 'vc_buildseries_list', 'vc_runtime_def', 'vc_istoolset', + 'msvc_version', + 'msvc_version_numeric', ]) MSVC_BUILDTOOLS_DEFINITION_LIST = [] @@ -134,34 +186,44 @@ MSVC_BUILDTOOLS_INTERNAL = {} MSVC_BUILDTOOLS_EXTERNAL = {} -VC_VERSION_MAP = {} - -for vc_buildtools, vc_version, cl_version, vc_runtime, vc_istoolset in [ - ('v143', '14.3', '19.3', '140', True), - ('v142', '14.2', '19.2', '140', True), - ('v141', '14.1', '19.1', '140', True), - ('v140', '14.0', '19.0', '140', True), - ('v120', '12.0', '18.0', '120', False), - ('v110', '11.0', '17.0', '110', False), - ('v100', '10.0', '16.0', '100', False), - ('v90', '9.0', '15.0', '90', False), - ('v80', '8.0', '14.0', '80', False), - ('v71', '7.1', '13.1', '71', False), - ('v70', '7.0', '13.0', '70', False), - ('v60', '6.0', '12.0', '60', False), +MSVC_VERSION_NEWEST = None +MSVC_VERSION_NEWEST_NUMERIC = 0.0 + +for vc_buildtools, vc_buildseries_list, vc_runtime, vc_istoolset in [ + ('v143', ['144', '143'], '140', True), + ('v142', ['142'], '140', True), + ('v141', ['141'], '140', True), + ('v140', ['140'], '140', True), + ('v120', ['120'], '120', False), + ('v110', ['110'], '110', False), + ('v100', ['100'], '100', False), + ('v90', ['90'], '90', False), + ('v80', ['80'], '80', False), + ('v71', ['71'], '71', False), + ('v70', ['70'], '70', False), + ('v60', ['60'], '60', False), ]: vc_runtime_def = MSVC_RUNTIME_INTERNAL[vc_runtime] + vc_buildseries_list = tuple( + MSVC_BUILDSERIES_INTERNAL[vc_buildseries] + for vc_buildseries in vc_buildseries_list + ) + + vc_buildtools_numstr = vc_buildtools[1:] + + msvc_version = vc_buildtools_numstr[:-1] + '.' + vc_buildtools_numstr[-1] + msvc_version_numeric = float(msvc_version) + vc_buildtools_def = MSVC_BUILDTOOLS_DEFINITION( vc_buildtools = vc_buildtools, vc_buildtools_numeric = int(vc_buildtools[1:]), - vc_version = vc_version, - vc_version_numeric = float(vc_version), - cl_version = cl_version, - cl_version_numeric = float(cl_version), + vc_buildseries_list = vc_buildseries_list, vc_runtime_def = vc_runtime_def, vc_istoolset = vc_istoolset, + msvc_version = msvc_version, + msvc_version_numeric = msvc_version_numeric, ) MSVC_BUILDTOOLS_DEFINITION_LIST.append(vc_buildtools_def) @@ -170,7 +232,12 @@ MSVC_BUILDTOOLS_EXTERNAL[vc_buildtools] = vc_buildtools_def MSVC_BUILDTOOLS_EXTERNAL[vc_version] = vc_buildtools_def - VC_VERSION_MAP[vc_version] = vc_buildtools_def + for vc_buildseries_def in vc_buildseries_list: + VC_BUILDTOOLS_MAP[vc_buildseries_def.vc_buildseries] = vc_buildtools_def + + if vc_buildtools_def.msvc_version_numeric > MSVC_VERSION_NEWEST_NUMERIC: + MSVC_VERSION_NEWEST_NUMERIC = vc_buildtools_def.msvc_version_numeric + MSVC_VERSION_NEWEST = vc_buildtools_def.msvc_version MSVS_VERSION_INTERNAL = {} MSVS_VERSION_EXTERNAL = {} @@ -181,8 +248,6 @@ MSVS_VERSION_MAJOR_MAP = {} -CL_VERSION_MAP = {} - MSVC_SDK_VERSIONS = set() VISUALSTUDIO_DEFINITION = namedtuple('VisualStudioDefinition', [ @@ -247,15 +312,15 @@ vc_buildtools_def.vc_runtime_def.vc_runtime_vsdef_list.append(vs_def) - vc_version = vc_buildtools_def.vc_version + msvc_version = vc_buildtools_def.msvc_version MSVS_VERSION_INTERNAL[vs_product] = vs_def MSVS_VERSION_EXTERNAL[vs_product] = vs_def MSVS_VERSION_EXTERNAL[vs_version] = vs_def - MSVC_VERSION_INTERNAL[vc_version] = vs_def + MSVC_VERSION_INTERNAL[msvc_version] = vs_def MSVC_VERSION_EXTERNAL[vs_product] = vs_def - MSVC_VERSION_EXTERNAL[vc_version] = vs_def + MSVC_VERSION_EXTERNAL[msvc_version] = vs_def MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_buildtools] = vs_def if vs_product in VS_PRODUCT_ALIAS: @@ -264,14 +329,12 @@ MSVS_VERSION_EXTERNAL[vs_product_alias] = vs_def MSVC_VERSION_EXTERNAL[vs_product_alias] = vs_def - MSVC_VERSION_SUFFIX[vc_version] = vs_def + MSVC_VERSION_SUFFIX[msvc_version] = vs_def if vs_express: - MSVC_VERSION_SUFFIX[vc_version + 'Exp'] = vs_def + MSVC_VERSION_SUFFIX[msvc_version + 'Exp'] = vs_def MSVS_VERSION_MAJOR_MAP[vs_version_major] = vs_def - CL_VERSION_MAP[vc_buildtools_def.cl_version] = vs_def - if vc_sdk: MSVC_SDK_VERSIONS.update(vc_sdk) @@ -292,7 +355,7 @@ for vs_def in VISUALSTUDIO_DEFINITION_LIST: if not vs_def.vc_buildtools_def.vc_istoolset: continue - version_key = vs_def.vc_buildtools_def.vc_version + version_key = vs_def.vc_buildtools_def.msvc_version MSVC_VERSION_TOOLSET_DEFAULTS_MAP[version_key] = [version_key] MSVC_VERSION_TOOLSET_SEARCH_MAP[version_key] = [] if vs_def.vs_express: @@ -305,11 +368,11 @@ for vs_def in VISUALSTUDIO_DEFINITION_LIST: if not vs_def.vc_buildtools_def.vc_istoolset: continue - version_key = vs_def.vc_buildtools_def.vc_version + version_key = vs_def.vc_buildtools_def.msvc_version for vc_buildtools in vs_def.vc_buildtools_all: toolset_buildtools_def = MSVC_BUILDTOOLS_INTERNAL[vc_buildtools] - toolset_vs_def = MSVC_VERSION_INTERNAL[toolset_buildtools_def.vc_version] - buildtools_key = toolset_buildtools_def.vc_version + toolset_vs_def = MSVC_VERSION_INTERNAL[toolset_buildtools_def.msvc_version] + buildtools_key = toolset_buildtools_def.msvc_version MSVC_VERSION_TOOLSET_SEARCH_MAP[buildtools_key].extend(MSVC_VERSION_TOOLSET_DEFAULTS_MAP[version_key]) # convert string version set to string version list ranked in descending order diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index c689d7a0b5..76ad8717b0 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -173,6 +173,12 @@ class SortOrder(enum.IntEnum): 'vs_def', ]) +TOOLSET_VERSION_ARGS_DEFINITION = namedtuple('ToolsetVersionArgsDefinition', [ + 'version', # full version (e.g., '14.1Exp', '14.32.31326') + 'vc_buildtools_def', + 'is_user', +]) + def _msvc_version(version): verstr = Util.get_msvc_version_prefix(version) @@ -185,14 +191,17 @@ def _msvc_version(version): return version_args -def _toolset_version(version): +def _toolset_version(version, is_user=False): - verstr = Util.get_msvc_version_prefix(version) - vs_def = Config.MSVC_VERSION_INTERNAL[verstr] + vc_series = Util.get_msvc_version_prefix(version) - version_args = MSVC_VERSION_ARGS_DEFINITION( + vc_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL[vc_series] + vc_buildtools_def = Config.VC_BUILDTOOLS_MAP[vc_buildseries_def.vc_buildseries] + + version_args = TOOLSET_VERSION_ARGS_DEFINITION( version = version, - vs_def = vs_def, + vc_buildtools_def = vc_buildtools_def, + is_user = is_user, ) return version_args @@ -208,14 +217,14 @@ def _msvc_script_argument_uwp(env, msvc, arglist, target_arch): if uwp_app not in _ARGUMENT_BOOLEAN_TRUE_LEGACY: return None - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: + if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc version constraint: %s < %s VS2015', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(VS2015.vc_buildtools_def.vc_version_numeric) + repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), + repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(uwp_app), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version) + repr(uwp_app), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) raise MSVCArgumentError(err_msg) @@ -270,14 +279,14 @@ def _user_script_argument_uwp(env, uwp, user_argstr) -> bool: def _msvc_script_argument_sdk_constraints(msvc, sdk_version, env): - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: + if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc_version constraint: %s < %s VS2015', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(VS2015.vc_buildtools_def.vc_version_numeric) + repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), + repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(sdk_version), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version) + repr(sdk_version), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) return err_msg @@ -303,23 +312,23 @@ def _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, p if sdk_version == '8.1' and platform_def.is_uwp: - vs_def = toolset.vs_def if toolset else msvc.vs_def + vc_buildtools_def = toolset.vc_buildtools_def if toolset else msvc.vs_def.vc_buildtools_def - if vs_def.vc_buildtools_def.vc_version_numeric > VS2015.vc_buildtools_def.vc_version_numeric: + if vc_buildtools_def.msvc_version_numeric > VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: uwp/store SDK 8.1 msvc_version constraint: %s > %s VS2015', - repr(vs_def.vc_buildtools_def.vc_version_numeric), - repr(VS2015.vc_buildtools_def.vc_version_numeric) + repr(vc_buildtools_def.msvc_version_numeric), + repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) - if toolset and toolset.vs_def != msvc.vs_def: - err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset version {} > {} VS2015".format( + if toolset and toolset.is_user: + err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset {} MSVC_VERSION {} > {} VS2015".format( repr(sdk_version), repr(platform_def.vc_platform), - repr(toolset.version), repr(VS2015.vc_buildtools_def.vc_version) + repr(toolset.version), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) else: err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: MSVC_VERSION {} > {} VS2015".format( repr(sdk_version), repr(platform_def.vc_platform), - repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version) + repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) return err_msg @@ -359,7 +368,7 @@ def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist): def _msvc_script_default_sdk(env, msvc, platform_def, arglist, force_sdk: bool=False): - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: + if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric: return None if not Kind.msvc_version_sdk_version_is_supported(msvc.version, env): @@ -444,10 +453,11 @@ def _msvc_sxs_toolset_folder(msvc, sxs_folder): if Util.is_toolset_sxs(sxs_folder): return sxs_folder, sxs_folder - key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_folder) - if key in _msvc_sxs_bugfix_folder: - sxs_version = _msvc_sxs_bugfix_folder[key] - return sxs_folder, sxs_version + for vc_buildseries_def in msvc.vs_def.vc_buildtools_def.vc_buildseries_list: + key = (vc_buildseries_def.vc_version, sxs_folder) + sxs_version = _msvc_sxs_bugfix_folder.get(key) + if sxs_version: + return sxs_folder, sxs_version debug('sxs folder: ignore version=%s', repr(sxs_folder)) return None, None @@ -603,49 +613,60 @@ def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version): def _msvc_script_argument_toolset_constraints(msvc, toolset_version): - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: + if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc version constraint: %s < %s VS2017', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(VS2017.vc_buildtools_def.vc_version_numeric) + repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), + repr(VS2017.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(toolset_version), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version) + repr(toolset_version), repr(msvc.version), repr(VS2017.vc_buildtools_def.msvc_version) ) return err_msg - toolset_verstr = Util.get_msvc_version_prefix(toolset_version) + toolset_series = Util.get_msvc_version_prefix(toolset_version) - if not toolset_verstr: + if not toolset_series: debug('invalid: msvc version: toolset_version=%s', repr(toolset_version)) err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format( repr(toolset_version) ) return err_msg - toolset_vernum = float(toolset_verstr) + toolset_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL.get(toolset_series) + if not toolset_buildseries_def: + debug('invalid: msvc version: toolset_version=%s', repr(toolset_version)) + err_msg = 'MSVC_TOOLSET_VERSION {} build series {} is not supported'.format( + repr(toolset_version), repr(toolset_series) + ) + return err_msg + + toolset_buildtools_def = Config.VC_BUILDTOOLS_MAP[toolset_buildseries_def.vc_buildseries] + + toolset_verstr = toolset_buildtools_def.msvc_version + toolset_vernum = toolset_buildtools_def.msvc_version_numeric - if toolset_vernum < VS2015.vc_buildtools_def.vc_version_numeric: + if toolset_vernum < VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: toolset version constraint: %s < %s VS2015', - repr(toolset_vernum), repr(VS2015.vc_buildtools_def.vc_version_numeric) + repr(toolset_vernum), repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format( - repr(toolset_version), repr(toolset_verstr), repr(VS2015.vc_buildtools_def.vc_version) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} < {} VS2015".format( + repr(toolset_version), repr(toolset_verstr), repr(VS2015.vc_buildtools_def.msvc_version) ) return err_msg - if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric: + if toolset_vernum > msvc.vs_def.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: toolset version constraint: toolset %s > %s msvc', - repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric) + repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric) ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format( + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} > {} MSVC_VERSION".format( repr(toolset_version), repr(toolset_verstr), repr(msvc.version) ) return err_msg - if toolset_vernum == VS2015.vc_buildtools_def.vc_version_numeric: + if toolset_vernum == VS2015.vc_buildtools_def.msvc_version_numeric: # tooset = 14.0 if Util.is_toolset_full(toolset_version): if not Util.is_toolset_140(toolset_version): @@ -653,7 +674,7 @@ def _msvc_script_argument_toolset_constraints(msvc, toolset_version): 'invalid: toolset version 14.0 constraint: %s != 14.0', repr(toolset_version) ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format( + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} != '14.0'".format( repr(toolset_version), repr(toolset_version) ) return err_msg @@ -717,7 +738,7 @@ def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist): def _msvc_script_default_toolset(env, msvc, vc_dir, arglist, force_toolset: bool=False): - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: + if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric: return None toolset_default = _msvc_default_toolset(msvc, vc_dir) @@ -758,26 +779,26 @@ def _user_script_argument_toolset(env, toolset_version, user_argstr): def _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def): - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: + if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc version constraint: %s < %s VS2017', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(VS2017.vc_buildtools_def.vc_version_numeric) + repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), + repr(VS2017.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version) + repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.msvc_version) ) return err_msg if toolset: - if toolset.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: + if toolset.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: toolset version constraint: %s < %s VS2017', - repr(toolset.vs_def.vc_buildtools_def.vc_version_numeric), - repr(VS2017.vc_buildtools_def.vc_version_numeric) + repr(toolset.vc_buildtools_def.msvc_version_numeric), + repr(VS2017.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: toolset version {} < {} VS2017".format( - repr(spectre_libs), repr(toolset.version), repr(VS2017.vc_buildtools_def.vc_version) + repr(spectre_libs), repr(toolset.version), repr(VS2017.vc_buildtools_def.msvc_version) ) return err_msg @@ -865,14 +886,14 @@ def _msvc_script_argument_user(env, msvc, arglist): if not script_args: return None - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: + if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc version constraint: %s < %s VS2015', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(VS2015.vc_buildtools_def.vc_version_numeric) + repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), + repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(script_args), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version) + repr(script_args), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) raise MSVCArgumentError(err_msg) @@ -968,7 +989,7 @@ def msvc_script_arguments(env, version, vc_dir, arg=None): if user_toolset: toolset = None elif toolset_version: - toolset = _toolset_version(toolset_version) + toolset = _toolset_version(toolset_version, is_user=True) elif default_toolset: toolset = _toolset_version(default_toolset) else: @@ -1000,7 +1021,7 @@ def msvc_script_arguments(env, version, vc_dir, arg=None): if user_argstr: _user_script_argument_spectre(env, spectre, user_argstr) - if msvc.vs_def.vc_buildtools_def.vc_version == '14.0': + if msvc.vs_def.vc_buildtools_def.msvc_version == '14.0': if user_uwp and sdk_version and len(arglist) == 2: # VS2015 toolset argument order issue: SDK store => store SDK arglist_reverse = True diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index c79f044c55..308e436976 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -317,9 +317,14 @@ def run_msvc_script_args(self) -> None: toolset_def = toolset_versions[0] if toolset_versions else Util.msvc_extended_version_components(version_def.msvc_verstr) - earlier_toolset_versions = [toolset_def for toolset_def in toolset_versions if toolset_def.msvc_vernum != version_def.msvc_vernum] + earlier_toolset_versions = [earlier_toolset_def for earlier_toolset_def in toolset_versions if earlier_toolset_def.msvc_vernum != version_def.msvc_vernum] earlier_toolset_def = earlier_toolset_versions[0] if earlier_toolset_versions else None + vc_buildtools_def = Config.MSVC_BUILDTOOLS_EXTERNAL[toolset_def.msvc_buildtools] + vc_buildseries_def = vc_buildtools_def.vc_buildseries_list[0] + + latest_buildseries_major, latest_buildseries_minor = [int(comp) for comp in vc_buildseries_def.vc_version.split('.')] + # should not raise exception (argument not validated) env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol') _ = func(env, version_def.msvc_version, vc_dir) @@ -372,7 +377,7 @@ def run_msvc_script_args(self) -> None: (expect, { 'MSVC_SDK_VERSION': sdk_def.sdk_version, 'MSVC_UWP_APP': msvc_uwp_app, - 'MSVC_TOOLSET_VERSION': version_def.msvc_verstr + 'MSVC_TOOLSET_VERSION': toolset_def.msvc_toolset_version }), ] + more_tests: env = Environment(**kwargs) @@ -392,8 +397,8 @@ def run_msvc_script_args(self) -> None: _ = func(env, version_def.msvc_version, vc_dir) for kwargs in [ - {'MSVC_SCRIPT_ARGS': '-vcvars_ver={}'.format(version_def.msvc_verstr)}, - {'MSVC_TOOLSET_VERSION': version_def.msvc_verstr}, + {'MSVC_SCRIPT_ARGS': '-vcvars_ver={}'.format(toolset_def.msvc_toolset_version)}, + {'MSVC_TOOLSET_VERSION': toolset_def.msvc_toolset_version}, ]: env = Environment(**kwargs) _ = func(env, version_def.msvc_version, vc_dir) @@ -453,14 +458,14 @@ def run_msvc_script_args(self) -> None: }, (MSVCArgumentError, ), ), # multiple definitions - ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, - 'MSVC_SCRIPT_ARGS': "-vcvars_ver={}".format(version_def.msvc_verstr) + ({'MSVC_TOOLSET_VERSION': toolset_def.msvc_toolset_version, + 'MSVC_SCRIPT_ARGS': "-vcvars_ver={}".format(toolset_def.msvc_toolset_version) }, (MSVCArgumentError, ), ), # multiple definitions (args) - ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, - 'MSVC_SCRIPT_ARGS': "-vcvars_ver={0} undefined -vcvars_ver={0}".format(version_def.msvc_verstr) + ({'MSVC_TOOLSET_VERSION': toolset_def.msvc_toolset_version, + 'MSVC_SCRIPT_ARGS': "-vcvars_ver={0} undefined -vcvars_ver={0}".format(toolset_def.msvc_toolset_version) }, (MSVCArgumentError, ), ), @@ -494,7 +499,7 @@ def run_msvc_script_args(self) -> None: (MSVCArgumentError, ), ), # toolset > msvc_version - ({'MSVC_TOOLSET_VERSION': '{}.{}'.format(version_def.msvc_major, version_def.msvc_minor+1), + ({'MSVC_TOOLSET_VERSION': '{}.{}'.format(latest_buildseries_major, latest_buildseries_minor+1), }, (MSVCArgumentError, ), ), @@ -600,7 +605,7 @@ def run_msvc_script_args(self) -> None: for msvc_uwp_app in (True, False): env = Environment(MSVC_UWP_APP=msvc_uwp_app) _ = func(env, version_def.msvc_version, vc_dir) - + else: # VS2015: MSVC_UWP_APP error diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index f6178e2886..67edfec349 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -358,6 +358,8 @@ def msvc_version_components(vcver): 'msvc_major', # msvc major version integer number (e.g., 14) 'msvc_minor', # msvc minor version integer number (e.g., 1) 'msvc_comps', # msvc version components tuple (e.g., ('14', '1')) + 'msvc_buildtools', # msvc build tools + 'msvc_buildseries', # msvc build series 'msvc_toolset_version', # msvc toolset version 'msvc_toolset_comps', # msvc toolset version components 'version', # msvc version or msvc toolset version @@ -385,10 +387,17 @@ def msvc_extended_version_components(version): msvc_toolset_version = m.group('version') msvc_toolset_comps = tuple(msvc_toolset_version.split('.')) - msvc_verstr = get_msvc_version_prefix(msvc_toolset_version) - if not msvc_verstr: + vc_verstr = get_msvc_version_prefix(msvc_toolset_version) + if not vc_verstr: return None + vc_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL.get(vc_verstr) + if not vc_buildseries_def: + return None + + vc_buildtools_def = Config.VC_BUILDTOOLS_MAP[vc_buildseries_def.vc_buildseries] + + msvc_verstr = vc_buildtools_def.msvc_version msvc_suffix = m.group('suffix') if m.group('suffix') else '' msvc_version = msvc_verstr + msvc_suffix @@ -409,6 +418,8 @@ def msvc_extended_version_components(version): msvc_major = msvc_major, msvc_minor = msvc_minor, msvc_comps = msvc_comps, + msvc_buildtools = vc_buildtools_def.vc_buildtools, + msvc_buildseries = vc_buildseries_def.vc_version, msvc_toolset_version = msvc_toolset_version, msvc_toolset_comps = msvc_toolset_comps, version = version, diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index 5a72885c00..b37f409d3d 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -224,6 +224,7 @@ Example usage: :: for version in [ + '14.4', '14.3', '14.2', '14.1', @@ -507,67 +508,100 @@ Notes: The Windows 11 SDK version number is 10.0.22000.194 and later. +BuildSeries Versions +-------------------- + ++-------------+-------+-------+ +| BuildSeries | VCVER | CLVER | ++=============+=======+=======+ +| 14.4 | 14.4X | 19.4 | ++-------------+-------+-------+ +| 14.3 | 14.3X | 19.3 | ++-------------+-------+-------+ +| 14.2 | 14.2X | 19.2 | ++-------------+-------+-------+ +| 14.1 | 14.1X | 19.1 | ++-------------+-------+-------+ +| 14.0 | 14.0 | 19.0 | ++-------------+-------+-------+ +| 12.0 | 12.0 | 18.0 | ++-------------+-------+-------+ +| 11.0 | 11.0 | 17.0 | ++-------------+-------+-------+ +| 10.0 | 10.0 | 16.0 | ++-------------+-------+-------+ +| 9.0 | 9.0 | 15.0 | ++-------------+-------+-------+ +| 8.0 | 8.0 | 14.0 | ++-------------+-------+-------+ +| 7.1 | 7.1 | 13.1 | ++-------------+-------+-------+ +| 7.0 | 7.0 | 13.0 | ++-------------+-------+-------+ +| 6.0 | 6.0 | 12.0 | ++-------------+-------+-------+ + BuildTools Versions ------------------- -+------------+-------+-------+----------+ -| BuildTools | VCVER | CLVER | MSVCRT | -+============+=======+=======+==========+ -| v143 | 14.3 | 19.3 | 140/ucrt | -+------------+-------+-------+----------+ -| v142 | 14.2 | 19.2 | 140/ucrt | -+------------+-------+-------+----------+ -| v141 | 14.1 | 19.1 | 140/ucrt | -+------------+-------+-------+----------+ -| v140 | 14.0 | 19.0 | 140/ucrt | -+------------+-------+-------+----------+ -| v120 | 12.0 | 18.0 | 120 | -+------------+-------+-------+----------+ -| v110 | 11.0 | 17.0 | 110 | -+------------+-------+-------+----------+ -| v100 | 10.0 | 16.0 | 100 | -+------------+-------+-------+----------+ -| v90 | 9.0 | 15.0 | 90 | -+------------+-------+-------+----------+ -| v80 | 8.0 | 14.0 | 80 | -+------------+-------+-------+----------+ -| v71 | 7.1 | 13.1 | 71 | -+------------+-------+-------+----------+ -| v70 | 7.0 | 13.0 | 70 | -+------------+-------+-------+----------+ -| v60 | 6.0 | 12.0 | 60 | -+------------+-------+-------+----------+ ++------------+-------------+----------+ +| BuildTools | BuildSeries | MSVCRT | ++============+=============+==========+ +| v143 | 14.4, 14.3 | 140/ucrt | ++------------+-------------+----------+ +| v142 | 14.2 | 140/ucrt | ++------------+-------------+----------+ +| v141 | 14.1 | 140/ucrt | ++------------+-------------+----------+ +| v140 | 14.0 | 140/ucrt | ++------------+-------------+----------+ +| v120 | 12.0 | 120 | ++------------+-------------+----------+ +| v110 | 11.0 | 110 | ++------------+-------------+----------+ +| v100 | 10.0 | 100 | ++------------+-------------+----------+ +| v90 | 9.0 | 90 | ++------------+-------------+----------+ +| v80 | 8.0 | 80 | ++------------+-------------+----------+ +| v71 | 7.1 | 71 | ++------------+-------------+----------+ +| v70 | 7.0 | 70 | ++------------+-------------+----------+ +| v60 | 6.0 | 60 | ++------------+-------------+----------+ Product Versions ---------------- -+----------+-------+-----------+------------------------+ -| Product | VSVER | SDK | BuildTools | -+==========+=======+===========+========================+ -| 2022 | 17.0 | 10.0, 8.1 | v143, v142, v141, v140 | -+----------+-------+-----------+------------------------+ -| 2019 | 16.0 | 10.0, 8.1 | v142, v141, v140 | -+----------+-------+-----------+------------------------+ -| 2017 | 15.0 | 10.0, 8.1 | v141, v140 | -+----------+-------+-----------+------------------------+ -| 2015 | 14.0 | 10.0, 8.1 | v140 | -+----------+-------+-----------+------------------------+ -| 2013 | 12.0 | | v120 | -+----------+-------+-----------+------------------------+ -| 2012 | 11.0 | | v110 | -+----------+-------+-----------+------------------------+ -| 2010 | 10.0 | | v100 | -+----------+-------+-----------+------------------------+ -| 2008 | 9.0 | | v90 | -+----------+-------+-----------+------------------------+ -| 2005 | 8.0 | | v80 | -+----------+-------+-----------+------------------------+ -| 2003.NET | 7.1 | | v71 | -+----------+-------+-----------+------------------------+ -| 2002.NET | 7.0 | | v70 | -+----------+-------+-----------+------------------------+ -| 6.0 | 6.0 | | v60 | -+----------+-------+-----------+------------------------+ ++----------+-------+-------+-----------+------------------------+ +| Product | VSVER | SCons | SDK | BuildTools | ++==========+=======+=======+===========+========================+ +| 2022 | 17.0 | 14.3 | 10.0, 8.1 | v143, v142, v141, v140 | ++----------+-------+-------+-----------+------------------------+ +| 2019 | 16.0 | 14.2 | 10.0, 8.1 | v142, v141, v140 | ++----------+-------+-------+-----------+------------------------+ +| 2017 | 15.0 | 14.1 | 10.0, 8.1 | v141, v140 | ++----------+-------+-------+-----------+------------------------+ +| 2015 | 14.0 | 14.0 | 10.0, 8.1 | v140 | ++----------+-------+-------+-----------+------------------------+ +| 2013 | 12.0 | 12.0 | | v120 | ++----------+-------+-------+-----------+------------------------+ +| 2012 | 11.0 | 11.0 | | v110 | ++----------+-------+-------+-----------+------------------------+ +| 2010 | 10.0 | 10.0 | | v100 | ++----------+-------+-------+-----------+------------------------+ +| 2008 | 9.0 | 9.0 | | v90 | ++----------+-------+-------+-----------+------------------------+ +| 2005 | 8.0 | 8.0 | | v80 | ++----------+-------+-------+-----------+------------------------+ +| 2003.NET | 7.1 | 7.1 | | v71 | ++----------+-------+-------+-----------+------------------------+ +| 2002.NET | 7.0 | 7.0 | | v70 | ++----------+-------+-------+-----------+------------------------+ +| 6.0 | 6.0 | 6.0 | | v60 | ++----------+-------+-------+-----------+------------------------+ SCons Implementation Notes diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 8a2fd8e026..907b2455b7 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -2516,7 +2516,7 @@ def msvc_sdk_versions(version=None, msvc_uwp_app: bool=False): msg = f'Unsupported version {version!r}' raise MSVCArgumentError(msg) - rval = MSVC.WinSDK.get_msvc_sdk_version_list(version, msvc_uwp_app) + rval = MSVC.WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app) return rval def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False, vswhere_exe=None): diff --git a/test/MSVC/MSVC_SDK_VERSION.py b/test/MSVC/MSVC_SDK_VERSION.py index f3f9913b52..0f0e25a65b 100644 --- a/test/MSVC/MSVC_SDK_VERSION.py +++ b/test/MSVC/MSVC_SDK_VERSION.py @@ -197,8 +197,8 @@ def version_major_list(version_list): """.format(repr(supported.msvc_version), repr(toolset_version)) )) test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_SDK_VERSION ('8.1') and platform type ('UWP') constraint violation: toolset version {} > '14.0' VS2015:".format( - repr(toolset_version) + expect = "MSVCArgumentError: MSVC_SDK_VERSION ('8.1') and platform type ('UWP') constraint violation: toolset {} MSVC_VERSION {} > '14.0' VS2015:".format( + repr(toolset_version), repr(supported.msvc_version) ) test.must_contain_all(test.stderr(), expect) diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index 7c93938019..e43f4d7f80 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -28,6 +28,7 @@ """ import textwrap +import SCons.Tool.MSCommon.MSVC.Config as Config from SCons.Tool.MSCommon.vc import get_installed_vcs_components from SCons.Tool.MSCommon import msvc_toolset_versions @@ -43,6 +44,26 @@ LT_VS2017_versions = [v for v in installed_versions if v.msvc_vernum < 14.1] LT_VS2015_versions = [v for v in LT_VS2017_versions if v.msvc_vernum < 14.0] +known_buildseries = set(Config.MSVC_BUILDSERIES_EXTERNAL.keys()) + +def get_toolset_buildseries_version(toolset_version): + comps = toolset_version.split('.') + buildseries_version = comps[0] + '.' + comps[1][0] + vc_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL[buildseries_version] + vc_version = vc_buildseries_def.vc_version + return vc_version + +def get_latest_buildseries_version(v): + vs_def = Config.MSVC_VERSION_EXTERNAL[v.msvc_verstr] + vc_version = vs_def.vc_buildtools_def.vc_buildseries_list[0].vc_version + return vc_version + +def is_buildseries_known(toolset_version): + comps = toolset_version.split('.') + buildseries_version = comps[0] + '.' + comps[1][0] + rval = bool(buildseries_version in known_buildseries) + return rval + if GE_VS2017_versions: # VS2017 and later for toolset argument @@ -54,6 +75,11 @@ toolset_sxs_versions = msvc_toolset_versions(supported.msvc_version, full=False, sxs=True) toolset_sxs_version = toolset_sxs_versions[0] if toolset_sxs_versions else None + toolset_version = toolset_full_version if toolset_full_version else supported.msvc_version + buildseries_version = get_toolset_buildseries_version(toolset_version) + + latest_buildseries_version = get_latest_buildseries_version(supported) + if toolset_full_version: # toolset version using construction variable @@ -91,21 +117,21 @@ )) test.run(arguments='-Q -s', stdout='') - # msvc_version as toolset version + # build series version as toolset version test.write('SConstruct', textwrap.dedent( """ DefaultEnvironment(tools=[]) env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) - """.format(repr(supported.msvc_version), repr(supported.msvc_verstr)) + """.format(repr(supported.msvc_version), repr(buildseries_version)) )) test.run(arguments='-Q -s', stdout='') - # msvc_version as toolset version using script argument + # build series version as toolset version using script argument test.write('SConstruct', textwrap.dedent( """ DefaultEnvironment(tools=[]) env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) - """.format(repr(supported.msvc_version), supported.msvc_verstr) + """.format(repr(supported.msvc_version), buildseries_version) )) test.run(arguments='-Q -s', stdout='') @@ -114,11 +140,11 @@ """ DefaultEnvironment(tools=[]) env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) - """.format(repr(supported.msvc_version), repr(supported.msvc_verstr), supported.msvc_verstr) + """.format(repr(supported.msvc_version), repr(buildseries_version), buildseries_version) )) test.run(arguments='-Q -s', status=2, stderr=None) expect = "MSVCArgumentError: multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS='-vcvars_ver={}':".format( - repr(supported.msvc_verstr), supported.msvc_verstr + repr(buildseries_version), buildseries_version ) test.must_contain_all(test.stderr(), expect) @@ -150,9 +176,19 @@ ) test.must_contain_all(test.stderr(), expect) - # msvc_toolset_version is invalid (version greater than msvc version) - invalid_toolset_vernum = round(supported.msvc_vernum + 0.1, 1) + # msvc_toolset_version is invalid (version not supported for msvc version) + invalid_toolset_vernum = round(float(latest_buildseries_version) + 0.1, 1) invalid_toolset_version = str(invalid_toolset_vernum) + + if is_buildseries_known(invalid_toolset_version): + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} > {} MSVC_VERSION:".format( + repr(invalid_toolset_version), repr(invalid_toolset_version), repr(supported.msvc_version) + ) + else: + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION {} build series {} is not supported:".format( + repr(invalid_toolset_version), repr(invalid_toolset_version) + ) + test.write('SConstruct', textwrap.dedent( """ DefaultEnvironment(tools=[]) @@ -160,9 +196,6 @@ """.format(repr(supported.msvc_version), repr(invalid_toolset_version)) )) test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION:".format( - repr(invalid_toolset_version), repr(invalid_toolset_version), repr(supported.msvc_version) - ) test.must_contain_all(test.stderr(), expect) # msvc_toolset_version is invalid (version less than 14.0) @@ -174,7 +207,7 @@ """.format(repr(supported.msvc_version), repr(invalid_toolset_version)) )) test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < '14.0' VS2015:".format( + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} < '14.0' VS2015:".format( repr(invalid_toolset_version), repr(invalid_toolset_version) ) test.must_contain_all(test.stderr(), expect) @@ -188,7 +221,7 @@ """.format(repr(supported.msvc_version), repr(invalid_toolset_version)) )) test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0':".format( + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} != '14.0':".format( repr(invalid_toolset_version), repr(invalid_toolset_version) ) test.must_contain_all(test.stderr(), expect) From da726100ca098becf9802e28657bbae3af04ee5f Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 27 May 2024 18:12:39 -0400 Subject: [PATCH 033/386] Rework experimental msvc_query_version_toolset function. Changes: * Add context manager functions to temporary change the policy for msvc not found and msvc script errors within a context block. * Change msvc_query_version_toolset function behavior: raise exceptions if not found and for argument validation errors; otherwise return msvc version and toolset version. * Additional logging of exception messages . * Update tests for 14.3X/14.4X toolsets for VS 2022. --- SCons/Tool/MSCommon/MSVC/Policy.py | 29 +++++ SCons/Tool/MSCommon/__init__.py | 2 + SCons/Tool/MSCommon/vc.py | 188 ++++++++++++++++------------- SCons/Tool/MSCommon/vcTests.py | 52 ++++++-- test/MSVC/no_msvc.py | 3 +- 5 files changed, 180 insertions(+), 94 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Policy.py b/SCons/Tool/MSCommon/MSVC/Policy.py index fe8da3156b..7e11a63ba5 100644 --- a/SCons/Tool/MSCommon/MSVC/Policy.py +++ b/SCons/Tool/MSCommon/MSVC/Policy.py @@ -38,6 +38,10 @@ namedtuple, ) +from contextlib import ( + contextmanager, +) + import SCons.Warnings from ..common import ( @@ -215,6 +219,18 @@ def msvc_notfound_handler(env, msg): else: SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) +@contextmanager +def msvc_notfound_policy_contextmanager(MSVC_NOTFOUND_POLICY=None): + """ Temporarily change the MSVC not found policy within a context. + + Args: + MSVC_NOTFOUND_POLICY: + string representing the policy behavior + when MSVC is not found or None + """ + prev_policy = msvc_set_notfound_policy(MSVC_NOTFOUND_POLICY) + yield + msvc_set_notfound_policy(prev_policy) def _msvc_scripterror_policy_lookup(symbol): @@ -299,3 +315,16 @@ def msvc_scripterror_handler(env, msg): else: SCons.Warnings.warn(MSVCScriptExecutionWarning, msg) +@contextmanager +def msvc_scripterror_policy_contextmanager(MSVC_SCRIPTERROR_POLICY=None): + """ Temporarily change the msvc batch execution errors policy within a context. + + Args: + MSVC_SCRIPTERROR_POLICY: + string representing the policy behavior + when msvc batch file execution errors are detected or None + """ + prev_policy = msvc_set_scripterror_policy(MSVC_SCRIPTERROR_POLICY) + yield + msvc_set_scripterror_policy(prev_policy) + diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index 4d7b8bcb3a..63f9ae4de6 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -63,6 +63,8 @@ msvc_get_notfound_policy, msvc_set_scripterror_policy, msvc_get_scripterror_policy, + msvc_notfound_policy_contextmanager, + msvc_scripterror_policy_contextmanager, ) from .MSVC.Exceptions import ( # noqa: F401 diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 907b2455b7..a92dd19f68 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -2572,28 +2572,24 @@ def msvc_toolset_versions_spectre(msvc_version=None, vswhere_exe=None): def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_exe=None): """ - Returns an msvc version and a toolset version given a version + Return an msvc version and a toolset version given a version specification. This is an EXPERIMENTAL proxy for using a toolset version to perform msvc instance selection. This function will be removed when toolset version is taken into account during msvc instance selection. - Search for an installed Visual Studio instance that supports the - specified version. + This function searches for an installed Visual Studio instance that + contains the requested version. A component suffix (e.g., Exp) is not + supported for toolset version specifications (e.g., 14.39). - When the specified version contains a component suffix (e.g., Exp), - the msvc version is returned and the toolset version is None. No - search if performed. + An MSVCArgumentError is raised when argument validation fails. An + MSVCToolsetVersionNotFound exception is raised when the requested + version is not found. - When the specified version does not contain a component suffix, the - version is treated as a toolset version specification. A search is - performed for the first msvc instance that contains the toolset - version. - - Only Visual Studio 2017 and later support toolset arguments. For - Visual Studio 2015 and earlier, the msvc version is returned and - the toolset version is None. + For Visual Studio 2015 and earlier, the msvc version is returned and + the toolset version is None. For Visual Studio 2017 and later, the + selected toolset version is returned. Args: @@ -2623,106 +2619,132 @@ def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_e msvc_version = None msvc_toolset_version = None - if not version: - version = msvc_default_version() + with MSVC.Policy.msvc_notfound_policy_contextmanager('suppress'): - if not version: - debug('no msvc versions detected') - return msvc_version, msvc_toolset_version + _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe) - version_def = MSVC.Util.msvc_extended_version_components(version) + vcs = get_installed_vcs() - if not version_def: - msg = f'Unsupported msvc version {version!r}' - raise MSVCArgumentError(msg) + if not version: + version = msvc_default_version() - if version_def.msvc_suffix: - if version_def.msvc_verstr != version_def.msvc_toolset_version: - # toolset version with component suffix - msg = f'Unsupported toolset version {version!r}' - raise MSVCArgumentError(msg) + if not version: + msg = f'No versions of the MSVC compiler were found' + debug(f'MSVCToolsetVersionNotFound: {msg}') + raise MSVCToolsetVersionNotFound(msg) - if version_def.msvc_vernum > 14.0: - # VS2017 and later - force_toolset_msvc_version = False - else: - # VS2015 and earlier - force_toolset_msvc_version = True - extended_version = version_def.msvc_verstr + '0.00000' - if not extended_version.startswith(version_def.msvc_toolset_version): - # toolset not equivalent to msvc version - msg = 'Unsupported toolset version {} (expected {})'.format( - repr(version), repr(extended_version) - ) + version_def = MSVC.Util.msvc_extended_version_components(version) + + if not version_def: + msg = f'Unsupported MSVC version {version!r}' + debug(f'MSVCArgumentError: {msg}') raise MSVCArgumentError(msg) - msvc_version = version_def.msvc_version + msvc_version = version_def.msvc_version - if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP: - # VS2013 and earlier - debug( - 'ignore: msvc_version=%s, msvc_toolset_version=%s', - repr(msvc_version), repr(msvc_toolset_version) - ) - return msvc_version, msvc_toolset_version + if version_def.msvc_suffix: - if force_toolset_msvc_version: - query_msvc_toolset_version = version_def.msvc_verstr - else: - query_msvc_toolset_version = version_def.msvc_toolset_version + if version_def.msvc_verstr != version_def.msvc_toolset_version: + # toolset version with component suffix + msg = f'Unsupported MSVC toolset version {version!r}' + debug(f'MSVCArgumentError: {msg}') + raise MSVCArgumentError(msg) - if prefer_newest: - query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version] - else: - query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_DEFAULTS_MAP[msvc_version] + \ - MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version] + if msvc_version not in vcs: + msg = f'MSVC version {msvc_version!r} not found' + debug(f'MSVCToolsetVersionNotFound: {msg}') + raise MSVCToolsetVersionNotFound(msg) - seen_msvc_version = set() - for query_msvc_version in query_version_list: + if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP: + debug( + 'suffix: msvc_version=%s, msvc_toolset_version=%s', + repr(msvc_version), repr(msvc_toolset_version) + ) + return msvc_version, msvc_toolset_version - if query_msvc_version in seen_msvc_version: - continue - seen_msvc_version.add(query_msvc_version) + if version_def.msvc_vernum > 14.0: + # VS2017 and later + force_toolset_msvc_version = False + else: + # VS2015 and earlier + force_toolset_msvc_version = True + extended_version = version_def.msvc_verstr + '0.00000' + if not extended_version.startswith(version_def.msvc_toolset_version): + # toolset not equivalent to msvc version + msg = 'Unsupported MSVC toolset version {} (expected {})'.format( + repr(version), repr(extended_version) + ) + debug(f'MSVCArgumentError: {msg}') + raise MSVCArgumentError(msg) - vc_dir = _find_vc_pdir(query_msvc_version, vswhere_exe) - if not vc_dir: - continue + if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP: + + # VS2013 and earlier + + if msvc_version not in vcs: + msg = f'MSVC version {version!r} not found' + debug(f'MSVCToolsetVersionNotFound: {msg}') + raise MSVCToolsetVersionNotFound(msg) - if query_msvc_version.startswith('14.0'): - # VS2015 does not support toolset version argument - msvc_toolset_version = None debug( - 'found: msvc_version=%s, msvc_toolset_version=%s', - repr(query_msvc_version), repr(msvc_toolset_version) + 'earlier: msvc_version=%s, msvc_toolset_version=%s', + repr(msvc_version), repr(msvc_toolset_version) ) - return query_msvc_version, msvc_toolset_version + return msvc_version, msvc_toolset_version - try: - toolset_vcvars = MSVC.ScriptArguments._msvc_toolset_internal(query_msvc_version, query_msvc_toolset_version, vc_dir) - if toolset_vcvars: - msvc_toolset_version = toolset_vcvars + if force_toolset_msvc_version: + query_msvc_toolset_version = version_def.msvc_verstr + else: + query_msvc_toolset_version = version_def.msvc_toolset_version + + if prefer_newest: + query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version] + else: + query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_DEFAULTS_MAP[msvc_version] + \ + MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version] + + seen_msvc_version = set() + for query_msvc_version in query_version_list: + + if query_msvc_version in seen_msvc_version: + continue + seen_msvc_version.add(query_msvc_version) + + vc_dir = _find_vc_pdir(query_msvc_version, vswhere_exe) + if not vc_dir: + continue + + if query_msvc_version.startswith('14.0'): + # VS2015 does not support toolset version argument + msvc_toolset_version = None debug( 'found: msvc_version=%s, msvc_toolset_version=%s', repr(query_msvc_version), repr(msvc_toolset_version) ) return query_msvc_version, msvc_toolset_version - except MSVCToolsetVersionNotFound: - pass + try: + toolset_vcvars = MSVC.ScriptArguments._msvc_toolset_internal(query_msvc_version, query_msvc_toolset_version, vc_dir) + if toolset_vcvars: + msvc_toolset_version = toolset_vcvars + debug( + 'found: msvc_version=%s, msvc_toolset_version=%s', + repr(query_msvc_version), repr(msvc_toolset_version) + ) + return query_msvc_version, msvc_toolset_version - msvc_toolset_version = query_msvc_toolset_version + except MSVCToolsetVersionNotFound: + pass + + msvc_toolset_version = query_msvc_toolset_version debug( 'not found: msvc_version=%s, msvc_toolset_version=%s', repr(msvc_version), repr(msvc_toolset_version) ) - if version_def.msvc_verstr == msvc_toolset_version: - msg = f'MSVC version {version!r} was not found' - MSVC.Policy.msvc_notfound_handler(None, msg) - return msvc_version, msvc_toolset_version - msg = f'MSVC toolset version {version!r} not found' + debug(f'MSVCToolsetVersionNotFound: {msg}') raise MSVCToolsetVersionNotFound(msg) diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index 3b30a570a0..d8813da2bd 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -234,8 +234,18 @@ class Data: HAVE_MSVC = False DEFAULT_VERSION_DEF = None + INSTALLED_VCS = MSCommon.vc.get_installed_vcs() INSTALLED_VCS_COMPONENTS = MSCommon.vc.get_installed_vcs_components() + @classmethod + def query_version_list(cls, vcver): + # VS 2022 (14.3) can have either/both toolset versions 14.3X and 14.4X + if vcver == '14.3' or (vcver is None and cls.DEFAULT_VERSION == '14.3'): + vcver_list = ['14.4', '14.3'] + else: + vcver_list = [vcver] + return vcver_list + @classmethod def _msvc_toolset_notfound_list(cls, toolset_seen, toolset_list): new_toolset_list = [] @@ -454,15 +464,26 @@ class MsvcQueryVersionToolsetTests(unittest.TestCase): def run_valid_default_msvc(self, have_msvc) -> None: for prefer_newest in (True, False): - msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( - version=None, prefer_newest=prefer_newest - ) - expect = (have_msvc and msvc_version) or (not have_msvc and not msvc_version) - self.assertTrue(expect, "unexpected msvc_version {} for for msvc version {}".format( + if not have_msvc: + with self.assertRaises(MSCommon.vc.MSVCToolsetVersionNotFound): + msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( + version=None, prefer_newest=prefer_newest + ) + continue + msvc_version = msvc_toolset_version = None + for vcver in Data.query_version_list(None): + try: + msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( + version=vcver, prefer_newest=prefer_newest + ) + break + except MSCommon.vc.MSVCToolsetVersionNotFound: + pass + self.assertTrue(msvc_version, "unexpected msvc_version {} for for msvc version {}".format( repr(msvc_version), repr(None) )) version_def = MSCommon.msvc_version_components(msvc_version) - if have_msvc and version_def.msvc_vernum > 14.0: + if version_def.msvc_vernum > 14.0: # VS2017 and later for toolset version self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format( repr(None) @@ -477,11 +498,24 @@ def test_valid_default_msvc(self) -> None: def test_valid_vcver(self) -> None: for symbol in MSCommon.vc._VCVER: + have_msvc = bool(symbol in Data.INSTALLED_VCS) version_def = MSCommon.msvc_version_components(symbol) for prefer_newest in (True, False): - msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( - version=symbol, prefer_newest=prefer_newest - ) + if not have_msvc: + with self.assertRaises(MSCommon.vc.MSVCToolsetVersionNotFound): + msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( + version=symbol, prefer_newest=prefer_newest + ) + continue + msvc_version = msvc_toolset_version = None + for vcver in Data.query_version_list(symbol): + try: + msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( + version=vcver, prefer_newest=prefer_newest + ) + break + except: + pass self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(symbol))) if version_def.msvc_vernum > 14.0: # VS2017 and later for toolset version diff --git a/test/MSVC/no_msvc.py b/test/MSVC/no_msvc.py index 35cce9258d..aa9e2b8e57 100644 --- a/test/MSVC/no_msvc.py +++ b/test/MSVC/no_msvc.py @@ -75,8 +75,7 @@ def exists(env): # test no msvc's and msvc_query_version_toolset() call test.file_fixture('no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py', 'SConstruct') -test.run(arguments='-Q -s') -test.must_contain_all(test.stdout(), 'msvc_version=None, msvc_toolset_version=None') +test.run(arguments='-Q -s', status=2, stderr=r"^.*MSVCToolsetVersionNotFound.+", match=TestSCons.match_re_dotall) test.pass_test() From 8abc02fcd2a1a7e5464ef01b992f7535ceba2bdd Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 28 May 2024 08:40:34 -0400 Subject: [PATCH 034/386] Fix msvc_query_version_toolset function test in vcTests.py Changes: * Add function to return msvc version and toolset version pairs for all installed vcs. * Construct set of installed toolset vcs for determining if msvc_query_version_toolset should raise an exception. --- SCons/Tool/MSCommon/vc.py | 32 ++++++++++++++++++++++++++++++-- SCons/Tool/MSCommon/vcTests.py | 8 +++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index a92dd19f68..63919bd7e2 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -2570,6 +2570,34 @@ def msvc_toolset_versions_spectre(msvc_version=None, vswhere_exe=None): rval = MSVC.ScriptArguments._msvc_toolset_versions_spectre_internal(msvc_version, vc_dir) return rval +def get_installed_vcs_toolsets_components(env=None): + + vcs = get_installed_vcs(env) + + msvc_toolset_component_defs = [] + + for msvc_version in vcs: + msvc_version_def = MSVC.Util.msvc_version_components(msvc_version) + if msvc_version_def.msvc_vernum > 14.0: + # VS2017 and later + toolset_all_list = msvc_toolset_versions(msvc_version=msvc_version, full=True, sxs=True) + for toolset_version in toolset_all_list: + debug('msvc_version=%s, toolset_version=%s', repr(msvc_version), repr(toolset_version)) + toolset_version_def = MSVC.Util.msvc_extended_version_components(toolset_version) + if not toolset_version_def: + continue + rval = (msvc_version_def, toolset_version_def) + msvc_toolset_component_defs.append(rval) + else: + # VS2015 and earlier + toolset_version = msvc_version_def.msvc_verstr + debug('msvc_version=%s, toolset_version=%s', repr(msvc_version), repr(toolset_version)) + toolset_version_def = MSVC.Util.msvc_extended_version_components(toolset_version) + rval = (msvc_version_def, toolset_version_def) + msvc_toolset_component_defs.append(rval) + + return msvc_toolset_component_defs + def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_exe=None): """ Return an msvc version and a toolset version given a version @@ -2616,13 +2644,13 @@ def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_e """ debug('version=%s, prefer_newest=%s', repr(version), repr(prefer_newest)) + _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe) + msvc_version = None msvc_toolset_version = None with MSVC.Policy.msvc_notfound_policy_contextmanager('suppress'): - _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe) - vcs = get_installed_vcs() if not version: diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index d8813da2bd..65ae904380 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -237,6 +237,12 @@ class Data: INSTALLED_VCS = MSCommon.vc.get_installed_vcs() INSTALLED_VCS_COMPONENTS = MSCommon.vc.get_installed_vcs_components() + INSTALLED_VCS_TOOLSETS_COMPONENTS = MSCommon.vc.get_installed_vcs_toolsets_components() + INSTALLED_VCS_TOOLSETS = set() + for msvc_version_def, toolset_version_def in INSTALLED_VCS_TOOLSETS_COMPONENTS: + INSTALLED_VCS_TOOLSETS.add(msvc_version_def.msvc_version) + INSTALLED_VCS_TOOLSETS.add(toolset_version_def.msvc_version) + @classmethod def query_version_list(cls, vcver): # VS 2022 (14.3) can have either/both toolset versions 14.3X and 14.4X @@ -498,7 +504,7 @@ def test_valid_default_msvc(self) -> None: def test_valid_vcver(self) -> None: for symbol in MSCommon.vc._VCVER: - have_msvc = bool(symbol in Data.INSTALLED_VCS) + have_msvc = bool(symbol in Data.INSTALLED_VCS_TOOLSETS) version_def = MSCommon.msvc_version_components(symbol) for prefer_newest in (True, False): if not have_msvc: From 8545d25c590c102d147f4a807aee39e10dcc0e95 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 28 May 2024 08:45:02 -0400 Subject: [PATCH 035/386] Restore PCH time multiplier in test/MSVC/msvc.py (changed from 0.90 to 1.00). --- test/MSVC/msvc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/MSVC/msvc.py b/test/MSVC/msvc.py index 8d018f82bf..e81ecea7e8 100644 --- a/test/MSVC/msvc.py +++ b/test/MSVC/msvc.py @@ -116,7 +116,7 @@ # TODO: Reevaluate if having this part of the test makes sense any longer # using precompiled headers should be faster -limit = slow +limit = slow*1.00 if fast >= limit: print("Using precompiled headers was not fast enough:") print("slow.obj: %.3fs" % slow) From ddaa853d1b05d0cbdcb828267b502452160c64da Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 28 May 2024 09:10:33 -0400 Subject: [PATCH 036/386] Fix grammatical error in RELEASE.txt --- RELEASE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE.txt b/RELEASE.txt index 7168ee5d1a..f88c91019f 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -69,7 +69,7 @@ FIXES - MSVC: For Visual Studio 2005 (8.0) to Visual Studio 2015 (14.0), detection of installed files was expanded to include the primary msvc batch file, dependent msvc batch file, and compiler executable. In certain installations, the - dependent msvc batch file may not exist while the compiler executable does exists + dependent msvc batch file may not exist while the compiler executable does exist resulting in a build failure. - MSVC: Visual Studio 2008 (9.0) Visual C++ For Python was not detected when installed using the ALLUSERS command-line option: From 4818f19eb0c11deee0d591d3727f4f82b666269a Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 28 May 2024 09:47:42 -0600 Subject: [PATCH 037/386] CI: enable msvc config cache for windows workflow (#4545) And suitable environment var for the two Windows runners tweaks: pip now won't show progress bars installs a few extra pkgs for Windows (winflexbison3 and dmd - but dmd still needs to be added to PATH) Also add "quiet" option to choco install. Enable pip cache Signed-off-by: Mats Wichmann --- .github/workflows/experimental_tests.yml | 8 ++++++-- .github/workflows/runtest-win.yml | 14 +++++++++++--- .github/workflows/runtest.yml | 7 ++++--- .github/workflows/scons-package.yml | 5 +++-- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/.github/workflows/experimental_tests.yml b/.github/workflows/experimental_tests.yml index 03f233c4f4..65a06127d4 100644 --- a/.github/workflows/experimental_tests.yml +++ b/.github/workflows/experimental_tests.yml @@ -13,6 +13,10 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +env: + # for use by the Windows runner (ignored by the others): + SCONS_CACHE_MSVC_CONFIG: 1 + # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" @@ -50,8 +54,8 @@ jobs: - name: Install dependencies including ninja ${{ matrix.os }} run: | - python -m pip install --upgrade pip setuptools wheel - python -m pip install ninja psutil + python -m pip install --progress-bar off --upgrade pip + python -m pip install --progress-bar off ninja psutil # sudo apt-get update - name: Test experimental packages ${{ matrix.os }} diff --git a/.github/workflows/runtest-win.yml b/.github/workflows/runtest-win.yml index ffbe071156..2dc2ce118f 100644 --- a/.github/workflows/runtest-win.yml +++ b/.github/workflows/runtest-win.yml @@ -13,6 +13,9 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +env: + SCONS_CACHE_MSVC_CONFIG: 1 + jobs: runtest-win32: runs-on: windows-latest @@ -23,11 +26,16 @@ jobs: uses: actions/setup-python@v5.1.0 with: python-version: '3.12' + cache: 'pip' + + - name: Install Python dependencies + run: | + python -m pip install --progress-bar off --upgrade pip + python -m pip install --progress-bar off -r requirements-dev.txt - - name: Install dependencies including ninja + - name: Install Chocolatey packages run: | - python -m pip install --upgrade pip - python -m pip install -r requirements-dev.txt + choco install --yes --no-progress dmd winflexbison3 - name: runtest run: | diff --git a/.github/workflows/runtest.yml b/.github/workflows/runtest.yml index 40ad3970ac..1e5087f120 100644 --- a/.github/workflows/runtest.yml +++ b/.github/workflows/runtest.yml @@ -37,11 +37,12 @@ jobs: uses: actions/setup-python@v5.1.0 with: python-version: '3.12' + cache: 'pip' - - name: Install dependencies including ninja ${{ matrix.os }} + - name: Install Python dependencies ${{ matrix.os }} run: | - python -m pip install --upgrade pip setuptools wheel - python -m pip install -r requirements-dev.txt + python -m pip install --progress-bar off --upgrade pip + python -m pip install --progress-bar off -r requirements-dev.txt # sudo apt-get update - name: runtest ${{ matrix.os }} diff --git a/.github/workflows/scons-package.yml b/.github/workflows/scons-package.yml index 69526aa041..19dcfb6d1b 100644 --- a/.github/workflows/scons-package.yml +++ b/.github/workflows/scons-package.yml @@ -20,12 +20,13 @@ jobs: uses: actions/setup-python@v5.1.0 with: python-version: '3.12' + cache: 'pip' - name: Install dependencies run: | - python -m pip install --upgrade pip setuptools wheel build + python -m pip install --progress-bar off --upgrade pip setuptools wheel #python -m pip install flake8 pytest - if [ -f requirements-pkg.txt ]; then pip install -r requirements-pkg.txt; elif [ -f requirements.txt ]; then pip install -r requirements.txt; fi + if [ -f requirements-pkg.txt ]; then pip install --progress-bar off -r requirements-pkg.txt; elif [ -f requirements.txt ]; then pip install --progress-bar off -r requirements.txt; fi sudo apt-get update sudo apt-get -y install docbook-xml docbook-xsl xsltproc fop docbook-xsl-doc-pdf # try to keep the texlive install as small as we can to save some time/space From 6a4c52bb516657e71ab6a7355ee8c5d84a23fb4a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 28 May 2024 13:04:18 -0400 Subject: [PATCH 038/386] Fix bug in Config.py initialization and rework support for MsvcQueryVersionToolsetTests. Changes: * module-level initialization index was not renamed from vc_version to msvc_version. * Add function to return toolset sxs map and versions list in ScriptArguments.py * Add additional fields to extended version components * Add data structure for installed vcs versions/toolsets for testing * Update vcTests.py to new data structure. --- SCons/Tool/MSCommon/MSVC/Config.py | 2 +- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 8 ++ SCons/Tool/MSCommon/MSVC/Util.py | 25 +++++-- SCons/Tool/MSCommon/vc.py | 81 ++++++++++++++++----- SCons/Tool/MSCommon/vcTests.py | 7 +- 5 files changed, 91 insertions(+), 32 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index a04db4904e..d0ccb6d0ba 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -230,7 +230,7 @@ MSVC_BUILDTOOLS_INTERNAL[vc_buildtools] = vc_buildtools_def MSVC_BUILDTOOLS_EXTERNAL[vc_buildtools] = vc_buildtools_def - MSVC_BUILDTOOLS_EXTERNAL[vc_version] = vc_buildtools_def + MSVC_BUILDTOOLS_EXTERNAL[msvc_version] = vc_buildtools_def for vc_buildseries_def in vc_buildseries_list: VC_BUILDTOOLS_MAP[vc_buildseries_def.vc_buildseries] = vc_buildtools_def diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 76ad8717b0..2c766fed4e 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -1066,6 +1066,14 @@ def _msvc_toolset_versions_internal(msvc_version, vc_dir, full: bool=True, sxs: return toolset_versions +def _msvc_version_toolsets_internal(msvc_version, vc_dir): + + msvc = _msvc_version(msvc_version) + + toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir) + + return toolsets_sxs, toolsets_full + def _msvc_toolset_versions_spectre_internal(msvc_version, vc_dir): msvc = _msvc_version(msvc_version) diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index 67edfec349..0da58e1e4d 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -351,17 +351,21 @@ def msvc_version_components(vcver): return msvc_version_components_def _MSVC_EXTENDED_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCExtendedVersionComponentsDefinition', [ - 'msvc_version', # msvc version (e.g., '14.1Exp') - 'msvc_verstr', # msvc version numeric string (e.g., '14.1') - 'msvc_suffix', # msvc version component type (e.g., 'Exp') - 'msvc_vernum', # msvc version floating point number (e.g, 14.1) - 'msvc_major', # msvc major version integer number (e.g., 14) - 'msvc_minor', # msvc minor version integer number (e.g., 1) - 'msvc_comps', # msvc version components tuple (e.g., ('14', '1')) + 'msvc_version', # msvc version (e.g., '14.1Exp') + 'msvc_verstr', # msvc version numeric string (e.g., '14.1') + 'msvc_suffix', # msvc version component type (e.g., 'Exp') + 'msvc_suffix_rank', # msvc version component rank (0, 1) + 'msvc_vernum', # msvc version floating point number (e.g, 14.1) + 'msvc_major', # msvc major version integer number (e.g., 14) + 'msvc_minor', # msvc minor version integer number (e.g., 1) + 'msvc_comps', # msvc version components tuple (e.g., ('14', '1')) 'msvc_buildtools', # msvc build tools + 'msvc_buildtools_num', # msvc build tools integer number 'msvc_buildseries', # msvc build series + 'msvc_buildseries_num', # msvc build series floating point number 'msvc_toolset_version', # msvc toolset version 'msvc_toolset_comps', # msvc toolset version components + 'msvc_toolset_is_sxs', # msvc toolset version is sxs 'version', # msvc version or msvc toolset version ]) @@ -386,6 +390,7 @@ def msvc_extended_version_components(version): msvc_toolset_version = m.group('version') msvc_toolset_comps = tuple(msvc_toolset_version.split('.')) + msvc_toolset_is_sxs = is_toolset_sxs(msvc_toolset_version) vc_verstr = get_msvc_version_prefix(msvc_toolset_version) if not vc_verstr: @@ -414,14 +419,18 @@ def msvc_extended_version_components(version): msvc_version = msvc_version, msvc_verstr = msvc_verstr, msvc_suffix = msvc_suffix, + msvc_suffix_rank = 0 if not msvc_suffix else 1, msvc_vernum = msvc_vernum, msvc_major = msvc_major, msvc_minor = msvc_minor, msvc_comps = msvc_comps, - msvc_buildtools = vc_buildtools_def.vc_buildtools, + msvc_buildtools = vc_buildtools_def.msvc_version, + msvc_buildtools_num = vc_buildtools_def.msvc_version_numeric, msvc_buildseries = vc_buildseries_def.vc_version, + msvc_buildseries_num = vc_buildseries_def.vc_version_numeric, msvc_toolset_version = msvc_toolset_version, msvc_toolset_comps = msvc_toolset_comps, + msvc_toolset_is_sxs = msvc_toolset_is_sxs, version = version, ) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 63919bd7e2..032dbf202e 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1229,7 +1229,10 @@ def vswhere_freeze_env(cls, env): # external use def vswhere_register_executable(vswhere_exe, priority=None, freeze=False): - debug('register vswhere_exe=%s, priority=%s, freeze=%s', repr(vswhere_exe), repr(priority), repr(freeze)) + debug( + 'register vswhere_exe=%s, priority=%s, freeze=%s', + repr(vswhere_exe), repr(priority), repr(freeze) + ) _VSWhereExecutable.register_vswhere_executable(vswhere_exe, priority=priority) if freeze: _VSWhereExecutable.freeze_vswhere_executable() @@ -2525,6 +2528,8 @@ def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False, v repr(msvc_version), repr(full), repr(sxs), repr(vswhere_exe) ) + _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe) + rval = [] if not msvc_version: @@ -2549,6 +2554,8 @@ def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False, v def msvc_toolset_versions_spectre(msvc_version=None, vswhere_exe=None): debug('msvc_version=%s, vswhere_exe=%s', repr(msvc_version), repr(vswhere_exe)) + _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe) + rval = [] if not msvc_version: @@ -2570,33 +2577,70 @@ def msvc_toolset_versions_spectre(msvc_version=None, vswhere_exe=None): rval = MSVC.ScriptArguments._msvc_toolset_versions_spectre_internal(msvc_version, vc_dir) return rval -def get_installed_vcs_toolsets_components(env=None): +_InstalledVersionToolset = namedtuple('_InstalledVersionToolset', [ + 'msvc_version_def', + 'toolset_version_def', +]) - vcs = get_installed_vcs(env) +_InstalledVCSToolsetsComponents = namedtuple('_InstalledVCSToolsetComponents', [ + 'sxs_map', + 'toolset_vcs', + 'msvc_toolset_component_defs', +]) +def get_installed_vcs_toolsets_components(vswhere_exe=None): + debug('vswhere_exe=%s', repr(vswhere_exe)) + + _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe) + + sxs_map = {} + toolset_vcs = set() msvc_toolset_component_defs = [] + vcs = get_installed_vcs() for msvc_version in vcs: + + vc_dir = _find_vc_pdir(msvc_version, vswhere_exe) + if not vc_dir: + continue + msvc_version_def = MSVC.Util.msvc_version_components(msvc_version) + toolset_vcs.add(msvc_version_def.msvc_version) + if msvc_version_def.msvc_vernum > 14.0: - # VS2017 and later - toolset_all_list = msvc_toolset_versions(msvc_version=msvc_version, full=True, sxs=True) - for toolset_version in toolset_all_list: - debug('msvc_version=%s, toolset_version=%s', repr(msvc_version), repr(toolset_version)) - toolset_version_def = MSVC.Util.msvc_extended_version_components(toolset_version) - if not toolset_version_def: - continue - rval = (msvc_version_def, toolset_version_def) - msvc_toolset_component_defs.append(rval) + + toolsets_sxs, toolsets_full = MSVC.ScriptArguments._msvc_version_toolsets_internal( + msvc_version, vc_dir + ) + + debug('msvc_version=%s, toolset_sxs=%s', repr(msvc_version), repr(toolsets_sxs)) + sxs_map.update(toolsets_sxs) + else: - # VS2015 and earlier - toolset_version = msvc_version_def.msvc_verstr + + toolsets_full = [msvc_version_def.msvc_verstr] + + for toolset_version in toolsets_full: debug('msvc_version=%s, toolset_version=%s', repr(msvc_version), repr(toolset_version)) + toolset_version_def = MSVC.Util.msvc_extended_version_components(toolset_version) - rval = (msvc_version_def, toolset_version_def) + if not toolset_version_def: + continue + toolset_vcs.add(toolset_version_def.msvc_version) + + rval = _InstalledVersionToolset( + msvc_version_def=msvc_version_def, + toolset_version_def=toolset_version_def, + ) msvc_toolset_component_defs.append(rval) - return msvc_toolset_component_defs + installed_vcs_toolsets_components = _InstalledVCSToolsetsComponents( + sxs_map=sxs_map, + toolset_vcs=toolset_vcs, + msvc_toolset_component_defs=msvc_toolset_component_defs, + ) + + return installed_vcs_toolsets_components def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_exe=None): """ @@ -2642,7 +2686,10 @@ def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_e MSVCToolsetVersionNotFound: when the specified version is not found. MSVCArgumentError: when argument validation fails. """ - debug('version=%s, prefer_newest=%s', repr(version), repr(prefer_newest)) + debug( + 'version=%s, prefer_newest=%s, vswhere_exe=%s', + repr(version), repr(prefer_newest), repr(vswhere_exe) + ) _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe) diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index 65ae904380..e1146e89be 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -236,12 +236,7 @@ class Data: INSTALLED_VCS = MSCommon.vc.get_installed_vcs() INSTALLED_VCS_COMPONENTS = MSCommon.vc.get_installed_vcs_components() - INSTALLED_VCS_TOOLSETS_COMPONENTS = MSCommon.vc.get_installed_vcs_toolsets_components() - INSTALLED_VCS_TOOLSETS = set() - for msvc_version_def, toolset_version_def in INSTALLED_VCS_TOOLSETS_COMPONENTS: - INSTALLED_VCS_TOOLSETS.add(msvc_version_def.msvc_version) - INSTALLED_VCS_TOOLSETS.add(toolset_version_def.msvc_version) @classmethod def query_version_list(cls, vcver): @@ -504,7 +499,7 @@ def test_valid_default_msvc(self) -> None: def test_valid_vcver(self) -> None: for symbol in MSCommon.vc._VCVER: - have_msvc = bool(symbol in Data.INSTALLED_VCS_TOOLSETS) + have_msvc = bool(symbol in Data.INSTALLED_VCS_TOOLSETS_COMPONENTS.toolset_vcs) version_def = MSCommon.msvc_version_components(symbol) for prefer_newest in (True, False): if not have_msvc: From 5274fa51b2741e30f9ab7d6f614ccd2276405484 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 13:30:51 -0700 Subject: [PATCH 039/386] speed up test/AddOption/optional-arg.py by disabling tool initialiazation which is not needed for this test --- test/AddOption/optional-arg.py | 213 +++++++++++++++++---------------- 1 file changed, 107 insertions(+), 106 deletions(-) diff --git a/test/AddOption/optional-arg.py b/test/AddOption/optional-arg.py index b88b796df7..1f597bf43e 100644 --- a/test/AddOption/optional-arg.py +++ b/test/AddOption/optional-arg.py @@ -1,106 +1,107 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify use of the nargs='?' keyword argument to specify a long -command-line option with an optional argument value. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -AddOption('--install', - nargs='?', - dest='install', - default='/default/directory', - const='/called/default/directory', - action='store', - type='string', - metavar='DIR', - help='installation directory') -print(GetOption('install')) -""") - -test.run('-Q -q', - stdout="/default/directory\n") - -test.run('-Q -q next-arg', - stdout="/default/directory\n", - status=1) - -test.run('-Q -q . --install', - stdout="/called/default/directory\n") - -test.run('-Q -q . --install next-arg', - stdout="/called/default/directory\n", - status=1) - -test.run('-Q -q . first-arg --install', - stdout="/called/default/directory\n", - status=1) - -test.run('-Q -q . first-arg --install next-arg', - stdout="/called/default/directory\n", - status=1) - -test.run('-Q -q . --install=/command/line/directory', - stdout="/command/line/directory\n") - -test.run('-Q -q . --install=/command/line/directory next-arg', - stdout="/command/line/directory\n", - status=1) - -test.run('-Q -q . first-arg --install=/command/line/directory', - stdout="/command/line/directory\n", - status=1) - -test.run('-Q -q . first-arg --install=/command/line/directory next-arg', - stdout="/command/line/directory\n", - status=1) - - -test.write('SConstruct', """\ -AddOption('-X', nargs='?') -""") - -expect = r""" -scons: \*\*\* option -X: nargs='\?' is incompatible with short options -File "[^"]+", line \d+, in \S+ -""" - -test.run(status=2, stderr=expect, match=TestSCons.match_re) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of the nargs='?' keyword argument to specify a long +command-line option with an optional argument value. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +AddOption('--install', + nargs='?', + dest='install', + default='/default/directory', + const='/called/default/directory', + action='store', + type='string', + metavar='DIR', + help='installation directory') +print(GetOption('install')) +""") + +test.run('-Q -q', + stdout="/default/directory\n") + +test.run('-Q -q next-arg', + stdout="/default/directory\n", + status=1) + +test.run('-Q -q . --install', + stdout="/called/default/directory\n") + +test.run('-Q -q . --install next-arg', + stdout="/called/default/directory\n", + status=1) + +test.run('-Q -q . first-arg --install', + stdout="/called/default/directory\n", + status=1) + +test.run('-Q -q . first-arg --install next-arg', + stdout="/called/default/directory\n", + status=1) + +test.run('-Q -q . --install=/command/line/directory', + stdout="/command/line/directory\n") + +test.run('-Q -q . --install=/command/line/directory next-arg', + stdout="/command/line/directory\n", + status=1) + +test.run('-Q -q . first-arg --install=/command/line/directory', + stdout="/command/line/directory\n", + status=1) + +test.run('-Q -q . first-arg --install=/command/line/directory next-arg', + stdout="/command/line/directory\n", + status=1) + + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +AddOption('-X', nargs='?') +""") + +expect = r""" +scons: \*\*\* option -X: nargs='\?' is incompatible with short options +File "[^"]+", line \d+, in \S+ +""" + +test.run(status=2, stderr=expect, match=TestSCons.match_re) + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: From 3848c5cf9b66a3d0a21e0949cd5bd318c1bf15b3 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 13:32:06 -0700 Subject: [PATCH 040/386] Disable tool initialization in tests which don't need it --- test/AddOption/args-and-targets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/AddOption/args-and-targets.py b/test/AddOption/args-and-targets.py index 56bdd8502d..856c868454 100644 --- a/test/AddOption/args-and-targets.py +++ b/test/AddOption/args-and-targets.py @@ -35,7 +35,8 @@ test.write( 'SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) AddOption( '--extra', nargs=1, From 4f44a22a5b8f86e994ba8b74aa4969d84b3fbe7a Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 13:35:51 -0700 Subject: [PATCH 041/386] Disable tool initialization in tests which don't need it --- test/AddOption/basic.py | 148 ++++++++++++++++---------------- test/AddOption/help.py | 166 ++++++++++++++++++------------------ test/AddOption/longopts.py | 1 + test/AddOption/multi-arg.py | 6 +- 4 files changed, 160 insertions(+), 161 deletions(-) diff --git a/test/AddOption/basic.py b/test/AddOption/basic.py index b1b8f2e1de..5f391784b2 100644 --- a/test/AddOption/basic.py +++ b/test/AddOption/basic.py @@ -1,74 +1,74 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify the help text when the AddOption() function is used (and when -it's not). -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -env = Environment() -AddOption('--force', - action="store_true", - help='force installation (overwrite any existing files)') -AddOption('--prefix', - nargs=1, - dest='prefix', - action='store', - type='string', - metavar='DIR', - help='installation prefix') -f = GetOption('force') -if f: - f = "True" -print(f) -print(GetOption('prefix')) -""") - -test.run('-Q -q .', - stdout="None\nNone\n") - -test.run('-Q -q . --force', - stdout="True\nNone\n") - -test.run('-Q -q . --prefix=/home/foo', - stdout="None\n/home/foo\n") - -test.run('-Q -q . -- --prefix=/home/foo --force', - status=1, - stdout="None\nNone\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the help text when the AddOption() function is used (and when +it's not). +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ + DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +AddOption('--force', + action="store_true", + help='force installation (overwrite any existing files)') +AddOption('--prefix', + nargs=1, + dest='prefix', + action='store', + type='string', + metavar='DIR', + help='installation prefix') +f = GetOption('force') +if f: + f = "True" +print(f) +print(GetOption('prefix')) +""") + +test.run('-Q -q .', + stdout="None\nNone\n") + +test.run('-Q -q . --force', + stdout="True\nNone\n") + +test.run('-Q -q . --prefix=/home/foo', + stdout="None\n/home/foo\n") + +test.run('-Q -q . -- --prefix=/home/foo --force', + status=1, + stdout="None\nNone\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AddOption/help.py b/test/AddOption/help.py index 23e5bd1d27..81bb8983fb 100644 --- a/test/AddOption/help.py +++ b/test/AddOption/help.py @@ -1,85 +1,81 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# 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 the help text when the AddOption() function is used (and when -it's not). -""" - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -env = Environment() -AddOption('--force', - action="store_true", - help='force installation (overwrite existing files)') -AddOption('--prefix', - nargs=1, - dest='prefix', - action='store', - type='string', - metavar='DIR', - help='installation prefix') -""") - -expected_lines = [ - 'Local Options:', - ' --force force installation (overwrite existing files)', - ' --prefix=DIR installation prefix', -] - -test.run(arguments = '-h') -lines = test.stdout().split('\n') -missing = [e for e in expected_lines if e not in lines] - -if missing: - print("====== STDOUT:") - print(test.stdout()) - print("====== Missing the following lines in the above AddOption() help output:") - print("\n".join(missing)) - test.fail_test() - -test.unlink('SConstruct') - -test.run(arguments = '-h') -lines = test.stdout().split('\n') -unexpected = [e for e in expected_lines if e in lines] - -if unexpected: - print("====== STDOUT:") - print(test.stdout()) - print("====== Unexpected lines in the above non-AddOption() help output:") - print("\n".join(unexpected)) - test.fail_test() - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +AddOption('--force', + action="store_true", + help='force installation (overwrite existing files)') +AddOption('--prefix', + nargs=1, + dest='prefix', + action='store', + type='string', + metavar='DIR', + help='installation prefix') +""") + +expected_lines = [ + 'Local Options:', + ' --force force installation (overwrite existing files)', + ' --prefix=DIR installation prefix', +] + +test.run(arguments = '-h') +lines = test.stdout().split('\n') +missing = [e for e in expected_lines if e not in lines] + +if missing: + print("====== STDOUT:") + print(test.stdout()) + print("====== Missing the following lines in the above AddOption() help output:") + print("\n".join(missing)) + test.fail_test() + +test.unlink('SConstruct') + +test.run(arguments = '-h') +lines = test.stdout().split('\n') +unexpected = [e for e in expected_lines if e in lines] + +if unexpected: + print("====== STDOUT:") + print(test.stdout()) + print("====== Unexpected lines in the above non-AddOption() help output:") + print("\n".join(unexpected)) + test.fail_test() + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AddOption/longopts.py b/test/AddOption/longopts.py index e0c0df4159..668591b7b7 100644 --- a/test/AddOption/longopts.py +++ b/test/AddOption/longopts.py @@ -35,6 +35,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) AddOption('--myargument', dest='myargument', type='string', default='gully') AddOption('--myarg', dest='myarg', type='string', default='balla') print("myargument: " + str(GetOption('myargument'))) diff --git a/test/AddOption/multi-arg.py b/test/AddOption/multi-arg.py index 1a71cfef06..c2b04841b1 100644 --- a/test/AddOption/multi-arg.py +++ b/test/AddOption/multi-arg.py @@ -36,7 +36,8 @@ test.write( 'SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) AddOption('--extras', nargs=2, dest='extras', @@ -71,7 +72,8 @@ test.write( 'SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) AddOption( '--prefix', nargs=1, From fac90976f67bfb7c01064fa62b8f7be8228f35e7 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 14:11:29 -0700 Subject: [PATCH 042/386] Disable tool initialization in tests which don't need it --- test/Actions/addpost-link.py | 149 ++++++++++++++++++----------------- test/Actions/append.py | 2 +- test/Actions/exitstatfunc.py | 146 +++++++++++++++++----------------- test/Alias/Alias.py | 4 +- test/Alias/Depends.py | 2 +- test/Alias/Dir-order.py | 98 +++++++++++------------ test/Alias/action.py | 2 +- test/Alias/errors.py | 98 +++++++++++------------ test/Alias/scanner.py | 127 ++++++++++++++--------------- test/Alias/srcdir.py | 11 ++- 10 files changed, 320 insertions(+), 319 deletions(-) diff --git a/test/Actions/addpost-link.py b/test/Actions/addpost-link.py index 0a238e11b7..3aa8352504 100644 --- a/test/Actions/addpost-link.py +++ b/test/Actions/addpost-link.py @@ -1,74 +1,75 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that AddPostAction() on a program target doesn't interfere with -linking. - -This is a test for fix of Issue 1004, reported by Matt Doar and -packaged by Gary Oberbrunner. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.dir_fixture('addpost-link-fixture') - -test.write('SConstruct', """\ -env = Environment() - -mylib = env.StaticLibrary('mytest', 'test_lib.c') - -myprog = env.Program('test1.c', - LIBPATH = ['.'], - LIBS = ['mytest'], - OBJSUFFIX = '.obj', - PROGSUFFIX = '.exe') -if ARGUMENTS['case']=='2': - AddPostAction(myprog, Action(r'%(_python_)s strip.py ' + myprog[0].get_abspath())) -""" % locals()) - -test.run(arguments="-Q case=1", stderr=None) - -test.run(arguments="-Q -c case=1") - -test.must_not_exist('test1.obj') - -test.run(arguments="-Q case=2", stderr=None) - -expect = 'strip.py: %s' % test.workpath('test1.exe') -test.must_contain_all_lines(test.stdout(), [expect]) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that AddPostAction() on a program target doesn't interfere with +linking. + +This is a test for fix of Issue 1004, reported by Matt Doar and +packaged by Gary Oberbrunner. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.dir_fixture('addpost-link-fixture') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment() + +mylib = env.StaticLibrary('mytest', 'test_lib.c') + +myprog = env.Program('test1.c', + LIBPATH = ['.'], + LIBS = ['mytest'], + OBJSUFFIX = '.obj', + PROGSUFFIX = '.exe') +if ARGUMENTS['case']=='2': + AddPostAction(myprog, Action(r'%(_python_)s strip.py ' + myprog[0].get_abspath())) +""" % locals()) + +test.run(arguments="-Q case=1", stderr=None) + +test.run(arguments="-Q -c case=1") + +test.must_not_exist('test1.obj') + +test.run(arguments="-Q case=2", stderr=None) + +expect = 'strip.py: %s' % test.workpath('test1.exe') +test.must_contain_all_lines(test.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/Actions/append.py b/test/Actions/append.py index 42a414bc62..01a9f23219 100644 --- a/test/Actions/append.py +++ b/test/Actions/append.py @@ -41,7 +41,7 @@ test.dir_fixture('append-fixture') test.write('SConstruct', """ - +DefaultEnvironment(tools=[]) env=Environment() def before(env, target, source): diff --git a/test/Actions/exitstatfunc.py b/test/Actions/exitstatfunc.py index 2e2a540849..e14b86b32f 100644 --- a/test/Actions/exitstatfunc.py +++ b/test/Actions/exitstatfunc.py @@ -1,73 +1,73 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that setting exitstatfunc on an Action works as advertised. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -def always_succeed(s): - # Always return 0, which indicates success. - return 0 - -def copy_fail(target, source, env): - with open(str(source[0]), 'rb') as infp, open(str(target[0]), 'wb') as f: - f.write(infp.read()) - return 2 - -a = Action(copy_fail, exitstatfunc=always_succeed) -Alias('test1', Command('test1.out', 'test1.in', a)) - -def fail(target, source, env): - return 2 - -t2 = Command('test2.out', 'test2.in', Copy('$TARGET', '$SOURCE')) -AddPostAction(t2, Action(fail, exitstatfunc=always_succeed)) -Alias('test2', t2) -""") - -test.write('test1.in', "test1.in\n") -test.write('test2.in', "test2.in\n") - -test.run(arguments = 'test1') - -test.must_match('test1.out', "test1.in\n") - -test.run(arguments = 'test2') - -test.must_match('test2.out', "test2.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that setting exitstatfunc on an Action works as advertised. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def always_succeed(s): + # Always return 0, which indicates success. + return 0 + +def copy_fail(target, source, env): + with open(str(source[0]), 'rb') as infp, open(str(target[0]), 'wb') as f: + f.write(infp.read()) + return 2 + +a = Action(copy_fail, exitstatfunc=always_succeed) +Alias('test1', Command('test1.out', 'test1.in', a)) + +def fail(target, source, env): + return 2 + +t2 = Command('test2.out', 'test2.in', Copy('$TARGET', '$SOURCE')) +AddPostAction(t2, Action(fail, exitstatfunc=always_succeed)) +Alias('test2', t2) +""") + +test.write('test1.in', "test1.in\n") +test.write('test2.in', "test2.in\n") + +test.run(arguments = 'test1') + +test.must_match('test1.out', "test1.in\n") + +test.run(arguments = 'test2') + +test.must_match('test2.out', "test2.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Alias/Alias.py b/test/Alias/Alias.py index 1bb52f6ee9..5915b2df7e 100644 --- a/test/Alias/Alias.py +++ b/test/Alias/Alias.py @@ -44,7 +44,7 @@ test.write('SConstruct', """ B = Builder(action = r'%(_python_)s build.py $TARGET $SOURCES') DefaultEnvironment(tools=[]) # test speedup -env = Environment() +env = Environment(tools=[]) env['BUILDERS']['B'] = B env.B(target = 'f1.out', source = 'f1.in') env.B(target = 'f2.out', source = 'f2.in') @@ -137,7 +137,7 @@ Decider('content') B = Builder(action = r'%(_python_)s build.py $TARGET $SOURCES') DefaultEnvironment(tools=[]) # test speedup -env = Environment() +env = Environment(tools=[]) env['BUILDERS']['B'] = B env.B(target = 'f1.out', source = 'f1.in') env.B(target = 'f2.out', source = 'f2.in') diff --git a/test/Alias/Depends.py b/test/Alias/Depends.py index b1051ad171..8ab2ed6fa6 100644 --- a/test/Alias/Depends.py +++ b/test/Alias/Depends.py @@ -44,7 +44,7 @@ test.write('SConstruct', """ B = Builder(action = r'%(_python_)s build.py $TARGET $SOURCES') DefaultEnvironment(tools=[]) # test speedup -env = Environment() +env = Environment(tools=[]) env['BUILDERS']['B'] = B env.B(target = 'f1.out', source = 'f1.in') env.B(target = 'f2.out', source = 'f2.in') diff --git a/test/Alias/Dir-order.py b/test/Alias/Dir-order.py index f0eaa0ce2d..6b62cb2020 100644 --- a/test/Alias/Dir-order.py +++ b/test/Alias/Dir-order.py @@ -1,49 +1,49 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Validate that calling Dir() for a string after we've used it as an -Alias() expansion works. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -Alias('afoo', 'foo') -f = Dir('foo') -""") - -test.run(arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Validate that calling Dir() for a string after we've used it as an +Alias() expansion works. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +Alias('afoo', 'foo') +f = Dir('foo') +""") + +test.run(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Alias/action.py b/test/Alias/action.py index 82124489d5..ecff388c39 100644 --- a/test/Alias/action.py +++ b/test/Alias/action.py @@ -52,7 +52,7 @@ def bar(target, source, env): f.write(bytearray("bar(%s, %s)\\n" % (target, source),'utf-8')) DefaultEnvironment(tools=[]) # test speedup -env = Environment(BUILDERS = {'Cat':Builder(action=cat)}) +env = Environment(tools=[], BUILDERS = {'Cat':Builder(action=cat)}) env.Alias(target = ['build-f1'], source = 'f1.out', action = foo) f1 = env.Cat('f1.out', 'f1.in') f2 = env.Cat('f2.out', 'f2.in') diff --git a/test/Alias/errors.py b/test/Alias/errors.py index 1334f4e95b..0bd24909d0 100644 --- a/test/Alias/errors.py +++ b/test/Alias/errors.py @@ -1,49 +1,49 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -env=Environment() -Decider('content') -env.Alias('C', 'D') -env.Alias('B', 'C') -env.Alias('A', 'B') -""") - -test.run(arguments='A', - stderr="scons: *** [C] Source `D' not found, needed by target `C'.\n", - status=2) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env=Environment(tools=[]) +Decider('content') +env.Alias('C', 'D') +env.Alias('B', 'C') +env.Alias('A', 'B') +""") + +test.run(arguments='A', + stderr="scons: *** [C] Source `D' not found, needed by target `C'.\n", + status=2) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Alias/scanner.py b/test/Alias/scanner.py index a6ded814d9..618c052ec1 100644 --- a/test/Alias/scanner.py +++ b/test/Alias/scanner.py @@ -1,63 +1,64 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# 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. -# - -""" -Test that an Alias of a node with a Scanner works. -""" - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: - for src in source: - with open(str(src), "rb") as ifp: - f.write(ifp.read()) - -XBuilder = Builder(action = cat, src_suffix = '.x', suffix = '.c') -env = Environment() -env.Append(BUILDERS = { 'XBuilder': XBuilder }) -f = env.XBuilder(source = ['file.x'], target = ['file.c']) -env.Alias(target = ['cfiles'], source = f) -Default(['cfiles']) -""") - -test.write('file.x', "file.x\n") - -test.run() - -test.fail_test(test.read('file.c') != b"file.x\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that an Alias of a node with a Scanner works. +""" + + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, "wb") as f: + for src in source: + with open(str(src), "rb") as ifp: + f.write(ifp.read()) + +XBuilder = Builder(action = cat, src_suffix = '.x', suffix = '.c') +env = Environment(tools=[]) +env.Append(BUILDERS = { 'XBuilder': XBuilder }) +f = env.XBuilder(source = ['file.x'], target = ['file.c']) +env.Alias(target = ['cfiles'], source = f) +Default(['cfiles']) +""") + +test.write('file.x', "file.x\n") + +test.run() + +test.fail_test(test.read('file.c') != b"file.x\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Alias/srcdir.py b/test/Alias/srcdir.py index 977d114488..5115452bf1 100644 --- a/test/Alias/srcdir.py +++ b/test/Alias/srcdir.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 an Alias for a VariantDir()'s source directory works as @@ -62,8 +61,8 @@ test.write('SConstruct', """\ import os.path - -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) def at_copy_ext(target, source, env): n = str(source[0]) From ac7abf113c46c5e8145cc846a4fbd09c6a5e3d76 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 14:48:47 -0700 Subject: [PATCH 043/386] cleaning up tool initialization to speed up tests --- test/AddOption/basic.py | 2 +- test/AddOption/multi-arg.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/AddOption/basic.py b/test/AddOption/basic.py index 5f391784b2..b087ac0aba 100644 --- a/test/AddOption/basic.py +++ b/test/AddOption/basic.py @@ -33,7 +33,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', """\ - DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) env = Environment(tools=[]) AddOption('--force', action="store_true", diff --git a/test/AddOption/multi-arg.py b/test/AddOption/multi-arg.py index c2b04841b1..4361214b8d 100644 --- a/test/AddOption/multi-arg.py +++ b/test/AddOption/multi-arg.py @@ -37,7 +37,6 @@ 'SConstruct', """\ DefaultEnvironment(tools=[]) -env = Environment(tools=[]) AddOption('--extras', nargs=2, dest='extras', @@ -73,7 +72,6 @@ 'SConstruct', """\ DefaultEnvironment(tools=[]) -env = Environment(tools=[]) AddOption( '--prefix', nargs=1, @@ -103,7 +101,7 @@ # one single-arg option test.run('-Q -q . --prefix=/home/foo', stdout="/home/foo\n()\n") # one two-arg option -test.run('-Q -q . --extras A B', status=1, stdout="None\n('A', 'B')\n") +test.run('-Q -q . --extras A B', status=2, stdout="None\n('A', 'B')\n") # single-arg option followed by two-arg option test.run( '-Q -q . --prefix=/home/foo --extras A B', From 278e0d38299ddf851196fea8efe49f833b5536f5 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 15:29:22 -0700 Subject: [PATCH 044/386] cleaning up tool initialization to speed up tests --- test/AR/AR.py | 8 +- test/AR/ARCOM.py | 128 +++++++++++----------- test/AR/ARCOMSTR.py | 134 +++++++++++------------ test/AR/ARFLAGS.py | 8 +- test/AS/AS.py | 11 +- test/AS/ASCOM.py | 179 +++++++++++++++---------------- test/AS/ASCOMSTR.py | 165 ++++++++++++++-------------- test/AS/ASFLAGS.py | 16 ++- test/AS/ASPP.py | 12 +-- test/AS/ASPPCOM.py | 137 ++++++++++++------------ test/AS/ASPPCOMSTR.py | 131 ++++++++++++----------- test/AS/ASPPFLAGS.py | 16 ++- test/AS/ml.py | 243 +++++++++++++++++++++--------------------- 13 files changed, 592 insertions(+), 596 deletions(-) diff --git a/test/AR/AR.py b/test/AR/AR.py index 2b92ff4256..fbf45569d9 100644 --- a/test/AR/AR.py +++ b/test/AR/AR.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__" import TestSCons @@ -35,6 +34,7 @@ test.file_fixture('wrapper.py') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) foo = Environment(LIBS = ['foo'], LIBPATH = ['.']) ar = foo.Dictionary('AR') bar = Environment(LIBS = ['bar'], LIBPATH = ['.'], AR = r'%(_python_)s wrapper.py ' + ar) diff --git a/test/AR/ARCOM.py b/test/AR/ARCOM.py index 9ae5b9fb65..010b6c584a 100644 --- a/test/AR/ARCOM.py +++ b/test/AR/ARCOM.py @@ -1,64 +1,64 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test the ability to configure the $ARCOM construction variable. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') -test.file_fixture('myrewrite.py') - -test.write('SConstruct', """ -env = Environment(tools=['default', 'ar'], - ARCOM = r'%(_python_)s mycompile.py ar $TARGET $SOURCES', - RANLIB = True, - RANLIBCOM = r'%(_python_)s myrewrite.py ranlib $TARGET', - LIBPREFIX = '', - LIBSUFFIX = '.lib') -env.Library(target = 'output', source = ['file.1', 'file.2']) -""" % locals()) - -test.write('file.1', "file.1\n/*ar*/\n/*ranlib*/\n") -test.write('file.2', "file.2\n/*ar*/\n/*ranlib*/\n") - - -test.run(arguments = '.') - -test.must_match('output.lib', "file.1\nfile.2\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to configure the $ARCOM construction variable. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') +test.file_fixture('myrewrite.py') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['default', 'ar'], + ARCOM = r'%(_python_)s mycompile.py ar $TARGET $SOURCES', + RANLIB = True, + RANLIBCOM = r'%(_python_)s myrewrite.py ranlib $TARGET', + LIBPREFIX = '', + LIBSUFFIX = '.lib') +env.Library(target = 'output', source = ['file.1', 'file.2']) +""" % locals()) + +test.write('file.1', "file.1\n/*ar*/\n/*ranlib*/\n") +test.write('file.2', "file.2\n/*ar*/\n/*ranlib*/\n") + + +test.run(arguments = '.') + +test.must_match('output.lib', "file.1\nfile.2\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AR/ARCOMSTR.py b/test/AR/ARCOMSTR.py index a3a9c8e565..95a0cd6ff6 100644 --- a/test/AR/ARCOMSTR.py +++ b/test/AR/ARCOMSTR.py @@ -1,67 +1,67 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that the $ARCOMSTR construction variable allows you to customize -the displayed archiver string. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') -test.file_fixture('myrewrite.py') - -test.write('SConstruct', """ -env = Environment(tools=['default', 'ar'], - ARCOM = r'%(_python_)s mycompile.py ar $TARGET $SOURCES', - ARCOMSTR = 'Archiving $TARGET from $SOURCES', - RANLIB = True, - RANLIBCOM = r'%(_python_)s myrewrite.py ranlib $TARGET', - LIBPREFIX = '', - LIBSUFFIX = '.lib') -env.Library(target = 'output', source = ['file.1', 'file.2']) -""" % locals()) - -test.write('file.1', "file.1\n/*ar*/\n/*ranlib*/\n") -test.write('file.2', "file.2\n/*ar*/\n/*ranlib*/\n") - -test.run() - -expect = 'Archiving output.lib from file.1 file.2' -test.must_contain_all_lines(test.stdout(), [expect]) -test.must_match('output.lib', "file.1\nfile.2\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $ARCOMSTR construction variable allows you to customize +the displayed archiver string. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') +test.file_fixture('myrewrite.py') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['default', 'ar'], + ARCOM = r'%(_python_)s mycompile.py ar $TARGET $SOURCES', + ARCOMSTR = 'Archiving $TARGET from $SOURCES', + RANLIB = True, + RANLIBCOM = r'%(_python_)s myrewrite.py ranlib $TARGET', + LIBPREFIX = '', + LIBSUFFIX = '.lib') +env.Library(target = 'output', source = ['file.1', 'file.2']) +""" % locals()) + +test.write('file.1', "file.1\n/*ar*/\n/*ranlib*/\n") +test.write('file.2', "file.2\n/*ar*/\n/*ranlib*/\n") + +test.run() + +expect = 'Archiving output.lib from file.1 file.2' +test.must_contain_all_lines(test.stdout(), [expect]) +test.must_match('output.lib', "file.1\nfile.2\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AR/ARFLAGS.py b/test/AR/ARFLAGS.py index 21b3793808..f1a0f98cbb 100644 --- a/test/AR/ARFLAGS.py +++ b/test/AR/ARFLAGS.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__" import TestSCons @@ -35,6 +34,7 @@ test.file_fixture('wrapper.py') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) foo = Environment(LIBS = ['foo'], LIBPATH = ['.']) bar = Environment(LIBS = ['bar'], LIBPATH = ['.'], AR = '', ARFLAGS = foo.subst(r'%(_python_)s wrapper.py $AR $ARFLAGS')) diff --git a/test/AS/AS.py b/test/AS/AS.py index 0b6611dd06..cd1c5fe456 100644 --- a/test/AS/AS.py +++ b/test/AS/AS.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 the ability to set the $AS construction variable to a different @@ -41,7 +40,9 @@ test.file_fixture(['fixture', 'myas.py']) test.write('SConstruct', """ -env = Environment(LINK = r'%(_python_)s mylink.py', +DefaultEnvironment(tools=[]) +env = Environment(tools=['link','as','gcc'], + LINK = r'%(_python_)s mylink.py', AS = r'%(_python_)s myas.py', CC = r'%(_python_)s myas.py') env.Program(target = 'test1', source = 'test1.s') diff --git a/test/AS/ASCOM.py b/test/AS/ASCOM.py index ab77586747..14291cf82c 100644 --- a/test/AS/ASCOM.py +++ b/test/AS/ASCOM.py @@ -1,89 +1,90 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test the ability to configure the $ASCOM construction variable. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.s') == os.path.normcase('.S'): - alt_s_suffix = '.S' - alt_asm_suffix = '.ASM' -else: - alt_s_suffix = '.s' - alt_asm_suffix = '.asm' - -test.write('SConstruct', """ -env = Environment(ASCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', - OBJSUFFIX = '.obj', - SHOBJPREFIX = '', - SHOBJSUFFIX = '.shobj') -env.Object(target = 'test1', source = 'test1.s') -env.Object(target = 'test2', source = 'test2%(alt_s_suffix)s') -env.Object(target = 'test3', source = 'test3.asm') -env.Object(target = 'test4', source = 'test4%(alt_asm_suffix)s') -env.SharedObject(target = 'test5', source = 'test5.s') -env.SharedObject(target = 'test6', source = 'test6%(alt_s_suffix)s') -env.SharedObject(target = 'test7', source = 'test7.asm') -env.SharedObject(target = 'test8', source = 'test8%(alt_asm_suffix)s') -""" % locals()) - -test.write('test1.s', "test1.s\n/*as*/\n") -test.write('test2'+alt_s_suffix, "test2.S\n/*as*/\n") -test.write('test3.asm', "test3.asm\n/*as*/\n") -test.write('test4'+alt_asm_suffix, "test4.ASM\n/*as*/\n") -test.write('test5.s', "test5.s\n/*as*/\n") -test.write('test6'+alt_s_suffix, "test6.S\n/*as*/\n") -test.write('test7.asm', "test7.asm\n/*as*/\n") -test.write('test8'+alt_asm_suffix, "test8.ASM\n/*as*/\n") - -test.run(arguments = '.') - -test.must_match('test1.obj', "test1.s\n") -test.must_match('test2.obj', "test2.S\n") -test.must_match('test3.obj', "test3.asm\n") -test.must_match('test4.obj', "test4.ASM\n") -test.must_match('test5.shobj', "test5.s\n") -test.must_match('test6.shobj', "test6.S\n") -test.must_match('test7.shobj', "test7.asm\n") -test.must_match('test8.shobj', "test8.ASM\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to configure the $ASCOM construction variable. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.s') == os.path.normcase('.S'): + alt_s_suffix = '.S' + alt_asm_suffix = '.ASM' +else: + alt_s_suffix = '.s' + alt_asm_suffix = '.asm' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['as'], + ASCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', + OBJSUFFIX = '.obj', + SHOBJPREFIX = '', + SHOBJSUFFIX = '.shobj') +env.Object(target = 'test1', source = 'test1.s') +env.Object(target = 'test2', source = 'test2%(alt_s_suffix)s') +env.Object(target = 'test3', source = 'test3.asm') +env.Object(target = 'test4', source = 'test4%(alt_asm_suffix)s') +env.SharedObject(target = 'test5', source = 'test5.s') +env.SharedObject(target = 'test6', source = 'test6%(alt_s_suffix)s') +env.SharedObject(target = 'test7', source = 'test7.asm') +env.SharedObject(target = 'test8', source = 'test8%(alt_asm_suffix)s') +""" % locals()) + +test.write('test1.s', "test1.s\n/*as*/\n") +test.write('test2'+alt_s_suffix, "test2.S\n/*as*/\n") +test.write('test3.asm', "test3.asm\n/*as*/\n") +test.write('test4'+alt_asm_suffix, "test4.ASM\n/*as*/\n") +test.write('test5.s', "test5.s\n/*as*/\n") +test.write('test6'+alt_s_suffix, "test6.S\n/*as*/\n") +test.write('test7.asm', "test7.asm\n/*as*/\n") +test.write('test8'+alt_asm_suffix, "test8.ASM\n/*as*/\n") + +test.run(arguments = '.') + +test.must_match('test1.obj', "test1.s\n") +test.must_match('test2.obj', "test2.S\n") +test.must_match('test3.obj', "test3.asm\n") +test.must_match('test4.obj', "test4.ASM\n") +test.must_match('test5.shobj', "test5.s\n") +test.must_match('test6.shobj', "test6.S\n") +test.must_match('test7.shobj', "test7.asm\n") +test.must_match('test8.shobj', "test8.ASM\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AS/ASCOMSTR.py b/test/AS/ASCOMSTR.py index 2aab94c5ba..a97c285775 100644 --- a/test/AS/ASCOMSTR.py +++ b/test/AS/ASCOMSTR.py @@ -1,82 +1,83 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that the $ASCOMSTR construction variable allows you to configure -the assembly output. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.s') == os.path.normcase('.S'): - alt_s_suffix = '.S' - alt_asm_suffix = '.ASM' -else: - alt_s_suffix = '.s' - alt_asm_suffix = '.asm' - -test.write('SConstruct', """ -env = Environment(ASCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', - ASCOMSTR = 'Assembling $TARGET from $SOURCE', - OBJSUFFIX = '.obj') -env.Object(target = 'test1', source = 'test1.s') -env.Object(target = 'test2', source = 'test2%(alt_s_suffix)s') -env.Object(target = 'test3', source = 'test3.asm') -env.Object(target = 'test4', source = 'test4%(alt_asm_suffix)s') -""" % locals()) - -test.write('test1.s', "test1.s\n/*as*/\n") -test.write('test2'+alt_s_suffix, "test2.S\n/*as*/\n") -test.write('test3.asm', "test3.asm\n/*as*/\n") -test.write('test4'+alt_asm_suffix, "test4.ASM\n/*as*/\n") - -test.run(stdout = test.wrap_stdout("""\ -Assembling test1.obj from test1.s -Assembling test2.obj from test2%(alt_s_suffix)s -Assembling test3.obj from test3.asm -Assembling test4.obj from test4%(alt_asm_suffix)s -""" % locals())) - -test.must_match('test1.obj', "test1.s\n") -test.must_match('test2.obj', "test2.S\n") -test.must_match('test3.obj', "test3.asm\n") -test.must_match('test4.obj', "test4.ASM\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $ASCOMSTR construction variable allows you to configure +the assembly output. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.s') == os.path.normcase('.S'): + alt_s_suffix = '.S' + alt_asm_suffix = '.ASM' +else: + alt_s_suffix = '.s' + alt_asm_suffix = '.asm' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['as'], + ASCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', + ASCOMSTR = 'Assembling $TARGET from $SOURCE', + OBJSUFFIX = '.obj') +env.Object(target = 'test1', source = 'test1.s') +env.Object(target = 'test2', source = 'test2%(alt_s_suffix)s') +env.Object(target = 'test3', source = 'test3.asm') +env.Object(target = 'test4', source = 'test4%(alt_asm_suffix)s') +""" % locals()) + +test.write('test1.s', "test1.s\n/*as*/\n") +test.write('test2'+alt_s_suffix, "test2.S\n/*as*/\n") +test.write('test3.asm', "test3.asm\n/*as*/\n") +test.write('test4'+alt_asm_suffix, "test4.ASM\n/*as*/\n") + +test.run(stdout = test.wrap_stdout("""\ +Assembling test1.obj from test1.s +Assembling test2.obj from test2%(alt_s_suffix)s +Assembling test3.obj from test3.asm +Assembling test4.obj from test4%(alt_asm_suffix)s +""" % locals())) + +test.must_match('test1.obj', "test1.s\n") +test.must_match('test2.obj', "test2.S\n") +test.must_match('test3.obj', "test3.asm\n") +test.must_match('test4.obj', "test4.ASM\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AS/ASFLAGS.py b/test/AS/ASFLAGS.py index 4a1c162d56..bbc2188300 100644 --- a/test/AS/ASFLAGS.py +++ b/test/AS/ASFLAGS.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__" import sys @@ -38,14 +37,11 @@ o = ' -x' o_c = ' -x -c' -if sys.platform == 'win32': - import SCons.Tool.MSCommon as msc - - if msc.msvc_exists(): - o_c = ' -x' test.write('SConstruct', """ -env = Environment(LINK = r'%(_python_)s mylink.py', +DefaultEnvironment(tools=[]) +env = Environment(tools=['link','as'], + LINK = r'%(_python_)s mylink.py', LINKFLAGS = [], AS = r'%(_python_)s myas_args.py', ASFLAGS = '-x', CC = r'%(_python_)s myas_args.py') diff --git a/test/AS/ASPP.py b/test/AS/ASPP.py index 9c8f047b26..b256ace597 100644 --- a/test/AS/ASPP.py +++ b/test/AS/ASPP.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,10 +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__" - import TestSCons @@ -36,7 +34,9 @@ test.file_fixture(['fixture', 'myas.py']) test.write('SConstruct', """ -env = Environment(LINK = r'%(_python_)s mylink.py', +DefaultEnvironment(tools=[]) +env = Environment(tools=['link','cc','as'], + LINK = r'%(_python_)s mylink.py', LINKFLAGS = [], ASPP = r'%(_python_)s myas.py', CC = r'%(_python_)s myas.py') diff --git a/test/AS/ASPPCOM.py b/test/AS/ASPPCOM.py index ce938bbc1c..6b704b437f 100644 --- a/test/AS/ASPPCOM.py +++ b/test/AS/ASPPCOM.py @@ -1,68 +1,69 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test the ability to configure the $ASPPCOM construction variable. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -test.write('SConstruct', """ -env = Environment(ASPPCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', - OBJSUFFIX = '.obj', - SHOBJPREFIX = '', - SHOBJSUFFIX = '.shobj') -env.Object(target = 'test1', source = 'test1.spp') -env.Object(target = 'test2', source = 'test2.SPP') -env.SharedObject(target = 'test3', source = 'test3.spp') -env.SharedObject(target = 'test4', source = 'test4.SPP') -""" % locals()) - -test.write('test1.spp', "test1.spp\n/*as*/\n") -test.write('test2.SPP', "test2.SPP\n/*as*/\n") -test.write('test3.spp', "test3.spp\n/*as*/\n") -test.write('test4.SPP', "test4.SPP\n/*as*/\n") - -test.run(arguments = '.') - -test.must_match('test1.obj', "test1.spp\n") -test.must_match('test2.obj', "test2.SPP\n") -test.must_match('test3.shobj', "test3.spp\n") -test.must_match('test4.shobj', "test4.SPP\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to configure the $ASPPCOM construction variable. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['as'], + ASPPCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', + OBJSUFFIX = '.obj', + SHOBJPREFIX = '', + SHOBJSUFFIX = '.shobj') +env.Object(target = 'test1', source = 'test1.spp') +env.Object(target = 'test2', source = 'test2.SPP') +env.SharedObject(target = 'test3', source = 'test3.spp') +env.SharedObject(target = 'test4', source = 'test4.SPP') +""" % locals()) + +test.write('test1.spp', "test1.spp\n/*as*/\n") +test.write('test2.SPP', "test2.SPP\n/*as*/\n") +test.write('test3.spp', "test3.spp\n/*as*/\n") +test.write('test4.SPP', "test4.SPP\n/*as*/\n") + +test.run(arguments = '.') + +test.must_match('test1.obj', "test1.spp\n") +test.must_match('test2.obj', "test2.SPP\n") +test.must_match('test3.shobj', "test3.spp\n") +test.must_match('test4.shobj', "test4.SPP\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AS/ASPPCOMSTR.py b/test/AS/ASPPCOMSTR.py index 0ee18f5d0d..11a9b468ee 100644 --- a/test/AS/ASPPCOMSTR.py +++ b/test/AS/ASPPCOMSTR.py @@ -1,65 +1,66 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that the $ASPPCOMSTR construction variable allows you to customize -the displayed assembler string. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -test.write('SConstruct', """ -env = Environment(ASPPCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', - ASPPCOMSTR = 'Assembling $TARGET from $SOURCE', - OBJSUFFIX = '.obj') -env.Object(target = 'test1', source = 'test1.spp') -env.Object(target = 'test2', source = 'test2.SPP') -""" % locals()) - -test.write('test1.spp', "test1.spp\n/*as*/\n") -test.write('test2.SPP', "test2.SPP\n/*as*/\n") - -test.run(stdout = test.wrap_stdout("""\ -Assembling test1.obj from test1.spp -Assembling test2.obj from test2.SPP -""")) - -test.must_match('test1.obj', "test1.spp\n") -test.must_match('test2.obj', "test2.SPP\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $ASPPCOMSTR construction variable allows you to customize +the displayed assembler string. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['as'], + ASPPCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', + ASPPCOMSTR = 'Assembling $TARGET from $SOURCE', + OBJSUFFIX = '.obj') +env.Object(target = 'test1', source = 'test1.spp') +env.Object(target = 'test2', source = 'test2.SPP') +""" % locals()) + +test.write('test1.spp', "test1.spp\n/*as*/\n") +test.write('test2.SPP', "test2.SPP\n/*as*/\n") + +test.run(stdout = test.wrap_stdout("""\ +Assembling test1.obj from test1.spp +Assembling test2.obj from test2.SPP +""")) + +test.must_match('test1.obj', "test1.spp\n") +test.must_match('test2.obj', "test2.SPP\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AS/ASPPFLAGS.py b/test/AS/ASPPFLAGS.py index f7d7c5cf71..90f3f4bb95 100644 --- a/test/AS/ASPPFLAGS.py +++ b/test/AS/ASPPFLAGS.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__" import sys @@ -38,14 +37,11 @@ o = ' -x' o_c = ' -x -c' -if sys.platform == 'win32': - import SCons.Tool.MSCommon as msc - - if msc.msvc_exists(): - o_c = ' -x' test.write('SConstruct', """ -env = Environment(LINK = r'%(_python_)s mylink.py', +DefaultEnvironment(tools=[]) +env = Environment(tools=['link','as','cc'], + LINK = r'%(_python_)s mylink.py', LINKFLAGS = [], ASPP = r'%(_python_)s myas_args.py', ASPPFLAGS = '-x', CC = r'%(_python_)s myas_args.py') diff --git a/test/AS/ml.py b/test/AS/ml.py index 0506f5f04e..76b76a9bd1 100644 --- a/test/AS/ml.py +++ b/test/AS/ml.py @@ -1,122 +1,121 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify correct use of the live 'ml' assembler. -""" - -import sys - -import TestSCons - -_python_ = TestSCons._python_ -_exe = TestSCons._exe - -test = TestSCons.TestSCons() - -if sys.platform != 'win32': - test.skip_test("Skipping ml test on non-win32 platform '%s'\n" % sys.platform) - -ml = test.where_is('ml') - -if not ml: - test.skip_test("ml not found; skipping test\n") - -test.file_fixture('wrapper.py') - -test.write('SConstruct', """ -import os -ccc = Environment(tools = ['msvc', 'mslink', 'masm'], - ASFLAGS = '/nologo /coff') -ccc['ENV']['PATH'] = ccc['ENV']['PATH'] + os.pathsep + os.environ['PATH'] -ddd = ccc.Clone(AS = r'%(_python_)s wrapper.py ' + ccc['AS']) -ccc.Program(target = 'ccc', source = ['ccc.asm', 'ccc_main.c']) -ddd.Program(target = 'ddd', source = ['ddd.asm', 'ddd_main.c']) -""" % locals()) - -test.write('ccc.asm', -""" -DSEG segment - PUBLIC _name -_name byte "ccc.asm",0 -DSEG ends - end -""") - -test.write('ddd.asm', -""" -DSEG segment - PUBLIC _name -_name byte "ddd.asm",0 -DSEG ends - end -""") - -test.write('ccc_main.c', r""" -extern char name[]; - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("ccc_main.c %s\n", name); - exit (0); -} -""") - -test.write('ddd_main.c', r""" -extern char name[]; - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("ddd_main.c %s\n", name); - exit (0); -} -""") - -test.run(arguments = 'ccc' + _exe, stderr = None) - -test.run(program = test.workpath('ccc'), stdout = "ccc_main.c ccc.asm\n") - -test.must_not_exist('wrapper.out') - -test.run(arguments = 'ddd' + _exe) - -test.run(program = test.workpath('ddd'), stdout = "ddd_main.c ddd.asm\n") - -test.must_match('wrapper.out', "wrapper.py\n") - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 correct use of the live 'ml' assembler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +if sys.platform != 'win32': + test.skip_test("Skipping ml test on non-win32 platform '%s'\n" % sys.platform) + +ml = test.where_is('ml') + +if not ml: + test.skip_test("ml not found; skipping test\n") + +test.file_fixture('wrapper.py') + +test.write('SConstruct', """ +import os +ccc = Environment(tools = ['msvc', 'mslink', 'masm'], + ASFLAGS = '/nologo /coff') +ccc['ENV']['PATH'] = ccc['ENV']['PATH'] + os.pathsep + os.environ['PATH'] +ddd = ccc.Clone(AS = r'%(_python_)s wrapper.py ' + ccc['AS']) +ccc.Program(target = 'ccc', source = ['ccc.asm', 'ccc_main.c']) +ddd.Program(target = 'ddd', source = ['ddd.asm', 'ddd_main.c']) +""" % locals()) + +test.write('ccc.asm', +""" +DSEG segment + PUBLIC _name +_name byte "ccc.asm",0 +DSEG ends + end +""") + +test.write('ddd.asm', +""" +DSEG segment + PUBLIC _name +_name byte "ddd.asm",0 +DSEG ends + end +""") + +test.write('ccc_main.c', r""" +extern char name[]; + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("ccc_main.c %s\n", name); + exit (0); +} +""") + +test.write('ddd_main.c', r""" +extern char name[]; + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("ddd_main.c %s\n", name); + exit (0); +} +""") + +test.run(arguments = 'ccc' + _exe, stderr = None) + +test.run(program = test.workpath('ccc'), stdout = "ccc_main.c ccc.asm\n") + +test.must_not_exist('wrapper.out') + +test.run(arguments = 'ddd' + _exe) + +test.run(program = test.workpath('ddd'), stdout = "ddd_main.c ddd.asm\n") + +test.must_match('wrapper.out', "wrapper.py\n") + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: From ccaffe9c9e5c7d70def83a032b8733d3fb490acf Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 15:45:40 -0700 Subject: [PATCH 045/386] cleaning up tool initialization to speed up tests --- test/Batch/Boolean.py | 161 +++++++------- test/Batch/CHANGED_SOURCES.py | 250 ++++++++++----------- test/Batch/SOURCES.py | 254 +++++++++++----------- test/Batch/action-changed.py | 207 +++++++++--------- test/Batch/callable.py | 220 +++++++++---------- test/Batch/changed_sources_alwaysbuild.py | 107 +++++---- test/Batch/generated.py | 169 +++++++------- test/Batch/removed.py | 224 +++++++++---------- test/Batch/up_to_date.py | 188 ++++++++-------- 9 files changed, 891 insertions(+), 889 deletions(-) diff --git a/test/Batch/Boolean.py b/test/Batch/Boolean.py index c11874502d..249599da73 100644 --- a/test/Batch/Boolean.py +++ b/test/Batch/Boolean.py @@ -1,80 +1,81 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify basic use of batch_key to write a batch builder that handles -arbitrary pairs of target + source files. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -def batch_build(target, source, env): - for t, s in zip(target, source): - with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: - f.write(infp.read()) -env = Environment() -bb = Action(batch_build, batch_key=True) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('f1a.out', 'f1a.in') -env1.Batch('f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('f3a.out', 'f3a.in') -env3.Batch('f3b.out', 'f3b.in') -""") - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -batch_build(["f1a.out", "f1b.out"], ["f1a.in", "f1b.in"]) -batch_build(["f2a.out"], ["f2a.in"]) -batch_build(["f3a.out", "f3b.out"], ["f3a.in", "f3b.in"]) -""") - -test.run(stdout = expect) - -test.must_match('f1a.out', "f1a.in\n") -test.must_match('f1b.out', "f1b.in\n") -test.must_match('f2a.out', "f2a.in\n") -test.must_match('f3a.out', "f3a.in\n") -test.must_match('f3b.out', "f3b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 basic use of batch_key to write a batch builder that handles +arbitrary pairs of target + source files. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + +def batch_build(target, source, env): + for t, s in zip(target, source): + with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: + f.write(infp.read()) +env = Environment(tools=[]) +bb = Action(batch_build, batch_key=True) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('f1a.out', 'f1a.in') +env1.Batch('f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('f3a.out', 'f3a.in') +env3.Batch('f3b.out', 'f3b.in') +""") + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +batch_build(["f1a.out", "f1b.out"], ["f1a.in", "f1b.in"]) +batch_build(["f2a.out"], ["f2a.in"]) +batch_build(["f3a.out", "f3b.out"], ["f3a.in", "f3b.in"]) +""") + +test.run(stdout = expect) + +test.must_match('f1a.out', "f1a.in\n") +test.must_match('f1b.out', "f1b.in\n") +test.must_match('f2a.out', "f2a.in\n") +test.must_match('f3a.out', "f3a.in\n") +test.must_match('f3b.out', "f3b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/CHANGED_SOURCES.py b/test/Batch/CHANGED_SOURCES.py index 477869ff1f..125a5562e0 100644 --- a/test/Batch/CHANGED_SOURCES.py +++ b/test/Batch/CHANGED_SOURCES.py @@ -1,125 +1,125 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify use of $CHANGED_SOURCES with batch builders correctly decides -to rebuild if any sources of changed, and specifies only the sources -on the rebuild. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.write('batch_build.py', """\ -import os -import sys -dir = sys.argv[1] -for infile in sys.argv[2:]: - inbase = os.path.splitext(os.path.split(infile)[1])[0] - outfile = os.path.join(dir, inbase+'.out') - with open(outfile, 'wb') as f, open(infile, 'rb') as infp: - f.write(infp.read()) -sys.exit(0) -""") - -test.write('SConstruct', """ -env = Environment() -env['BATCH_BUILD'] = 'batch_build.py' -env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $CHANGED_SOURCES' -bb = Action('$BATCHCOM', batch_key=True, targets='CHANGED_TARGETS') -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('out1/f1a.out', 'f1a.in') -env1.Batch('out1/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('out2/f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('out3/f3a.out', 'f3a.in') -env3.Batch('out3/f3b.out', 'f3b.in') -""" % locals()) - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - - - -test.write('f1b.in', "f1b.in 2\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - - - -test.write('f3a.in', "f3a.in 2\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out3 f3a.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in 2\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of $CHANGED_SOURCES with batch builders correctly decides +to rebuild if any sources of changed, and specifies only the sources +on the rebuild. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.write('batch_build.py', """\ +import os +import sys +dir = sys.argv[1] +for infile in sys.argv[2:]: + inbase = os.path.splitext(os.path.split(infile)[1])[0] + outfile = os.path.join(dir, inbase+'.out') + with open(outfile, 'wb') as f, open(infile, 'rb') as infp: + f.write(infp.read()) +sys.exit(0) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +env['BATCH_BUILD'] = 'batch_build.py' +env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $CHANGED_SOURCES' +bb = Action('$BATCHCOM', batch_key=True, targets='CHANGED_TARGETS') +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('out1/f1a.out', 'f1a.in') +env1.Batch('out1/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('out2/f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('out3/f3a.out', 'f3a.in') +env3.Batch('out3/f3b.out', 'f3b.in') +""" % locals()) + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + + + +test.write('f1b.in', "f1b.in 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + + + +test.write('f3a.in', "f3a.in 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out3 f3a.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in 2\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/SOURCES.py b/test/Batch/SOURCES.py index 8198f922de..2cbb205cfc 100644 --- a/test/Batch/SOURCES.py +++ b/test/Batch/SOURCES.py @@ -1,127 +1,127 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify use of $SOURCES with batch builders correctly decides to -rebuild if any sources of changed, and specifies all the sources -on the rebuild. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.write('batch_build.py', """\ -import os -import sys -dir = sys.argv[1] -for infile in sys.argv[2:]: - inbase = os.path.splitext(os.path.split(infile)[1])[0] - outfile = os.path.join(dir, inbase+'.out') - with open(outfile, 'wb') as f, open(infile, 'rb') as infp: - f.write(infp.read()) -sys.exit(0) -""") - -test.write('SConstruct', """ -env = Environment() -env['BATCH_BUILD'] = 'batch_build.py' -env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $SOURCES' -bb = Action('$BATCHCOM', batch_key=True) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('out1/f1a.out', 'f1a.in') -env1.Batch('out1/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('out2/f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('out3/f3a.out', 'f3a.in') -env3.Batch('out3/f3b.out', 'f3b.in') -""" % locals()) - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.up_to_date(options = '--debug=explain', arguments = '.') - - - - -test.write('f1b.in', "f1b.in 2\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - - -test.write('f3a.in', "f3a.in 2\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in 2\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of $SOURCES with batch builders correctly decides to +rebuild if any sources of changed, and specifies all the sources +on the rebuild. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.write('batch_build.py', """\ +import os +import sys +dir = sys.argv[1] +for infile in sys.argv[2:]: + inbase = os.path.splitext(os.path.split(infile)[1])[0] + outfile = os.path.join(dir, inbase+'.out') + with open(outfile, 'wb') as f, open(infile, 'rb') as infp: + f.write(infp.read()) +sys.exit(0) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +env['BATCH_BUILD'] = 'batch_build.py' +env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $SOURCES' +bb = Action('$BATCHCOM', batch_key=True) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('out1/f1a.out', 'f1a.in') +env1.Batch('out1/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('out2/f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('out3/f3a.out', 'f3a.in') +env3.Batch('out3/f3b.out', 'f3b.in') +""" % locals()) + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.up_to_date(options = '--debug=explain', arguments = '.') + + + + +test.write('f1b.in', "f1b.in 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + + +test.write('f3a.in', "f3a.in 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in 2\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/action-changed.py b/test/Batch/action-changed.py index da5115e9ed..bbe7da3b3c 100644 --- a/test/Batch/action-changed.py +++ b/test/Batch/action-changed.py @@ -1,103 +1,104 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that targets in a batch builder are rebuilt when the -build action changes. -""" - -import os - -import TestSCons - -# swap slashes because on py3 on win32 inside the generated build.py -# the backslashes are getting interpretted as unicode which is -# invalid. -python = TestSCons.python.replace('\\','//') -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -build_py_contents = """\ -#!/usr/bin/env %s -import sys -sep = sys.argv.index('--') -targets = sys.argv[1:sep] -sources = sys.argv[sep+1:] -for t, s in zip(targets, sources): - with open(t, 'wb') as ofp, open(s, 'rb') as ifp: - ofp.write(bytearray('%s\\n', 'utf-8')) - ofp.write(ifp.read()) -sys.exit(0) -""" - -test.write('build.py', build_py_contents % (python, 'one')) -os.chmod(test.workpath('build.py'), 0o755) - -build_py_workpath = test.workpath('build.py') - -# Provide IMPLICIT_COMMAND_DEPENDENCIES=2 so we take a dependency on build.py. -# Without that, we only scan the first entry in the action string. -test.write('SConstruct', """ -env = Environment(IMPLICIT_COMMAND_DEPENDENCIES=2) -env.PrependENVPath('PATHEXT', '.PY') -bb = Action(r'%(_python_)s "%(build_py_workpath)s" $CHANGED_TARGETS -- $CHANGED_SOURCES', - batch_key=True, - targets='CHANGED_TARGETS') -env['BUILDERS']['Batch'] = Builder(action=bb) -env.Batch('f1.out', 'f1.in') -env.Batch('f2.out', 'f2.in') -env.Batch('f3.out', 'f3.in') -""" % locals()) - -test.write('f1.in', "f1.in\n") -test.write('f2.in', "f2.in\n") -test.write('f3.in', "f3.in\n") - -test.run(arguments = '.') - -test.must_match('f1.out', "one\nf1.in\n") -test.must_match('f2.out', "one\nf2.in\n") -test.must_match('f3.out', "one\nf3.in\n") - -test.up_to_date(arguments = '.') - -test.write('build.py', build_py_contents % (python, 'two')) -os.chmod(test.workpath('build.py'), 0o755) - -test.not_up_to_date(arguments = '.') - -test.must_match('f1.out', "two\nf1.in\n") -test.must_match('f2.out', "two\nf2.in\n") -test.must_match('f3.out', "two\nf3.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that targets in a batch builder are rebuilt when the +build action changes. +""" + +import os + +import TestSCons + +# swap slashes because on py3 on win32 inside the generated build.py +# the backslashes are getting interpretted as unicode which is +# invalid. +python = TestSCons.python.replace('\\','//') +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +build_py_contents = """\ +#!/usr/bin/env %s +import sys +sep = sys.argv.index('--') +targets = sys.argv[1:sep] +sources = sys.argv[sep+1:] +for t, s in zip(targets, sources): + with open(t, 'wb') as ofp, open(s, 'rb') as ifp: + ofp.write(bytearray('%s\\n', 'utf-8')) + ofp.write(ifp.read()) +sys.exit(0) +""" + +test.write('build.py', build_py_contents % (python, 'one')) +os.chmod(test.workpath('build.py'), 0o755) + +build_py_workpath = test.workpath('build.py') + +# Provide IMPLICIT_COMMAND_DEPENDENCIES=2 so we take a dependency on build.py. +# Without that, we only scan the first entry in the action string. +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[], + IMPLICIT_COMMAND_DEPENDENCIES=2) +env.PrependENVPath('PATHEXT', '.PY') +bb = Action(r'%(_python_)s "%(build_py_workpath)s" $CHANGED_TARGETS -- $CHANGED_SOURCES', + batch_key=True, + targets='CHANGED_TARGETS') +env['BUILDERS']['Batch'] = Builder(action=bb) +env.Batch('f1.out', 'f1.in') +env.Batch('f2.out', 'f2.in') +env.Batch('f3.out', 'f3.in') +""" % locals()) + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") +test.write('f3.in', "f3.in\n") + +test.run(arguments = '.') + +test.must_match('f1.out', "one\nf1.in\n") +test.must_match('f2.out', "one\nf2.in\n") +test.must_match('f3.out', "one\nf3.in\n") + +test.up_to_date(arguments = '.') + +test.write('build.py', build_py_contents % (python, 'two')) +os.chmod(test.workpath('build.py'), 0o755) + +test.not_up_to_date(arguments = '.') + +test.must_match('f1.out', "two\nf1.in\n") +test.must_match('f2.out', "two\nf2.in\n") +test.must_match('f3.out', "two\nf3.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/callable.py b/test/Batch/callable.py index 9fe14ee87e..9f9a119a2a 100644 --- a/test/Batch/callable.py +++ b/test/Batch/callable.py @@ -1,110 +1,110 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify passing in a batch_key callable for more control over how -batch builders behave. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('sub1', 'sub2') - -test.write('SConstruct', """ -def batch_build(target, source, env): - for t, s in zip(target, source): - with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: - f.write(infp.read()) -if ARGUMENTS.get('BATCH_CALLABLE'): - def batch_key(action, env, target, source): - return (id(action), id(env), target[0].dir) -else: - batch_key=True -env = Environment() -bb = Action(batch_build, batch_key=batch_key) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('sub1/f1a.out', 'f1a.in') -env1.Batch('sub2/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('sub1/f2a.out', 'f2a.in') -env2.Batch('sub2/f2b.out', 'f2b.in') -""") - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f2b.in', "f2b.in\n") - -sub1_f1a_out = os.path.join('sub1', 'f1a.out') -sub2_f1b_out = os.path.join('sub2', 'f1b.out') -sub1_f2a_out = os.path.join('sub1', 'f2a.out') -sub2_f2b_out = os.path.join('sub2', 'f2b.out') - -expect = test.wrap_stdout("""\ -batch_build(["%(sub1_f1a_out)s", "%(sub2_f1b_out)s"], ["f1a.in", "f1b.in"]) -batch_build(["%(sub1_f2a_out)s", "%(sub2_f2b_out)s"], ["f2a.in", "f2b.in"]) -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['sub1', 'f1a.out'], "f1a.in\n") -test.must_match(['sub2', 'f1b.out'], "f1b.in\n") -test.must_match(['sub1', 'f2a.out'], "f2a.in\n") -test.must_match(['sub2', 'f2b.out'], "f2b.in\n") - -test.run(arguments = '-c') - -test.must_not_exist(['sub1', 'f1a.out']) -test.must_not_exist(['sub2', 'f1b.out']) -test.must_not_exist(['sub1', 'f2a.out']) -test.must_not_exist(['sub2', 'f2b.out']) - -expect = test.wrap_stdout("""\ -batch_build(["%(sub1_f1a_out)s"], ["f1a.in"]) -batch_build(["%(sub1_f2a_out)s"], ["f2a.in"]) -batch_build(["%(sub2_f1b_out)s"], ["f1b.in"]) -batch_build(["%(sub2_f2b_out)s"], ["f2b.in"]) -""" % locals()) - -test.run(arguments = 'BATCH_CALLABLE=1', stdout = expect) - -test.must_match(['sub1', 'f1a.out'], "f1a.in\n") -test.must_match(['sub2', 'f1b.out'], "f1b.in\n") -test.must_match(['sub1', 'f2a.out'], "f2a.in\n") -test.must_match(['sub2', 'f2b.out'], "f2b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 passing in a batch_key callable for more control over how +batch builders behave. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('sub1', 'sub2') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +def batch_build(target, source, env): + for t, s in zip(target, source): + with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: + f.write(infp.read()) +if ARGUMENTS.get('BATCH_CALLABLE'): + def batch_key(action, env, target, source): + return (id(action), id(env), target[0].dir) +else: + batch_key=True +env = Environment(tools=[]) +bb = Action(batch_build, batch_key=batch_key) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('sub1/f1a.out', 'f1a.in') +env1.Batch('sub2/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('sub1/f2a.out', 'f2a.in') +env2.Batch('sub2/f2b.out', 'f2b.in') +""") + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f2b.in', "f2b.in\n") + +sub1_f1a_out = os.path.join('sub1', 'f1a.out') +sub2_f1b_out = os.path.join('sub2', 'f1b.out') +sub1_f2a_out = os.path.join('sub1', 'f2a.out') +sub2_f2b_out = os.path.join('sub2', 'f2b.out') + +expect = test.wrap_stdout("""\ +batch_build(["%(sub1_f1a_out)s", "%(sub2_f1b_out)s"], ["f1a.in", "f1b.in"]) +batch_build(["%(sub1_f2a_out)s", "%(sub2_f2b_out)s"], ["f2a.in", "f2b.in"]) +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['sub1', 'f1a.out'], "f1a.in\n") +test.must_match(['sub2', 'f1b.out'], "f1b.in\n") +test.must_match(['sub1', 'f2a.out'], "f2a.in\n") +test.must_match(['sub2', 'f2b.out'], "f2b.in\n") + +test.run(arguments = '-c') + +test.must_not_exist(['sub1', 'f1a.out']) +test.must_not_exist(['sub2', 'f1b.out']) +test.must_not_exist(['sub1', 'f2a.out']) +test.must_not_exist(['sub2', 'f2b.out']) + +expect = test.wrap_stdout("""\ +batch_build(["%(sub1_f1a_out)s"], ["f1a.in"]) +batch_build(["%(sub1_f2a_out)s"], ["f2a.in"]) +batch_build(["%(sub2_f1b_out)s"], ["f1b.in"]) +batch_build(["%(sub2_f2b_out)s"], ["f2b.in"]) +""" % locals()) + +test.run(arguments = 'BATCH_CALLABLE=1', stdout = expect) + +test.must_match(['sub1', 'f1a.out'], "f1a.in\n") +test.must_match(['sub2', 'f1b.out'], "f1b.in\n") +test.must_match(['sub1', 'f2a.out'], "f2a.in\n") +test.must_match(['sub2', 'f2b.out'], "f2b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/changed_sources_alwaysbuild.py b/test/Batch/changed_sources_alwaysbuild.py index ba947d6c35..06a2e02c2b 100644 --- a/test/Batch/changed_sources_alwaysbuild.py +++ b/test/Batch/changed_sources_alwaysbuild.py @@ -1,54 +1,53 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that files marked AlwaysBuild also get put into CHANGED_SOURCES. -Tigris bug 2622 -""" - -import TestSCons - -test = TestSCons.TestSCons() -test.file_fixture('SConstruct_changed_sources_alwaysBuild','SConstruct') -test.file_fixture('changed_sources_main.cpp') -# always works on first run -test.run() - -# On second run prior to fix the file hasn't changed and so never -# makes it into CHANGED_SOURCES. -# Compile is triggered because SCons knows it needs to build it. -# This tests that on second run the source file is in the scons -# output. Also prior to fix the compile would fail because -# it would produce a compile command line lacking a source file. -test.run() -test.must_contain_all_lines(test.stdout(),['changed_sources_main.cpp']) -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that files marked AlwaysBuild also get put into CHANGED_SOURCES. +Tigris bug 2622 +""" + +import TestSCons + +test = TestSCons.TestSCons() +test.file_fixture('SConstruct_changed_sources_alwaysBuild','SConstruct') +test.file_fixture('changed_sources_main.cpp') +# always works on first run +test.run() + +# On second run prior to fix the file hasn't changed and so never +# makes it into CHANGED_SOURCES. +# Compile is triggered because SCons knows it needs to build it. +# This tests that on second run the source file is in the scons +# output. Also prior to fix the compile would fail because +# it would produce a compile command line lacking a source file. +test.run() +test.must_contain_all_lines(test.stdout(),['changed_sources_main.cpp']) +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/generated.py b/test/Batch/generated.py index 65ce8a855c..91b4609f2d 100644 --- a/test/Batch/generated.py +++ b/test/Batch/generated.py @@ -1,84 +1,85 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify use of a batch builder when one of the later targets in the -list the list depends on a generated file. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -def batch_build(target, source, env): - for t, s in zip(target, source): - with open(str(t), 'wb') as fp: - if str(t) == 'f3.out': - with open('f3.include', 'rb') as f: - fp.write(f.read()) - with open(str(s), 'rb') as f: - fp.write(f.read()) -env = Environment() -bb = Action(batch_build, batch_key=True) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('f1.out', 'f1.in') -env1.Batch('f2.out', 'f2.mid') -f3_out = env1.Batch('f3.out', 'f3.in') - -env2 = env.Clone() -env2.Batch('f2.mid', 'f2.in') - -f3_include = env.Batch('f3.include', 'f3.include.in') -env.Depends(f3_out, f3_include) -""") - -test.write('f1.in', "f1.in\n") -test.write('f2.in', "f2.in\n") -test.write('f3.in', "f3.in\n") -test.write('f3.include.in', "f3.include.in\n") - -expect = test.wrap_stdout("""\ -batch_build(["f2.mid"], ["f2.in"]) -batch_build(["f3.include"], ["f3.include.in"]) -batch_build(["f1.out", "f2.out", "f3.out"], ["f1.in", "f2.mid", "f3.in"]) -""") - -test.run(stdout = expect) - -test.must_match('f1.out', "f1.in\n") -test.must_match('f2.out', "f2.in\n") - -test.up_to_date(arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of a batch builder when one of the later targets in the +list the list depends on a generated file. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + +def batch_build(target, source, env): + for t, s in zip(target, source): + with open(str(t), 'wb') as fp: + if str(t) == 'f3.out': + with open('f3.include', 'rb') as f: + fp.write(f.read()) + with open(str(s), 'rb') as f: + fp.write(f.read()) +env = Environment(tools=[]) +bb = Action(batch_build, batch_key=True) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('f1.out', 'f1.in') +env1.Batch('f2.out', 'f2.mid') +f3_out = env1.Batch('f3.out', 'f3.in') + +env2 = env.Clone() +env2.Batch('f2.mid', 'f2.in') + +f3_include = env.Batch('f3.include', 'f3.include.in') +env.Depends(f3_out, f3_include) +""") + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") +test.write('f3.in', "f3.in\n") +test.write('f3.include.in', "f3.include.in\n") + +expect = test.wrap_stdout("""\ +batch_build(["f2.mid"], ["f2.in"]) +batch_build(["f3.include"], ["f3.include.in"]) +batch_build(["f1.out", "f2.out", "f3.out"], ["f1.in", "f2.mid", "f3.in"]) +""") + +test.run(stdout = expect) + +test.must_match('f1.out', "f1.in\n") +test.must_match('f2.out', "f2.in\n") + +test.up_to_date(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/removed.py b/test/Batch/removed.py index b244cb3b56..b64958dda3 100644 --- a/test/Batch/removed.py +++ b/test/Batch/removed.py @@ -1,112 +1,112 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify correct batch builder $CHANGED_SOURCES behavior when some of -the targets have been removed. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.write('batch_build.py', """\ -import os -import sys -dir = sys.argv[1] -for infile in sys.argv[2:]: - inbase = os.path.splitext(os.path.split(infile)[1])[0] - outfile = os.path.join(dir, inbase+'.out') - with open(outfile, 'wb') as f, open(infile, 'rb') as infp: - f.write(infp.read()) -sys.exit(0) -""") - -test.write('SConstruct', """ -env = Environment() -env['BATCH_BUILD'] = 'batch_build.py' -env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $CHANGED_SOURCES' -bb = Action('$BATCHCOM', batch_key=True, targets='CHANGED_TARGETS') -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('out1/f1a.out', 'f1a.in') -env1.Batch('out1/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('out2/f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('out3/f3a.out', 'f3a.in') -env3.Batch('out3/f3b.out', 'f3b.in') -""" % locals()) - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.up_to_date(options = '--debug=explain', arguments = '.') - -test.unlink(['out1', 'f1b.out']) -test.unlink(['out2', 'f2a.out']) -test.unlink(['out3', 'f3a.out']) - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in -""" % locals()) - -test.run(arguments = '.', stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 correct batch builder $CHANGED_SOURCES behavior when some of +the targets have been removed. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.write('batch_build.py', """\ +import os +import sys +dir = sys.argv[1] +for infile in sys.argv[2:]: + inbase = os.path.splitext(os.path.split(infile)[1])[0] + outfile = os.path.join(dir, inbase+'.out') + with open(outfile, 'wb') as f, open(infile, 'rb') as infp: + f.write(infp.read()) +sys.exit(0) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +env['BATCH_BUILD'] = 'batch_build.py' +env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $CHANGED_SOURCES' +bb = Action('$BATCHCOM', batch_key=True, targets='CHANGED_TARGETS') +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('out1/f1a.out', 'f1a.in') +env1.Batch('out1/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('out2/f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('out3/f3a.out', 'f3a.in') +env3.Batch('out3/f3b.out', 'f3b.in') +""" % locals()) + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.up_to_date(options = '--debug=explain', arguments = '.') + +test.unlink(['out1', 'f1b.out']) +test.unlink(['out2', 'f2a.out']) +test.unlink(['out3', 'f3a.out']) + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in +""" % locals()) + +test.run(arguments = '.', stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/up_to_date.py b/test/Batch/up_to_date.py index 229c88f4b8..4ba304b6c0 100644 --- a/test/Batch/up_to_date.py +++ b/test/Batch/up_to_date.py @@ -1,94 +1,94 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify simple use of $SOURCES with batch builders correctly decide -that files are up to date on a rebuild. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.write('batch_build.py', """\ -import os -import sys -dir = sys.argv[1] -for infile in sys.argv[2:]: - inbase = os.path.splitext(os.path.split(infile)[1])[0] - outfile = os.path.join(dir, inbase+'.out') - with open(outfile, 'wb') as f, open(infile, 'rb') as infp: - f.write(infp.read()) -sys.exit(0) -""") - -test.write('SConstruct', """ -env = Environment() -env['BATCH_BUILD'] = 'batch_build.py' -env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $SOURCES' -bb = Action('$BATCHCOM', batch_key=True) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('out1/f1a.out', 'f1a.in') -env1.Batch('out1/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('out2/f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('out3/f3a.out', 'f3a.in') -env3.Batch('out3/f3b.out', 'f3b.in') -""" % locals()) - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.up_to_date(options = '--debug=explain', arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 simple use of $SOURCES with batch builders correctly decide +that files are up to date on a rebuild. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.write('batch_build.py', """\ +import os +import sys +dir = sys.argv[1] +for infile in sys.argv[2:]: + inbase = os.path.splitext(os.path.split(infile)[1])[0] + outfile = os.path.join(dir, inbase+'.out') + with open(outfile, 'wb') as f, open(infile, 'rb') as infp: + f.write(infp.read()) +sys.exit(0) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +env['BATCH_BUILD'] = 'batch_build.py' +env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $SOURCES' +bb = Action('$BATCHCOM', batch_key=True) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('out1/f1a.out', 'f1a.in') +env1.Batch('out1/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('out2/f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('out3/f3a.out', 'f3a.in') +env3.Batch('out3/f3b.out', 'f3b.in') +""" % locals()) + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.up_to_date(options = '--debug=explain', arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: From 91831f9a589cc44d5f25864085e161673cd563f3 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 16:43:09 -0700 Subject: [PATCH 046/386] cleaning up tool initialization to speed up tests --- test/Builder/TargetSubst.py | 105 +++-- test/Builder/add_src_builder.py | 143 ++++--- test/Builder/different-actions.py | 117 +++--- test/Builder/ensure_suffix.py | 123 +++--- test/Builder/errors.py | 11 +- test/Builder/non-multi.py | 135 ++++--- test/Builder/not-a-Builder.py | 121 +++--- test/Builder/same-actions-diff-envs.py | 127 +++--- test/Builder/same-actions-diff-overrides.py | 125 +++--- test/Builder/srcdir.py | 167 ++++---- test/Builder/wrapper.py | 155 ++++---- test/CC/CC.py | 16 +- test/CC/CCCOM.py | 143 +++---- test/CC/CCCOMSTR.py | 153 ++++---- test/CC/CCFLAGS.py | 220 +++++------ test/CC/CCVERSION.py | 7 +- test/CC/CFLAGS.py | 248 ++++++------ test/CC/SHCC.py | 10 +- test/CC/SHCCCOM.py | 144 +++---- test/CC/SHCCCOMSTR.py | 158 ++++---- test/CC/SHCFLAGS.py | 274 ++++++------- test/CacheDir/CacheDir.py | 329 ++++++++-------- test/CacheDir/CacheDir_TryCompile.py | 7 +- test/CacheDir/NoCache.py | 7 +- test/CacheDir/SideEffect.py | 229 ++++++----- test/CacheDir/VariantDir.py | 311 +++++++-------- test/CacheDir/debug.py | 403 ++++++++++---------- test/CacheDir/environment.py | 341 +++++++++-------- test/CacheDir/multi-targets.py | 157 ++++---- test/CacheDir/multiple-targets.py | 7 +- test/CacheDir/option--cd.py | 7 +- test/CacheDir/option--cf.py | 263 +++++++------ test/CacheDir/option--cr.py | 7 +- test/CacheDir/option--cs.py | 9 +- test/CacheDir/readonly-cache.py | 9 +- test/CacheDir/scanner-target.py | 9 +- test/CacheDir/source-scanner.py | 9 +- test/CacheDir/symlink.py | 153 ++++---- test/CacheDir/up-to-date-q.py | 173 +++++---- test/CacheDir/value_dependencies.py | 103 +++-- test/CacheDir/value_dependencies/SConstruct | 2 +- 41 files changed, 2609 insertions(+), 2628 deletions(-) diff --git a/test/Builder/TargetSubst.py b/test/Builder/TargetSubst.py index 76ca76c5fd..3983eee174 100644 --- a/test/Builder/TargetSubst.py +++ b/test/Builder/TargetSubst.py @@ -1,53 +1,52 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that the ensure_suffix argument to causes us to add the suffix -configured for the Builder even if it looks like the target already has -a different suffix. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -builder = Builder(action=Copy('$TARGET', '$SOURCE')) -tgt = builder(env, target="${SOURCE}.out", source="infile") -""") - -test.write('infile', "infile\n") -test.run(arguments = '.') -test.must_match('infile.out', "infile\n") -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the ensure_suffix argument to causes us to add the suffix +configured for the Builder even if it looks like the target already has +a different suffix. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +builder = Builder(action=Copy('$TARGET', '$SOURCE')) +tgt = builder(env, target="${SOURCE}.out", source="infile") +""") + +test.write('infile', "infile\n") +test.run(arguments = '.') +test.must_match('infile.out', "infile\n") +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/add_src_builder.py b/test/Builder/add_src_builder.py index e499933d5e..78e30069c4 100644 --- a/test/Builder/add_src_builder.py +++ b/test/Builder/add_src_builder.py @@ -1,72 +1,71 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that we can call add_src_builder() to add a builder to -another on the fly. - -This used to trigger infinite recursion (issue 1681) because the -same src_builder list object was being re-used between all Builder -objects that weren't initialized with a separate src_builder. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -copy_out = Builder(action = Copy('$TARGET', '$SOURCE'), - suffix = '.out', - src_suffix = '.mid') - -copy_mid = Builder(action = Copy('$TARGET', '$SOURCE'), - suffix = '.mid', \ - src_suffix = '.in') - -env = Environment(tools=[]) -env['BUILDERS']['CopyOut'] = copy_out -env['BUILDERS']['CopyMid'] = copy_mid - -copy_out.add_src_builder(copy_mid) - -env.CopyOut('file1.out', 'file1.in') -""") - -test.write('file1.in', "file1.in\n") - -test.run() - -test.must_match('file1.mid', "file1.in\n") -test.must_match('file1.out', "file1.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that we can call add_src_builder() to add a builder to +another on the fly. + +This used to trigger infinite recursion (issue 1681) because the +same src_builder list object was being re-used between all Builder +objects that weren't initialized with a separate src_builder. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +copy_out = Builder(action = Copy('$TARGET', '$SOURCE'), + suffix = '.out', + src_suffix = '.mid') + +copy_mid = Builder(action = Copy('$TARGET', '$SOURCE'), + suffix = '.mid', \ + src_suffix = '.in') + +env = Environment(tools=[]) +env['BUILDERS']['CopyOut'] = copy_out +env['BUILDERS']['CopyMid'] = copy_mid + +copy_out.add_src_builder(copy_mid) + +env.CopyOut('file1.out', 'file1.in') +""") + +test.write('file1.in', "file1.in\n") + +test.run() + +test.must_match('file1.mid', "file1.in\n") +test.must_match('file1.out', "file1.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/different-actions.py b/test/Builder/different-actions.py index f355586665..c7191bf097 100644 --- a/test/Builder/different-actions.py +++ b/test/Builder/different-actions.py @@ -1,59 +1,58 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that two builders in two environments with different -actions generate an error. -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -e1 = Environment(tools=[]) -e2 = Environment(tools=[]) - -e1.Command('out.txt', [], 'echo 1 > $TARGET') -e2.Command('out.txt', [], 'echo 2 > $TARGET') -""",'w') - -expect = TestSCons.re_escape(""" -scons: *** Two environments with different actions were specified for the same target: out.txt -(action 1: echo 1 > out.txt) -(action 2: echo 2 > out.txt) -""") + TestSCons.file_expr - -test.run(arguments='out.txt', status=2, stderr=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that two builders in two environments with different +actions generate an error. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +e1 = Environment(tools=[]) +e2 = Environment(tools=[]) + +e1.Command('out.txt', [], 'echo 1 > $TARGET') +e2.Command('out.txt', [], 'echo 2 > $TARGET') +""",'w') + +expect = TestSCons.re_escape(""" +scons: *** Two environments with different actions were specified for the same target: out.txt +(action 1: echo 1 > out.txt) +(action 2: echo 2 > out.txt) +""") + TestSCons.file_expr + +test.run(arguments='out.txt', status=2, stderr=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/Builder/ensure_suffix.py b/test/Builder/ensure_suffix.py index 52fb1d4217..94a7e1f5ed 100644 --- a/test/Builder/ensure_suffix.py +++ b/test/Builder/ensure_suffix.py @@ -1,62 +1,61 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that the ensure_suffix argument to causes us to add the suffix -configured for the Builder even if it looks like the target already has -a different suffix. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) - -tbuilder = Builder(action=Copy('$TARGET', '$SOURCE'), - suffix='.dll', - ensure_suffix=True) - -env['BUILDERS']['TBuilder'] = tbuilder - -env.TBuilder("aa.bb.cc.dd","aa.aa.txt") -""") - -test.write('aa.aa.txt', "clean test\n") - -test.run(arguments = '.') - -test.must_match('aa.bb.cc.dd.dll', "clean test\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the ensure_suffix argument to causes us to add the suffix +configured for the Builder even if it looks like the target already has +a different suffix. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) + +tbuilder = Builder(action=Copy('$TARGET', '$SOURCE'), + suffix='.dll', + ensure_suffix=True) + +env['BUILDERS']['TBuilder'] = tbuilder + +env.TBuilder("aa.bb.cc.dd","aa.aa.txt") +""") + +test.write('aa.aa.txt', "clean test\n") + +test.run(arguments = '.') + +test.must_match('aa.bb.cc.dd.dll', "clean test\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/errors.py b/test/Builder/errors.py index 375e052a16..7a713a3aac 100644 --- a/test/Builder/errors.py +++ b/test/Builder/errors.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__" """ Test the ability to catch Builder creation with poorly specified Actions. @@ -36,6 +35,8 @@ SConstruct_path = test.workpath('SConstruct') sconstruct = """ +DefaultEnvironment(tools=[]) + def buildop(env, source, target): with open(str(target[0]), 'wb') as outf, open(str(source[0]), 'r') as infp: for line in inpf.readlines(): @@ -54,7 +55,7 @@ def buildop(env, source, target): built """) -python_file_line = test.python_file_line(SConstruct_path, 11) +python_file_line = test.python_file_line(SConstruct_path, 13) ### Gross mistake in Builder spec diff --git a/test/Builder/non-multi.py b/test/Builder/non-multi.py index 3c09db168c..cdbd5b044f 100644 --- a/test/Builder/non-multi.py +++ b/test/Builder/non-multi.py @@ -1,68 +1,67 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that a builder without "multi" set can still be called multiple -times if the calls are the same. -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) - -def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: - f.write(infp.read()) - -B = Builder(action=build, multi=0) -env = Environment(tools=[], BUILDERS = { 'B' : B }) -env.B(target = 'file7.out', source = 'file7.in') -env.B(target = 'file7.out', source = 'file7.in') -env.B(target = 'file8.out', source = 'file8.in', arg=1) -env.B(target = 'file8.out', source = 'file8.in', arg=1) -""") - -test.write('file7.in', 'file7.in\n') -test.write('file8.in', 'file8.in\n') - -test.run(arguments='file7.out') -test.run(arguments='file8.out') - -test.must_match('file7.out', "file7.in\n") -test.must_match('file8.out', "file8.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that a builder without "multi" set can still be called multiple +times if the calls are the same. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + +def build(env, target, source): + with open(str(target[0]), 'wb') as f: + for s in source: + with open(str(s), 'rb') as infp: + f.write(infp.read()) + +B = Builder(action=build, multi=0) +env = Environment(tools=[], BUILDERS = { 'B' : B }) +env.B(target = 'file7.out', source = 'file7.in') +env.B(target = 'file7.out', source = 'file7.in') +env.B(target = 'file8.out', source = 'file8.in', arg=1) +env.B(target = 'file8.out', source = 'file8.in', arg=1) +""") + +test.write('file7.in', 'file7.in\n') +test.write('file8.in', 'file8.in\n') + +test.run(arguments='file7.out') +test.run(arguments='file8.out') + +test.must_match('file7.out', "file7.in\n") +test.must_match('file8.out', "file8.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/not-a-Builder.py b/test/Builder/not-a-Builder.py index 37ce6056ce..2cee0aaef7 100644 --- a/test/Builder/not-a-Builder.py +++ b/test/Builder/not-a-Builder.py @@ -1,61 +1,60 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test the error when trying to configure a Builder with a non-Builder object. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -SConstruct_path = test.workpath('SConstruct') - -test.write(SConstruct_path, """\ -def mkdir(env, target, source): - return None -mkdir = 1 -env = Environment(BUILDERS={'mkdir': 1}) -env.mkdir(env.Dir('src'), None) -""") - -expect_stderr = """\ - -scons: *** 1 is not a Builder. -""" + test.python_file_line(SConstruct_path, 4) - -test.run(arguments='.', - stderr=expect_stderr, - status=2) - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the error when trying to configure a Builder with a non-Builder object. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +SConstruct_path = test.workpath('SConstruct') + +test.write(SConstruct_path, """\ +def mkdir(env, target, source): + return None +mkdir = 1 +env = Environment(BUILDERS={'mkdir': 1}) +env.mkdir(env.Dir('src'), None) +""") + +expect_stderr = """\ + +scons: *** 1 is not a Builder. +""" + test.python_file_line(SConstruct_path, 4) + +test.run(arguments='.', + stderr=expect_stderr, + status=2) + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/same-actions-diff-envs.py b/test/Builder/same-actions-diff-envs.py index b80c9883b0..f3bff437c3 100644 --- a/test/Builder/same-actions-diff-envs.py +++ b/test/Builder/same-actions-diff-envs.py @@ -1,64 +1,63 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that two builders in two environments with the same actions generate -a warning -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) - -def build(env, target, source): - with open(str(target[0]), 'w') as f: - f.write('1') - -B = Builder(action=build) -env = Environment(tools=[], BUILDERS = { 'B' : B }) -env2 = Environment(tools=[], BUILDERS = { 'B' : B }) -env.B('out.txt', []) -env2.B('out.txt', []) -""") - -expect = TestSCons.re_escape(""" -scons: warning: Two different environments were specified for target out.txt, -\tbut they appear to have the same action: build(target, source, env) -""") + TestSCons.file_expr - -test.run(arguments='out.txt', status=0, stderr=expect) -test.must_match('out.txt', '1') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that two builders in two environments with the same actions generate +a warning +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + +def build(env, target, source): + with open(str(target[0]), 'w') as f: + f.write('1') + +B = Builder(action=build) +env = Environment(tools=[], BUILDERS = { 'B' : B }) +env2 = Environment(tools=[], BUILDERS = { 'B' : B }) +env.B('out.txt', []) +env2.B('out.txt', []) +""") + +expect = TestSCons.re_escape(""" +scons: warning: Two different environments were specified for target out.txt, +\tbut they appear to have the same action: build(target, source, env) +""") + TestSCons.file_expr + +test.run(arguments='out.txt', status=0, stderr=expect) +test.must_match('out.txt', '1') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/same-actions-diff-overrides.py b/test/Builder/same-actions-diff-overrides.py index 8f6bdca866..596c8687f2 100644 --- a/test/Builder/same-actions-diff-overrides.py +++ b/test/Builder/same-actions-diff-overrides.py @@ -1,63 +1,62 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that two calls to a builder with different overrides, but the same -action, generate a warning -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) - -def build(env, target, source): - with open(str(target[0]), 'w') as f: - f.write('1') - -B = Builder(action=build) -env = Environment(tools=[], BUILDERS = { 'B' : B }) -env.B('out.txt', [], arg=1) -env.B('out.txt', [], arg=2) -""") - -expect = TestSCons.re_escape(""" -scons: warning: Two different environments were specified for target out.txt, -\tbut they appear to have the same action: build(target, source, env) -""") + TestSCons.file_expr - -test.run(arguments='out.txt', status=0, stderr=expect) -test.must_match('out.txt', '1') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that two calls to a builder with different overrides, but the same +action, generate a warning +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + +def build(env, target, source): + with open(str(target[0]), 'w') as f: + f.write('1') + +B = Builder(action=build) +env = Environment(tools=[], BUILDERS = { 'B' : B }) +env.B('out.txt', [], arg=1) +env.B('out.txt', [], arg=2) +""") + +expect = TestSCons.re_escape(""" +scons: warning: Two different environments were specified for target out.txt, +\tbut they appear to have the same action: build(target, source, env) +""") + TestSCons.file_expr + +test.run(arguments='out.txt', status=0, stderr=expect) +test.must_match('out.txt', '1') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/srcdir.py b/test/Builder/srcdir.py index 6ce27feed8..0ce6b5e0a0 100644 --- a/test/Builder/srcdir.py +++ b/test/Builder/srcdir.py @@ -1,84 +1,83 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that specifying a srcdir when calling a Builder correctly -prefixes each relative-path string with the specified srcdir. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.subdir('src', ['src', 'foo']) - -file3 = test.workpath('file3') - -test.write(['src', 'cat.py'], """\ -import sys -with open(sys.argv[1], 'wb') as o: - for f in sys.argv[2:]: - with open(f, 'rb') as i: - o.write(i.read()) -""") - -test.write(['src', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) - -Command('output', - ['file1', File('file2'), r'%(file3)s', 'file4'], - r'%(_python_)s cat.py $TARGET $SOURCES', - srcdir='foo') -""" % locals()) - -test.write(['src', 'foo', 'file1'], "file1\n") - -test.write(['src', 'file2'], "file2\n") - -test.write(file3, "file3\n") - -test.write(['src', 'foo', 'file4'], "file4\n") - -test.run(chdir = 'src', arguments = '.') - -expected = """\ -file1 -file2 -file3 -file4 -""" - -test.must_match(['src', 'output'], expected) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that specifying a srcdir when calling a Builder correctly +prefixes each relative-path string with the specified srcdir. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.subdir('src', ['src', 'foo']) + +file3 = test.workpath('file3') + +test.write(['src', 'cat.py'], """\ +import sys +with open(sys.argv[1], 'wb') as o: + for f in sys.argv[2:]: + with open(f, 'rb') as i: + o.write(i.read()) +""") + +test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) + +Command('output', + ['file1', File('file2'), r'%(file3)s', 'file4'], + r'%(_python_)s cat.py $TARGET $SOURCES', + srcdir='foo') +""" % locals()) + +test.write(['src', 'foo', 'file1'], "file1\n") + +test.write(['src', 'file2'], "file2\n") + +test.write(file3, "file3\n") + +test.write(['src', 'foo', 'file4'], "file4\n") + +test.run(chdir = 'src', arguments = '.') + +expected = """\ +file1 +file2 +file3 +file4 +""" + +test.must_match(['src', 'output'], expected) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/wrapper.py b/test/Builder/wrapper.py index acb1d44fd2..d68a6cab61 100644 --- a/test/Builder/wrapper.py +++ b/test/Builder/wrapper.py @@ -1,78 +1,77 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test the ability to use a direct Python function to wrap -calls to other Builder(s). -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) - -import os.path -import string -def cat(target, source, env): - with open(str(target[0]), 'wb') as fp: - for s in map(str, source): - with open(s, 'rb') as infp: - fp.write(infp.read()) -Cat = Builder(action=cat) -def Wrapper(env, target, source): - if not target: - target = [str(source[0]).replace('.in', '.wout')] - t1 = 't1-'+str(target[0]) - source = 's-'+str(source[0]) - env.Cat(t1, source) - t2 = 't2-'+str(target[0]) - env.Cat(t2, source) -env = Environment(tools=[], - BUILDERS = {'Cat' : Cat, - 'Wrapper' : Wrapper}) -env.Wrapper('f1.out', 'f1.in') -env.Wrapper('f2.in') -""") - -test.write('s-f1.in', "s-f1.in\n") -test.write('s-f2.in', "s-f2.in\n") - -test.run() - -test.must_match('t1-f1.out', "s-f1.in\n") -test.must_match('t1-f2.wout', "s-f2.in\n") -test.must_match('t2-f1.out', "s-f1.in\n") -test.must_match('t2-f2.wout', "s-f2.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to use a direct Python function to wrap +calls to other Builder(s). +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + +import os.path +import string +def cat(target, source, env): + with open(str(target[0]), 'wb') as fp: + for s in map(str, source): + with open(s, 'rb') as infp: + fp.write(infp.read()) +Cat = Builder(action=cat) +def Wrapper(env, target, source): + if not target: + target = [str(source[0]).replace('.in', '.wout')] + t1 = 't1-'+str(target[0]) + source = 's-'+str(source[0]) + env.Cat(t1, source) + t2 = 't2-'+str(target[0]) + env.Cat(t2, source) +env = Environment(tools=[], + BUILDERS = {'Cat' : Cat, + 'Wrapper' : Wrapper}) +env.Wrapper('f1.out', 'f1.in') +env.Wrapper('f2.in') +""") + +test.write('s-f1.in', "s-f1.in\n") +test.write('s-f2.in', "s-f2.in\n") + +test.run() + +test.must_match('t1-f1.out', "s-f1.in\n") +test.must_match('t1-f2.wout', "s-f2.in\n") +test.must_match('t2-f1.out', "s-f1.in\n") +test.must_match('t2-f2.wout', "s-f2.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/CC.py b/test/CC/CC.py index a548421ece..d43106dd5a 100644 --- a/test/CC/CC.py +++ b/test/CC/CC.py @@ -36,13 +36,12 @@ test.file_fixture('mylink.py') test.write('SConstruct', """ -cc = Environment().Dictionary('CC') +DefaultEnvironment(tools=[]) env = Environment( + tools=['link','cc'], LINK=r'%(_python_)s mylink.py', LINKFLAGS=[], CC=r'%(_python_)s mycc.py', - CXX=cc, - CXXFLAGS=[], ) env.Program(target='test1', source='test1.c') """ % locals()) @@ -54,11 +53,12 @@ if os.path.normcase('.c') == os.path.normcase('.C'): test.write('SConstruct', """ -cc = Environment().Dictionary('CC') +DefaultEnvironment(tools=[]) + env = Environment( + tools=['link','cc'], LINK=r'%(_python_)s mylink.py', CC=r'%(_python_)s mycc.py', - CXX=cc, ) env.Program(target='test2', source='test2.C') """ % locals()) @@ -69,9 +69,11 @@ test.file_fixture('wrapper.py') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + foo = Environment() -cc = foo.Dictionary('CC') -bar = Environment(CC=r'%(_python_)s wrapper.py ' + cc) +bar = Environment() +bar['CC'] = r'%(_python_)s wrapper.py ' + foo['CC'] foo.Program(target='foo', source='foo.c') bar.Program(target='bar', source='bar.c') """ % locals()) diff --git a/test/CC/CCCOM.py b/test/CC/CCCOM.py index 291dad86ae..717b36f984 100644 --- a/test/CC/CCCOM.py +++ b/test/CC/CCCOM.py @@ -1,71 +1,72 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test the ability to configure the $CCCOM construction variable. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.c') == os.path.normcase('.C'): - alt_c_suffix = '.C' -else: - alt_c_suffix = '.c' - -test.write('SConstruct', """ -env = Environment(CCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', - OBJSUFFIX='.obj') -env.Object(target = 'test1', source = 'test1.c') -env.Object(target = 'test2', source = 'test2%(alt_c_suffix)s') -""" % locals()) - -test.write('test1.c', 'test1.c\n/*cc*/\n') - -test.write('test2'+alt_c_suffix, """\ -test2.C -/*cc*/ -""") - -test.run() - -test.must_match('test1.obj', "test1.c\n") -test.must_match('test2.obj', "test2.C\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to configure the $CCCOM construction variable. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.c') == os.path.normcase('.C'): + alt_c_suffix = '.C' +else: + alt_c_suffix = '.c' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['cc'], + CCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', + OBJSUFFIX='.obj') +env.Object(target = 'test1', source = 'test1.c') +env.Object(target = 'test2', source = 'test2%(alt_c_suffix)s') +""" % locals()) + +test.write('test1.c', 'test1.c\n/*cc*/\n') + +test.write('test2'+alt_c_suffix, """\ +test2.C +/*cc*/ +""") + +test.run() + +test.must_match('test1.obj', "test1.c\n") +test.must_match('test2.obj', "test2.C\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/CCCOMSTR.py b/test/CC/CCCOMSTR.py index 9977243867..55d1db26d7 100644 --- a/test/CC/CCCOMSTR.py +++ b/test/CC/CCCOMSTR.py @@ -1,76 +1,77 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that the $CCCOMSTR construction variable allows you to configure -the C compilation output. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.c') == os.path.normcase('.C'): - alt_c_suffix = '.C' -else: - alt_c_suffix = '.c' - -test.write('SConstruct', """ -env = Environment(CCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', - CCCOMSTR = 'Building $TARGET from $SOURCE', - OBJSUFFIX='.obj') -env.Object(target = 'test1', source = 'test1.c') -env.Object(target = 'test2', source = 'test2%(alt_c_suffix)s') -""" % locals()) - -test.write('test1.c', 'test1.c\n/*cc*/\n') - -test.write('test2'+alt_c_suffix, """\ -test2.C -/*cc*/ -""") - -test.run(stdout = test.wrap_stdout("""\ -Building test1.obj from test1.c -Building test2.obj from test2%(alt_c_suffix)s -""" % locals())) - -test.must_match('test1.obj', "test1.c\n") -test.must_match('test2.obj', "test2.C\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $CCCOMSTR construction variable allows you to configure +the C compilation output. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.c') == os.path.normcase('.C'): + alt_c_suffix = '.C' +else: + alt_c_suffix = '.c' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['cc'], + CCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', + CCCOMSTR = 'Building $TARGET from $SOURCE', + OBJSUFFIX='.obj') +env.Object(target = 'test1', source = 'test1.c') +env.Object(target = 'test2', source = 'test2%(alt_c_suffix)s') +""" % locals()) + +test.write('test1.c', 'test1.c\n/*cc*/\n') + +test.write('test2'+alt_c_suffix, """\ +test2.C +/*cc*/ +""") + +test.run(stdout = test.wrap_stdout("""\ +Building test1.obj from test1.c +Building test2.obj from test2%(alt_c_suffix)s +""" % locals())) + +test.must_match('test1.obj', "test1.c\n") +test.must_match('test2.obj', "test2.C\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/CCFLAGS.py b/test/CC/CCFLAGS.py index 069b429449..8afa3b342f 100644 --- a/test/CC/CCFLAGS.py +++ b/test/CC/CCFLAGS.py @@ -1,109 +1,111 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import sys -import TestSCons - -_obj = TestSCons._obj - -if sys.platform == 'win32': - import SCons.Tool.MSCommon as msc - - if not msc.msvc_exists(): - fooflags = '-DFOO' - barflags = '-DBAR' - else: - fooflags = '/nologo -DFOO' - barflags = '/nologo -DBAR' -else: - fooflags = '-DFOO' - barflags = '-DBAR' - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -foo = Environment(CCFLAGS = '%s') -bar = Environment(CCFLAGS = '%s') -foo.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -foo.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -foo.Program(target = 'prog', source = 'prog.c', - CCFLAGS = '$CCFLAGS -DBAR $BAZ', BAZ = '-DBAZ') -""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) - -test.write('prog.c', r""" -#include -#include - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; -#ifdef FOO - printf("prog.c: FOO\n"); -#endif -#ifdef BAR - printf("prog.c: BAR\n"); -#endif -#ifdef BAZ - printf("prog.c: BAZ\n"); -#endif - exit (0); -} -""") - - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('prog'), stdout = """\ -prog.c: FOO -prog.c: BAR -prog.c: BAZ -""") - -test.write('SConstruct', """ -bar = Environment(CCFLAGS = '%s') -bar.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -bar.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -""" % (barflags, _obj, _obj, _obj, _obj)) - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import sys +import TestSCons + +_obj = TestSCons._obj + +if sys.platform == 'win32': + import SCons.Tool.MSCommon as msc + + if not msc.msvc_exists(): + fooflags = '-DFOO' + barflags = '-DBAR' + else: + fooflags = '/nologo -DFOO' + barflags = '/nologo -DBAR' +else: + fooflags = '-DFOO' + barflags = '-DBAR' + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tool=[]) +foo = Environment(CCFLAGS = '%s') +bar = Environment(CCFLAGS = '%s') +foo.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +foo.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +foo.Program(target = 'prog', source = 'prog.c', + CCFLAGS = '$CCFLAGS -DBAR $BAZ', BAZ = '-DBAZ') +""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) + +test.write('prog.c', r""" +#include +#include + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; +#ifdef FOO + printf("prog.c: FOO\n"); +#endif +#ifdef BAR + printf("prog.c: BAR\n"); +#endif +#ifdef BAZ + printf("prog.c: BAZ\n"); +#endif + exit (0); +} +""") + + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('prog'), stdout = """\ +prog.c: FOO +prog.c: BAR +prog.c: BAZ +""") + +test.write('SConstruct', """ +DefaultEnvironment(tool=[]) + +bar = Environment(CCFLAGS = '%s') +bar.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +bar.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +""" % (barflags, _obj, _obj, _obj, _obj)) + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/CCVERSION.py b/test/CC/CCVERSION.py index 3ee601fa1d..b12849af0d 100644 --- a/test/CC/CCVERSION.py +++ b/test/CC/CCVERSION.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__" import sys import TestSCons diff --git a/test/CC/CFLAGS.py b/test/CC/CFLAGS.py index 590d6b5439..59ac137b6c 100644 --- a/test/CC/CFLAGS.py +++ b/test/CC/CFLAGS.py @@ -1,124 +1,124 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import sys -import TestSCons - -test = TestSCons.TestSCons() - -# Make sure CFLAGS is not passed to CXX by just expanding CXXCOM -test.write('SConstruct', """ -env = Environment(CFLAGS='-xyz', CCFLAGS='-abc') -print(env.subst('$CXXCOM')) -print(env.subst('$CXXCOMSTR')) -print(env.subst('$SHCXXCOM')) -print(env.subst('$SHCXXCOMSTR')) -""") -test.run(arguments = '.') -test.must_not_contain_any_line(test.stdout(), ["-xyz"]) -test.must_contain_all_lines(test.stdout(), ["-abc"]) - -_obj = TestSCons._obj - -# Test passing CFLAGS to C compiler by actually compiling programs -if sys.platform == 'win32': - import SCons.Tool.MSCommon as msc - - if not msc.msvc_exists(): - fooflags = '-DFOO' - barflags = '-DBAR' - else: - fooflags = '/nologo -DFOO' - barflags = '/nologo -DBAR' -else: - fooflags = '-DFOO' - barflags = '-DBAR' - - -test.write('SConstruct', """ -foo = Environment(CFLAGS = '%s') -bar = Environment(CFLAGS = '%s') -foo.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -foo.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -foo.Program(target = 'prog', source = 'prog.c', - CFLAGS = '$CFLAGS -DBAR $BAZ', BAZ = '-DBAZ') -""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) - -test.write('prog.c', r""" -#include -#include - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; -#ifdef FOO - printf("prog.c: FOO\n"); -#endif -#ifdef BAR - printf("prog.c: BAR\n"); -#endif -#ifdef BAZ - printf("prog.c: BAZ\n"); -#endif - exit (0); -} -""") - - - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('prog'), stdout = """\ -prog.c: FOO -prog.c: BAR -prog.c: BAZ -""") - -test.write('SConstruct', """ -bar = Environment(CFLAGS = '%s') -bar.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -bar.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -""" % (barflags, _obj, _obj, _obj, _obj)) - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import sys +import TestSCons + +test = TestSCons.TestSCons() + +# Make sure CFLAGS is not passed to CXX by just expanding CXXCOM +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CFLAGS='-xyz', CCFLAGS='-abc') +print(env.subst('$CXXCOM')) +print(env.subst('$CXXCOMSTR')) +print(env.subst('$SHCXXCOM')) +print(env.subst('$SHCXXCOMSTR')) +""") +test.run(arguments = '.') +test.must_not_contain_any_line(test.stdout(), ["-xyz"]) +test.must_contain_all_lines(test.stdout(), ["-abc"]) + +_obj = TestSCons._obj + +# Test passing CFLAGS to C compiler by actually compiling programs +if sys.platform == 'win32': + import SCons.Tool.MSCommon as msc + + if not msc.msvc_exists(): + fooflags = '-DFOO' + barflags = '-DBAR' + else: + fooflags = '/nologo -DFOO' + barflags = '/nologo -DBAR' +else: + fooflags = '-DFOO' + barflags = '-DBAR' + + +test.write('SConstruct', """ +foo = Environment(CFLAGS = '%s') +bar = Environment(CFLAGS = '%s') +foo.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +foo.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +foo.Program(target = 'prog', source = 'prog.c', + CFLAGS = '$CFLAGS -DBAR $BAZ', BAZ = '-DBAZ') +""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) + +test.write('prog.c', r""" +#include +#include + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; +#ifdef FOO + printf("prog.c: FOO\n"); +#endif +#ifdef BAR + printf("prog.c: BAR\n"); +#endif +#ifdef BAZ + printf("prog.c: BAZ\n"); +#endif + exit (0); +} +""") + + + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('prog'), stdout = """\ +prog.c: FOO +prog.c: BAR +prog.c: BAZ +""") + +test.write('SConstruct', """ +bar = Environment(CFLAGS = '%s') +bar.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +bar.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +""" % (barflags, _obj, _obj, _obj, _obj)) + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/SHCC.py b/test/CC/SHCC.py index 1c9f68d489..3645c2dd96 100644 --- a/test/CC/SHCC.py +++ b/test/CC/SHCC.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__" import TestSCons @@ -34,8 +33,9 @@ test.file_fixture('wrapper.py') test.write('SConstruct', """ +DefaultEnvironment(tools=[]) foo = Environment() -shcc = foo.Dictionary('SHCC') +shcc = foo['SHCC'] bar = Environment(SHCC = r'%(_python_)s wrapper.py ' + shcc) foo.SharedObject(target = 'foo/foo', source = 'foo.c') bar.SharedObject(target = 'bar/bar', source = 'bar.c') diff --git a/test/CC/SHCCCOM.py b/test/CC/SHCCCOM.py index 5326c01ed9..55ff1e1ff4 100644 --- a/test/CC/SHCCCOM.py +++ b/test/CC/SHCCCOM.py @@ -1,72 +1,72 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import os - -import TestSCons - -""" -Test the ability to configure the $SHCCCOM construction variable. -""" - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.c') == os.path.normcase('.C'): - alt_c_suffix = '.C' -else: - alt_c_suffix = '.c' - -test.write('SConstruct', """ -env = Environment(SHCCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', - SHOBJPREFIX='', - SHOBJSUFFIX='.obj') -env.SharedObject(target = 'test1', source = 'test1.c') -env.SharedObject(target = 'test2', source = 'test2%(alt_c_suffix)s') -""" % locals()) - -test.write('test1.c', 'test1.c\n/*cc*/\n') - -test.write('test2'+alt_c_suffix, """\ -test2.C -/*cc*/ -""") - -test.run() - -test.must_match('test1.obj', "test1.c\n") -test.must_match('test2.obj', "test2.C\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import os + +import TestSCons + +""" +Test the ability to configure the $SHCCCOM construction variable. +""" + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.c') == os.path.normcase('.C'): + alt_c_suffix = '.C' +else: + alt_c_suffix = '.c' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(SHCCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', + SHOBJPREFIX='', + SHOBJSUFFIX='.obj') +env.SharedObject(target = 'test1', source = 'test1.c') +env.SharedObject(target = 'test2', source = 'test2%(alt_c_suffix)s') +""" % locals()) + +test.write('test1.c', 'test1.c\n/*cc*/\n') + +test.write('test2'+alt_c_suffix, """\ +test2.C +/*cc*/ +""") + +test.run() + +test.must_match('test1.obj', "test1.c\n") +test.must_match('test2.obj', "test2.C\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/SHCCCOMSTR.py b/test/CC/SHCCCOMSTR.py index 75f3aadcf7..e41de80849 100644 --- a/test/CC/SHCCCOMSTR.py +++ b/test/CC/SHCCCOMSTR.py @@ -1,79 +1,79 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that the $SHCCCOMSTR construction variable allows you to customize -the shared object C compilation output. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.c') == os.path.normcase('.C'): - alt_c_suffix = '.C' -else: - alt_c_suffix = '.c' - -test.write('SConstruct', """ -env = Environment(SHCCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', - SHCCCOMSTR = 'Building $TARGET from $SOURCE', - SHOBJPREFIX='', - SHOBJSUFFIX='.obj') -env.SharedObject(target = 'test1', source = 'test1.c') -env.SharedObject(target = 'test2', source = 'test2%(alt_c_suffix)s') -""" % locals()) - -test.write('test1.c', 'test1.c\n/*cc*/\n') - -test.write('test2'+alt_c_suffix, """\ -test2.C -/*cc*/ -""") - -test.run(stdout = test.wrap_stdout("""\ -Building test1.obj from test1.c -Building test2.obj from test2%(alt_c_suffix)s -""" % locals())) - -test.must_match('test1.obj', "test1.c\n") -test.must_match('test2.obj', "test2.C\n") - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $SHCCCOMSTR construction variable allows you to customize +the shared object C compilation output. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.c') == os.path.normcase('.C'): + alt_c_suffix = '.C' +else: + alt_c_suffix = '.c' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(SHCCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', + SHCCCOMSTR = 'Building $TARGET from $SOURCE', + SHOBJPREFIX='', + SHOBJSUFFIX='.obj') +env.SharedObject(target = 'test1', source = 'test1.c') +env.SharedObject(target = 'test2', source = 'test2%(alt_c_suffix)s') +""" % locals()) + +test.write('test1.c', 'test1.c\n/*cc*/\n') + +test.write('test2'+alt_c_suffix, """\ +test2.C +/*cc*/ +""") + +test.run(stdout = test.wrap_stdout("""\ +Building test1.obj from test1.c +Building test2.obj from test2%(alt_c_suffix)s +""" % locals())) + +test.must_match('test1.obj', "test1.c\n") +test.must_match('test2.obj', "test2.C\n") + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/SHCFLAGS.py b/test/CC/SHCFLAGS.py index a691dbaab9..b110f59de9 100644 --- a/test/CC/SHCFLAGS.py +++ b/test/CC/SHCFLAGS.py @@ -1,136 +1,138 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import sys -import TestSCons -import os - -test = TestSCons.TestSCons() - -e = test.Environment() -fooflags = e['SHCFLAGS'] + ' -DFOO' -barflags = e['SHCFLAGS'] + ' -DBAR' - -if os.name == 'posix': - os.environ['LD_LIBRARY_PATH'] = '.' -if sys.platform.find('irix') > -1: - os.environ['LD_LIBRARYN32_PATH'] = '.' - -test.write('SConstruct', """ -foo = Environment(SHCFLAGS = '%s', WINDOWS_INSERT_DEF=1) -bar = Environment(SHCFLAGS = '%s', WINDOWS_INSERT_DEF=1) - -foo_obj = foo.SharedObject(target = 'foo', source = 'prog.c') -foo.SharedLibrary(target = 'foo', source = foo_obj) - -bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') -bar.SharedLibrary(target = 'bar', source = bar_obj) - -fooMain = foo.Clone(LIBS='foo', LIBPATH='.') -foomain_obj = fooMain.Object(target='foomain', source='main.c') -fooMain.Program(target='fooprog', source=foomain_obj) - -barMain = bar.Clone(LIBS='bar', LIBPATH='.') -barmain_obj = barMain.Object(target='barmain', source='main.c') -barMain.Program(target='barprog', source=barmain_obj) -""" % (fooflags, barflags)) - -test.write('foo.def', r""" -LIBRARY "foo" -DESCRIPTION "Foo Shared Library" - -EXPORTS - doIt -""") - -test.write('bar.def', r""" -LIBRARY "bar" -DESCRIPTION "Bar Shared Library" - -EXPORTS - doIt -""") - -test.write('prog.c', r""" -#include - -void -doIt() -{ -#ifdef FOO - printf("prog.c: FOO\n"); -#endif -#ifdef BAR - printf("prog.c: BAR\n"); -#endif -} -""") - -test.write('main.c', r""" - -void doIt(); - -int -main(int argc, char* argv[]) -{ - doIt(); - return 0; -} -""") - -test.run(arguments = '.') - -test.run(program = test.workpath('fooprog'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") - -test.write('SConstruct', """ -bar = Environment(SHCFLAGS = '%s', WINDOWS_INSERT_DEF=1) - -foo_obj = bar.SharedObject(target = 'foo', source = 'prog.c') -bar.SharedLibrary(target = 'foo', source = foo_obj) - -bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') -bar.SharedLibrary(target = 'bar', source = bar_obj) - -barMain = bar.Clone(LIBS='bar', LIBPATH='.') -foomain_obj = barMain.Object(target='foomain', source='main.c') -barmain_obj = barMain.Object(target='barmain', source='main.c') -barMain.Program(target='barprog', source=foomain_obj) -barMain.Program(target='fooprog', source=barmain_obj) -""" % barflags) - -test.run(arguments = '.') - -test.run(program = test.workpath('fooprog'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys +import TestSCons +import os + +test = TestSCons.TestSCons() + +e = test.Environment() +fooflags = e['SHCFLAGS'] + ' -DFOO' +barflags = e['SHCFLAGS'] + ' -DBAR' + +if os.name == 'posix': + os.environ['LD_LIBRARY_PATH'] = '.' +if sys.platform.find('irix') > -1: + os.environ['LD_LIBRARYN32_PATH'] = '.' + +test.write('SConstruct', f""" +DefaultEnvironment(tools=[]) +foo = Environment(SHCFLAGS = '{fooflags}', WINDOWS_INSERT_DEF=1) +bar = Environment(SHCFLAGS = '{barflags}', WINDOWS_INSERT_DEF=1) + +foo_obj = foo.SharedObject(target = 'foo', source = 'prog.c') +foo.SharedLibrary(target = 'foo', source = foo_obj) + +bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') +bar.SharedLibrary(target = 'bar', source = bar_obj) + +fooMain = foo.Clone(LIBS='foo', LIBPATH='.') +foomain_obj = fooMain.Object(target='foomain', source='main.c') +fooMain.Program(target='fooprog', source=foomain_obj) + +barMain = bar.Clone(LIBS='bar', LIBPATH='.') +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=barmain_obj) +""") + +test.write('foo.def', r""" +LIBRARY "foo" +DESCRIPTION "Foo Shared Library" + +EXPORTS + doIt +""") + +test.write('bar.def', r""" +LIBRARY "bar" +DESCRIPTION "Bar Shared Library" + +EXPORTS + doIt +""") + +test.write('prog.c', r""" +#include + +void +doIt() +{ +#ifdef FOO + printf("prog.c: FOO\n"); +#endif +#ifdef BAR + printf("prog.c: BAR\n"); +#endif +} +""") + +test.write('main.c', r""" + +void doIt(); + +int +main(int argc, char* argv[]) +{ + doIt(); + return 0; +} +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('fooprog'), stdout = "prog.c: FOO\n") +test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") + +test.write('SConstruct', f""" +DefaultEnvironment(tools=[]) +bar = Environment(SHCFLAGS = '{barflags}', WINDOWS_INSERT_DEF=1) + +foo_obj = bar.SharedObject(target = 'foo', source = 'prog.c') +bar.SharedLibrary(target = 'foo', source = foo_obj) + +bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') +bar.SharedLibrary(target = 'bar', source = bar_obj) + +barMain = bar.Clone(LIBS='bar', LIBPATH='.') +foomain_obj = barMain.Object(target='foomain', source='main.c') +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=foomain_obj) +barMain.Program(target='fooprog', source=barmain_obj) +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('fooprog'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/CacheDir.py b/test/CacheDir/CacheDir.py index 3b3e72bb18..f59da13ea9 100644 --- a/test/CacheDir/CacheDir.py +++ b/test/CacheDir/CacheDir.py @@ -1,165 +1,164 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test retrieving derived files from a CacheDir. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -cache = test.workpath('cache') - -src_aaa_out = test.workpath('src', 'aaa.out') -src_bbb_out = test.workpath('src', 'bbb.out') -src_ccc_out = test.workpath('src', 'ccc.out') -src_cat_out = test.workpath('src', 'cat.out') -src_all = test.workpath('src', 'all') - -test.subdir('cache', 'src') - -test.write(['src', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -CacheDir(r'%(cache)s') -SConscript('SConscript') -""" % locals()) - -test.write(['src', 'SConscript'], """\ -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -""") - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# Verify that building with -n and an empty cache reports that proper -# build operations would be taken, but that nothing is actually built -# and that the cache is still empty. -test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ -cat(["aaa.out"], ["aaa.in"]) -cat(["bbb.out"], ["bbb.in"]) -cat(["ccc.out"], ["ccc.in"]) -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_not_exist(src_aaa_out) -test.must_not_exist(src_bbb_out) -test.must_not_exist(src_ccc_out) -test.must_not_exist(src_all) -# Even if you do -n, the cache will be configured. -test.fail_test(os.listdir(cache) != ['config']) - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run(chdir = 'src', arguments = '.') - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(['src', 'cat.out']) - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. Then clean up. -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.must_not_exist(src_cat_out) - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') - -# Verify that rebuilding with -n reports that everything was retrieved -# from the cache, but that nothing really was. -test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.must_not_exist(src_aaa_out) -test.must_not_exist(src_bbb_out) -test.must_not_exist(src_ccc_out) -test.must_not_exist(src_all) - -# Verify that rebuilding with -s retrieves everything from the cache -# even though it doesn't report anything. -test.run(chdir = 'src', arguments = '-s .', stdout = "") - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_not_exist(src_cat_out) - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') - -# Verify that updating one input file builds its derived file and -# dependency but that the other files are retrieved from cache. -test.write(['src', 'bbb.in'], "bbb.in 2\n") - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -cat(["bbb.out"], ["bbb.in"]) -Retrieved `ccc.out' from cache -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') -test.must_match(['src', 'cat.out'], "bbb.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test retrieving derived files from a CacheDir. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +cache = test.workpath('cache') + +src_aaa_out = test.workpath('src', 'aaa.out') +src_bbb_out = test.workpath('src', 'bbb.out') +src_ccc_out = test.workpath('src', 'ccc.out') +src_cat_out = test.workpath('src', 'cat.out') +src_all = test.workpath('src', 'all') + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +CacheDir(r'%(cache)s') +SConscript('SConscript') +""" % locals()) + +test.write(['src', 'SConscript'], """\ +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that building with -n and an empty cache reports that proper +# build operations would be taken, but that nothing is actually built +# and that the cache is still empty. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +cat(["bbb.out"], ["bbb.in"]) +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) +# Even if you do -n, the cache will be configured. +test.fail_test(os.listdir(cache) != ['config']) + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.must_not_exist(src_cat_out) + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') + +# Verify that rebuilding with -n reports that everything was retrieved +# from the cache, but that nothing really was. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) + +# Verify that rebuilding with -s retrieves everything from the cache +# even though it doesn't report anything. +test.run(chdir = 'src', arguments = '-s .', stdout = "") + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_not_exist(src_cat_out) + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') + +# Verify that updating one input file builds its derived file and +# dependency but that the other files are retrieved from cache. +test.write(['src', 'bbb.in'], "bbb.in 2\n") + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') +test.must_match(['src', 'cat.out'], "bbb.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/CacheDir_TryCompile.py b/test/CacheDir/CacheDir_TryCompile.py index dbea9be3e8..b00c5d4956 100644 --- a/test/CacheDir/CacheDir_TryCompile.py +++ b/test/CacheDir/CacheDir_TryCompile.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__" """ Test that CacheDir functions with TryCompile. diff --git a/test/CacheDir/NoCache.py b/test/CacheDir/NoCache.py index 506a85945b..e1cecee351 100644 --- a/test/CacheDir/NoCache.py +++ b/test/CacheDir/NoCache.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 the NoCache environment method works. diff --git a/test/CacheDir/SideEffect.py b/test/CacheDir/SideEffect.py index 3242e780f5..c93485f817 100644 --- a/test/CacheDir/SideEffect.py +++ b/test/CacheDir/SideEffect.py @@ -1,115 +1,114 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that use of SideEffect() doesn't interfere with CacheDir. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'work') - -cache = test.workpath('cache') - -test.write(['work', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -def copy(source, target): - with open(target, "w") as f, open(source, "r") as f2: - f.write(f2.read()) - -def build(env, source, target): - s = str(source[0]) - t = str(target[0]) - copy(s, t) - if target[0].side_effects: - with open(str(target[0].side_effects[0]), "a") as side_effect: - side_effect.write(s + ' -> ' + t + '\\n') - -CacheDir(r'%(cache)s') - -Build = Builder(action=build) -env = Environment(tools=[], BUILDERS={'Build':Build}, SUBDIR='subdir') -env.Build('f1.out', 'f1.in') -env.Build('f2.out', 'f2.in') -env.Build('f3.out', 'f3.in') -SideEffect('log.txt', ['f1.out', 'f2.out', 'f3.out']) -""" % locals()) - -test.write(['work', 'f1.in'], 'f1.in\n') -test.write(['work', 'f2.in'], 'f2.in\n') -test.write(['work', 'f3.in'], 'f3.in\n') - -test.run(chdir='work', arguments='f1.out f2.out') - -expect = """\ -f1.in -> f1.out -f2.in -> f2.out -""" - -test.must_match(['work', 'log.txt'], expect, mode='r') - - - -test.write(['work', 'f2.in'], 'f2.in 2 \n') - -test.run(chdir='work', arguments='log.txt') - -expect = """\ -f1.in -> f1.out -f2.in -> f2.out -f2.in -> f2.out -f3.in -> f3.out -""" - -test.must_match(['work', 'log.txt'], expect, mode='r') - - - -test.write(['work', 'f1.in'], 'f1.in 2 \n') - -test.run(chdir='work', arguments=".") - -expect = """\ -f1.in -> f1.out -f2.in -> f2.out -f2.in -> f2.out -f3.in -> f3.out -f1.in -> f1.out -""" - -test.must_match(['work', 'log.txt'], expect, mode='r') - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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._"_" + +""" +Test that use of SideEffect() doesn't interfere with CacheDir. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'work') + +cache = test.workpath('cache') + +test.write(['work', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +def copy(source, target): + with open(target, "w") as f, open(source, "r") as f2: + f.write(f2.read()) + +def build(env, source, target): + s = str(source[0]) + t = str(target[0]) + copy(s, t) + if target[0].side_effects: + with open(str(target[0].side_effects[0]), "a") as side_effect: + side_effect.write(s + ' -> ' + t + '\\n') + +CacheDir(r'%(cache)s') + +Build = Builder(action=build) +env = Environment(tools=[], BUILDERS={'Build':Build}, SUBDIR='subdir') +env.Build('f1.out', 'f1.in') +env.Build('f2.out', 'f2.in') +env.Build('f3.out', 'f3.in') +SideEffect('log.txt', ['f1.out', 'f2.out', 'f3.out']) +""" % locals()) + +test.write(['work', 'f1.in'], 'f1.in\n') +test.write(['work', 'f2.in'], 'f2.in\n') +test.write(['work', 'f3.in'], 'f3.in\n') + +test.run(chdir='work', arguments='f1.out f2.out') + +expect = """\ +f1.in -> f1.out +f2.in -> f2.out +""" + +test.must_match(['work', 'log.txt'], expect, mode='r') + + + +test.write(['work', 'f2.in'], 'f2.in 2 \n') + +test.run(chdir='work', arguments='log.txt') + +expect = """\ +f1.in -> f1.out +f2.in -> f2.out +f2.in -> f2.out +f3.in -> f3.out +""" + +test.must_match(['work', 'log.txt'], expect, mode='r') + + + +test.write(['work', 'f1.in'], 'f1.in 2 \n') + +test.run(chdir='work', arguments=".") + +expect = """\ +f1.in -> f1.out +f2.in -> f2.out +f2.in -> f2.out +f3.in -> f3.out +f1.in -> f1.out +""" + +test.must_match(['work', 'log.txt'], expect, mode='r') + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/VariantDir.py b/test/CacheDir/VariantDir.py index 2c3d73f613..77cd117dd1 100644 --- a/test/CacheDir/VariantDir.py +++ b/test/CacheDir/VariantDir.py @@ -1,155 +1,156 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test retrieving derived files from a CacheDir when a VariantDir is used. -""" - -import os.path - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'src') - -cache = test.workpath('cache') -cat_out = test.workpath('cat.out') - -test.write(['src', 'SConscript'], """\ -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -""") - -build_aaa_out = os.path.join('build', 'aaa.out') -build_bbb_out = os.path.join('build', 'bbb.out') -build_ccc_out = os.path.join('build', 'ccc.out') -build_all = os.path.join('build', 'all') - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[], TWO = '2') -CacheDir(r'%s') -VariantDir('build', 'src', duplicate=0) -SConscript('build/SConscript') -""" % test.workpath('cache${TWO}')) - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run() - -test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_match('cat.out', "%s\n%s\n%s\n%s\n" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all), mode='r') - -test.up_to_date(arguments = '.') - -test.run(arguments = '-c .') -test.unlink('cat.out') - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. Then clean up. -test.run(stdout = test.wrap_stdout("""\ -Retrieved `%s' from cache -Retrieved `%s' from cache -Retrieved `%s' from cache -Retrieved `%s' from cache -""" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))) - -test.must_not_exist(cat_out) - -test.up_to_date(arguments = '.') - -test.run(arguments = '-c .') - -# Verify that rebuilding with -n reports that everything was retrieved -# from the cache, but that nothing really was. -test.run(arguments = '-n .', stdout = test.wrap_stdout("""\ -Retrieved `%s' from cache -Retrieved `%s' from cache -Retrieved `%s' from cache -Retrieved `%s' from cache -""" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))) - -test.must_not_exist(test.workpath('build', 'aaa.out')) -test.must_not_exist(test.workpath('build', 'bbb.out')) -test.must_not_exist(test.workpath('build', 'ccc.out')) -test.must_not_exist(test.workpath('build', 'all')) - -# Verify that rebuilding with -s retrieves everything from the cache -# even though it doesn't report anything. -test.run(arguments = '-s .', stdout = "") - -test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_not_exist(cat_out) - -test.up_to_date(arguments = '.') - -test.run(arguments = '-c .') - -# Verify that updating one input file builds its derived file and -# dependency but that the other files are retrieved from cache. -test.write(['src', 'bbb.in'], "bbb.in 2\n") - -test.run(stdout = test.wrap_stdout("""\ -Retrieved `%s' from cache -cat(["%s"], ["%s"]) -Retrieved `%s' from cache -cat(["%s"], ["%s", "%s", "%s"]) -""" % (build_aaa_out, - build_bbb_out, os.path.join('src', 'bbb.in'), - build_ccc_out, - build_all, build_aaa_out, build_bbb_out, build_ccc_out))) - -test.must_match(['build', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') -test.must_match('cat.out', "%s\n%s\n" % (build_bbb_out, build_all), mode='r') - -test.up_to_date(arguments = '.') - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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._" + +""" +Test retrieving derived files from a CacheDir when a VariantDir is used. +""" + +import os.path + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'src') + +cache = test.workpath('cache') +cat_out = test.workpath('cat.out') + +test.write(['src', 'SConscript'], """\ +DefaultEnvironment(tools=[]) + +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +build_aaa_out = os.path.join('build', 'aaa.out') +build_bbb_out = os.path.join('build', 'bbb.out') +build_ccc_out = os.path.join('build', 'ccc.out') +build_all = os.path.join('build', 'all') + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[], TWO = '2') +CacheDir(r'%s') +VariantDir('build', 'src', duplicate=0) +SConscript('build/SConscript') +""" % test.workpath('cache${TWO}')) + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run() + +test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_match('cat.out', "%s\n%s\n%s\n%s\n" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all), mode='r') + +test.up_to_date(arguments = '.') + +test.run(arguments = '-c .') +test.unlink('cat.out') + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(stdout = test.wrap_stdout("""\ +Retrieved `%s' from cache +Retrieved `%s' from cache +Retrieved `%s' from cache +Retrieved `%s' from cache +""" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))) + +test.must_not_exist(cat_out) + +test.up_to_date(arguments = '.') + +test.run(arguments = '-c .') + +# Verify that rebuilding with -n reports that everything was retrieved +# from the cache, but that nothing really was. +test.run(arguments = '-n .', stdout = test.wrap_stdout("""\ +Retrieved `%s' from cache +Retrieved `%s' from cache +Retrieved `%s' from cache +Retrieved `%s' from cache +""" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))) + +test.must_not_exist(test.workpath('build', 'aaa.out')) +test.must_not_exist(test.workpath('build', 'bbb.out')) +test.must_not_exist(test.workpath('build', 'ccc.out')) +test.must_not_exist(test.workpath('build', 'all')) + +# Verify that rebuilding with -s retrieves everything from the cache +# even though it doesn't report anything. +test.run(arguments = '-s .', stdout = "") + +test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_not_exist(cat_out) + +test.up_to_date(arguments = '.') + +test.run(arguments = '-c .') + +# Verify that updating one input file builds its derived file and +# dependency but that the other files are retrieved from cache. +test.write(['src', 'bbb.in'], "bbb.in 2\n") + +test.run(stdout = test.wrap_stdout("""\ +Retrieved `%s' from cache +cat(["%s"], ["%s"]) +Retrieved `%s' from cache +cat(["%s"], ["%s", "%s", "%s"]) +""" % (build_aaa_out, + build_bbb_out, os.path.join('src', 'bbb.in'), + build_ccc_out, + build_all, build_aaa_out, build_bbb_out, build_ccc_out))) + +test.must_match(['build', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') +test.must_match('cat.out', "%s\n%s\n" % (build_bbb_out, build_all), mode='r') + +test.up_to_date(arguments = '.') + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/debug.py b/test/CacheDir/debug.py index 9b3b633ee8..2c7ff0dd54 100644 --- a/test/CacheDir/debug.py +++ b/test/CacheDir/debug.py @@ -1,202 +1,201 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test the --cache-debug option to see if it prints the expected messages. - -Note that we don't check for the "race condition" message when someone -else's build populates the CacheDir with a file in between the time we -to build it because it doesn't exist in the CacheDir, and the time our -build of the file completes and we push it out. -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.subdir('cache', 'src') - -cache = test.workpath('cache') -debug_out = test.workpath('cache-debug.out') - - - -test.write(['src', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -CacheDir(r'%(cache)s') -SConscript('SConscript') -""" % locals()) - -test.write(['src', 'SConscript'], """\ -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -""") - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - - - -# Test for messages about files not being in CacheDir, with -n (don't -# actually build or push) and sendinig the message to a file. - -expect = \ -r"""cat\(\["aaa.out"\], \["aaa.in"\]\) -cat\(\["bbb.out"\], \["bbb.in"\]\) -cat\(\["ccc.out"\], \["ccc.in"\]\) -cat\(\["all"\], \["aaa.out", "bbb.out", "ccc.out"\]\) -""" - -test.run(chdir='src', - arguments='-n -Q --cache-debug=%s .' % debug_out, - stdout=expect) - -expect = \ -r"""CacheRetrieve\(aaa.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(bbb.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(ccc.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(all\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -""" - -test.must_match(debug_out, expect, mode='r') - - - -# Test for messages about actually pushing to the cache, without -n -# and to standard ouput. - -expect = \ -r"""CacheRetrieve\(aaa.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -cat\(\["aaa.out"\], \["aaa.in"\]\) -CachePush\(aaa.out\): pushing to [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(bbb.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -cat\(\["bbb.out"\], \["bbb.in"\]\) -CachePush\(bbb.out\): pushing to [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(ccc.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -cat\(\["ccc.out"\], \["ccc.in"\]\) -CachePush\(ccc.out\): pushing to [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(all\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -cat\(\["all"\], \["aaa.out", "bbb.out", "ccc.out"\]\) -CachePush\(all\): pushing to [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -""" - -test.run(chdir='src', - arguments='-Q --cache-debug=- .', - stdout=expect) - - - -# Clean up the local targets. - -test.run(chdir='src', arguments='-c --cache-debug=%s .' % debug_out) -test.unlink(['src', 'cat.out']) - - - -# Test for messages about retrieving files from CacheDir, with -n -# and sending the messages to standard output. - -expect = \ -r"""Retrieved `aaa.out' from cache -CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -Retrieved `bbb.out' from cache -CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -Retrieved `ccc.out' from cache -CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -Retrieved `all' from cache -CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -""" - -test.run(chdir='src', - arguments='-n -Q --cache-debug=- .', - stdout=expect) - - - -# And finally test for message about retrieving file from CacheDir -# *without* -n and sending the message to a file. - -expect = \ -r"""Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""" - -test.run(chdir='src', - arguments='-Q --cache-debug=%s .' % debug_out, - stdout=expect) - -expect = \ -r"""CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -""" - -test.must_match(debug_out, expect, mode='r') - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the --cache-debug option to see if it prints the expected messages. + +Note that we don't check for the "race condition" message when someone +else's build populates the CacheDir with a file in between the time we +to build it because it doesn't exist in the CacheDir, and the time our +build of the file completes and we push it out. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.subdir('cache', 'src') + +cache = test.workpath('cache') +debug_out = test.workpath('cache-debug.out') + + + +test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +CacheDir(r'%(cache)s') +SConscript('SConscript') +""" % locals()) + +test.write(['src', 'SConscript'], """\ +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + + + +# Test for messages about files not being in CacheDir, with -n (don't +# actually build or push) and sendinig the message to a file. + +expect = \ +r"""cat\(\["aaa.out"\], \["aaa.in"\]\) +cat\(\["bbb.out"\], \["bbb.in"\]\) +cat\(\["ccc.out"\], \["ccc.in"\]\) +cat\(\["all"\], \["aaa.out", "bbb.out", "ccc.out"\]\) +""" + +test.run(chdir='src', + arguments='-n -Q --cache-debug=%s .' % debug_out, + stdout=expect) + +expect = \ +r"""CacheRetrieve\(aaa.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(bbb.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(ccc.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(all\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +""" + +test.must_match(debug_out, expect, mode='r') + + + +# Test for messages about actually pushing to the cache, without -n +# and to standard ouput. + +expect = \ +r"""CacheRetrieve\(aaa.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +cat\(\["aaa.out"\], \["aaa.in"\]\) +CachePush\(aaa.out\): pushing to [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(bbb.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +cat\(\["bbb.out"\], \["bbb.in"\]\) +CachePush\(bbb.out\): pushing to [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(ccc.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +cat\(\["ccc.out"\], \["ccc.in"\]\) +CachePush\(ccc.out\): pushing to [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(all\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +cat\(\["all"\], \["aaa.out", "bbb.out", "ccc.out"\]\) +CachePush\(all\): pushing to [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +""" + +test.run(chdir='src', + arguments='-Q --cache-debug=- .', + stdout=expect) + + + +# Clean up the local targets. + +test.run(chdir='src', arguments='-c --cache-debug=%s .' % debug_out) +test.unlink(['src', 'cat.out']) + + + +# Test for messages about retrieving files from CacheDir, with -n +# and sending the messages to standard output. + +expect = \ +r"""Retrieved `aaa.out' from cache +CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +Retrieved `bbb.out' from cache +CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +Retrieved `ccc.out' from cache +CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +Retrieved `all' from cache +CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +""" + +test.run(chdir='src', + arguments='-n -Q --cache-debug=- .', + stdout=expect) + + + +# And finally test for message about retrieving file from CacheDir +# *without* -n and sending the message to a file. + +expect = \ +r"""Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""" + +test.run(chdir='src', + arguments='-Q --cache-debug=%s .' % debug_out, + stdout=expect) + +expect = \ +r"""CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +""" + +test.must_match(debug_out, expect, mode='r') + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/environment.py b/test/CacheDir/environment.py index 1024ce0383..60c52ebfa5 100644 --- a/test/CacheDir/environment.py +++ b/test/CacheDir/environment.py @@ -1,171 +1,170 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that whether or not a target gets retrieved from a CacheDir -is configurable by construction environment. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -cache = test.workpath('cache') - -src_aaa_out = test.workpath('src', 'aaa.out') -src_bbb_out = test.workpath('src', 'bbb.out') -src_ccc_out = test.workpath('src', 'ccc.out') -src_cat_out = test.workpath('src', 'cat.out') -src_all = test.workpath('src', 'all') - -test.subdir('cache', 'src') - -test.write(['src', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -CacheDir(r'%(cache)s') -SConscript('SConscript') -""" % locals()) - -test.write(['src', 'SConscript'], """\ -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env_cache = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env_nocache = env_cache.Clone() -env_nocache.CacheDir(None) -env_cache.Cat('aaa.out', 'aaa.in') -env_nocache.Cat('bbb.out', 'bbb.in') -env_cache.Cat('ccc.out', 'ccc.in') -env_nocache.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -""") - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# Verify that building with -n and an empty cache reports that proper -# build operations would be taken, but that nothing is actually built -# and that the cache is still empty. -test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ -cat(["aaa.out"], ["aaa.in"]) -cat(["bbb.out"], ["bbb.in"]) -cat(["ccc.out"], ["ccc.in"]) -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_not_exist(src_aaa_out) -test.must_not_exist(src_bbb_out) -test.must_not_exist(src_ccc_out) -test.must_not_exist(src_all) -# Even if you do -n, the cache will be configured. -test.fail_test(os.listdir(cache) != ['config']) - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run(chdir = 'src', arguments = '.') - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_match(src_cat_out, "aaa.out\nbbb.out\nccc.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(src_cat_out) - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. Then clean up. -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -cat(["bbb.out"], ["bbb.in"]) -Retrieved `ccc.out' from cache -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(src_cat_out) - -# Verify that rebuilding with -n reports that files were retrieved -# from the cache, but that nothing really was. -test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -cat(["bbb.out"], ["bbb.in"]) -Retrieved `ccc.out' from cache -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_not_exist(src_aaa_out) -test.must_not_exist(src_bbb_out) -test.must_not_exist(src_ccc_out) -test.must_not_exist(src_all) - -# Verify that rebuilding with -s retrieves everything from the cache -# even though it doesn't report anything. -test.run(chdir = 'src', arguments = '-s .', stdout = "") - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') - -test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(src_cat_out) - -# Verify that updating one input file builds its derived file and -# dependency but that the other files are retrieved from cache. -test.write(['src', 'bbb.in'], "bbb.in 2\n") - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -cat(["bbb.out"], ["bbb.in"]) -Retrieved `ccc.out' from cache -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') -test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that whether or not a target gets retrieved from a CacheDir +is configurable by construction environment. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +cache = test.workpath('cache') + +src_aaa_out = test.workpath('src', 'aaa.out') +src_bbb_out = test.workpath('src', 'bbb.out') +src_ccc_out = test.workpath('src', 'ccc.out') +src_cat_out = test.workpath('src', 'cat.out') +src_all = test.workpath('src', 'all') + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +CacheDir(r'%(cache)s') +SConscript('SConscript') +""" % locals()) + +test.write(['src', 'SConscript'], """\ +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env_cache = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env_nocache = env_cache.Clone() +env_nocache.CacheDir(None) +env_cache.Cat('aaa.out', 'aaa.in') +env_nocache.Cat('bbb.out', 'bbb.in') +env_cache.Cat('ccc.out', 'ccc.in') +env_nocache.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that building with -n and an empty cache reports that proper +# build operations would be taken, but that nothing is actually built +# and that the cache is still empty. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +cat(["bbb.out"], ["bbb.in"]) +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) +# Even if you do -n, the cache will be configured. +test.fail_test(os.listdir(cache) != ['config']) + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_match(src_cat_out, "aaa.out\nbbb.out\nccc.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(src_cat_out) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(src_cat_out) + +# Verify that rebuilding with -n reports that files were retrieved +# from the cache, but that nothing really was. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) + +# Verify that rebuilding with -s retrieves everything from the cache +# even though it doesn't report anything. +test.run(chdir = 'src', arguments = '-s .', stdout = "") + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') + +test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(src_cat_out) + +# Verify that updating one input file builds its derived file and +# dependency but that the other files are retrieved from cache. +test.write(['src', 'bbb.in'], "bbb.in 2\n") + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') +test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/multi-targets.py b/test/CacheDir/multi-targets.py index fa6e8d13c0..ed11f779ca 100644 --- a/test/CacheDir/multi-targets.py +++ b/test/CacheDir/multi-targets.py @@ -1,79 +1,78 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that multiple target files get retrieved from a CacheDir correctly. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'multiple') - -cache = test.workpath('cache') - -multiple_bar = test.workpath('multiple', 'bar') -multiple_foo = test.workpath('multiple', 'foo') - -test.write(['multiple', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -def touch(env, source, target): - with open('foo', 'w') as f: - f.write("") - with open('bar', 'w') as f: - f.write("") -CacheDir(r'%(cache)s') -env = Environment(tools=[]) -env.Command(['foo', 'bar'], ['input'], touch) -""" % locals()) - -test.write(['multiple', 'input'], "multiple/input\n") - -test.run(chdir = 'multiple') - -test.must_exist(multiple_foo) -test.must_exist(multiple_bar) - -test.run(chdir = 'multiple', arguments = '-c') - -test.must_not_exist(multiple_foo) -test.must_not_exist(multiple_bar) - -test.run(chdir = 'multiple') - -test.must_exist(multiple_foo) -test.must_exist(multiple_bar) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that multiple target files get retrieved from a CacheDir correctly. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'multiple') + +cache = test.workpath('cache') + +multiple_bar = test.workpath('multiple', 'bar') +multiple_foo = test.workpath('multiple', 'foo') + +test.write(['multiple', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +def touch(env, source, target): + with open('foo', 'w') as f: + f.write("") + with open('bar', 'w') as f: + f.write("") +CacheDir(r'%(cache)s') +env = Environment(tools=[]) +env.Command(['foo', 'bar'], ['input'], touch) +""" % locals()) + +test.write(['multiple', 'input'], "multiple/input\n") + +test.run(chdir = 'multiple') + +test.must_exist(multiple_foo) +test.must_exist(multiple_bar) + +test.run(chdir = 'multiple', arguments = '-c') + +test.must_not_exist(multiple_foo) +test.must_not_exist(multiple_bar) + +test.run(chdir = 'multiple') + +test.must_exist(multiple_foo) +test.must_exist(multiple_bar) + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/multiple-targets.py b/test/CacheDir/multiple-targets.py index d985ca07f3..1218e9b794 100644 --- a/test/CacheDir/multiple-targets.py +++ b/test/CacheDir/multiple-targets.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__" """ Test that multiple target files get retrieved from a CacheDir correctly. diff --git a/test/CacheDir/option--cd.py b/test/CacheDir/option--cd.py index 3ea739c28b..692207d973 100644 --- a/test/CacheDir/option--cd.py +++ b/test/CacheDir/option--cd.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__" """ Test the --cache-disable option when retrieving derived files from a diff --git a/test/CacheDir/option--cf.py b/test/CacheDir/option--cf.py index 2d51e03fec..f93b4db4f7 100644 --- a/test/CacheDir/option--cf.py +++ b/test/CacheDir/option--cf.py @@ -1,132 +1,131 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test populating a CacheDir with the --cache-force option. -""" - -import os.path -import shutil - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'src') - -test.write(['src', 'SConstruct'], """ -DefaultEnvironment(tools=[]) -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -CacheDir(r'%s') -""" % test.workpath('cache')) - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run(chdir = 'src', arguments = '.') - -test.must_match(['src','all'],"aaa.in\nbbb.in\nccc.in\n", mode='r') -# test.fail_test(test.read(['src', 'all']) != "aaa.in\nbbb.in\nccc.in\n") -test.must_match(['src','cat.out'],"aaa.out\nbbb.out\nccc.out\nall\n", mode='r') -# test.fail_test(test.read(['src', 'cat.out']) != "aaa.out\nbbb.out\nccc.out\nall\n") - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(['src', 'cat.out']) - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. DO NOT CLEAN UP. -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) - -test.up_to_date(chdir = 'src', arguments = '.') - -# Blow away and recreate the CacheDir, then verify that --cache-force -# repopulates the cache with the local built targets. DO NOT CLEAN UP. -shutil.rmtree(test.workpath('cache')) -test.subdir('cache') - -test.run(chdir = 'src', arguments = '--cache-force .') - -test.run(chdir = 'src', arguments = '-c .') - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) - -# Blow away and recreate the CacheDir, then verify that --cache-populate -# repopulates the cache with the local built targets. DO NOT CLEAN UP. -shutil.rmtree(test.workpath('cache')) -test.subdir('cache') - -test.run(chdir = 'src', arguments = '--cache-populate .') - -test.run(chdir = 'src', arguments = '-c .') - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) - -# All done. -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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." + +""" +Test populating a CacheDir with the --cache-force option. +""" + +import os.path +import shutil + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +CacheDir(r'%s') +""" % test.workpath('cache')) + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.must_match(['src','all'],"aaa.in\nbbb.in\nccc.in\n", mode='r') +# test.fail_test(test.read(['src', 'all']) != "aaa.in\nbbb.in\nccc.in\n") +test.must_match(['src','cat.out'],"aaa.out\nbbb.out\nccc.out\nall\n", mode='r') +# test.fail_test(test.read(['src', 'cat.out']) != "aaa.out\nbbb.out\nccc.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. DO NOT CLEAN UP. +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) + +test.up_to_date(chdir = 'src', arguments = '.') + +# Blow away and recreate the CacheDir, then verify that --cache-force +# repopulates the cache with the local built targets. DO NOT CLEAN UP. +shutil.rmtree(test.workpath('cache')) +test.subdir('cache') + +test.run(chdir = 'src', arguments = '--cache-force .') + +test.run(chdir = 'src', arguments = '-c .') + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) + +# Blow away and recreate the CacheDir, then verify that --cache-populate +# repopulates the cache with the local built targets. DO NOT CLEAN UP. +shutil.rmtree(test.workpath('cache')) +test.subdir('cache') + +test.run(chdir = 'src', arguments = '--cache-populate .') + +test.run(chdir = 'src', arguments = '-c .') + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) + +# All done. +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/option--cr.py b/test/CacheDir/option--cr.py index 0395876b39..6ff6974b92 100644 --- a/test/CacheDir/option--cr.py +++ b/test/CacheDir/option--cr.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__" """ Test the --cache-readonly option when retrieving derived files from a diff --git a/test/CacheDir/option--cs.py b/test/CacheDir/option--cs.py index c3e5e5b573..ce48ac0417 100644 --- a/test/CacheDir/option--cs.py +++ b/test/CacheDir/option--cs.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 @@ -19,10 +21,7 @@ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE._" """ Test printing build actions when using the --cache-show option and diff --git a/test/CacheDir/readonly-cache.py b/test/CacheDir/readonly-cache.py index 4aad0ee387..579909959d 100755 --- a/test/CacheDir/readonly-cache.py +++ b/test/CacheDir/readonly-cache.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 @@ -19,10 +21,7 @@ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE._" """ Verify accessing cache works even if it's read-only. diff --git a/test/CacheDir/scanner-target.py b/test/CacheDir/scanner-target.py index 44ba199032..dd8791d031 100644 --- a/test/CacheDir/scanner-target.py +++ b/test/CacheDir/scanner-target.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 @@ -19,10 +21,7 @@ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE._" """ Test the case (reported by Jeff Petkau, bug #694744) where a target diff --git a/test/CacheDir/source-scanner.py b/test/CacheDir/source-scanner.py index 1f6fef0c15..0dda4f43c8 100644 --- a/test/CacheDir/source-scanner.py +++ b/test/CacheDir/source-scanner.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 @@ -19,10 +21,7 @@ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE._"" """ Test retrieving derived files from a CacheDir. diff --git a/test/CacheDir/symlink.py b/test/CacheDir/symlink.py index e23c25eba5..9645f4d2df 100644 --- a/test/CacheDir/symlink.py +++ b/test/CacheDir/symlink.py @@ -1,77 +1,76 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# 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. -# -import os -import sys - -import TestSCons - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that we push and retrieve a built symlink to/from a CacheDir() -as an actualy symlink, not by copying the file contents. -""" - - - - -test = TestSCons.TestSCons() - -if not hasattr(os, 'symlink') or sys.platform == 'win32': - # Skip test on windows as well, because this requires permissions which aren't default - import sys - test.skip_test('%s has no os.symlink() method; skipping test\n' % sys.executable) - -test.write('SConstruct', """\ -CacheDir('cache') -import os -Symlink = Action(lambda target, source, env: - os.symlink(str(source[0]), str(target[0])), - "os.symlink($SOURCE, $TARGET)") -Command('file.symlink', 'file.txt', Symlink) -""") - -test.write('file.txt', "file.txt\n") - -test.run(arguments = '.') - -test.fail_test(not os.path.islink('file.symlink')) -test.must_match('file.symlink', "file.txt\n") - -test.run(arguments = '-c .') - -test.must_not_exist('file.symlink') - -test.run(arguments = '.') - -test.fail_test(not os.path.islink('file.symlink')) -test.must_match('file.symlink', "file.txt\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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._" +import os +import sys + +import TestSCons + +""" +Verify that we push and retrieve a built symlink to/from a CacheDir() +as an actualy symlink, not by copying the file contents. +""" + + + + +test = TestSCons.TestSCons() + +if not hasattr(os, 'symlink') or sys.platform == 'win32': + # Skip test on windows as well, because this requires permissions which aren't default + import sys + test.skip_test('%s has no os.symlink() method; skipping test\n' % sys.executable) + +test.write('SConstruct', """\ +CacheDir('cache') +import os +Symlink = Action(lambda target, source, env: + os.symlink(str(source[0]), str(target[0])), + "os.symlink($SOURCE, $TARGET)") +Command('file.symlink', 'file.txt', Symlink) +""") + +test.write('file.txt', "file.txt\n") + +test.run(arguments = '.') + +test.fail_test(not os.path.islink('file.symlink')) +test.must_match('file.symlink', "file.txt\n") + +test.run(arguments = '-c .') + +test.must_not_exist('file.symlink') + +test.run(arguments = '.') + +test.fail_test(not os.path.islink('file.symlink')) +test.must_match('file.symlink', "file.txt\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/up-to-date-q.py b/test/CacheDir/up-to-date-q.py index f0e3962eb4..c25a6777ad 100644 --- a/test/CacheDir/up-to-date-q.py +++ b/test/CacheDir/up-to-date-q.py @@ -1,87 +1,86 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that targets retrieved from CacheDir() are reported as -up-to-date by the -q option. - -Thanks to dvitek for the test case. -""" - -# Demonstrate a regression between 0.96.1 and 0.96.93. -# -# SCons would incorrectly believe files are stale if they were retrieved -# from the cache in a previous invocation. -# -# What this script does: -# 1. Set up two identical C project directories called 'alpha' and -# 'beta', which use the same cache -# 2. Invoke scons on 'alpha' -# 3. Invoke scons on 'beta', which successfully draws output -# files from the cache -# 4. Invoke scons again, asserting (with -q) that 'beta' is up to date -# -# Step 4 failed in 0.96.93. In practice, this problem would lead to -# lots of unecessary fetches from the cache during incremental -# builds (because they behaved like non-incremental builds). - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'alpha', 'beta') - -foo_c = """ -int main(void){ return 0; } -""" - -sconstruct = """ -CacheDir(r'%s') -Program('foo', 'foo.c') -""" % test.workpath('cache') - -test.write('alpha/foo.c', foo_c) -test.write('alpha/SConstruct', sconstruct) - -test.write('beta/foo.c', foo_c) -test.write('beta/SConstruct', sconstruct) - -# First build, populates the cache -test.run(chdir = 'alpha', arguments = '.') - -# Second build, everything is a cache hit -test.run(chdir = 'beta', arguments = '.') - -# Since we just built 'beta', it ought to be up to date. -test.run(chdir = 'beta', arguments = '. -q') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that targets retrieved from CacheDir() are reported as +up-to-date by the -q option. + +Thanks to dvitek for the test case. +""" + +# Demonstrate a regression between 0.96.1 and 0.96.93. +# +# SCons would incorrectly believe files are stale if they were retrieved +# from the cache in a previous invocation. +# +# What this script does: +# 1. Set up two identical C project directories called 'alpha' and +# 'beta', which use the same cache +# 2. Invoke scons on 'alpha' +# 3. Invoke scons on 'beta', which successfully draws output +# files from the cache +# 4. Invoke scons again, asserting (with -q) that 'beta' is up to date +# +# Step 4 failed in 0.96.93. In practice, this problem would lead to +# lots of unecessary fetches from the cache during incremental +# builds (because they behaved like non-incremental builds). + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'alpha', 'beta') + +foo_c = """ +int main(void){ return 0; } +""" + +sconstruct = """ +CacheDir(r'%s') +Program('foo', 'foo.c') +""" % test.workpath('cache') + +test.write('alpha/foo.c', foo_c) +test.write('alpha/SConstruct', sconstruct) + +test.write('beta/foo.c', foo_c) +test.write('beta/SConstruct', sconstruct) + +# First build, populates the cache +test.run(chdir = 'alpha', arguments = '.') + +# Second build, everything is a cache hit +test.run(chdir = 'beta', arguments = '.') + +# Since we just built 'beta', it ought to be up to date. +test.run(chdir = 'beta', arguments = '. -q') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/value_dependencies.py b/test/CacheDir/value_dependencies.py index 7992bef79a..73ebc60e04 100644 --- a/test/CacheDir/value_dependencies.py +++ b/test/CacheDir/value_dependencies.py @@ -1,52 +1,51 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that bwuilds with caching work for an action with a Value as a child -in a variety of cases. Specifically: - -1. A source file that depends on a Value. -2. A source directory that depends on a Value. -3. A scanner that returns a Value and a directory that depends on a Value. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.dir_fixture('value_dependencies') -test.subdir('cache') - -# First build, populates the cache -test.run(arguments='.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that bwuilds with caching work for an action with a Value as a child +in a variety of cases. Specifically: + +1. A source file that depends on a Value. +2. A source directory that depends on a Value. +3. A scanner that returns a Value and a directory that depends on a Value. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.dir_fixture('value_dependencies') +test.subdir('cache') + +# First build, populates the cache +test.run(arguments='.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/value_dependencies/SConstruct b/test/CacheDir/value_dependencies/SConstruct index 55c22ffca3..9e971c64d3 100644 --- a/test/CacheDir/value_dependencies/SConstruct +++ b/test/CacheDir/value_dependencies/SConstruct @@ -20,7 +20,7 @@ scanner = Scanner(function=scan, node_class=SCons.Node.Node) builder = Builder(action=b, source_scanner=scanner) DefaultEnvironment(tools=[]) -env = Environment() +env = Environment(tools=[]) env.Append(BUILDERS={'B': builder}) # Create a node and a directory that each depend on an instance of From 7d33a9397580555eeb97d4587d7f7d749e326bc0 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 17:02:01 -0700 Subject: [PATCH 047/386] Setting tools=[], etc to speed up tests --- test/CC/SHCCFLAGS.py | 7 +- test/Clang/clang_default_environment.py | 125 ++++--- test/Clang/clang_specific_environment.py | 121 ++++--- test/Clang/clang_static_library.py | 137 ++++---- test/Clang/clangxx_default_environment.py | 125 ++++--- test/Clang/clangxx_shared_library.py | 155 +++++---- test/Clang/clangxx_specific_environment.py | 121 ++++--- test/Clang/clangxx_static_library.py | 137 ++++---- test/Clean/Option.py | 7 +- test/Clean/basic.py | 371 ++++++++++----------- test/Clean/function.py | 239 +++++++------ test/Clean/mkfifo.py | 163 +++++---- test/Clean/symlinks.py | 127 ++++--- test/Climb/U-Default-dir.py | 96 +++--- test/Climb/U-Default-no-target.py | 100 +++--- test/Climb/U-no-Default.py | 95 +++--- test/Climb/explicit-parent--D.py | 159 +++++---- test/Climb/explicit-parent--U.py | 143 ++++---- test/Climb/explicit-parent-u.py | 155 ++++----- test/Climb/filename--U.py | 155 +++++---- test/Climb/filename-u.py | 155 +++++---- test/Climb/option--D.py | 179 +++++----- test/Climb/option--U.py | 289 ++++++++-------- test/Climb/option-u.py | 297 ++++++++--------- test/Configure/Action-error.py | 109 +++--- test/Configure/basic.py | 181 +++++----- 26 files changed, 1965 insertions(+), 1983 deletions(-) diff --git a/test/CC/SHCCFLAGS.py b/test/CC/SHCCFLAGS.py index 71ed1c0f98..a923f90010 100644 --- a/test/CC/SHCCFLAGS.py +++ b/test/CC/SHCCFLAGS.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__" import sys import TestSCons diff --git a/test/Clang/clang_default_environment.py b/test/Clang/clang_default_environment.py index 5ebd839e46..dc75bd4898 100644 --- a/test/Clang/clang_default_environment.py +++ b/test/Clang/clang_default_environment.py @@ -1,63 +1,62 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang', skipping test.\n") - -## This will likely NOT use clang - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clang','link']) -env.Program('foo.c') -""") - -test.write('foo.c', """\ -#include -int main(int argc, char ** argv) { - printf("Hello!"); - return 0; -} -""") - -test.run() - -test.run(program=test.workpath('foo'+_exe)) - -test.fail_test(not test.stdout() == 'Hello!') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang', skipping test.\n") + +## This will likely NOT use clang + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang','link']) +env.Program('foo.c') +""") + +test.write('foo.c', """\ +#include +int main(int argc, char ** argv) { + printf("Hello!"); + return 0; +} +""") + +test.run() + +test.run(program=test.workpath('foo'+_exe)) + +test.fail_test(not test.stdout() == 'Hello!') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clang_specific_environment.py b/test/Clang/clang_specific_environment.py index 81991f0cbf..d0e145d206 100644 --- a/test/Clang/clang_specific_environment.py +++ b/test/Clang/clang_specific_environment.py @@ -1,61 +1,60 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang', skipping test.\n") - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clang', 'link']) -env.Program('foo.c') -""") - -test.write('foo.c', """\ -#include -int main(int argc, char ** argv) { - printf("Hello!"); - return 0; -} -""") - -test.run() - -test.run(program=test.workpath('foo'+_exe)) - -test.fail_test(not test.stdout() == 'Hello!') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang', skipping test.\n") + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang', 'link']) +env.Program('foo.c') +""") + +test.write('foo.c', """\ +#include +int main(int argc, char ** argv) { + printf("Hello!"); + return 0; +} +""") + +test.run() + +test.run(program=test.workpath('foo'+_exe)) + +test.fail_test(not test.stdout() == 'Hello!') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clang_static_library.py b/test/Clang/clang_static_library.py index 7b3f5df088..9d21def92d 100644 --- a/test/Clang/clang_static_library.py +++ b/test/Clang/clang_static_library.py @@ -1,69 +1,68 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons -from TestCmd import IS_WINDOWS - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang', skipping test.\n") - -if IS_WINDOWS: - foo_lib = 'foo.lib' - archiver = 'mslib' - # TODO: other Windows combinations exist (not depending on - # mslib (lib.exe) from MS Build Tools / Visual Studio). - # Expand this if there is demand. -else: - foo_lib = 'libfoo.a' - archiver = 'ar' - - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['mingw','clang', '%s']) -env.StaticLibrary('foo', 'foo.c') -""" % archiver) - -test.write('foo.c', """\ -int bar() { - return 42; -} -""") - -test.run() - -test.must_exist(test.workpath(foo_lib)) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons +from TestCmd import IS_WINDOWS + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang', skipping test.\n") + +if IS_WINDOWS: + foo_lib = 'foo.lib' + archiver = 'mslib' + # TODO: other Windows combinations exist (not depending on + # mslib (lib.exe) from MS Build Tools / Visual Studio). + # Expand this if there is demand. +else: + foo_lib = 'libfoo.a' + archiver = 'ar' + + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang', '%s']) +env.StaticLibrary('foo', 'foo.c') +""" % archiver) + +test.write('foo.c', """\ +int bar() { + return 42; +} +""") + +test.run() + +test.must_exist(test.workpath(foo_lib)) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clangxx_default_environment.py b/test/Clang/clangxx_default_environment.py index 5e46404f46..82d99aa24f 100644 --- a/test/Clang/clangxx_default_environment.py +++ b/test/Clang/clangxx_default_environment.py @@ -1,63 +1,62 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang++', skipping test.\n") - -## This will likely NOT use clang++. - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clangxx','link']) -env.Program('foo.cpp') -""") - -test.write('foo.cpp', """\ -#include -int main(int argc, char ** argv) { - std::cout << "Hello!" << std::endl; - return 0; -} -""") - -test.run() - -test.run(program=test.workpath('foo'+_exe)) - -test.fail_test(not test.stdout() == 'Hello!\n') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang++', skipping test.\n") + +## This will likely NOT use clang++. + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clangxx','link']) +env.Program('foo.cpp') +""") + +test.write('foo.cpp', """\ +#include +int main(int argc, char ** argv) { + std::cout << "Hello!" << std::endl; + return 0; +} +""") + +test.run() + +test.run(program=test.workpath('foo'+_exe)) + +test.fail_test(not test.stdout() == 'Hello!\n') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clangxx_shared_library.py b/test/Clang/clangxx_shared_library.py index a16be6beaf..dbc4831b9b 100644 --- a/test/Clang/clangxx_shared_library.py +++ b/test/Clang/clangxx_shared_library.py @@ -1,78 +1,77 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -from SCons.Environment import Base - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang++', skipping test.\n") - -platform = Base()['PLATFORM'] -if platform == 'posix': - filename_options = ['foo.os'] - libraryname = 'libfoo.so' -elif platform == 'darwin': - filename_options = ['foo.os'] - libraryname = 'libfoo.dylib' -elif platform == 'win32': - filename_options = ['foo.obj','foo.os'] - libraryname = 'foo.dll' -elif platform == 'sunos': - filename_options = ['foo.pic.o'] - libraryname = 'libfoo.so' -else: - test.fail_test() - - - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clang++', 'link']) -env.SharedLibrary('foo', 'foo.cpp') -""") - -test.write('foo.cpp', """\ -int bar() { - return 42; -} -""") - -test.run() - -test.must_exist_one_of([test.workpath(f) for f in filename_options]) -test.must_exist(test.workpath(libraryname)) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +from SCons.Environment import Base + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang++', skipping test.\n") + +platform = Base()['PLATFORM'] +if platform == 'posix': + filename_options = ['foo.os'] + libraryname = 'libfoo.so' +elif platform == 'darwin': + filename_options = ['foo.os'] + libraryname = 'libfoo.dylib' +elif platform == 'win32': + filename_options = ['foo.obj','foo.os'] + libraryname = 'foo.dll' +elif platform == 'sunos': + filename_options = ['foo.pic.o'] + libraryname = 'libfoo.so' +else: + test.fail_test() + + + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang++', 'link']) +env.SharedLibrary('foo', 'foo.cpp') +""") + +test.write('foo.cpp', """\ +int bar() { + return 42; +} +""") + +test.run() + +test.must_exist_one_of([test.workpath(f) for f in filename_options]) +test.must_exist(test.workpath(libraryname)) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clangxx_specific_environment.py b/test/Clang/clangxx_specific_environment.py index 39eaab135f..5ca3c1229e 100644 --- a/test/Clang/clangxx_specific_environment.py +++ b/test/Clang/clangxx_specific_environment.py @@ -1,61 +1,60 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang++', skipping test.\n") - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clang++', 'link']) -env.Program('foo.cpp') -""") - -test.write('foo.cpp', """\ -#include -int main(int argc, char ** argv) { - std::cout << "Hello!" << std::endl; - return 0; -} -""") - -test.run() - -test.run(program=test.workpath('foo'+_exe)) - -test.fail_test(not test.stdout() == 'Hello!\n') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang++', skipping test.\n") + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang++', 'link']) +env.Program('foo.cpp') +""") + +test.write('foo.cpp', """\ +#include +int main(int argc, char ** argv) { + std::cout << "Hello!" << std::endl; + return 0; +} +""") + +test.run() + +test.run(program=test.workpath('foo'+_exe)) + +test.fail_test(not test.stdout() == 'Hello!\n') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clangxx_static_library.py b/test/Clang/clangxx_static_library.py index 3ced7fce4c..62711115ef 100644 --- a/test/Clang/clangxx_static_library.py +++ b/test/Clang/clangxx_static_library.py @@ -1,69 +1,68 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons -from TestCmd import IS_WINDOWS - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang++', skipping test.\n") - -if IS_WINDOWS: - foo_lib = 'foo.lib' - archiver = 'mslib' - # TODO: other Windows combinations exist (not depending on - # mslib (lib.exe) from MS Build Tools / Visual Studio). - # Expand this if there is demand. -else: - foo_lib = 'libfoo.a' - archiver = 'ar' - - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['mingw','clang++', '%s']) -env.StaticLibrary('foo', 'foo.cpp') -""" % archiver) - -test.write('foo.cpp', """\ -int bar() { - return 42; -} -""") - -test.run() - -test.must_exist(test.workpath(foo_lib)) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons +from TestCmd import IS_WINDOWS + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang++', skipping test.\n") + +if IS_WINDOWS: + foo_lib = 'foo.lib' + archiver = 'mslib' + # TODO: other Windows combinations exist (not depending on + # mslib (lib.exe) from MS Build Tools / Visual Studio). + # Expand this if there is demand. +else: + foo_lib = 'libfoo.a' + archiver = 'ar' + + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang++', '%s']) +env.StaticLibrary('foo', 'foo.cpp') +""" % archiver) + +test.write('foo.cpp', """\ +int bar() { + return 42; +} +""") + +test.run() + +test.must_exist(test.workpath(foo_lib)) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clean/Option.py b/test/Clean/Option.py index c63dff733e..8616092ccf 100644 --- a/test/Clean/Option.py +++ b/test/Clean/Option.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 {Set,Get}Option('clean') works correctly to control diff --git a/test/Clean/basic.py b/test/Clean/basic.py index fbff9b1836..1fc90efdde 100644 --- a/test/Clean/basic.py +++ b/test/Clean/basic.py @@ -1,186 +1,185 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test various basic uses of the -c (clean) option. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.write('build.py', r""" -import sys -with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: - ofp.write(ifp.read()) -""") - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') -env = Environment(tools=[], BUILDERS = { 'B' : B }) -env.B(target = 'foo1.out', source = 'foo1.in') -env.B(target = 'foo2.out', source = 'foo2.xxx') -env.B(target = 'foo2.xxx', source = 'foo2.in') -env.B(target = 'foo3.out', source = 'foo3.in') -env.B(target = 'foo4.out', source = 'foo4.in') -env.NoClean('foo4.out') -import os -import sys -if hasattr(os, 'symlink') and sys.platform !='win32': - def symlink1(env, target, source): - # symlink to a file that exists - os.symlink(str(source[0]), str(target[0])) - env.Command(target = 'symlink1', source = 'foo1.in', action = symlink1) - def symlink2(env, target, source): - # force symlink to a file that doesn't exist - os.symlink('does_not_exist', str(target[0])) - env.Command(target = 'symlink2', source = 'foo1.in', action = symlink2) -# Test handling of Builder calls that have multiple targets. -env.Command(['touch1.out', 'touch2.out'], - [], - [Touch('${TARGETS[0]}'), Touch('${TARGETS[1]}')]) -""" % locals()) - -test.write('foo1.in', "foo1.in\n") - -test.write('foo2.in', "foo2.in\n") - -test.write('foo3.in', "foo3.in\n") - -test.write('foo4.in', "foo4.in\n") - -test.run(arguments = 'foo1.out foo2.out foo3.out foo4.out') - -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo4.out'), "foo4.in\n") - -test.run(arguments = '-c foo1.out', - stdout = test.wrap_stdout("Removed foo1.out\n", cleaning=1)) - -test.must_not_exist(test.workpath('foo1.out')) -test.must_exist(test.workpath('foo2.xxx')) -test.must_exist(test.workpath('foo2.out')) -test.must_exist(test.workpath('foo3.out')) -test.must_exist(test.workpath('foo4.out')) - -test.run(arguments = '--clean foo2.out foo2.xxx', - stdout = test.wrap_stdout("Removed foo2.xxx\nRemoved foo2.out\n", - cleaning=1)) - -test.must_not_exist(test.workpath('foo1.out')) -test.must_not_exist(test.workpath('foo2.xxx')) -test.must_not_exist(test.workpath('foo2.out')) -test.must_exist(test.workpath('foo3.out')) -test.must_exist(test.workpath('foo4.out')) - -test.run(arguments = '--remove foo3.out', - stdout = test.wrap_stdout("Removed foo3.out\n", cleaning=1)) - -test.must_not_exist(test.workpath('foo1.out')) -test.must_not_exist(test.workpath('foo2.xxx')) -test.must_not_exist(test.workpath('foo2.out')) -test.must_not_exist(test.workpath('foo3.out')) -test.must_exist(test.workpath('foo4.out')) - -test.run(arguments = '.') - -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo4.out'), "foo4.in\n") -test.must_exist(test.workpath('touch1.out')) -test.must_exist(test.workpath('touch2.out')) - -if test.platform_has_symlink(): - test.fail_test(not os.path.islink(test.workpath('symlink1'))) - test.fail_test(not os.path.islink(test.workpath('symlink2'))) - -test.run(arguments = '-c foo2.xxx', - stdout = test.wrap_stdout("Removed foo2.xxx\n", cleaning=1)) - -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_not_exist(test.workpath('foo2.xxx')) -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo4.out'), "foo4.in\n") -test.must_exist(test.workpath('touch1.out')) -test.must_exist(test.workpath('touch2.out')) - -test.run(arguments = '-c .') - -test.must_not_exist(test.workpath('foo1.out')) -test.must_not_exist(test.workpath('foo2.out')) -test.must_not_exist(test.workpath('foo3.out')) -test.must_exist(test.workpath('foo4.out')) -test.must_not_exist(test.workpath('touch1.out')) -test.must_not_exist(test.workpath('touch2.out')) - -if test.platform_has_symlink(): - test.fail_test(os.path.islink(test.workpath('symlink1'))) - test.fail_test(os.path.islink(test.workpath('symlink2'))) - -args = 'foo1.out foo2.out foo3.out touch1.out' - -expect = test.wrap_stdout("""\ -Removed foo1.out -Removed foo2.xxx -Removed foo2.out -Removed foo3.out -Removed touch1.out -Removed touch2.out -""", cleaning=1) - -test.run(arguments = args) - -test.run(arguments = '-c -n ' + args, stdout = expect) - -test.run(arguments = '-n -c ' + args, stdout = expect) - -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo4.out'), "foo4.in\n") -test.must_exist(test.workpath('touch1.out')) -test.must_exist(test.workpath('touch2.out')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test various basic uses of the -c (clean) option. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('build.py', r""" +import sys +with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: + ofp.write(ifp.read()) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') +env = Environment(tools=[], BUILDERS = { 'B' : B }) +env.B(target = 'foo1.out', source = 'foo1.in') +env.B(target = 'foo2.out', source = 'foo2.xxx') +env.B(target = 'foo2.xxx', source = 'foo2.in') +env.B(target = 'foo3.out', source = 'foo3.in') +env.B(target = 'foo4.out', source = 'foo4.in') +env.NoClean('foo4.out') +import os +import sys +if hasattr(os, 'symlink') and sys.platform !='win32': + def symlink1(env, target, source): + # symlink to a file that exists + os.symlink(str(source[0]), str(target[0])) + env.Command(target = 'symlink1', source = 'foo1.in', action = symlink1) + def symlink2(env, target, source): + # force symlink to a file that doesn't exist + os.symlink('does_not_exist', str(target[0])) + env.Command(target = 'symlink2', source = 'foo1.in', action = symlink2) +# Test handling of Builder calls that have multiple targets. +env.Command(['touch1.out', 'touch2.out'], + [], + [Touch('${TARGETS[0]}'), Touch('${TARGETS[1]}')]) +""" % locals()) + +test.write('foo1.in', "foo1.in\n") + +test.write('foo2.in', "foo2.in\n") + +test.write('foo3.in', "foo3.in\n") + +test.write('foo4.in', "foo4.in\n") + +test.run(arguments = 'foo1.out foo2.out foo3.out foo4.out') + +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo4.out'), "foo4.in\n") + +test.run(arguments = '-c foo1.out', + stdout = test.wrap_stdout("Removed foo1.out\n", cleaning=1)) + +test.must_not_exist(test.workpath('foo1.out')) +test.must_exist(test.workpath('foo2.xxx')) +test.must_exist(test.workpath('foo2.out')) +test.must_exist(test.workpath('foo3.out')) +test.must_exist(test.workpath('foo4.out')) + +test.run(arguments = '--clean foo2.out foo2.xxx', + stdout = test.wrap_stdout("Removed foo2.xxx\nRemoved foo2.out\n", + cleaning=1)) + +test.must_not_exist(test.workpath('foo1.out')) +test.must_not_exist(test.workpath('foo2.xxx')) +test.must_not_exist(test.workpath('foo2.out')) +test.must_exist(test.workpath('foo3.out')) +test.must_exist(test.workpath('foo4.out')) + +test.run(arguments = '--remove foo3.out', + stdout = test.wrap_stdout("Removed foo3.out\n", cleaning=1)) + +test.must_not_exist(test.workpath('foo1.out')) +test.must_not_exist(test.workpath('foo2.xxx')) +test.must_not_exist(test.workpath('foo2.out')) +test.must_not_exist(test.workpath('foo3.out')) +test.must_exist(test.workpath('foo4.out')) + +test.run(arguments = '.') + +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo4.out'), "foo4.in\n") +test.must_exist(test.workpath('touch1.out')) +test.must_exist(test.workpath('touch2.out')) + +if test.platform_has_symlink(): + test.fail_test(not os.path.islink(test.workpath('symlink1'))) + test.fail_test(not os.path.islink(test.workpath('symlink2'))) + +test.run(arguments = '-c foo2.xxx', + stdout = test.wrap_stdout("Removed foo2.xxx\n", cleaning=1)) + +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_not_exist(test.workpath('foo2.xxx')) +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo4.out'), "foo4.in\n") +test.must_exist(test.workpath('touch1.out')) +test.must_exist(test.workpath('touch2.out')) + +test.run(arguments = '-c .') + +test.must_not_exist(test.workpath('foo1.out')) +test.must_not_exist(test.workpath('foo2.out')) +test.must_not_exist(test.workpath('foo3.out')) +test.must_exist(test.workpath('foo4.out')) +test.must_not_exist(test.workpath('touch1.out')) +test.must_not_exist(test.workpath('touch2.out')) + +if test.platform_has_symlink(): + test.fail_test(os.path.islink(test.workpath('symlink1'))) + test.fail_test(os.path.islink(test.workpath('symlink2'))) + +args = 'foo1.out foo2.out foo3.out touch1.out' + +expect = test.wrap_stdout("""\ +Removed foo1.out +Removed foo2.xxx +Removed foo2.out +Removed foo3.out +Removed touch1.out +Removed touch2.out +""", cleaning=1) + +test.run(arguments = args) + +test.run(arguments = '-c -n ' + args, stdout = expect) + +test.run(arguments = '-n -c ' + args, stdout = expect) + +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo4.out'), "foo4.in\n") +test.must_exist(test.workpath('touch1.out')) +test.must_exist(test.workpath('touch2.out')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clean/function.py b/test/Clean/function.py index 45c9753664..b73fe6757b 100644 --- a/test/Clean/function.py +++ b/test/Clean/function.py @@ -1,120 +1,119 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify use of the Clean() function. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.write('build.py', r""" -import sys -with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: - ofp.write(ifp.read()) -""") - -test.subdir('subd') - -subd_SConscript = os.path.join('subd', 'SConscript') -subd_foon_in = os.path.join('subd', 'foon.in') -subd_foox_in = os.path.join('subd', 'foox.in') - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') -env = Environment(tools=[], BUILDERS = { 'B' : B }, FOO = 'foo2') -env.B(target = 'foo1.out', source = 'foo1.in') -env.B(target = 'foo2.out', source = 'foo2.xxx') -foo2_xxx = env.B(target = 'foo2.xxx', source = 'foo2.in') -env.B(target = 'foo3.out', source = 'foo3.in') -SConscript('subd/SConscript') -Clean(foo2_xxx, ['aux1.x']) -env.Clean(['${FOO}.xxx'], ['aux2.x']) -Clean('.', ['subd']) -""" % locals()) - -test.write(['subd', 'SConscript'], """ -Clean('.', 'foox.in') -""") - -test.write('foo1.in', "foo1.in\n") -test.write('foo2.in', "foo2.in\n") -test.write('foo3.in', "foo3.in\n") -test.write(['subd', 'foon.in'], "foon.in\n") -test.write(['subd', 'foox.in'], "foox.in\n") -test.write('aux1.x', "aux1.x\n") -test.write('aux2.x', "aux2.x\n") - -test.run() - -expect = test.wrap_stdout("""Removed foo2.xxx -Removed aux1.x -Removed aux2.x -""", cleaning=1) -test.run(arguments = '-c foo2.xxx', stdout=expect) -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_not_exist(test.workpath('foo2.xxx')) -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") - -expect = test.wrap_stdout("Removed %s\n" % subd_foox_in, cleaning = 1) -test.run(arguments = '-c subd', stdout=expect) -test.must_not_exist(test.workpath('foox.in')) - -expect = test.wrap_stdout("""Removed foo1.out -Removed foo2.xxx -Removed foo2.out -Removed foo3.out -Removed %(subd_SConscript)s -Removed %(subd_foon_in)s -Removed directory subd -""" % locals(), cleaning = 1) -test.run(arguments = '-c -n .', stdout=expect) - -expect = test.wrap_stdout("""Removed foo1.out -Removed foo2.out -Removed foo3.out -Removed %(subd_SConscript)s -Removed %(subd_foon_in)s -Removed directory subd -""" % locals(), cleaning = 1) -test.run(arguments = '-c .', stdout=expect) -test.must_not_exist(test.workpath('subdir', 'foon.in')) -test.must_not_exist(test.workpath('subdir')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of the Clean() function. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('build.py', r""" +import sys +with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: + ofp.write(ifp.read()) +""") + +test.subdir('subd') + +subd_SConscript = os.path.join('subd', 'SConscript') +subd_foon_in = os.path.join('subd', 'foon.in') +subd_foox_in = os.path.join('subd', 'foox.in') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') +env = Environment(tools=[], BUILDERS = { 'B' : B }, FOO = 'foo2') +env.B(target = 'foo1.out', source = 'foo1.in') +env.B(target = 'foo2.out', source = 'foo2.xxx') +foo2_xxx = env.B(target = 'foo2.xxx', source = 'foo2.in') +env.B(target = 'foo3.out', source = 'foo3.in') +SConscript('subd/SConscript') +Clean(foo2_xxx, ['aux1.x']) +env.Clean(['${FOO}.xxx'], ['aux2.x']) +Clean('.', ['subd']) +""" % locals()) + +test.write(['subd', 'SConscript'], """ +Clean('.', 'foox.in') +""") + +test.write('foo1.in', "foo1.in\n") +test.write('foo2.in', "foo2.in\n") +test.write('foo3.in', "foo3.in\n") +test.write(['subd', 'foon.in'], "foon.in\n") +test.write(['subd', 'foox.in'], "foox.in\n") +test.write('aux1.x', "aux1.x\n") +test.write('aux2.x', "aux2.x\n") + +test.run() + +expect = test.wrap_stdout("""Removed foo2.xxx +Removed aux1.x +Removed aux2.x +""", cleaning=1) +test.run(arguments = '-c foo2.xxx', stdout=expect) +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_not_exist(test.workpath('foo2.xxx')) +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") + +expect = test.wrap_stdout("Removed %s\n" % subd_foox_in, cleaning = 1) +test.run(arguments = '-c subd', stdout=expect) +test.must_not_exist(test.workpath('foox.in')) + +expect = test.wrap_stdout("""Removed foo1.out +Removed foo2.xxx +Removed foo2.out +Removed foo3.out +Removed %(subd_SConscript)s +Removed %(subd_foon_in)s +Removed directory subd +""" % locals(), cleaning = 1) +test.run(arguments = '-c -n .', stdout=expect) + +expect = test.wrap_stdout("""Removed foo1.out +Removed foo2.out +Removed foo3.out +Removed %(subd_SConscript)s +Removed %(subd_foon_in)s +Removed directory subd +""" % locals(), cleaning = 1) +test.run(arguments = '-c .', stdout=expect) +test.must_not_exist(test.workpath('subdir', 'foon.in')) +test.must_not_exist(test.workpath('subdir')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clean/mkfifo.py b/test/Clean/mkfifo.py index 01e4d98958..cfe20c6b6b 100644 --- a/test/Clean/mkfifo.py +++ b/test/Clean/mkfifo.py @@ -1,82 +1,81 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that SCons reports an error when cleaning up a target directory -containing a named pipe created with o.mkfifo(). -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -if not hasattr(os, 'mkfifo'): - test.skip_test('No os.mkfifo() function; skipping test\n') - -test_dir_name = 'testdir' -pipe_path = os.path.join(test_dir_name, 'namedpipe') - -test.write('SConstruct', """\ -Execute(Mkdir("{0}")) -dir = Dir("{0}") -Clean(dir, '{0}') -""".format(test_dir_name)) - -test.run(arguments='-Q -q', stdout='Mkdir("{0}")\n'.format(test_dir_name)) - -os.mkfifo(pipe_path) - -test.must_exist(test.workpath(pipe_path)) - -expect1 = """\ -Mkdir("{0}") -Path '{1}' exists but isn't a file or directory. -scons: Could not remove '{0}': Directory not empty -""".format(test_dir_name, pipe_path) - -expect2 = """\ -Mkdir("{0}") -Path '{1}' exists but isn't a file or directory. -scons: Could not remove '{0}': File exists -""".format(test_dir_name, pipe_path) - -test.run(arguments='-c -Q -q') - -test.must_exist(test.workpath(pipe_path)) - -if test.stdout() not in [expect1, expect2]: - test.diff(expect1, test.stdout(), 'STDOUT ') - test.fail_test() - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that SCons reports an error when cleaning up a target directory +containing a named pipe created with o.mkfifo(). +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +if not hasattr(os, 'mkfifo'): + test.skip_test('No os.mkfifo() function; skipping test\n') + +test_dir_name = 'testdir' +pipe_path = os.path.join(test_dir_name, 'namedpipe') + +test.write('SConstruct', """\ +Execute(Mkdir("{0}")) +dir = Dir("{0}") +Clean(dir, '{0}') +""".format(test_dir_name)) + +test.run(arguments='-Q -q', stdout='Mkdir("{0}")\n'.format(test_dir_name)) + +os.mkfifo(pipe_path) + +test.must_exist(test.workpath(pipe_path)) + +expect1 = """\ +Mkdir("{0}") +Path '{1}' exists but isn't a file or directory. +scons: Could not remove '{0}': Directory not empty +""".format(test_dir_name, pipe_path) + +expect2 = """\ +Mkdir("{0}") +Path '{1}' exists but isn't a file or directory. +scons: Could not remove '{0}': File exists +""".format(test_dir_name, pipe_path) + +test.run(arguments='-c -Q -q') + +test.must_exist(test.workpath(pipe_path)) + +if test.stdout() not in [expect1, expect2]: + test.diff(expect1, test.stdout(), 'STDOUT ') + test.fail_test() + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clean/symlinks.py b/test/Clean/symlinks.py index c782860a76..2f5a1f8602 100644 --- a/test/Clean/symlinks.py +++ b/test/Clean/symlinks.py @@ -1,64 +1,63 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify correct deletion of broken symlinks. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -if not test.platform_has_symlink(): - test.skip_test('No os.symlink() function; skipping test\n') - -test.write('SConstruct', """\ -Execute(Mkdir("testdir")) -dir = Dir("testdir") -Clean(dir, 'testdir') -""") - -test.run(arguments = '-Q -q', stdout='Mkdir("testdir")\n') - -os.symlink('testdir/symlinksrc', 'testdir/symlinkdst') - -expect = """\ -Mkdir("testdir") -Removed %s -Removed directory testdir -""" % os.path.join('testdir', 'symlinkdst') - -test.run(arguments = '-c -Q -q', stdout=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 correct deletion of broken symlinks. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +if not test.platform_has_symlink(): + test.skip_test('No os.symlink() function; skipping test\n') + +test.write('SConstruct', """\ +Execute(Mkdir("testdir")) +dir = Dir("testdir") +Clean(dir, 'testdir') +""") + +test.run(arguments = '-Q -q', stdout='Mkdir("testdir")\n') + +os.symlink('testdir/symlinksrc', 'testdir/symlinkdst') + +expect = """\ +Mkdir("testdir") +Removed %s +Removed directory testdir +""" % os.path.join('testdir', 'symlinkdst') + +test.run(arguments = '-c -Q -q', 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/Climb/U-Default-dir.py b/test/Climb/U-Default-dir.py index 323ceb1463..4c0eb6689f 100644 --- a/test/Climb/U-Default-dir.py +++ b/test/Climb/U-Default-dir.py @@ -1,48 +1,48 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Make sure that a Default() directory doesn't cause an exception -when used with the -U option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -Default('.') -""") - -test.run(arguments = '-U') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure that a Default() directory doesn't cause an exception +when used with the -U option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +Default('.') +""") + +test.run(arguments = '-U') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/U-Default-no-target.py b/test/Climb/U-Default-no-target.py index 62f10675df..f580bffb23 100644 --- a/test/Climb/U-Default-no-target.py +++ b/test/Climb/U-Default-no-target.py @@ -1,50 +1,50 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Make sure a Default() target that doesn't exist is handled with -the correct failure when used with the -U option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -Default('not_a_target.in') -""") - -test.run(arguments = '-U', status=2, match=TestSCons.match_re, stderr=\ -r"""scons: \*\*\* Do not know how to make File target `not_a_target.in' \(.*not_a_target.in\). Stop. -""") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure a Default() target that doesn't exist is handled with +the correct failure when used with the -U option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +Default('not_a_target.in') +""") + +test.run(arguments = '-U', status=2, match=TestSCons.match_re, stderr=\ +r"""scons: \*\*\* Do not know how to make File target `not_a_target.in' \(.*not_a_target.in\). Stop. +""") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/U-no-Default.py b/test/Climb/U-no-Default.py index 24ed4bc8af..9cd1ebf157 100644 --- a/test/Climb/U-no-Default.py +++ b/test/Climb/U-no-Default.py @@ -1,48 +1,47 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Make sure no Default() targets in the SConstruct doesn't cause an -exception when used with the -U option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', "\n") - -test.run(arguments = '-U', - stderr = "scons: *** No targets specified and no Default() targets found. Stop.\n", - status = 2) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure no Default() targets in the SConstruct doesn't cause an +exception when used with the -U option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', "\n") + +test.run(arguments = '-U', + stderr = "scons: *** No targets specified and no Default() targets found. Stop.\n", + status = 2) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/explicit-parent--D.py b/test/Climb/explicit-parent--D.py index a1c3aeeeab..51686fe48f 100644 --- a/test/Climb/explicit-parent--D.py +++ b/test/Climb/explicit-parent--D.py @@ -1,80 +1,79 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Make sure explicit targets beginning with ../ get built correctly -by the -D option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir(['subdir']) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: - for src in source: - with open(str(src), 'rb') as ifp: - ofp.write(ifp.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('f1.out', 'f1.in') -f2 = env.Cat('f2.out', 'f2.in') -Default(f2) -SConscript('subdir/SConscript', "env") -""") - -test.write('f1.in', "f1.in\n") -test.write('f2.in', "f2.in\n") - -test.write(['subdir', 'SConscript'], """\ -Import("env") -f3 = env.Cat('f3.out', 'f3.in') -env.Cat('f4.out', 'f4.in') -Default(f3) -""") - -test.write(['subdir', 'f3.in'], "subdir/f3.in\n") -test.write(['subdir', 'f4.in'], "subdir/f4.in\n") - -test.run(chdir = 'subdir', arguments = '-D ../f1.out') - -test.must_exist(test.workpath('f1.out')) -test.must_not_exist(test.workpath('f2.out')) -test.must_not_exist(test.workpath('dir', 'f3.out')) -test.must_not_exist(test.workpath('dir', 'f4.out')) - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure explicit targets beginning with ../ get built correctly +by the -D option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir(['subdir']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, 'wb') as ofp: + for src in source: + with open(str(src), 'rb') as ifp: + ofp.write(ifp.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('f1.out', 'f1.in') +f2 = env.Cat('f2.out', 'f2.in') +Default(f2) +SConscript('subdir/SConscript', "env") +""") + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") + +test.write(['subdir', 'SConscript'], """\ +Import("env") +f3 = env.Cat('f3.out', 'f3.in') +env.Cat('f4.out', 'f4.in') +Default(f3) +""") + +test.write(['subdir', 'f3.in'], "subdir/f3.in\n") +test.write(['subdir', 'f4.in'], "subdir/f4.in\n") + +test.run(chdir = 'subdir', arguments = '-D ../f1.out') + +test.must_exist(test.workpath('f1.out')) +test.must_not_exist(test.workpath('f2.out')) +test.must_not_exist(test.workpath('dir', 'f3.out')) +test.must_not_exist(test.workpath('dir', 'f4.out')) + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/explicit-parent--U.py b/test/Climb/explicit-parent--U.py index 53086328b9..973ce26cc1 100644 --- a/test/Climb/explicit-parent--U.py +++ b/test/Climb/explicit-parent--U.py @@ -1,71 +1,72 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Make sure explicit targets beginning with ../ get built correctly. -by the -U option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('subdir') - -test.write('SConstruct', """\ -def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: - for src in source: - with open(str(src), 'rb') as ifp: - ofp.write(ifp.read()) -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('foo.out', 'foo.in') -SConscript('subdir/SConscript', "env") -""") - -test.write('foo.in', "foo.in\n") - -test.write(['subdir', 'SConscript'], """\ -Import("env") -bar = env.Cat('bar.out', 'bar.in') -Default(bar) -""") - -test.write(['subdir', 'bar.in'], "subdir/bar.in\n") - -test.run(chdir = 'subdir', arguments = '-U ../foo.out') - -test.must_exist(test.workpath('foo.out')) -test.must_not_exist(test.workpath('subdir', 'bar.out')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure explicit targets beginning with ../ get built correctly. +by the -U option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('subdir') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, 'wb') as ofp: + for src in source: + with open(str(src), 'rb') as ifp: + ofp.write(ifp.read()) +env = Environment(tools=[], + BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('foo.out', 'foo.in') +SConscript('subdir/SConscript', "env") +""") + +test.write('foo.in', "foo.in\n") + +test.write(['subdir', 'SConscript'], """\ +Import("env") +bar = env.Cat('bar.out', 'bar.in') +Default(bar) +""") + +test.write(['subdir', 'bar.in'], "subdir/bar.in\n") + +test.run(chdir = 'subdir', arguments = '-U ../foo.out') + +test.must_exist(test.workpath('foo.out')) +test.must_not_exist(test.workpath('subdir', 'bar.out')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/explicit-parent-u.py b/test/Climb/explicit-parent-u.py index 72104441f0..c42e9db2ab 100644 --- a/test/Climb/explicit-parent-u.py +++ b/test/Climb/explicit-parent-u.py @@ -1,77 +1,78 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that the -u option only builds targets at or below -the current directory. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -# Make sure explicit targets beginning with ../ get built. -test.subdir('subdir') - -test.write('SConstruct', """\ -def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: - for src in source: - with open(str(src), 'rb') as ifp: - ofp.write(ifp.read()) -env = Environment(BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('f1.out', 'f1.in') -env.Cat('f2.out', 'f2.in') -SConscript('subdir/SConscript', "env") -""") - -test.write('f1.in', "f1.in\n") -test.write('f2.in', "f2.in\n") - -test.write(['subdir', 'SConscript'], """\ -Import("env") -env.Cat('f3.out', 'f3.in') -env.Cat('f4.out', 'f4.in') -""") - -test.write(['subdir', 'f3.in'], "subdir/f3.in\n") -test.write(['subdir', 'f4.in'], "subdir/f4.in\n") - -test.run(chdir = 'subdir', arguments = '-u ../f2.out') - -test.must_not_exist(test.workpath('f1.out')) -test.must_exist(test.workpath('f2.out')) -test.must_not_exist(test.workpath('dir', 'f3.out')) -test.must_not_exist(test.workpath('dir', 'f4.out')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the -u option only builds targets at or below +the current directory. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +# Make sure explicit targets beginning with ../ get built. +test.subdir('subdir') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, 'wb') as ofp: + for src in source: + with open(str(src), 'rb') as ifp: + ofp.write(ifp.read()) +env = Environment(tools=[], + BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('f1.out', 'f1.in') +env.Cat('f2.out', 'f2.in') +SConscript('subdir/SConscript', "env") +""") + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") + +test.write(['subdir', 'SConscript'], """\ +Import("env") +env.Cat('f3.out', 'f3.in') +env.Cat('f4.out', 'f4.in') +""") + +test.write(['subdir', 'f3.in'], "subdir/f3.in\n") +test.write(['subdir', 'f4.in'], "subdir/f4.in\n") + +test.run(chdir = 'subdir', arguments = '-u ../f2.out') + +test.must_not_exist(test.workpath('f1.out')) +test.must_exist(test.workpath('f2.out')) +test.must_not_exist(test.workpath('dir', 'f3.out')) +test.must_not_exist(test.workpath('dir', 'f4.out')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/filename--U.py b/test/Climb/filename--U.py index 91a83f471a..5242add13e 100644 --- a/test/Climb/filename--U.py +++ b/test/Climb/filename--U.py @@ -1,78 +1,77 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify the ability to use the -U option with the -f option to -specify a different top-level file name. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('subdir', 'other') - -test.write('main.scons', """\ -DefaultEnvironment(tools=[]) -print("main.scons") -SConscript('subdir/sub.scons') -""") - -test.write(['subdir', 'sub.scons'], """\ -print("subdir/sub.scons") -""") - -read_str = """\ -main.scons -subdir/sub.scons -""" - -expect = "scons: Entering directory `%s'\n" % test.workpath() \ - + test.wrap_stdout(read_str = read_str, - build_str = "scons: `subdir' is up to date.\n") - -test.run(chdir='subdir', arguments='-U -f main.scons .', stdout=expect) - - - -expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", - build_str = "scons: `.' is up to date.\n") - -test.run(chdir='other', arguments='-U -f ../subdir/sub.scons .', stdout=expect) - -test.run(chdir='other', - arguments='-U -f %s .' % test.workpath('subdir', 'sub.scons'), - stdout=expect) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the ability to use the -U option with the -f option to +specify a different top-level file name. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('subdir', 'other') + +test.write('main.scons', """\ +DefaultEnvironment(tools=[]) +print("main.scons") +SConscript('subdir/sub.scons') +""") + +test.write(['subdir', 'sub.scons'], """\ +print("subdir/sub.scons") +""") + +read_str = """\ +main.scons +subdir/sub.scons +""" + +expect = "scons: Entering directory `%s'\n" % test.workpath() \ + + test.wrap_stdout(read_str = read_str, + build_str = "scons: `subdir' is up to date.\n") + +test.run(chdir='subdir', arguments='-U -f main.scons .', stdout=expect) + + + +expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", + build_str = "scons: `.' is up to date.\n") + +test.run(chdir='other', arguments='-U -f ../subdir/sub.scons .', stdout=expect) + +test.run(chdir='other', + arguments='-U -f %s .' % test.workpath('subdir', 'sub.scons'), + 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/Climb/filename-u.py b/test/Climb/filename-u.py index 006e53e79c..844f2cda1f 100644 --- a/test/Climb/filename-u.py +++ b/test/Climb/filename-u.py @@ -1,78 +1,77 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify the ability to use the -u option with the -f option to -specify a different top-level file name. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('subdir', 'other') - -test.write('main.scons', """\ -DefaultEnvironment(tools=[]) -print("main.scons") -SConscript('subdir/sub.scons') -""") - -test.write(['subdir', 'sub.scons'], """\ -print("subdir/sub.scons") -""") - -read_str = """\ -main.scons -subdir/sub.scons -""" - -expect = "scons: Entering directory `%s'\n" % test.workpath() \ - + test.wrap_stdout(read_str = read_str, - build_str = "scons: `subdir' is up to date.\n") - -test.run(chdir='subdir', arguments='-u -f main.scons .', stdout=expect) - - - -expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", - build_str = "scons: `.' is up to date.\n") - -test.run(chdir='other', arguments='-u -f ../subdir/sub.scons .', stdout=expect) - -test.run(chdir='other', - arguments='-u -f %s .' % test.workpath('subdir', 'sub.scons'), - stdout=expect) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the ability to use the -u option with the -f option to +specify a different top-level file name. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('subdir', 'other') + +test.write('main.scons', """\ +DefaultEnvironment(tools=[]) +print("main.scons") +SConscript('subdir/sub.scons') +""") + +test.write(['subdir', 'sub.scons'], """\ +print("subdir/sub.scons") +""") + +read_str = """\ +main.scons +subdir/sub.scons +""" + +expect = "scons: Entering directory `%s'\n" % test.workpath() \ + + test.wrap_stdout(read_str = read_str, + build_str = "scons: `subdir' is up to date.\n") + +test.run(chdir='subdir', arguments='-u -f main.scons .', stdout=expect) + + + +expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", + build_str = "scons: `.' is up to date.\n") + +test.run(chdir='other', arguments='-u -f ../subdir/sub.scons .', stdout=expect) + +test.run(chdir='other', + arguments='-u -f %s .' % test.workpath('subdir', 'sub.scons'), + 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/Climb/option--D.py b/test/Climb/option--D.py index 06c5fa87dd..5b66f6865c 100644 --- a/test/Climb/option--D.py +++ b/test/Climb/option--D.py @@ -1,90 +1,89 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.subdir('sub1', 'sub2') - -test.write('build.py', r""" -import sys -with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: - ofp.write(ifp.read()) -""") - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -import SCons.Defaults -B = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES') -env = Environment(tools=[]) -env['BUILDERS']['B'] = B -env.B(target = 'sub1/foo.out', source = 'sub1/foo.in') -Export('env') -SConscript('sub1/SConscript') -SConscript('sub2/SConscript') -""" % locals()) - -test.write(['sub1', 'SConscript'], """ -Import('env') -env.B(target = 'foo.out', source = 'foo.in') -Default('.') -""") - -test.write(['sub1', 'foo.in'], "sub1/foo.in") - -test.write(['sub2', 'SConscript'], """ -Import('env') -env.Alias('bar', env.B(target = 'bar.out', source = 'bar.in')) -Default('.') - -""") - -test.write(['sub2', 'bar.in'], "sub2/bar.in") - -test.run(arguments = '-D', chdir = 'sub1') - -test.must_match(['sub1', 'foo.out'], "sub1/foo.in") -test.must_match(['sub2', 'bar.out'], "sub2/bar.in") - -test.unlink(['sub1', 'foo.out']) -test.unlink(['sub2', 'bar.out']) - -test.run(arguments = '-D bar', chdir = 'sub1') - -test.must_not_exist(test.workpath('sub1', 'foo.out')) -test.must_exist(test.workpath('sub2', 'bar.out')) - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.subdir('sub1', 'sub2') + +test.write('build.py', r""" +import sys +with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: + ofp.write(ifp.read()) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +import SCons.Defaults +B = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES') +env = Environment(tools=[]) +env['BUILDERS']['B'] = B +env.B(target = 'sub1/foo.out', source = 'sub1/foo.in') +Export('env') +SConscript('sub1/SConscript') +SConscript('sub2/SConscript') +""" % locals()) + +test.write(['sub1', 'SConscript'], """ +Import('env') +env.B(target = 'foo.out', source = 'foo.in') +Default('.') +""") + +test.write(['sub1', 'foo.in'], "sub1/foo.in") + +test.write(['sub2', 'SConscript'], """ +Import('env') +env.Alias('bar', env.B(target = 'bar.out', source = 'bar.in')) +Default('.') + +""") + +test.write(['sub2', 'bar.in'], "sub2/bar.in") + +test.run(arguments = '-D', chdir = 'sub1') + +test.must_match(['sub1', 'foo.out'], "sub1/foo.in") +test.must_match(['sub2', 'bar.out'], "sub2/bar.in") + +test.unlink(['sub1', 'foo.out']) +test.unlink(['sub2', 'bar.out']) + +test.run(arguments = '-D bar', chdir = 'sub1') + +test.must_not_exist(test.workpath('sub1', 'foo.out')) +test.must_exist(test.workpath('sub2', 'bar.out')) + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/option--U.py b/test/Climb/option--U.py index 137bb268cc..4bf5b3558e 100644 --- a/test/Climb/option--U.py +++ b/test/Climb/option--U.py @@ -1,145 +1,144 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import sys - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.subdir('sub1', 'sub2', 'sub3') - -test.write('build.py', r""" -import sys -with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: - ofp.write(ifp.read()) -""") - -test.write('SConstruct', r""" -DefaultEnvironment(tools=[]) -import SCons.Defaults -env = Environment(tools=[]) -env['BUILDERS']['B'] = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES', multi=1) -Default(env.B(target = 'sub1/foo.out', source = 'sub1/foo.in')) -Export('env') -SConscript('sub2/SConscript') -Default(env.B(target = 'sub3/baz.out', source = 'sub3/baz.in')) -VariantDir('sub2b', 'sub2') -SConscript('sub2b/SConscript') -Default(env.B(target = 'sub2/xxx.out', source = 'xxx.in')) -SConscript('SConscript') -""" % locals()) - -test.write(['sub2', 'SConscript'], """ -Import('env') -bar = env.B(target = 'bar.out', source = 'bar.in') -Default(bar) -env.Alias('bar', bar) -Default(env.B(target = '../bar.out', source = 'bar.in')) -""") - - -test.write(['sub1', 'foo.in'], "sub1/foo.in\n") -test.write(['sub2', 'bar.in'], "sub2/bar.in\n") -test.write(['sub3', 'baz.in'], "sub3/baz.in\n") -test.write('xxx.in', "xxx.in\n") - -test.write('SConscript', """assert GetLaunchDir() == r'%s'\n"""%test.workpath('sub1')) -test.run(arguments = '-U foo.out', chdir = 'sub1') - -test.must_exist(test.workpath('sub1', 'foo.out')) -test.must_not_exist(test.workpath('sub2', 'bar.out')) -test.must_not_exist(test.workpath('sub2b', 'bar.out')) -test.must_not_exist(test.workpath('sub3', 'baz.out')) -test.must_not_exist(test.workpath('bar.out')) -test.must_not_exist(test.workpath('sub2/xxx.out')) - -test.unlink(['sub1', 'foo.out']) - -test.write('SConscript', """\ -env = Environment(tools=[], ) -assert env.GetLaunchDir() == r'%s' -"""%test.workpath('sub1')) -test.run(arguments = '-U', - chdir = 'sub1', - stderr = "scons: *** No targets specified and no Default() targets found. Stop.\n", - status = 2) -test.must_not_exist(test.workpath('sub1', 'foo.out')) -test.must_not_exist(test.workpath('sub2', 'bar.out')) -test.must_not_exist(test.workpath('sub2b', 'bar.out')) -test.must_not_exist(test.workpath('sub3', 'baz.out')) -test.must_not_exist(test.workpath('bar.out')) -test.must_not_exist(test.workpath('sub2/xxx.out')) - - -if sys.platform == 'win32': - sub2 = 'SUB2' -else: - sub2 = 'sub2' -test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath(sub2)) -test.run(chdir = sub2, arguments = '-U') -test.must_not_exist(test.workpath('sub1', 'foo.out')) -test.must_exist(test.workpath('sub2', 'bar.out')) -test.must_exist(test.workpath('sub2b', 'bar.out')) -test.must_not_exist(test.workpath('sub3', 'baz.out')) -test.must_exist(test.workpath('bar.out')) -test.must_not_exist(test.workpath('sub2/xxx.out')) - -test.unlink(['sub2', 'bar.out']) -test.unlink(['sub2b', 'bar.out']) -test.unlink('bar.out') - -test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath()) -test.run(arguments='-U') -test.must_exist(test.workpath('sub1', 'foo.out')) -test.must_not_exist(test.workpath('sub2', 'bar.out')) -test.must_not_exist(test.workpath('sub2b', 'bar.out')) -test.must_exist(test.workpath('sub3', 'baz.out')) -test.must_not_exist(test.workpath('bar.out')) -test.must_exist(test.workpath('sub2/xxx.out')) - -test.unlink(['sub1', 'foo.out']) -test.unlink(['sub3', 'baz.out']) -test.unlink(['sub2', 'xxx.out']) - -test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath('sub3')) -test.run(chdir = 'sub3', arguments='-U bar') -test.must_not_exist(test.workpath('sub1', 'foo.out')) -test.must_exist(test.workpath('sub2', 'bar.out')) -test.must_exist(test.workpath('sub2b', 'bar.out')) -test.must_not_exist(test.workpath('sub3', 'baz.out')) -test.must_not_exist(test.workpath('bar.out')) -test.must_not_exist(test.workpath('sub2/xxx.out')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import sys + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.subdir('sub1', 'sub2', 'sub3') + +test.write('build.py', r""" +import sys +with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: + ofp.write(ifp.read()) +""") + +test.write('SConstruct', r""" +DefaultEnvironment(tools=[]) +import SCons.Defaults +env = Environment(tools=[]) +env['BUILDERS']['B'] = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES', multi=1) +Default(env.B(target = 'sub1/foo.out', source = 'sub1/foo.in')) +Export('env') +SConscript('sub2/SConscript') +Default(env.B(target = 'sub3/baz.out', source = 'sub3/baz.in')) +VariantDir('sub2b', 'sub2') +SConscript('sub2b/SConscript') +Default(env.B(target = 'sub2/xxx.out', source = 'xxx.in')) +SConscript('SConscript') +""" % locals()) + +test.write(['sub2', 'SConscript'], """ +Import('env') +bar = env.B(target = 'bar.out', source = 'bar.in') +Default(bar) +env.Alias('bar', bar) +Default(env.B(target = '../bar.out', source = 'bar.in')) +""") + + +test.write(['sub1', 'foo.in'], "sub1/foo.in\n") +test.write(['sub2', 'bar.in'], "sub2/bar.in\n") +test.write(['sub3', 'baz.in'], "sub3/baz.in\n") +test.write('xxx.in', "xxx.in\n") + +test.write('SConscript', """assert GetLaunchDir() == r'%s'\n"""%test.workpath('sub1')) +test.run(arguments = '-U foo.out', chdir = 'sub1') + +test.must_exist(test.workpath('sub1', 'foo.out')) +test.must_not_exist(test.workpath('sub2', 'bar.out')) +test.must_not_exist(test.workpath('sub2b', 'bar.out')) +test.must_not_exist(test.workpath('sub3', 'baz.out')) +test.must_not_exist(test.workpath('bar.out')) +test.must_not_exist(test.workpath('sub2/xxx.out')) + +test.unlink(['sub1', 'foo.out']) + +test.write('SConscript', """\ +env = Environment(tools=[], ) +assert env.GetLaunchDir() == r'%s' +"""%test.workpath('sub1')) +test.run(arguments = '-U', + chdir = 'sub1', + stderr = "scons: *** No targets specified and no Default() targets found. Stop.\n", + status = 2) +test.must_not_exist(test.workpath('sub1', 'foo.out')) +test.must_not_exist(test.workpath('sub2', 'bar.out')) +test.must_not_exist(test.workpath('sub2b', 'bar.out')) +test.must_not_exist(test.workpath('sub3', 'baz.out')) +test.must_not_exist(test.workpath('bar.out')) +test.must_not_exist(test.workpath('sub2/xxx.out')) + + +if sys.platform == 'win32': + sub2 = 'SUB2' +else: + sub2 = 'sub2' +test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath(sub2)) +test.run(chdir = sub2, arguments = '-U') +test.must_not_exist(test.workpath('sub1', 'foo.out')) +test.must_exist(test.workpath('sub2', 'bar.out')) +test.must_exist(test.workpath('sub2b', 'bar.out')) +test.must_not_exist(test.workpath('sub3', 'baz.out')) +test.must_exist(test.workpath('bar.out')) +test.must_not_exist(test.workpath('sub2/xxx.out')) + +test.unlink(['sub2', 'bar.out']) +test.unlink(['sub2b', 'bar.out']) +test.unlink('bar.out') + +test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath()) +test.run(arguments='-U') +test.must_exist(test.workpath('sub1', 'foo.out')) +test.must_not_exist(test.workpath('sub2', 'bar.out')) +test.must_not_exist(test.workpath('sub2b', 'bar.out')) +test.must_exist(test.workpath('sub3', 'baz.out')) +test.must_not_exist(test.workpath('bar.out')) +test.must_exist(test.workpath('sub2/xxx.out')) + +test.unlink(['sub1', 'foo.out']) +test.unlink(['sub3', 'baz.out']) +test.unlink(['sub2', 'xxx.out']) + +test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath('sub3')) +test.run(chdir = 'sub3', arguments='-U bar') +test.must_not_exist(test.workpath('sub1', 'foo.out')) +test.must_exist(test.workpath('sub2', 'bar.out')) +test.must_exist(test.workpath('sub2b', 'bar.out')) +test.must_not_exist(test.workpath('sub3', 'baz.out')) +test.must_not_exist(test.workpath('bar.out')) +test.must_not_exist(test.workpath('sub2/xxx.out')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/option-u.py b/test/Climb/option-u.py index 49874f9211..f7c1e9110e 100644 --- a/test/Climb/option-u.py +++ b/test/Climb/option-u.py @@ -1,149 +1,148 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that the -u option only builds targets at or below -the current directory. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('sub1', - 'sub2', ['sub2', 'dir'], - 'sub3', - 'sub4', ['sub4', 'dir']) - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: - for src in source: - with open(str(src), 'rb') as ifp: - ofp.write(ifp.read()) -env = Environment(tools=[]) -env.Append(BUILDERS = {'Cat' : Builder(action=cat)}) -env.Cat(target = 'sub1/f1a.out', source = 'sub1/f1a.in') -env.Cat(target = 'sub1/f1b.out', source = 'sub1/f1b.in') -Export('env') -SConscript('sub2/SConscript') -f3 = env.Cat(target = 'sub3/f3.out', source = 'sub3/f3.in') -env.Alias('my_alias', f3) -VariantDir('build', 'sub4') -SConscript('build/SConscript') -""") - -test.write(['sub2', 'SConscript'], """ -Import('env') -env.Cat(target = 'f2a.out', source = 'f2a.in') -env.Cat(target = 'dir/f2b.out', source = 'dir/f2b.in') -""") - -test.write(['sub4', 'SConscript'], """ -Import('env') -env.Cat(target = 'f4a.out', source = 'f4a.in') -f4b_in = File('dir/f4b.in') -f4b_in.exists() -f4b_in.is_derived() -env.Cat(target = 'dir/f4b.out', source = f4b_in) -""") - -test.write(['sub1', 'f1a.in'], "sub1/f1a.in") -test.write(['sub1', 'f1b.in'], "sub1/f1b.in") -test.write(['sub2', 'f2a.in'], "sub2/f2a.in") -test.write(['sub2', 'dir', 'f2b.in'], "sub2/dir/f2b.in") -test.write(['sub3', 'f3.in'], "sub3/f3.in") -test.write(['sub4', 'f4a.in'], "sub4/f4a.in") -test.write(['sub4', 'dir', 'f4b.in'], "sub4/dir/f4b.in") - -# Verify that we only build the specified local argument. -test.run(chdir = 'sub1', arguments = '-u f1a.out') - -test.must_match(['sub1', 'f1a.out'], "sub1/f1a.in") -test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) -test.must_not_exist(test.workpath('sub2', 'f2a.out')) -test.must_not_exist(test.workpath('sub2', 'dir', 'f2b.out')) -test.must_not_exist(test.workpath('sub3', 'f3.out')) -test.must_not_exist(test.workpath('sub4', 'f4a.out')) -test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) -test.must_not_exist(test.workpath('build', 'f4a.out')) -test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) - -# Verify that we build everything at or below our current directory. -test.run(chdir = 'sub2', arguments = '-u') - -test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) -test.must_match(['sub2', 'f2a.out'], "sub2/f2a.in") -test.must_match(['sub2', 'dir', 'f2b.out'], "sub2/dir/f2b.in") -test.must_not_exist(test.workpath('sub3', 'f3.out')) -test.must_not_exist(test.workpath('sub4', 'f4a.out')) -test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) -test.must_not_exist(test.workpath('build', 'f4a.out')) -test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) - -# Verify that we build a specified alias, regardless of where. -test.run(chdir = 'sub2', arguments = '-u my_alias') - -test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) -test.must_match(['sub3', 'f3.out'], "sub3/f3.in") -test.must_not_exist(test.workpath('sub4', 'f4a.out')) -test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) -test.must_not_exist(test.workpath('build', 'f4a.out')) -test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) - -# Verify that we build things in a linked VariantDir. -f4a_in = os.path.join('build', 'f4a.in') -f4a_out = os.path.join('build', 'f4a.out') -f4b_in = os.path.join('build', 'dir', 'f4b.in') -f4b_out = os.path.join('build', 'dir', 'f4b.out') -test.run(chdir = 'sub4', - arguments = '-u', - stdout = "scons: Entering directory `%s'\n" % test.workpath() + \ - test.wrap_stdout("""\ -scons: building associated VariantDir targets: build -cat(["%s"], ["%s"]) -cat(["%s"], ["%s"]) -scons: `sub4' is up to date. -""" % (f4b_out, f4b_in, f4a_out, f4a_in))) - -test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) -test.must_not_exist(test.workpath('sub4', 'f4a.out')) -test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) -test.must_match(['build', 'f4a.out'], "sub4/f4a.in") -test.must_match(['build', 'dir', 'f4b.out'], "sub4/dir/f4b.in") - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the -u option only builds targets at or below +the current directory. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('sub1', + 'sub2', ['sub2', 'dir'], + 'sub3', + 'sub4', ['sub4', 'dir']) + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, 'wb') as ofp: + for src in source: + with open(str(src), 'rb') as ifp: + ofp.write(ifp.read()) +env = Environment(tools=[]) +env.Append(BUILDERS = {'Cat' : Builder(action=cat)}) +env.Cat(target = 'sub1/f1a.out', source = 'sub1/f1a.in') +env.Cat(target = 'sub1/f1b.out', source = 'sub1/f1b.in') +Export('env') +SConscript('sub2/SConscript') +f3 = env.Cat(target = 'sub3/f3.out', source = 'sub3/f3.in') +env.Alias('my_alias', f3) +VariantDir('build', 'sub4') +SConscript('build/SConscript') +""") + +test.write(['sub2', 'SConscript'], """ +Import('env') +env.Cat(target = 'f2a.out', source = 'f2a.in') +env.Cat(target = 'dir/f2b.out', source = 'dir/f2b.in') +""") + +test.write(['sub4', 'SConscript'], """ +Import('env') +env.Cat(target = 'f4a.out', source = 'f4a.in') +f4b_in = File('dir/f4b.in') +f4b_in.exists() +f4b_in.is_derived() +env.Cat(target = 'dir/f4b.out', source = f4b_in) +""") + +test.write(['sub1', 'f1a.in'], "sub1/f1a.in") +test.write(['sub1', 'f1b.in'], "sub1/f1b.in") +test.write(['sub2', 'f2a.in'], "sub2/f2a.in") +test.write(['sub2', 'dir', 'f2b.in'], "sub2/dir/f2b.in") +test.write(['sub3', 'f3.in'], "sub3/f3.in") +test.write(['sub4', 'f4a.in'], "sub4/f4a.in") +test.write(['sub4', 'dir', 'f4b.in'], "sub4/dir/f4b.in") + +# Verify that we only build the specified local argument. +test.run(chdir = 'sub1', arguments = '-u f1a.out') + +test.must_match(['sub1', 'f1a.out'], "sub1/f1a.in") +test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) +test.must_not_exist(test.workpath('sub2', 'f2a.out')) +test.must_not_exist(test.workpath('sub2', 'dir', 'f2b.out')) +test.must_not_exist(test.workpath('sub3', 'f3.out')) +test.must_not_exist(test.workpath('sub4', 'f4a.out')) +test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) +test.must_not_exist(test.workpath('build', 'f4a.out')) +test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) + +# Verify that we build everything at or below our current directory. +test.run(chdir = 'sub2', arguments = '-u') + +test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) +test.must_match(['sub2', 'f2a.out'], "sub2/f2a.in") +test.must_match(['sub2', 'dir', 'f2b.out'], "sub2/dir/f2b.in") +test.must_not_exist(test.workpath('sub3', 'f3.out')) +test.must_not_exist(test.workpath('sub4', 'f4a.out')) +test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) +test.must_not_exist(test.workpath('build', 'f4a.out')) +test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) + +# Verify that we build a specified alias, regardless of where. +test.run(chdir = 'sub2', arguments = '-u my_alias') + +test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) +test.must_match(['sub3', 'f3.out'], "sub3/f3.in") +test.must_not_exist(test.workpath('sub4', 'f4a.out')) +test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) +test.must_not_exist(test.workpath('build', 'f4a.out')) +test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) + +# Verify that we build things in a linked VariantDir. +f4a_in = os.path.join('build', 'f4a.in') +f4a_out = os.path.join('build', 'f4a.out') +f4b_in = os.path.join('build', 'dir', 'f4b.in') +f4b_out = os.path.join('build', 'dir', 'f4b.out') +test.run(chdir = 'sub4', + arguments = '-u', + stdout = "scons: Entering directory `%s'\n" % test.workpath() + \ + test.wrap_stdout("""\ +scons: building associated VariantDir targets: build +cat(["%s"], ["%s"]) +cat(["%s"], ["%s"]) +scons: `sub4' is up to date. +""" % (f4b_out, f4b_in, f4a_out, f4a_in))) + +test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) +test.must_not_exist(test.workpath('sub4', 'f4a.out')) +test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) +test.must_match(['build', 'f4a.out'], "sub4/f4a.in") +test.must_match(['build', 'dir', 'f4b.out'], "sub4/dir/f4b.in") + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/Action-error.py b/test/Configure/Action-error.py index 90ba1ee22b..69ba7f7045 100644 --- a/test/Configure/Action-error.py +++ b/test/Configure/Action-error.py @@ -1,54 +1,55 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that calling Configure from an Action results in a readable error. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -def ConfigureAction(target, source, env): - env.Configure() - return 0 -env = Environment(BUILDERS = {'MyAction' : - Builder(action=Action(ConfigureAction))}) -env.MyAction('target', []) -""") - -expect = "scons: *** [target] Calling Configure from Builders is not supported.\n" - -test.run(status=2, stderr=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that calling Configure from an Action results in a readable error. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def ConfigureAction(target, source, env): + env.Configure() + return 0 +env = Environment(tools=[], + BUILDERS = {'MyAction' : + Builder(action=Action(ConfigureAction))}) +env.MyAction('target', []) +""") + +expect = "scons: *** [target] Calling Configure from Builders is not supported.\n" + +test.run(status=2, stderr=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/Configure/basic.py b/test/Configure/basic.py index 253fa2510a..b68890e17c 100644 --- a/test/Configure/basic.py +++ b/test/Configure/basic.py @@ -1,91 +1,90 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that basic builds work with Configure contexts. -""" - -from TestSCons import TestSCons, ConfigCheckInfo, _obj -from TestCmd import IS_WINDOWS - - -test = TestSCons(match = TestSCons.match_re_dotall) - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -test.write('SConstruct', """\ -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -# Throw in a bad variable name intentionally used by Ubuntu packaging. -env['ENV']['HASH(0x12345678)'] = 'Bad variable name!' -conf = Configure(env) -r1 = conf.CheckCHeader( 'math.h' ) -r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -env = conf.Finish() -Export( 'env' ) -SConscript( 'SConscript' ) -""") - -test.write('SConscript', """\ -Import( 'env' ) -env.Program( 'TestProgram', 'TestProgram.c' ) -""") - -test.write('TestProgram.c', """\ -#include - -int main(void) { - printf( "Hello\\n" ); -} -""") - -test.run() -test.checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.run() -test.checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that basic builds work with Configure contexts. +""" + +from TestSCons import TestSCons, ConfigCheckInfo, _obj +from TestCmd import IS_WINDOWS + + +test = TestSCons(match = TestSCons.match_re_dotall) + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +# Throw in a bad variable name intentionally used by Ubuntu packaging. +env['ENV']['HASH(0x12345678)'] = 'Bad variable name!' +conf = Configure(env) +r1 = conf.CheckCHeader( 'math.h' ) +r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +env = conf.Finish() +Export( 'env' ) +SConscript( 'SConscript' ) +""") + +test.write('SConscript', """\ +Import( 'env' ) +env.Program( 'TestProgram', 'TestProgram.c' ) +""") + +test.write('TestProgram.c', """\ +#include + +int main(void) { + printf( "Hello\\n" ); +} +""") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: From f8bb7b9fbf65402159d4822f0e0026a4ec35372d Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 21:26:10 -0700 Subject: [PATCH 048/386] minimize tool initialization in tests --- test/Configure/Builder-call.py | 128 ++++---- test/Configure/CONFIGUREDIR.py | 114 +++---- test/Configure/CONFIGURELOG.py | 136 ++++---- test/Configure/SConscript.py | 160 +++++----- test/Configure/Streamer1.py | 170 +++++----- test/Configure/VariantDir-SConscript.py | 10 +- test/Configure/VariantDir.py | 197 ++++++------ test/Configure/VariantDir2.py | 10 +- test/Configure/basic.py | 1 + test/Configure/build-fail.py | 194 ++++++------ test/Configure/cache-not-ok.py | 204 ++++++------ test/Configure/cache-ok.py | 252 +++++++-------- test/Configure/clean.py | 168 +++++----- test/Configure/config-h.py | 1 + test/Configure/custom-tests.py | 402 ++++++++++++------------ test/Configure/from-SConscripts.py | 126 ++++---- test/Configure/help.py | 188 +++++------ test/Configure/implicit-cache.py | 8 +- test/Configure/option--Q.py | 104 +++--- test/Configure/option--config.py | 13 +- 20 files changed, 1297 insertions(+), 1289 deletions(-) diff --git a/test/Configure/Builder-call.py b/test/Configure/Builder-call.py index b4c9d334d8..3427fafde0 100644 --- a/test/Configure/Builder-call.py +++ b/test/Configure/Builder-call.py @@ -1,64 +1,64 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that calling normal Builders from an actual Configure -context environment works correctly. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.write('mycommand.py', r""" -import sys -sys.stderr.write( 'Hello World on stderr\n' ) -sys.stdout.write( 'Hello World on stdout\n' ) -with open(sys.argv[1], 'w') as f: - f.write( 'Hello World\n' ) -""") - -test.write('SConstruct', """\ -env = Environment() -def CustomTest(*args): - return 0 -conf = env.Configure(custom_tests = {'MyTest' : CustomTest}) -if not conf.MyTest(): - env.Command("hello", [], r'%(_python_)s mycommand.py $TARGET') -env = conf.Finish() -""" % locals()) - -test.run(stderr="Hello World on stderr\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that calling normal Builders from an actual Configure +context environment works correctly. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('mycommand.py', r""" +import sys +sys.stderr.write( 'Hello World on stderr\n' ) +sys.stdout.write( 'Hello World on stdout\n' ) +with open(sys.argv[1], 'w') as f: + f.write( 'Hello World\n' ) +""") + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +def CustomTest(*args): + return 0 +conf = env.Configure(custom_tests = {'MyTest' : CustomTest}) +if not conf.MyTest(): + env.Command("hello", [], r'%(_python_)s mycommand.py $TARGET') +env = conf.Finish() +""" % locals()) + +test.run(stderr="Hello World on stderr\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/CONFIGUREDIR.py b/test/Configure/CONFIGUREDIR.py index 4b5a02e0da..61d0b56e7e 100644 --- a/test/Configure/CONFIGUREDIR.py +++ b/test/Configure/CONFIGUREDIR.py @@ -1,57 +1,57 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that the configure context directory can be specified by -setting the $CONFIGUREDIR construction variable. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write("SConstruct", """\ -def CustomTest(context): - context.Message('Executing Custom Test ... ') - context.Result(1) - -env = Environment(CONFIGUREDIR = 'custom_config_dir') -conf = Configure(env, custom_tests = {'CustomTest' : CustomTest}) -conf.CustomTest(); -env = conf.Finish() -""") - -test.run() - -test.must_exist('custom_config_dir') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Test that the configure context directory can be specified by +setting the $CONFIGUREDIR construction variable. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write("SConstruct", """\ +DefaultEnvironment(tools=[]) +def CustomTest(context): + context.Message('Executing Custom Test ... ') + context.Result(1) + +env = Environment(tools=[], CONFIGUREDIR = 'custom_config_dir') +conf = Configure(env, custom_tests = {'CustomTest' : CustomTest}) +conf.CustomTest(); +env = conf.Finish() +""") + +test.run() + +test.must_exist('custom_config_dir') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/CONFIGURELOG.py b/test/Configure/CONFIGURELOG.py index 9b6221beeb..e10a0aa2fa 100644 --- a/test/Configure/CONFIGURELOG.py +++ b/test/Configure/CONFIGURELOG.py @@ -1,68 +1,68 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test that the configure context log file name can be specified by -setting the $CONFIGURELOG construction variable. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -SConstruct_path = test.workpath('SConstruct') - -test.write(SConstruct_path, """\ -def CustomTest(context): - context.Message('Executing Custom Test ...') - context.Result(1) - -env = Environment(CONFIGURELOG = 'custom.logfile') -conf = Configure(env, custom_tests = {'CustomTest' : CustomTest}) -conf.CustomTest(); -env = conf.Finish() -""") - -test.run() - -expect = """\ -file %(SConstruct_path)s,line 6: -\tConfigure(confdir = .sconf_temp) -scons: Configure: Executing Custom Test ... -scons: Configure: (cached) yes - - -""" % locals() - -test.must_match('custom.logfile', expect, mode='r') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Test that the configure context log file name can be specified by +setting the $CONFIGURELOG construction variable. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +SConstruct_path = test.workpath('SConstruct') + +test.write(SConstruct_path, """\ +DefaultEnvironment(tools=[]) +def CustomTest(context): + context.Message('Executing Custom Test ...') + context.Result(1) + +env = Environment(tools=[], CONFIGURELOG = 'custom.logfile') +conf = Configure(env, custom_tests = {'CustomTest' : CustomTest}) +conf.CustomTest(); +env = conf.Finish() +""") + +test.run() + +expect = """\ +file %(SConstruct_path)s,line 7: +\tConfigure(confdir = .sconf_temp) +scons: Configure: Executing Custom Test ... +scons: Configure: (cached) yes + + +""" % locals() + +test.must_match('custom.logfile', expect, mode='r') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/SConscript.py b/test/Configure/SConscript.py index 73afef766f..4bca8c134d 100644 --- a/test/Configure/SConscript.py +++ b/test/Configure/SConscript.py @@ -1,80 +1,80 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that Configure contexts from multiple subsidiary SConscript -files work without error. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir(['dir1'], - ['dir2'], - ['dir2', 'sub1'], - ['dir2', 'sub1', 'sub2']) - -test.write('SConstruct', """\ -env = Environment() -SConscript(dirs=['dir1', 'dir2'], exports="env") -""") - -test.write(['dir1', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -""") - -test.write(['dir2', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -SConscript(dirs=['sub1'], exports="env") -""") - -test.write(['dir2', 'sub1', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -SConscript(dirs=['sub2'], exports="env") -""") - -test.write(['dir2', 'sub1', 'sub2', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -""") - -test.run() - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that Configure contexts from multiple subsidiary SConscript +files work without error. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir(['dir1'], + ['dir2'], + ['dir2', 'sub1'], + ['dir2', 'sub1', 'sub2']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +SConscript(dirs=['dir1', 'dir2'], exports="env") +""") + +test.write(['dir1', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +""") + +test.write(['dir2', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +SConscript(dirs=['sub1'], exports="env") +""") + +test.write(['dir2', 'sub1', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +SConscript(dirs=['sub2'], exports="env") +""") + +test.write(['dir2', 'sub1', 'sub2', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +""") + +test.run() + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/Streamer1.py b/test/Configure/Streamer1.py index 318a9361df..2b29c40adc 100644 --- a/test/Configure/Streamer1.py +++ b/test/Configure/Streamer1.py @@ -1,85 +1,85 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Test for BitBucket PR 126: - -SConf doesn't work well with 'io' module on pre-3.0 Python. This is because -io.StringIO (used by SCons.SConf.Streamer) accepts only unicode strings. -Non-unicode input causes it to raise an exception. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -# SConstruct -# -# The CheckHello should return 'yes' if everything works fine. Otherwise it -# returns 'failed'. -# -def hello(target, source, env): - import traceback - try: - print('hello!\\n') # this breaks the script - with open(env.subst('$TARGET', target = target),'w') as f: - f.write('yes') - except: - # write to file, as stdout/stderr is broken - traceback.print_exc(file=open('traceback','w')) - return 0 - -def CheckHello(context): - import sys - context.Display('Checking whether hello works... ') - stat,out = context.TryAction(hello,'','.in') - if stat and out: - context.Result(out) - else: - context.Result('failed') - return out - -env = Environment() -cfg = Configure(env) - -cfg.AddTest('CheckHello', CheckHello) -cfg.CheckHello() - -env = cfg.Finish() -""") - -test.run(arguments = '.') -test.must_contain_all_lines(test.stdout(), ['Checking whether hello works... yes']) -test.must_not_exist('traceback') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Test for BitBucket PR 126: + +SConf doesn't work well with 'io' module on pre-3.0 Python. This is because +io.StringIO (used by SCons.SConf.Streamer) accepts only unicode strings. +Non-unicode input causes it to raise an exception. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +# SConstruct +# +# The CheckHello should return 'yes' if everything works fine. Otherwise it +# returns 'failed'. +# +def hello(target, source, env): + import traceback + try: + print('hello!\\n') # this breaks the script + with open(env.subst('$TARGET', target = target),'w') as f: + f.write('yes') + except: + # write to file, as stdout/stderr is broken + traceback.print_exc(file=open('traceback','w')) + return 0 + +def CheckHello(context): + import sys + context.Display('Checking whether hello works... ') + stat,out = context.TryAction(hello,'','.in') + if stat and out: + context.Result(out) + else: + context.Result('failed') + return out + +env = Environment(tools=[]) +cfg = Configure(env) + +cfg.AddTest('CheckHello', CheckHello) +cfg.CheckHello() + +env = cfg.Finish() +""") + +test.run(arguments = '.') +test.must_contain_all_lines(test.stdout(), ['Checking whether hello works... yes']) +test.must_not_exist('traceback') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/VariantDir-SConscript.py b/test/Configure/VariantDir-SConscript.py index 5818fc7793..cb50d3afe2 100644 --- a/test/Configure/VariantDir-SConscript.py +++ b/test/Configure/VariantDir-SConscript.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 @@ -19,10 +21,7 @@ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE """ Verify that Configure calls in SConscript files work when used @@ -45,6 +44,7 @@ CF = test.CF # cached build failure test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) opts = Variables() opts.Add('chdir') env = Environment(options=opts) diff --git a/test/Configure/VariantDir.py b/test/Configure/VariantDir.py index 2c54b84400..0296d1c7f1 100644 --- a/test/Configure/VariantDir.py +++ b/test/Configure/VariantDir.py @@ -1,98 +1,99 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that Configure contexts work with basic use of VariantDir. -""" - -import os - -import TestSCons - -_obj = TestSCons._obj - -test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -test.write('SConstruct', """\ -env = Environment(LOGFILE='build/config.log') -import os -env.AppendENVPath('PATH', os.environ['PATH']) -VariantDir('build', '.') -conf = env.Configure(conf_dir='build/config.tests', log_file='$LOGFILE') -r1 = conf.CheckCHeader('math.h') -r2 = conf.CheckCHeader('no_std_c_header.h') # leads to compile error -env = conf.Finish() -Export('env') -# with open('build/config.log') as f: -# print f.readlines() -SConscript('build/SConscript') -""") - -test.write('SConscript', """\ -Import('env') -env.Program('TestProgram', 'TestProgram.c') -""") - -test.write('TestProgram.c', """\ -#include - -int main(void) { - printf("Hello\\n"); -} -""") - -test.run() -test.checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))]], - os.path.join("build", "config.log"), - os.path.join("build", "config.tests"), - "SConstruct") - -test.run() -test.checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - os.path.join("build", "config.log"), - os.path.join("build", "config.tests"), - "SConstruct") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that Configure contexts work with basic use of VariantDir. +""" + +import os + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + +env = Environment(LOGFILE='build/config.log') +import os +env.AppendENVPath('PATH', os.environ['PATH']) +VariantDir('build', '.') +conf = env.Configure(conf_dir='build/config.tests', log_file='$LOGFILE') +r1 = conf.CheckCHeader('math.h') +r2 = conf.CheckCHeader('no_std_c_header.h') # leads to compile error +env = conf.Finish() +Export('env') +# with open('build/config.log') as f: +# print f.readlines() +SConscript('build/SConscript') +""") + +test.write('SConscript', """\ +Import('env') +env.Program('TestProgram', 'TestProgram.c') +""") + +test.write('TestProgram.c', """\ +#include + +int main(void) { + printf("Hello\\n"); +} +""") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))]], + os.path.join("build", "config.log"), + os.path.join("build", "config.tests"), + "SConstruct") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + os.path.join("build", "config.log"), + os.path.join("build", "config.tests"), + "SConstruct") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/VariantDir2.py b/test/Configure/VariantDir2.py index 98f4ccde8b..e640fac3c7 100644 --- a/test/Configure/VariantDir2.py +++ b/test/Configure/VariantDir2.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 @@ -19,10 +21,7 @@ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE """ Verify that Configure contexts work with SConstruct/SConscript structure @@ -34,6 +33,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) SConscript('SConscript', variant_dir='build', src='.') """) diff --git a/test/Configure/basic.py b/test/Configure/basic.py index b68890e17c..d2f7111d8e 100644 --- a/test/Configure/basic.py +++ b/test/Configure/basic.py @@ -39,6 +39,7 @@ CF = test.CF # cached build failure test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment() import os env.AppendENVPath('PATH', os.environ['PATH']) diff --git a/test/Configure/build-fail.py b/test/Configure/build-fail.py index 218cf1807a..b835cb7418 100644 --- a/test/Configure/build-fail.py +++ b/test/Configure/build-fail.py @@ -1,97 +1,97 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that Configure tests work even after an earlier test fails. - -This was broken in 0.98.3 because we'd mark the /usr/bin/g++ compiler -as having failed (because it was on the candidates list as the implicit -command dependency for both the object file and executable generated -for the configuration test) and then avoid trying to rebuild anything -else that used the "failed" Node. - -Thanks to Ben Webb for the test case. -""" - -import os -import re - -import TestSCons - -_obj = TestSCons._obj - -test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) - -test.subdir('a', 'b') - -a_boost_hpp = os.path.join('..', 'a', 'boost.hpp') -b_boost_hpp = os.path.join('..', 'b', 'boost.hpp') - -test.write('SConstruct', """\ -import os -def _check(context): - for dir in ['a', 'b']: - inc = os.path.join('..', dir, 'boost.hpp') - result = context.TryRun(''' - #include "%s" - - int main(void) { return 0; } - ''' % inc, '.cpp')[0] - if result: - import sys - sys.stdout.write('%s: ' % inc) - break - context.Result(result) - return result -env = Environment() -conf = env.Configure(custom_tests={'CheckBoost':_check}) -conf.CheckBoost() -conf.Finish() -""") - -test.write(['b', 'boost.hpp'], """#define FILE "b/boost.hpp"\n""") - -expect = test.wrap_stdout(read_str = "%s: yes\n" % re.escape(b_boost_hpp), - build_str = "scons: `.' is up to date.\n") - -test.run(arguments='--config=force', stdout=expect) - -expect = test.wrap_stdout(read_str = "%s: yes\n" % re.escape(a_boost_hpp), - build_str = "scons: `.' is up to date.\n") - -test.write(['a', 'boost.hpp'], """#define FILE "a/boost.hpp"\n""") - -test.run(arguments='--config=force', stdout=expect) - -test.run() - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that Configure tests work even after an earlier test fails. + +This was broken in 0.98.3 because we'd mark the /usr/bin/g++ compiler +as having failed (because it was on the candidates list as the implicit +command dependency for both the object file and executable generated +for the configuration test) and then avoid trying to rebuild anything +else that used the "failed" Node. + +Thanks to Ben Webb for the test case. +""" + +import os +import re + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +test.subdir('a', 'b') + +a_boost_hpp = os.path.join('..', 'a', 'boost.hpp') +b_boost_hpp = os.path.join('..', 'b', 'boost.hpp') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +import os +def _check(context): + for dir in ['a', 'b']: + inc = os.path.join('..', dir, 'boost.hpp') + result = context.TryRun(''' + #include "%s" + + int main(void) { return 0; } + ''' % inc, '.cpp')[0] + if result: + import sys + sys.stdout.write('%s: ' % inc) + break + context.Result(result) + return result +env = Environment() +conf = env.Configure(custom_tests={'CheckBoost':_check}) +conf.CheckBoost() +conf.Finish() +""") + +test.write(['b', 'boost.hpp'], """#define FILE "b/boost.hpp"\n""") + +expect = test.wrap_stdout(read_str = "%s: yes\n" % re.escape(b_boost_hpp), + build_str = "scons: `.' is up to date.\n") + +test.run(arguments='--config=force', stdout=expect) + +expect = test.wrap_stdout(read_str = "%s: yes\n" % re.escape(a_boost_hpp), + build_str = "scons: `.' is up to date.\n") + +test.write(['a', 'boost.hpp'], """#define FILE "a/boost.hpp"\n""") + +test.run(arguments='--config=force', stdout=expect) + +test.run() + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/cache-not-ok.py b/test/Configure/cache-not-ok.py index 7502f7a25c..0944996441 100644 --- a/test/Configure/cache-not-ok.py +++ b/test/Configure/cache-not-ok.py @@ -1,102 +1,102 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that the cache mechanism works when checks are not ok. -""" - -import TestSCons - -_exe = TestSCons._exe -_obj = TestSCons._obj - -test = TestSCons.TestSCons() - -lib = test.Configure_lib - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -test.write('SConstruct', """\ -if not int(ARGUMENTS.get('target_signatures_content', 0)): - Decider('timestamp-newer') -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = env.Configure() -r1 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -r2 = conf.CheckLib( 'no_c_library_SAFFDG' ) # leads to link error -env = conf.Finish() -if not (not r1 and not r2): - print("FAIL: ", r1, r2) - Exit(1) -""") - -# Verify correct behavior when we call Decider('timestamp-newer'). - -test.run() -test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", NCR), (_obj, NCF))], - [((".c", NCR), (_obj, NCR), (_exe, NCF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.run() -test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", CR), (_obj, NCF))], - [((".c", CR), (_obj, CR), (_exe, NCF))]], - "config.log", ".sconf_temp", "SConstruct") - -# Same should be true for the default behavior of Decider('content'). - -test.run(arguments='target_signatures_content=1 --config=force') -test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", NCR), (_obj, NCF))], - [((".c", NCR), (_obj, NCR), (_exe, NCF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.run(arguments='target_signatures_content=1') -test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", CR), (_obj, CF))], - [((".c", CR), (_obj, CR), (_exe, CF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the cache mechanism works when checks are not ok. +""" + +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj + +test = TestSCons.TestSCons() + +lib = test.Configure_lib + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +if not int(ARGUMENTS.get('target_signatures_content', 0)): + Decider('timestamp-newer') +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = env.Configure() +r1 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +r2 = conf.CheckLib( 'no_c_library_SAFFDG' ) # leads to link error +env = conf.Finish() +if not (not r1 and not r2): + print("FAIL: ", r1, r2) + Exit(1) +""") + +# Verify correct behavior when we call Decider('timestamp-newer'). + +test.run() +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", NCR), (_obj, NCF))], + [((".c", NCR), (_obj, NCR), (_exe, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run() +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", CR), (_obj, NCF))], + [((".c", CR), (_obj, CR), (_exe, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +# Same should be true for the default behavior of Decider('content'). + +test.run(arguments='target_signatures_content=1 --config=force') +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", NCR), (_obj, NCF))], + [((".c", NCR), (_obj, NCR), (_exe, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='target_signatures_content=1') +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", CR), (_obj, CF))], + [((".c", CR), (_obj, CR), (_exe, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/cache-ok.py b/test/Configure/cache-ok.py index 93d1308fb9..dc2d0c32b9 100644 --- a/test/Configure/cache-ok.py +++ b/test/Configure/cache-ok.py @@ -1,126 +1,126 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that the cache mechanism works when checks are ok. -""" - -import TestSCons - -_exe = TestSCons._exe -_obj = TestSCons._obj - -test = TestSCons.TestSCons(match = TestSCons.match_re) - -lib = test.Configure_lib - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -test.write('SConstruct', """\ -if not int(ARGUMENTS.get('target_signatures_content', 0)): - Decider('timestamp-newer') -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env) -r1 = conf.CheckLibWithHeader( '%(lib)s', 'math.h', 'c' ) -r2 = conf.CheckLibWithHeader( None, 'math.h', 'c' ) -r3 = conf.CheckLib( '%(lib)s', autoadd=0 ) -r4 = conf.CheckLib( None, autoadd=0 ) -r5 = conf.CheckCHeader( 'math.h' ) -r6 = conf.CheckCXXHeader( 'vector' ) -env = conf.Finish() -if not (r1 and r2 and r3 and r4 and r5 and r6): - Exit(1) -""" % locals()) - -# Verify correct behavior when we call Decider('timestamp-newer') - -test.run() -test.checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + - [[((".c", NCR), (_obj, NCR))]] + - [[((".cpp", NCR), (_obj, NCR))]], - "config.log", ".sconf_temp", "SConstruct") - - -test.run() -test.checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + - [[((".c", CR), (_obj, CR))]] + - [[((".cpp", CR), (_obj, CR))]], - "config.log", ".sconf_temp", "SConstruct") - -# same should be true for the default behavior of Decider('content') - -test.run(arguments='target_signatures_content=1 --config=force') -test.checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + - [[((".c", NCR), (_obj, NCR))]] + - [[((".cpp", NCR), (_obj, NCR))]], - "config.log", ".sconf_temp", "SConstruct") - -test.run(arguments='target_signatures_content=1') -test.checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + - [[((".c", CR), (_obj, CR))]] + - [[((".cpp", CR), (_obj, CR))]], - "config.log", ".sconf_temp", "SConstruct") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the cache mechanism works when checks are ok. +""" + +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re) + +lib = test.Configure_lib + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +if not int(ARGUMENTS.get('target_signatures_content', 0)): + Decider('timestamp-newer') +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env) +r1 = conf.CheckLibWithHeader( '%(lib)s', 'math.h', 'c' ) +r2 = conf.CheckLibWithHeader( None, 'math.h', 'c' ) +r3 = conf.CheckLib( '%(lib)s', autoadd=0 ) +r4 = conf.CheckLib( None, autoadd=0 ) +r5 = conf.CheckCHeader( 'math.h' ) +r6 = conf.CheckCXXHeader( 'vector' ) +env = conf.Finish() +if not (r1 and r2 and r3 and r4 and r5 and r6): + Exit(1) +""" % locals()) + +# Verify correct behavior when we call Decider('timestamp-newer') + +test.run() +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + + [[((".c", NCR), (_obj, NCR))]] + + [[((".cpp", NCR), (_obj, NCR))]], + "config.log", ".sconf_temp", "SConstruct") + + +test.run() +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + + [[((".c", CR), (_obj, CR))]] + + [[((".cpp", CR), (_obj, CR))]], + "config.log", ".sconf_temp", "SConstruct") + +# same should be true for the default behavior of Decider('content') + +test.run(arguments='target_signatures_content=1 --config=force') +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + + [[((".c", NCR), (_obj, NCR))]] + + [[((".cpp", NCR), (_obj, NCR))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='target_signatures_content=1') +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + + [[((".c", CR), (_obj, CR))]] + + [[((".cpp", CR), (_obj, CR))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/clean.py b/test/Configure/clean.py index d7a5dc7b5f..3370cd251c 100644 --- a/test/Configure/clean.py +++ b/test/Configure/clean.py @@ -1,84 +1,84 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that we don't perform Configure context actions when the --c or --clean options have been specified. -""" - -import TestSCons - -test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) - -test.write('SConstruct', """\ -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env, clean=int(ARGUMENTS['clean'])) -r1 = conf.CheckCHeader( 'math.h' ) -r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -env = conf.Finish() -Export( 'env' ) -SConscript( 'SConscript' ) -""") - -test.write('SConscript', """\ -Import( 'env' ) -env.Program( 'TestProgram', 'TestProgram.c' ) -""") - -test.write('TestProgram.c', """\ -#include - -int main(void) { - printf( "Hello\\n" ); -} -""") - -lines = [ - "Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... " -] - -test.run(arguments = '-c clean=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '-c clean=1') -test.must_contain_all_lines(test.stdout(), lines) - -test.run(arguments = '--clean clean=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '--clean clean=1') -test.must_contain_all_lines(test.stdout(), lines) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that we don't perform Configure context actions when the +-c or --clean options have been specified. +""" + +import TestSCons + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env, clean=int(ARGUMENTS['clean'])) +r1 = conf.CheckCHeader( 'math.h' ) +r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +env = conf.Finish() +Export( 'env' ) +SConscript( 'SConscript' ) +""") + +test.write('SConscript', """\ +Import( 'env' ) +env.Program( 'TestProgram', 'TestProgram.c' ) +""") + +test.write('TestProgram.c', """\ +#include + +int main(void) { + printf( "Hello\\n" ); +} +""") + +lines = [ + "Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... " +] + +test.run(arguments = '-c clean=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '-c clean=1') +test.must_contain_all_lines(test.stdout(), lines) + +test.run(arguments = '--clean clean=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '--clean clean=1') +test.must_contain_all_lines(test.stdout(), lines) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/config-h.py b/test/Configure/config-h.py index aca18e42ac..221e3bd209 100644 --- a/test/Configure/config-h.py +++ b/test/Configure/config-h.py @@ -37,6 +37,7 @@ LIB = "LIB" + lib.upper() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment() import os env.AppendENVPath('PATH', os.environ['PATH']) diff --git a/test/Configure/custom-tests.py b/test/Configure/custom-tests.py index 47b344aff0..a4c29c78e1 100644 --- a/test/Configure/custom-tests.py +++ b/test/Configure/custom-tests.py @@ -1,200 +1,202 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# 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 execution of custom test cases. -""" - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import TestSCons - -_exe = TestSCons._exe -_obj = TestSCons._obj -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -compileOK = '#include \\nint main(void) {printf("Hello");return 0;}' -compileFAIL = "syntax error" -linkOK = compileOK -linkFAIL = "void myFunc(); int main(void) { myFunc(); }" -runOK = compileOK -runFAIL = "int main(void) { return 1; }" - -test.write('pyAct.py', """\ -import sys -print(sys.argv[1]) -sys.exit(int(sys.argv[1])) -""") - -test.write('SConstruct', """\ -def CheckCustom(test): - test.Message( 'Executing MyTest ... ' ) - retCompileOK = test.TryCompile( '%(compileOK)s', '.c' ) - retCompileFAIL = test.TryCompile( '%(compileFAIL)s', '.c' ) - retLinkOK = test.TryLink( '%(linkOK)s', '.c' ) - retLinkFAIL = test.TryLink( '%(linkFAIL)s', '.c' ) - (retRunOK, outputRunOK) = test.TryRun( '%(runOK)s', '.c' ) - (retRunFAIL, outputRunFAIL) = test.TryRun( '%(runFAIL)s', '.c' ) - (retActOK, outputActOK) = test.TryAction( r'%(_python_)s pyAct.py 0 > $TARGET' ) - (retActFAIL, outputActFAIL) = test.TryAction( r'%(_python_)s pyAct.py 1 > $TARGET' ) - resOK = retCompileOK and retLinkOK and retRunOK and outputRunOK=="Hello" - resOK = resOK and retActOK and int(outputActOK)==0 - resFAIL = retCompileFAIL or retLinkFAIL or retRunFAIL or outputRunFAIL!="" - resFAIL = resFAIL or retActFAIL or outputActFAIL!="" - test.Result( resOK and not resFAIL ) - return resOK and not resFAIL - -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure( env, custom_tests={'CheckCustom' : CheckCustom} ) -conf.CheckCustom() -env = conf.Finish() -""" % locals()) - -test.run() - -test.checkLogAndStdout(["Executing MyTest ... "], - ["yes"], - [[(('.c', NCR), (_obj, NCR)), - (('.c', NCR), (_obj, NCF)), - (('.c', NCR), (_obj, NCR), (_exe, NCR)), - (('.c', NCR), (_obj, NCR), (_exe, NCF)), - (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCR)), - (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCF)), - (('', NCR),), - (('', NCF),)]], - "config.log", ".sconf_temp", "SConstruct") - -test.run() - -# Try again to check caching -test.checkLogAndStdout(["Executing MyTest ... "], - ["yes"], - [[(('.c', CR), (_obj, CR)), - (('.c', CR), (_obj, CF)), - (('.c', CR), (_obj, CR), (_exe, CR)), - (('.c', CR), (_obj, CR), (_exe, CF)), - (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CR)), - (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CF)), - (('', CR),), - (('', CF),)]], - "config.log", ".sconf_temp", "SConstruct") - -# Test other customs: -test.write('SConstruct', """\ -def CheckList(test): - test.Message( 'Display of list ...' ) - res = [1, 2, 3, 4] - test.Result( res ) - return res - -def CheckEmptyList(test): - test.Message( 'Display of empty list ...' ) - res = list() - test.Result( res ) - return res - -def CheckRandomStr(test): - test.Message( 'Display of random string ...' ) - res = "a random string" - test.Result( res ) - return res - -def CheckEmptyStr(test): - test.Message( 'Display of empty string ...' ) - res = "" - test.Result( res ) - return res - -def CheckDict(test): - test.Message( 'Display of dictionary ...' ) - res = {"key1" : 1, "key2" : "text"} - test.Result( res ) - return res - -def CheckEmptyDict(test): - test.Message( 'Display of empty dictionary ...' ) - res = dict - test.Result( res ) - return res - -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure( env, custom_tests={'CheckList' : CheckList, - 'CheckEmptyList' : CheckEmptyList, - 'CheckRandomStr' : CheckRandomStr, - 'CheckEmptyStr' : CheckEmptyStr, - 'CheckDict' : CheckDict, - 'CheckEmptyDict' : CheckEmptyDict} ) -conf.CheckList() -conf.CheckEmptyList() -conf.CheckRandomStr() -conf.CheckEmptyStr() -conf.CheckDict() -conf.CheckEmptyDict() -env = conf.Finish() -""" % locals()) - -test.run() - -test.must_match('config.log', -r""".* -.* -scons: Configure: Display of list ... -scons: Configure: \(cached\) yes - -scons: Configure: Display of empty list ... -scons: Configure: \(cached\) no - -scons: Configure: Display of random string ... -scons: Configure: \(cached\) a random string - -scons: Configure: Display of empty string ... -scons: Configure: \(cached\) * - -scons: Configure: Display of dictionary ... -scons: Configure: \(cached\) yes - -scons: Configure: Display of empty dictionary ... -scons: Configure: \(cached\) yes - - -""", -match=TestSCons.match_re) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 execution of custom test cases. +""" + + +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +compileOK = '#include \\nint main(void) {printf("Hello");return 0;}' +compileFAIL = "syntax error" +linkOK = compileOK +linkFAIL = "void myFunc(); int main(void) { myFunc(); }" +runOK = compileOK +runFAIL = "int main(void) { return 1; }" + +test.write('pyAct.py', """\ +import sys +print(sys.argv[1]) +sys.exit(int(sys.argv[1])) +""") + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def CheckCustom(test): + test.Message( 'Executing MyTest ... ' ) + retCompileOK = test.TryCompile( '%(compileOK)s', '.c' ) + retCompileFAIL = test.TryCompile( '%(compileFAIL)s', '.c' ) + retLinkOK = test.TryLink( '%(linkOK)s', '.c' ) + retLinkFAIL = test.TryLink( '%(linkFAIL)s', '.c' ) + (retRunOK, outputRunOK) = test.TryRun( '%(runOK)s', '.c' ) + (retRunFAIL, outputRunFAIL) = test.TryRun( '%(runFAIL)s', '.c' ) + (retActOK, outputActOK) = test.TryAction( r'%(_python_)s pyAct.py 0 > $TARGET' ) + (retActFAIL, outputActFAIL) = test.TryAction( r'%(_python_)s pyAct.py 1 > $TARGET' ) + resOK = retCompileOK and retLinkOK and retRunOK and outputRunOK=="Hello" + resOK = resOK and retActOK and int(outputActOK)==0 + resFAIL = retCompileFAIL or retLinkFAIL or retRunFAIL or outputRunFAIL!="" + resFAIL = resFAIL or retActFAIL or outputActFAIL!="" + test.Result( resOK and not resFAIL ) + return resOK and not resFAIL + +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure( env, custom_tests={'CheckCustom' : CheckCustom} ) +conf.CheckCustom() +env = conf.Finish() +""" % locals()) + +test.run() + +test.checkLogAndStdout(["Executing MyTest ... "], + ["yes"], + [[(('.c', NCR), (_obj, NCR)), + (('.c', NCR), (_obj, NCF)), + (('.c', NCR), (_obj, NCR), (_exe, NCR)), + (('.c', NCR), (_obj, NCR), (_exe, NCF)), + (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCR)), + (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCF)), + (('', NCR),), + (('', NCF),)]], + "config.log", ".sconf_temp", "SConstruct") + +test.run() + +# Try again to check caching +test.checkLogAndStdout(["Executing MyTest ... "], + ["yes"], + [[(('.c', CR), (_obj, CR)), + (('.c', CR), (_obj, CF)), + (('.c', CR), (_obj, CR), (_exe, CR)), + (('.c', CR), (_obj, CR), (_exe, CF)), + (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CR)), + (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CF)), + (('', CR),), + (('', CF),)]], + "config.log", ".sconf_temp", "SConstruct") + +# Test other customs: +test.write('SConstruct', """\ +def CheckList(test): + test.Message( 'Display of list ...' ) + res = [1, 2, 3, 4] + test.Result( res ) + return res + +def CheckEmptyList(test): + test.Message( 'Display of empty list ...' ) + res = list() + test.Result( res ) + return res + +def CheckRandomStr(test): + test.Message( 'Display of random string ...' ) + res = "a random string" + test.Result( res ) + return res + +def CheckEmptyStr(test): + test.Message( 'Display of empty string ...' ) + res = "" + test.Result( res ) + return res + +def CheckDict(test): + test.Message( 'Display of dictionary ...' ) + res = {"key1" : 1, "key2" : "text"} + test.Result( res ) + return res + +def CheckEmptyDict(test): + test.Message( 'Display of empty dictionary ...' ) + res = dict + test.Result( res ) + return res + +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure( env, custom_tests={'CheckList' : CheckList, + 'CheckEmptyList' : CheckEmptyList, + 'CheckRandomStr' : CheckRandomStr, + 'CheckEmptyStr' : CheckEmptyStr, + 'CheckDict' : CheckDict, + 'CheckEmptyDict' : CheckEmptyDict} ) +conf.CheckList() +conf.CheckEmptyList() +conf.CheckRandomStr() +conf.CheckEmptyStr() +conf.CheckDict() +conf.CheckEmptyDict() +env = conf.Finish() +""" % locals()) + +test.run() + +test.must_match('config.log', +r""".* +.* +scons: Configure: Display of list ... +scons: Configure: \(cached\) yes + +scons: Configure: Display of empty list ... +scons: Configure: \(cached\) no + +scons: Configure: Display of random string ... +scons: Configure: \(cached\) a random string + +scons: Configure: Display of empty string ... +scons: Configure: \(cached\) * + +scons: Configure: Display of dictionary ... +scons: Configure: \(cached\) yes + +scons: Configure: Display of empty dictionary ... +scons: Configure: \(cached\) yes + + +""", +match=TestSCons.match_re) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/from-SConscripts.py b/test/Configure/from-SConscripts.py index 59eba535b1..5d16592be4 100644 --- a/test/Configure/from-SConscripts.py +++ b/test/Configure/from-SConscripts.py @@ -1,63 +1,63 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Make sure we can call Configure() from subsidiary SConscript calls. - -This was broken at one point when we were using the internal -sconscript_reading flag (which is basically a hint for whether or not -we're in a Builder call) as a semaphore, not a counter. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -env = SConscript('x.scons') -""") - -test.write('x.scons', """\ -env = SConscript('y.scons') -config = env.Configure() -env = config.Finish() -Return('env') -""") - -test.write('y.scons', """\ -env = Environment() -Return('env') -""") - -test.run(arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Make sure we can call Configure() from subsidiary SConscript calls. + +This was broken at one point when we were using the internal +sconscript_reading flag (which is basically a hint for whether or not +we're in a Builder call) as a semaphore, not a counter. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = SConscript('x.scons') +""") + +test.write('x.scons', """\ +env = SConscript('y.scons') +config = env.Configure() +env = config.Finish() +Return('env') +""") + +test.write('y.scons', """\ +env = Environment(tools=[]) +Return('env') +""") + +test.run(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/help.py b/test/Configure/help.py index 32f74dac47..abe446d13c 100644 --- a/test/Configure/help.py +++ b/test/Configure/help.py @@ -1,94 +1,94 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that we don't perform Configure context actions when the --H, -h or --help options have been specified. -""" - -import TestSCons - -test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) - -test.write('SConstruct', """\ -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env, help=int(ARGUMENTS['help'])) -r1 = conf.CheckCHeader( 'math.h' ) -r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -env = conf.Finish() -Export( 'env' ) -SConscript( 'SConscript' ) -""") - -test.write('SConscript', """\ -Import( 'env' ) -env.Program( 'TestProgram', 'TestProgram.c' ) -""") - -test.write('TestProgram.c', """\ -#include - -int main(void) { - printf( "Hello\\n" ); -} -""") - -lines = [ - "Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... " -] - -# The help setting should have no effect on -H, so the -H output -# should never contain the lines. -test.run(arguments = '-H help=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '-H help=1') -test.must_not_contain_any_line(test.stdout(), lines) - -# For -h and --help, the lines appear or not depending on how Configure() -# is initialized. -test.run(arguments = '-h help=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '-h help=1') -test.must_contain_all_lines(test.stdout(), lines) - -test.run(arguments = '--help help=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '--help help=1') -test.must_contain_all_lines(test.stdout(), lines) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that we don't perform Configure context actions when the +-H, -h or --help options have been specified. +""" + +import TestSCons + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env, help=int(ARGUMENTS['help'])) +r1 = conf.CheckCHeader( 'math.h' ) +r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +env = conf.Finish() +Export( 'env' ) +SConscript( 'SConscript' ) +""") + +test.write('SConscript', """\ +Import( 'env' ) +env.Program( 'TestProgram', 'TestProgram.c' ) +""") + +test.write('TestProgram.c', """\ +#include + +int main(void) { + printf( "Hello\\n" ); +} +""") + +lines = [ + "Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... " +] + +# The help setting should have no effect on -H, so the -H output +# should never contain the lines. +test.run(arguments = '-H help=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '-H help=1') +test.must_not_contain_any_line(test.stdout(), lines) + +# For -h and --help, the lines appear or not depending on how Configure() +# is initialized. +test.run(arguments = '-h help=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '-h help=1') +test.must_contain_all_lines(test.stdout(), lines) + +test.run(arguments = '--help help=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '--help help=1') +test.must_contain_all_lines(test.stdout(), lines) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/implicit-cache.py b/test/Configure/implicit-cache.py index 2cf74d17ea..f5fdd4b42b 100644 --- a/test/Configure/implicit-cache.py +++ b/test/Configure/implicit-cache.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 @@ -19,7 +21,7 @@ # 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. +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE """ Verify that use of --implicit-cache with the Python Value Nodes @@ -52,7 +54,6 @@ get longer and longer until it blew out the users's memory. """ -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import TestSConsign from SCons.Util import get_hash_format, get_current_hash_algorithm_used @@ -60,6 +61,7 @@ test = TestSConsign.TestSConsign() test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment(CPPPATH=['.']) conf = Configure(env) conf.CheckHeader( 'math.h' ) diff --git a/test/Configure/option--Q.py b/test/Configure/option--Q.py index 198e94fd49..4602a50f68 100644 --- a/test/Configure/option--Q.py +++ b/test/Configure/option--Q.py @@ -1,52 +1,52 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that the -Q option suppresses Configure context output. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env) -r1 = conf.CheckCHeader('stdio.h') -env = conf.Finish() -""") - -test.run(arguments='-Q', stdout="scons: `.' is up to date.\n", stderr="") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the -Q option suppresses Configure context output. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env) +r1 = conf.CheckCHeader('stdio.h') +env = conf.Finish() +""") + +test.run(arguments='-Q', stdout="scons: `.' is up to date.\n", stderr="") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/option--config.py b/test/Configure/option--config.py index 3fd0691df7..4c3d7abc47 100644 --- a/test/Configure/option--config.py +++ b/test/Configure/option--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 @@ -19,10 +21,7 @@ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE """ Verify use of the --config= option. @@ -51,6 +50,7 @@ SConstruct_path = test.workpath('SConstruct') test.write(SConstruct_path, """ +DefaultEnvironment(tools=[]) env = Environment(CPPPATH='#/include') import os env.AppendENVPath('PATH', os.environ['PATH']) @@ -78,7 +78,7 @@ conftest_0_c = conftest_0_base%'.c' conftest_1_base = os.path.join(".sconf_temp", "conftest_%s_0%%s"%conftest_1_c_hash) -SConstruct_file_line = test.python_file_line(SConstruct_path, 6)[:-1] +SConstruct_file_line = test.python_file_line(SConstruct_path, 7)[:-1] expect = """ scons: *** "%(conftest_0_c)s" is not yet built and cache is forced. @@ -191,6 +191,7 @@ # Check the combination of --config=force and Decider('MD5-timestamp') SConstruct_path = test.workpath('SConstruct') test.write(SConstruct_path, """ +DefaultEnvironment(tools=[]) env = Environment() env.Decider('MD5-timestamp') conf = Configure(env) From 1535671313b56e0ad06550178b81b9dcc11fb067 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 21:33:17 -0700 Subject: [PATCH 049/386] minimize tool initialization in tests --- test/CPPDEFINES/basic.py | 5 +- test/CPPDEFINES/fixture/SConstruct-Append | 24 ++-- test/CPPDEFINES/fixture/SConstruct-Prepend | 24 ++-- test/CPPDEFINES/live.py | 1 + test/CPPDEFINES/undefined.py | 3 +- test/Climb/filename--D.py | 159 ++++++++++----------- 6 files changed, 109 insertions(+), 107 deletions(-) diff --git a/test/CPPDEFINES/basic.py b/test/CPPDEFINES/basic.py index b66ea8a876..a85e7eb36b 100755 --- a/test/CPPDEFINES/basic.py +++ b/test/CPPDEFINES/basic.py @@ -32,6 +32,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) test_list = [ 'xyz', ['x', 'y', 'z'], @@ -47,7 +48,7 @@ def generator(target, source, env, for_signature): return 'TARGET_AND_SOURCE_ARE_MISSING' for i in test_list: - env = Environment( + env = Environment(tools=['cc'], CPPDEFPREFIX='-D', CPPDEFSUFFIX='', INTEGER=0, @@ -65,7 +66,7 @@ def generator(target, source, env, for_signature): ) for i in test_list: - env = Environment( + env = Environment(tools=['cc'], CPPDEFPREFIX='|', CPPDEFSUFFIX='|', INTEGER=1, diff --git a/test/CPPDEFINES/fixture/SConstruct-Append b/test/CPPDEFINES/fixture/SConstruct-Append index 8c26270f3d..e991c88087 100644 --- a/test/CPPDEFINES/fixture/SConstruct-Append +++ b/test/CPPDEFINES/fixture/SConstruct-Append @@ -8,38 +8,38 @@ DefaultEnvironment(tools=[]) # Special cases: # https://github.com/SCons/scons/issues/1738 -env_1738_2 = Environment(CPPDEFPREFIX='-D') +env_1738_2 = Environment(tools=['gcc'],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 = Environment(tools=['gcc'],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 = Environment(tools=['gcc'],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 = Environment(tools=['gcc'],CPPDEFPREFIX='-D') env_multi['CPPDEFINES'] = "foo bar" env_multi.Append(CPPDEFINES="baz") print(env_multi.subst('$_CPPDEFFLAGS')) -env_multi = Environment(CPPDEFPREFIX='-D') +env_multi = Environment(tools=['gcc'],CPPDEFPREFIX='-D') env_multi['CPPDEFINES'] = ["foo bar"] env_multi.Append(CPPDEFINES="baz") print(env_multi.subst('$_CPPDEFFLAGS')) -env_multi = Environment(CPPDEFPREFIX='-D') +env_multi = Environment(tools=['gcc'],CPPDEFPREFIX='-D') env_multi['CPPDEFINES'] = "foo" env_multi.Append(CPPDEFINES=["bar baz"]) print(env_multi.subst('$_CPPDEFFLAGS')) -env_multi = Environment(CPPDEFPREFIX='-D') +env_multi = Environment(tools=['gcc'],CPPDEFPREFIX='-D') env_multi['CPPDEFINES'] = "foo" env_multi.Append(CPPDEFINES="bar baz") print(env_multi.subst('$_CPPDEFFLAGS')) @@ -47,7 +47,7 @@ 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 = Environment(tools=['gcc'],CPPDEFPREFIX='-D') env_multi.Append(CPPDEFINES=["Macro1=Value1", ("Macro2", "Value2"), {"Macro3": "Value3"}, "Macro4"]) try: env_multi.AppendUnique(CPPDEFINES="Macro2=Value2", delete_existing=True) @@ -60,9 +60,9 @@ else: print(env_multi.subst('$_CPPDEFFLAGS')) # A lone tuple handled differently than a lone list. -env_multi = Environment(CPPDEFPREFIX='-D', CPPDEFINES=("Macro1", "Value1")) +env_multi = Environment(tools=['gcc'],CPPDEFPREFIX='-D', CPPDEFINES=("Macro1", "Value1")) print(env_multi.subst('$_CPPDEFFLAGS')) -env_multi = Environment(CPPDEFPREFIX='-D', CPPDEFINES=["Macro1", "Value1"]) +env_multi = Environment(tools=['gcc'],CPPDEFPREFIX='-D', CPPDEFINES=["Macro1", "Value1"]) print(env_multi.subst('$_CPPDEFFLAGS')) # https://github.com/SCons/scons/issues/1152 @@ -114,7 +114,7 @@ for (t1, c1) in cases: orig = f"{c1!r}" if isinstance(c1, str) else c1 app = f"{c2!r}" if isinstance(c2, str) else c2 print(f" orig = {orig}, append = {app}") - env = Environment(CPPDEFINES=c1, CPPDEFPREFIX='-D') + env = Environment(tools=['gcc'],CPPDEFINES=c1, CPPDEFPREFIX='-D') try: env.Append(CPPDEFINES=c2) final = env.subst('$_CPPDEFFLAGS', source="src", target="tgt") @@ -122,7 +122,7 @@ for (t1, c1) in cases: except Exception as t: print(f"Append:\n FAILED: {t}") - env = Environment(CPPDEFINES=c1, CPPDEFPREFIX='-D') + env = Environment(tools=['gcc'],CPPDEFINES=c1, CPPDEFPREFIX='-D') try: env.AppendUnique(CPPDEFINES=c2) final = env.subst('$_CPPDEFFLAGS', source="src", target="tgt") diff --git a/test/CPPDEFINES/fixture/SConstruct-Prepend b/test/CPPDEFINES/fixture/SConstruct-Prepend index 26546f1135..37126c3729 100644 --- a/test/CPPDEFINES/fixture/SConstruct-Prepend +++ b/test/CPPDEFINES/fixture/SConstruct-Prepend @@ -8,38 +8,38 @@ DefaultEnvironment(tools=[]) # Special cases: # https://github.com/SCons/scons/issues/1738 -env_1738_2 = Environment(CPPDEFPREFIX='-D') +env_1738_2 = Environment(tools=['cc'],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 = Environment(tools=['cc'],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 = Environment(tools=['cc'],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 = Environment(tools=['cc'],CPPDEFPREFIX='-D') env_multi['CPPDEFINES'] = "foo bar" env_multi.Prepend(CPPDEFINES="baz") print(env_multi.subst('$_CPPDEFFLAGS')) -env_multi = Environment(CPPDEFPREFIX='-D') +env_multi = Environment(tools=['cc'],CPPDEFPREFIX='-D') env_multi['CPPDEFINES'] = ["foo bar"] env_multi.Prepend(CPPDEFINES="baz") print(env_multi.subst('$_CPPDEFFLAGS')) -env_multi = Environment(CPPDEFPREFIX='-D') +env_multi = Environment(tools=['cc'],CPPDEFPREFIX='-D') env_multi['CPPDEFINES'] = "foo" env_multi.Prepend(CPPDEFINES=["bar baz"]) print(env_multi.subst('$_CPPDEFFLAGS')) -env_multi = Environment(CPPDEFPREFIX='-D') +env_multi = Environment(tools=['cc'],CPPDEFPREFIX='-D') env_multi['CPPDEFINES'] = "foo" env_multi.Prepend(CPPDEFINES="bar baz") print(env_multi.subst('$_CPPDEFFLAGS')) @@ -47,7 +47,7 @@ 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 = Environment(tools=['cc'],CPPDEFPREFIX='-D') env_multi.Prepend(CPPDEFINES=["Macro1=Value1", ("Macro2", "Value2"), {"Macro3": "Value3"}]) try: env_multi.PrependUnique(CPPDEFINES="Macro2=Value2", delete_existing=True) @@ -60,9 +60,9 @@ else: print(env_multi.subst('$_CPPDEFFLAGS')) # A lone tuple handled differently than a lone list. -env_tuple = Environment(CPPDEFPREFIX='-D', CPPDEFINES=("Macro1", "Value1")) +env_tuple = Environment(tools=['cc'],CPPDEFPREFIX='-D', CPPDEFINES=("Macro1", "Value1")) print(env_tuple.subst('$_CPPDEFFLAGS')) -env_multi = Environment(CPPDEFPREFIX='-D', CPPDEFINES=["Macro1", "Value1"]) +env_multi = Environment(tools=['cc'],CPPDEFPREFIX='-D', CPPDEFINES=["Macro1", "Value1"]) print(env_multi.subst('$_CPPDEFFLAGS')) # https://github.com/SCons/scons/issues/1152 @@ -115,7 +115,7 @@ for (t1, c1) in cases: orig = f"{c1!r}" if isinstance(c1, str) else c1 pre = f"{c2!r}" if isinstance(c2, str) else c2 print(f" orig = {orig}, prepend = {pre}") - env = Environment(CPPDEFINES=c1, CPPDEFPREFIX='-D') + env = Environment(tools=['cc'],CPPDEFINES=c1, CPPDEFPREFIX='-D') try: env.Prepend(CPPDEFINES=c2) final = env.subst('$_CPPDEFFLAGS', source="src", target="tgt") @@ -123,7 +123,7 @@ for (t1, c1) in cases: except Exception as t: print(f"Prepend:\n FAILED: {t}") - env = Environment(CPPDEFINES=c1, CPPDEFPREFIX='-D') + env = Environment(tools=['cc'],CPPDEFINES=c1, CPPDEFPREFIX='-D') try: env.PrependUnique(CPPDEFINES=c2) final = env.subst('$_CPPDEFFLAGS', source="src", target="tgt") diff --git a/test/CPPDEFINES/live.py b/test/CPPDEFINES/live.py index 97e0e13237..6735721ace 100644 --- a/test/CPPDEFINES/live.py +++ b/test/CPPDEFINES/live.py @@ -32,6 +32,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) foo = Environment(CPPDEFINES=['FOO', ('VAL', '$VALUE')], VALUE=7) bar = Environment(CPPDEFINES={'BAR': None, 'VAL': 8}) baz = Environment(CPPDEFINES=['BAZ', ('VAL', 9)]) diff --git a/test/CPPDEFINES/undefined.py b/test/CPPDEFINES/undefined.py index 31568ea4bf..749c63c1d4 100644 --- a/test/CPPDEFINES/undefined.py +++ b/test/CPPDEFINES/undefined.py @@ -32,7 +32,8 @@ test = TestSCons.TestSCons() test.write('SConstruct', """\ -env = Environment() +DefaultEnvironment(tools=[]) +env = Environment(tools=['cc']) print(env.subst('$_CPPDEFFLAGS')) """) diff --git a/test/Climb/filename--D.py b/test/Climb/filename--D.py index fee72f6a21..c747a81871 100644 --- a/test/Climb/filename--D.py +++ b/test/Climb/filename--D.py @@ -1,80 +1,79 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify the ability to use the -D option with the -f option to -specify a different top-level file name. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('subdir', 'other') - -test.write('main.scons', """\ -DefaultEnvironment(tools=[]) -print("main.scons") -SConscript('subdir/sub.scons') -""") - -test.write(['subdir', 'sub.scons'], """\ -print("subdir/sub.scons") -""") - - - -read_str = """\ -main.scons -subdir/sub.scons -""" - -expect = "scons: Entering directory `%s'\n" % test.workpath() \ - + test.wrap_stdout(read_str = read_str, - build_str = "scons: `subdir' is up to date.\n") - -test.run(chdir='subdir', arguments='-D -f main.scons .', stdout=expect) - - - -expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", - build_str = "scons: `.' is up to date.\n") - -test.run(chdir='other', arguments='-D -f ../subdir/sub.scons .', stdout=expect) - -test.run(chdir='other', - arguments='-D -f %s .' % test.workpath('subdir', 'sub.scons'), - stdout=expect) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the ability to use the -D option with the -f option to +specify a different top-level file name. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('subdir', 'other') + +test.write('main.scons', """\ +DefaultEnvironment(tools=[]) +print("main.scons") +SConscript('subdir/sub.scons') +""") + +test.write(['subdir', 'sub.scons'], """\ +print("subdir/sub.scons") +""") + + + +read_str = """\ +main.scons +subdir/sub.scons +""" + +expect = "scons: Entering directory `%s'\n" % test.workpath() \ + + test.wrap_stdout(read_str = read_str, + build_str = "scons: `subdir' is up to date.\n") + +test.run(chdir='subdir', arguments='-D -f main.scons .', stdout=expect) + + + +expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", + build_str = "scons: `.' is up to date.\n") + +test.run(chdir='other', arguments='-D -f ../subdir/sub.scons .', stdout=expect) + +test.run(chdir='other', + arguments='-D -f %s .' % test.workpath('subdir', 'sub.scons'), + stdout=expect) + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: From 0bf264eacaa8e29dec30c352f45eb2951ac3ac63 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 27 May 2024 21:39:37 -0700 Subject: [PATCH 050/386] minimize tool initialization in tests --- test/CPPPATH/Dir.py | 170 ++++++++--------- test/CPPPATH/absolute-path.py | 201 ++++++++++---------- test/CPPPATH/expand-object.py | 138 +++++++------- test/CPPPATH/function-expansion.py | 285 ++++++++++++++--------------- test/CPPPATH/list-expansion.py | 276 ++++++++++++++-------------- test/CPPPATH/match-dir.py | 148 +++++++-------- test/CPPPATH/nested-lists.py | 166 ++++++++--------- test/CPPPATH/null.py | 122 ++++++------ test/CPPPATH/subdir-as-include.py | 196 ++++++++++---------- 9 files changed, 850 insertions(+), 852 deletions(-) diff --git a/test/CPPPATH/Dir.py b/test/CPPPATH/Dir.py index 8b557dcfab..8275c0e243 100644 --- a/test/CPPPATH/Dir.py +++ b/test/CPPPATH/Dir.py @@ -1,85 +1,85 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that CPPPATH values with Dir nodes work correctly. -""" - -import TestSCons - -_exe = TestSCons._exe - -test = TestSCons.TestSCons() - -test.subdir('inc1', 'inc2', 'inc3', ['inc3', 'subdir']) - -test.write('SConstruct', """ -env = Environment(CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir'], - INC2 = Dir('inc2'), - INC3 = Dir('inc3')) -env.Program('prog.c') -""") - -test.write('prog.c', """\ -#include -#include - -#include "one.h" -#include "two.h" -#include "three.h" -int -main(int argc, char *argv[]) -{ - printf("%s\\n", ONE); - printf("%s\\n", TWO); - printf("%s\\n", THREE); - return (0); -} -""") - -test.write(['inc1', 'one.h'], """\ -#define ONE "1" -""") - -test.write(['inc2', 'two.h'], """\ -#define TWO "2" -""") - -test.write(['inc3', 'subdir', 'three.h'], """\ -#define THREE "3" -""") - -test.run(arguments = '.') - -test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that CPPPATH values with Dir nodes work correctly. +""" + +import TestSCons + +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.subdir('inc1', 'inc2', 'inc3', ['inc3', 'subdir']) + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir'], + INC2 = Dir('inc2'), + INC3 = Dir('inc3')) +env.Program('prog.c') +""") + +test.write('prog.c', """\ +#include +#include + +#include "one.h" +#include "two.h" +#include "three.h" +int +main(int argc, char *argv[]) +{ + printf("%s\\n", ONE); + printf("%s\\n", TWO); + printf("%s\\n", THREE); + return (0); +} +""") + +test.write(['inc1', 'one.h'], """\ +#define ONE "1" +""") + +test.write(['inc2', 'two.h'], """\ +#define TWO "2" +""") + +test.write(['inc3', 'subdir', 'three.h'], """\ +#define THREE "3" +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/absolute-path.py b/test/CPPPATH/absolute-path.py index f414e09ecb..a73f4fcec8 100644 --- a/test/CPPPATH/absolute-path.py +++ b/test/CPPPATH/absolute-path.py @@ -1,101 +1,100 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify the ability to #include a file with an absolute path name. (Which -is not strictly a test of using $CPPPATH, but it's in the ball park...) -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('include', 'work') - -inc1_h = test.workpath('include', 'inc1.h') -inc2_h = test.workpath('include', 'inc2.h') -does_not_exist_h = test.workpath('include', 'does_not_exist.h') - -# Verify that including an absolute path still works even if they -# double the separators in the input file. This can happen especially -# on Windows if they use \\ to represent an escaped backslash. -inc2_h = inc2_h.replace(os.sep, os.sep+os.sep) - -test.write(['work', 'SConstruct'], """\ -Program('prog.c') -""") - -test.write(['work', 'prog.c'], """\ -#include -#include "%(inc1_h)s" -#include "%(inc2_h)s" -#if 0 -#include "%(does_not_exist_h)s" -#endif - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("%%s\\n", STRING1); - printf("%%s\\n", STRING2); - return 0; -} -""" % locals()) - -test.write(['include', 'inc1.h'], """\ -#define STRING1 "include/inc1.h A\\n" -""") - -test.write(['include', 'inc2.h'], """\ -#define STRING2 "include/inc2.h A\\n" -""") - -test.run(chdir = 'work', arguments = '.') - -test.up_to_date(chdir = 'work', arguments = '.') - -test.write(['include', 'inc1.h'], """\ -#define STRING1 "include/inc1.h B\\n" -""") - -test.not_up_to_date(chdir = 'work', arguments = '.') - -test.write(['include', 'inc2.h'], """\ -#define STRING2 "include/inc2.h B\\n" -""") - -test.not_up_to_date(chdir = 'work', arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the ability to #include a file with an absolute path name. (Which +is not strictly a test of using $CPPPATH, but it's in the ball park...) +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('include', 'work') + +inc1_h = test.workpath('include', 'inc1.h') +inc2_h = test.workpath('include', 'inc2.h') +does_not_exist_h = test.workpath('include', 'does_not_exist.h') + +# Verify that including an absolute path still works even if they +# double the separators in the input file. This can happen especially +# on Windows if they use \\ to represent an escaped backslash. +inc2_h = inc2_h.replace(os.sep, os.sep+os.sep) + +test.write(['work', 'SConstruct'], """\ +Program('prog.c') +""") + +test.write(['work', 'prog.c'], """\ +#include +#include "%(inc1_h)s" +#include "%(inc2_h)s" +#if 0 +#include "%(does_not_exist_h)s" +#endif + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("%%s\\n", STRING1); + printf("%%s\\n", STRING2); + return 0; +} +""" % locals()) + +test.write(['include', 'inc1.h'], """\ +#define STRING1 "include/inc1.h A\\n" +""") + +test.write(['include', 'inc2.h'], """\ +#define STRING2 "include/inc2.h A\\n" +""") + +test.run(chdir = 'work', arguments = '.') + +test.up_to_date(chdir = 'work', arguments = '.') + +test.write(['include', 'inc1.h'], """\ +#define STRING1 "include/inc1.h B\\n" +""") + +test.not_up_to_date(chdir = 'work', arguments = '.') + +test.write(['include', 'inc2.h'], """\ +#define STRING2 "include/inc2.h B\\n" +""") + +test.not_up_to_date(chdir = 'work', arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/expand-object.py b/test/CPPPATH/expand-object.py index 54e1d397a0..5b751bb823 100644 --- a/test/CPPPATH/expand-object.py +++ b/test/CPPPATH/expand-object.py @@ -1,69 +1,69 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Make sure that $CPPPATH expands correctly if one of the subsidiary -expansions contains a stringable non-Node object. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -class XXX: - def __init__(self, value): - self.value = value - def __str__(self): - return 'XXX-' + self.value + '-XXX' -env = Environment(CPPPATH = ['#', - '$BUILDDIR', - '/tmp/xyzzy'], - BUILDDIR = '#scons_build/$EXPANSION', - EXPANSION = XXX('win32')) -env.Object('foo.c') -""") - -test.write('foo.c', """\ -#include -void -foo(void) -{ - printf("foo.c\\n"); -} -""") - -test.run(arguments = '.') - -test.must_exist(test.workpath('foo' + TestSCons._obj)) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Make sure that $CPPPATH expands correctly if one of the subsidiary +expansions contains a stringable non-Node object. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +class XXX: + def __init__(self, value): + self.value = value + def __str__(self): + return 'XXX-' + self.value + '-XXX' +env = Environment(CPPPATH = ['#', + '$BUILDDIR', + '/tmp/xyzzy'], + BUILDDIR = '#scons_build/$EXPANSION', + EXPANSION = XXX('win32')) +env.Object('foo.c') +""") + +test.write('foo.c', """\ +#include +void +foo(void) +{ + printf("foo.c\\n"); +} +""") + +test.run(arguments = '.') + +test.must_exist(test.workpath('foo' + TestSCons._obj)) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/function-expansion.py b/test/CPPPATH/function-expansion.py index 8ddd23c142..b9bc5be8db 100644 --- a/test/CPPPATH/function-expansion.py +++ b/test/CPPPATH/function-expansion.py @@ -1,143 +1,142 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that expansion of construction variables whose values are functions -(that return lists that contain Nodes, even) works as expected within -a $CPPPATH list definition. - -This used to cause TypeErrors when the _concat expansion tried to -join the Nodes with the strings. - -Test courtesy Konstantin Bozhikov. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('list_inc1', - 'list_inc2', - 'inc1', - 'inc2', - 'inc3', - ['inc3', 'subdir']) - -test.write('SConstruct', """\ -env = Environment() -def my_cpppaths( target, source, env, for_signature ): - return [ Dir('list_inc1'), Dir('list_inc2') ] - -env = Environment( CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir', '$MY_CPPPATHS' ], - INC2 = Dir('inc2'), - INC3 = Dir('inc3'), - MY_CPPPATHS = my_cpppaths ) - -env.Program('prog.c') -""") - -test.write('prog.c', """\ -#include -#include -#include "string_list_1.h" -#include "string_list_2.h" -#include "string_1.h" -#include "string_2.h" -#include "string_3.h" - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("prog.c\\n"); - printf("%s\\n", STRING_LIST_1); - printf("%s\\n", STRING_LIST_2); - printf("%s\\n", STRING_1); - printf("%s\\n", STRING_2); - printf("%s\\n", STRING_3); - exit (0); -} -""") - -test.write(['list_inc1', 'string_list_1.h'], """\ -#define STRING_LIST_1 "list_inc1/string_list_1.h" -""") - -test.write(['list_inc2', 'string_list_2.h'], """\ -#define STRING_LIST_2 "list_inc2/string_list_2.h" -""") - -test.write(['inc1', 'string_1.h'], """\ -#define STRING_1 "inc1/string_1.h" -""") - -test.write(['inc2', 'string_2.h'], """\ -#define STRING_2 "inc2/string_2.h" -""") - -test.write(['inc3', 'subdir', 'string_3.h'], """\ -#define STRING_3 "inc3/subdir/string_3.h" -""") - -test.run() - -test.up_to_date(arguments = '.') - -expect = """\ -prog.c -list_inc1/string_list_1.h -list_inc2/string_list_2.h -inc1/string_1.h -inc2/string_2.h -inc3/subdir/string_3.h -""" - -test.run(program = test.workpath('prog' + TestSCons._exe), stdout=expect) - -test.write(['inc3', 'subdir', 'string_3.h'], """\ -#define STRING_3 "inc3/subdir/string_3.h 2" -""") - -test.not_up_to_date(arguments = '.') - -expect = """\ -prog.c -list_inc1/string_list_1.h -list_inc2/string_list_2.h -inc1/string_1.h -inc2/string_2.h -inc3/subdir/string_3.h 2 -""" - -test.run(program = test.workpath('prog' + TestSCons._exe), stdout=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that expansion of construction variables whose values are functions +(that return lists that contain Nodes, even) works as expected within +a $CPPPATH list definition. + +This used to cause TypeErrors when the _concat expansion tried to +join the Nodes with the strings. + +Test courtesy Konstantin Bozhikov. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('list_inc1', + 'list_inc2', + 'inc1', + 'inc2', + 'inc3', + ['inc3', 'subdir']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def my_cpppaths( target, source, env, for_signature ): + return [ Dir('list_inc1'), Dir('list_inc2') ] + +env = Environment( CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir', '$MY_CPPPATHS' ], + INC2 = Dir('inc2'), + INC3 = Dir('inc3'), + MY_CPPPATHS = my_cpppaths ) + +env.Program('prog.c') +""") + +test.write('prog.c', """\ +#include +#include +#include "string_list_1.h" +#include "string_list_2.h" +#include "string_1.h" +#include "string_2.h" +#include "string_3.h" + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("prog.c\\n"); + printf("%s\\n", STRING_LIST_1); + printf("%s\\n", STRING_LIST_2); + printf("%s\\n", STRING_1); + printf("%s\\n", STRING_2); + printf("%s\\n", STRING_3); + exit (0); +} +""") + +test.write(['list_inc1', 'string_list_1.h'], """\ +#define STRING_LIST_1 "list_inc1/string_list_1.h" +""") + +test.write(['list_inc2', 'string_list_2.h'], """\ +#define STRING_LIST_2 "list_inc2/string_list_2.h" +""") + +test.write(['inc1', 'string_1.h'], """\ +#define STRING_1 "inc1/string_1.h" +""") + +test.write(['inc2', 'string_2.h'], """\ +#define STRING_2 "inc2/string_2.h" +""") + +test.write(['inc3', 'subdir', 'string_3.h'], """\ +#define STRING_3 "inc3/subdir/string_3.h" +""") + +test.run() + +test.up_to_date(arguments = '.') + +expect = """\ +prog.c +list_inc1/string_list_1.h +list_inc2/string_list_2.h +inc1/string_1.h +inc2/string_2.h +inc3/subdir/string_3.h +""" + +test.run(program = test.workpath('prog' + TestSCons._exe), stdout=expect) + +test.write(['inc3', 'subdir', 'string_3.h'], """\ +#define STRING_3 "inc3/subdir/string_3.h 2" +""") + +test.not_up_to_date(arguments = '.') + +expect = """\ +prog.c +list_inc1/string_list_1.h +list_inc2/string_list_2.h +inc1/string_1.h +inc2/string_2.h +inc3/subdir/string_3.h 2 +""" + +test.run(program = test.workpath('prog' + TestSCons._exe), 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/CPPPATH/list-expansion.py b/test/CPPPATH/list-expansion.py index 98817b9a9e..ad41ba24eb 100644 --- a/test/CPPPATH/list-expansion.py +++ b/test/CPPPATH/list-expansion.py @@ -1,138 +1,138 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that expansion of construction variables whose values are -lists works as expected within a $CPPPATH list definition. - -Previously, the stringification of the expansion of the individual -variables would turn a list like ['sub1', 'sub2'] below into "-Isub1 sub2" -on the command line. - -Test case courtesy Daniel Svensson. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('sub1', 'sub2', 'sub3', 'sub4') - -test.write('SConstruct', """\ -class _inc_test: - def __init__(self, name): - self.name = name - - def __call__(self, target, source, env, for_signature): - return env.something[self.name] - -env = Environment() - -env.something = {} -env.something['test'] = ['sub1', 'sub2'] - -env['INC_PATHS1'] = _inc_test - -env['INC_PATHS2'] = ['sub3', 'sub4'] - -env.Append(CPPPATH = ['${INC_PATHS1("test")}', '$INC_PATHS2']) -env.Program('test', 'test.c') -""") - -test.write('test.c', """\ -#include -#include -#include "string1.h" -#include "string2.h" -#include "string3.h" -#include "string4.h" - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("test.c\\n"); - printf("%s\\n", STRING1); - printf("%s\\n", STRING2); - printf("%s\\n", STRING3); - printf("%s\\n", STRING4); - exit (0); -} -""") - -test.write(['sub1', 'string1.h'], """\ -#define STRING1 "sub1/string1.h" -""") - -test.write(['sub2', 'string2.h'], """\ -#define STRING2 "sub2/string2.h" -""") - -test.write(['sub3', 'string3.h'], """\ -#define STRING3 "sub3/string3.h" -""") - -test.write(['sub4', 'string4.h'], """\ -#define STRING4 "sub4/string4.h" -""") - -test.run() - -test.up_to_date(arguments = '.') - -expect = """\ -test.c -sub1/string1.h -sub2/string2.h -sub3/string3.h -sub4/string4.h -""" - -test.run(program = test.workpath('test' + TestSCons._exe), stdout=expect) - -test.write(['sub2', 'string2.h'], """\ -#define STRING2 "sub2/string2.h 2" -""") - -test.not_up_to_date(arguments = '.') - -expect = """\ -test.c -sub1/string1.h -sub2/string2.h 2 -sub3/string3.h -sub4/string4.h -""" - -test.run(program = test.workpath('test' + TestSCons._exe), stdout=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that expansion of construction variables whose values are +lists works as expected within a $CPPPATH list definition. + +Previously, the stringification of the expansion of the individual +variables would turn a list like ['sub1', 'sub2'] below into "-Isub1 sub2" +on the command line. + +Test case courtesy Daniel Svensson. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('sub1', 'sub2', 'sub3', 'sub4') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +class _inc_test: + def __init__(self, name): + self.name = name + + def __call__(self, target, source, env, for_signature): + return env.something[self.name] + +env = Environment() + +env.something = {} +env.something['test'] = ['sub1', 'sub2'] + +env['INC_PATHS1'] = _inc_test + +env['INC_PATHS2'] = ['sub3', 'sub4'] + +env.Append(CPPPATH = ['${INC_PATHS1("test")}', '$INC_PATHS2']) +env.Program('test', 'test.c') +""") + +test.write('test.c', """\ +#include +#include +#include "string1.h" +#include "string2.h" +#include "string3.h" +#include "string4.h" + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("test.c\\n"); + printf("%s\\n", STRING1); + printf("%s\\n", STRING2); + printf("%s\\n", STRING3); + printf("%s\\n", STRING4); + exit (0); +} +""") + +test.write(['sub1', 'string1.h'], """\ +#define STRING1 "sub1/string1.h" +""") + +test.write(['sub2', 'string2.h'], """\ +#define STRING2 "sub2/string2.h" +""") + +test.write(['sub3', 'string3.h'], """\ +#define STRING3 "sub3/string3.h" +""") + +test.write(['sub4', 'string4.h'], """\ +#define STRING4 "sub4/string4.h" +""") + +test.run() + +test.up_to_date(arguments = '.') + +expect = """\ +test.c +sub1/string1.h +sub2/string2.h +sub3/string3.h +sub4/string4.h +""" + +test.run(program = test.workpath('test' + TestSCons._exe), stdout=expect) + +test.write(['sub2', 'string2.h'], """\ +#define STRING2 "sub2/string2.h 2" +""") + +test.not_up_to_date(arguments = '.') + +expect = """\ +test.c +sub1/string1.h +sub2/string2.h 2 +sub3/string3.h +sub4/string4.h +""" + +test.run(program = test.workpath('test' + TestSCons._exe), 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/CPPPATH/match-dir.py b/test/CPPPATH/match-dir.py index 6ec30b4b1f..fdbdfee901 100644 --- a/test/CPPPATH/match-dir.py +++ b/test/CPPPATH/match-dir.py @@ -1,74 +1,74 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that we don't blow up if there's a directory name within -$CPPPATH that matches a #include file name. -""" - -import sys - -import TestSCons - -test = TestSCons.TestSCons() - -# TODO(sgk): get this to work everywhere by using fake compilers -if sys.platform.find('sunos') != -1: - msg = 'SunOS C compiler does not handle this case; skipping test.\n' - test.skip_test(msg) - -test.subdir(['src'], - ['src', 'inc'], - ['src', 'inc', 'inc2']) - -test.write('SConstruct', """\ -SConscript('src/SConscript', variant_dir = 'build', duplicate = 0) -""") - -test.write(['src', 'SConscript'], """\ -env = Environment(CPPPATH = ['#build/inc', '#build/inc/inc2']) -env.Object('foo.c') -""") - -test.write(['src', 'foo.c'], """\ -#include "inc1" -""") - -test.subdir(['src', 'inc', 'inc1']) - -test.write(['src', 'inc', 'inc2', 'inc1'], "\n") - -test.run(arguments = '.') - -test.up_to_date(arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that we don't blow up if there's a directory name within +$CPPPATH that matches a #include file name. +""" + +import sys + +import TestSCons + +test = TestSCons.TestSCons() + +# TODO(sgk): get this to work everywhere by using fake compilers +if sys.platform.find('sunos') != -1: + msg = 'SunOS C compiler does not handle this case; skipping test.\n' + test.skip_test(msg) + +test.subdir(['src'], + ['src', 'inc'], + ['src', 'inc', 'inc2']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +SConscript('src/SConscript', variant_dir = 'build', duplicate = 0) +""") + +test.write(['src', 'SConscript'], """\ +env = Environment(CPPPATH = ['#build/inc', '#build/inc/inc2']) +env.Object('foo.c') +""") + +test.write(['src', 'foo.c'], """\ +#include "inc1" +""") + +test.subdir(['src', 'inc', 'inc1']) + +test.write(['src', 'inc', 'inc2', 'inc1'], "\n") + +test.run(arguments = '.') + +test.up_to_date(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/nested-lists.py b/test/CPPPATH/nested-lists.py index aa48b920b9..f47829d64c 100644 --- a/test/CPPPATH/nested-lists.py +++ b/test/CPPPATH/nested-lists.py @@ -1,83 +1,83 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that CPPPATH values consisting of nested lists work correctly. -""" - -import TestSCons - -_exe = TestSCons._exe - -test = TestSCons.TestSCons() - -test.subdir('inc1', 'inc2', 'inc3') - -test.write('SConstruct', """ -env = Environment(CPPPATH = ['inc1', ['inc2', ['inc3']]]) -env.Program('prog.c') -""") - -test.write('prog.c', """\ -#include -#include - -#include "one.h" -#include "two.h" -#include "three.h" -int -main(int argc, char *argv[]) -{ - printf("%s\\n", ONE); - printf("%s\\n", TWO); - printf("%s\\n", THREE); - return (0); -} -""") - -test.write(['inc1', 'one.h'], """\ -#define ONE "1" -""") - -test.write(['inc2', 'two.h'], """\ -#define TWO "2" -""") - -test.write(['inc3', 'three.h'], """\ -#define THREE "3" -""") - -test.run(arguments = '.') - -test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that CPPPATH values consisting of nested lists work correctly. +""" + +import TestSCons + +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.subdir('inc1', 'inc2', 'inc3') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CPPPATH = ['inc1', ['inc2', ['inc3']]]) +env.Program('prog.c') +""") + +test.write('prog.c', """\ +#include +#include + +#include "one.h" +#include "two.h" +#include "three.h" +int +main(int argc, char *argv[]) +{ + printf("%s\\n", ONE); + printf("%s\\n", TWO); + printf("%s\\n", THREE); + return (0); +} +""") + +test.write(['inc1', 'one.h'], """\ +#define ONE "1" +""") + +test.write(['inc2', 'two.h'], """\ +#define TWO "2" +""") + +test.write(['inc3', 'three.h'], """\ +#define THREE "3" +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/null.py b/test/CPPPATH/null.py index e23f65582e..17f0a8c9cc 100644 --- a/test/CPPPATH/null.py +++ b/test/CPPPATH/null.py @@ -1,61 +1,61 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that neither a null-string CPPPATH nor a -a CPPPATH containing null values blows up. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -env = Environment(CPPPATH = '') -env.Library('one', source = 'empty1.c') -env = Environment(CPPPATH = [None]) -env.Library('two', source = 'empty2.c') -env = Environment(CPPPATH = ['']) -env.Library('three', source = 'empty3.c') -""") - -test.write('empty1.c', "int a=0;\n") -test.write('empty2.c', "int b=0;\n") -test.write('empty3.c', "int c=0;\n") - -test.run(arguments = '.', - stderr=TestSCons.noisy_ar, - match=TestSCons.match_re_dotall) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 SOFTWAR + +""" +Verify that neither a null-string CPPPATH nor a +a CPPPATH containing null values blows up. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CPPPATH = '') +env.Library('one', source = 'empty1.c') +env = Environment(CPPPATH = [None]) +env.Library('two', source = 'empty2.c') +env = Environment(CPPPATH = ['']) +env.Library('three', source = 'empty3.c') +""") + +test.write('empty1.c', "int a=0;\n") +test.write('empty2.c', "int b=0;\n") +test.write('empty3.c', "int c=0;\n") + +test.run(arguments = '.', + stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/subdir-as-include.py b/test/CPPPATH/subdir-as-include.py index 06a1a58ca0..27e687fd83 100644 --- a/test/CPPPATH/subdir-as-include.py +++ b/test/CPPPATH/subdir-as-include.py @@ -1,98 +1,98 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -This is an obscure test case. When a file without a suffix is included in -a c++ build and there is a directory with the same name as that file in a -sub-build directory, verify that an Errno 21 is not thrown upon trying to -recursively scan the contents of includes. The Errno 21 indicates that -the directory with the same name was trying to be scanned as the include -file, which it clearly is not. -""" - -import TestSCons - -_exe = TestSCons._exe - -test = TestSCons.TestSCons() - -test.subdir('inc1', ['inc1', 'iterator']) - -test.write('SConstruct', """ -env = Environment(CPPPATH = [Dir('inc1')]) -env.Program('prog.cpp') - -Export('env') -SConscript('inc1/SConscript', variant_dir='inc1/build', duplicate=0) -""") - -test.write('prog.cpp', """\ -#include -#include - -#include "one.h" -#include -int main(int argc, char* argv[]) -{ - printf("%s\\n", ONE); - return 0; -} -""") - -test.write(['inc1', 'SConscript'], """\ -Import('env') -oneenv = env.Clone() -oneenv.Program('one.cpp') -""") - -test.write(['inc1', 'one.h'], """\ -#define ONE "1" -""") - -test.write(['inc1', 'one.cpp'], """\ -#include -#include -#include "one.h" - -int main(int argc, char* argv[]) -{ - printf("%s\\n", ONE); - return 0; -} -""") - -test.run(arguments = '.') - -test.run(program = test.workpath('prog' + _exe), stdout = "1\n") -test.run(program = test.workpath('inc1/build/one' + _exe), stdout = "1\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 SOFTWAR + +""" +This is an obscure test case. When a file without a suffix is included in +a c++ build and there is a directory with the same name as that file in a +sub-build directory, verify that an Errno 21 is not thrown upon trying to +recursively scan the contents of includes. The Errno 21 indicates that +the directory with the same name was trying to be scanned as the include +file, which it clearly is not. +""" + +import TestSCons + +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.subdir('inc1', ['inc1', 'iterator']) + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CPPPATH = [Dir('inc1')]) +env.Program('prog.cpp') + +Export('env') +SConscript('inc1/SConscript', variant_dir='inc1/build', duplicate=0) +""") + +test.write('prog.cpp', """\ +#include +#include + +#include "one.h" +#include +int main(int argc, char* argv[]) +{ + printf("%s\\n", ONE); + return 0; +} +""") + +test.write(['inc1', 'SConscript'], """\ +Import('env') +oneenv = env.Clone() +oneenv.Program('one.cpp') +""") + +test.write(['inc1', 'one.h'], """\ +#define ONE "1" +""") + +test.write(['inc1', 'one.cpp'], """\ +#include +#include +#include "one.h" + +int main(int argc, char* argv[]) +{ + printf("%s\\n", ONE); + return 0; +} +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('prog' + _exe), stdout = "1\n") +test.run(program = test.workpath('inc1/build/one' + _exe), stdout = "1\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: From 760baa87801203063ee8244215ce42853602e9d9 Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Thu, 30 May 2024 11:37:47 -0500 Subject: [PATCH 051/386] Update `.gitattributes` to match `.editorconfig` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Expand `.editorconfig` with a few missing extensions & match line-length specified in `.flake8` --- .editorconfig | 8 ++++---- .gitattributes | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.editorconfig b/.editorconfig index 1889885a7d..f6fbafbbe5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,17 +10,17 @@ trim_trailing_whitespace = true end_of_line = lf charset = utf-8 -[*.py] -max_line_length = 78 +[{*.py,SConstruct,SConscript}] +max_line_length = 88 ensure_newline_before_comments = true include_trailing_comma = true use_parentheses = true -[*.xml] +[*.{xml,yml,yaml}] indent_size = 2 [*.rst] indent_size = 3 -[*.bat] +[*.{bat,cmd,ps1}] end_of_line = crlf diff --git a/.gitattributes b/.gitattributes index 8b807dfb94..dcf034f78c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,10 @@ +* text=auto eol=lf +*.bat eol=crlf +*.cmd eol=crlf +*.ps1 eol=crlf + doc/* linguist-documentation SCons/Tool/docbook/docbook-xsl-1.76.1 linguist-vendored *.xml linguist-documentation *.xsl linguist-documentation *.gen linguist-documentation - -*.in eol=lf From fbb026ef1145fe29e0ec3c1b66a3e99cac51e18d Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Thu, 30 May 2024 16:56:09 -0500 Subject: [PATCH 052/386] Enforce `.editorconfig` eol settings --- test/AR/ARCOM.py | 128 +++---- test/AR/ARCOMSTR.py | 134 +++---- test/AS/ASCOM.py | 180 ++++----- test/AS/ASCOMSTR.py | 166 ++++---- test/AS/ASPPCOM.py | 138 +++---- test/AS/ASPPCOMSTR.py | 132 +++---- test/AS/ml.py | 242 ++++++------ test/Actions/addpost-link.py | 150 ++++---- test/Actions/exitstatfunc.py | 146 +++---- test/AddOption/basic.py | 148 +++---- test/AddOption/help.py | 162 ++++---- test/AddOption/optional-arg.py | 214 +++++------ test/Alias/Dir-order.py | 98 ++--- test/Alias/errors.py | 98 ++--- test/Alias/scanner.py | 128 +++---- test/Batch/Boolean.py | 162 ++++---- test/Batch/CHANGED_SOURCES.py | 250 ++++++------ test/Batch/SOURCES.py | 254 ++++++------ test/Batch/action-changed.py | 208 +++++----- test/Batch/callable.py | 220 +++++------ test/Batch/changed_sources_alwaysbuild.py | 106 ++--- test/Batch/generated.py | 170 ++++---- test/Batch/removed.py | 224 +++++------ test/Batch/up_to_date.py | 188 ++++----- test/Builder/TargetSubst.py | 104 ++--- test/Builder/add_src_builder.py | 142 +++---- test/Builder/different-actions.py | 116 +++--- test/Builder/ensure_suffix.py | 122 +++--- test/Builder/non-multi.py | 134 +++---- test/Builder/not-a-Builder.py | 120 +++--- test/Builder/same-actions-diff-envs.py | 126 +++--- test/Builder/same-actions-diff-overrides.py | 124 +++--- test/Builder/srcdir.py | 166 ++++---- test/Builder/wrapper.py | 154 ++++---- test/CC/CCCOM.py | 144 +++---- test/CC/CCCOMSTR.py | 154 ++++---- test/CC/CCFLAGS.py | 222 +++++------ test/CC/CFLAGS.py | 248 ++++++------ test/CC/SHCCCOM.py | 144 +++---- test/CC/SHCCCOMSTR.py | 158 ++++---- test/CC/SHCFLAGS.py | 276 ++++++------- test/CPPPATH/Dir.py | 170 ++++---- test/CPPPATH/absolute-path.py | 200 +++++----- test/CPPPATH/expand-object.py | 138 +++---- test/CPPPATH/function-expansion.py | 284 +++++++------- test/CPPPATH/list-expansion.py | 276 ++++++------- test/CPPPATH/match-dir.py | 148 +++---- test/CPPPATH/nested-lists.py | 166 ++++---- test/CPPPATH/null.py | 122 +++--- test/CPPPATH/subdir-as-include.py | 196 +++++----- test/CacheDir/CacheDir.py | 328 ++++++++-------- test/CacheDir/SideEffect.py | 228 +++++------ test/CacheDir/VariantDir.py | 312 +++++++-------- test/CacheDir/debug.py | 402 +++++++++---------- test/CacheDir/environment.py | 340 ++++++++-------- test/CacheDir/multi-targets.py | 156 ++++---- test/CacheDir/option--cf.py | 262 ++++++------- test/CacheDir/symlink.py | 152 ++++---- test/CacheDir/up-to-date-q.py | 172 ++++----- test/CacheDir/value_dependencies.py | 102 ++--- test/Clang/clang_default_environment.py | 124 +++--- test/Clang/clang_specific_environment.py | 120 +++--- test/Clang/clang_static_library.py | 136 +++---- test/Clang/clangxx_default_environment.py | 124 +++--- test/Clang/clangxx_shared_library.py | 154 ++++---- test/Clang/clangxx_specific_environment.py | 120 +++--- test/Clang/clangxx_static_library.py | 136 +++---- test/Clean/basic.py | 370 +++++++++--------- test/Clean/function.py | 238 ++++++------ test/Clean/mkfifo.py | 162 ++++---- test/Clean/symlinks.py | 126 +++--- test/Climb/U-Default-dir.py | 96 ++--- test/Climb/U-Default-no-target.py | 100 ++--- test/Climb/U-no-Default.py | 94 ++--- test/Climb/explicit-parent--D.py | 158 ++++---- test/Climb/explicit-parent--U.py | 144 +++---- test/Climb/explicit-parent-u.py | 156 ++++---- test/Climb/filename--D.py | 158 ++++---- test/Climb/filename--U.py | 154 ++++---- test/Climb/filename-u.py | 154 ++++---- test/Climb/option--D.py | 178 ++++----- test/Climb/option--U.py | 288 +++++++------- test/Climb/option-u.py | 296 +++++++------- test/Configure/Action-error.py | 110 +++--- test/Configure/Builder-call.py | 128 +++---- test/Configure/CONFIGUREDIR.py | 114 +++--- test/Configure/CONFIGURELOG.py | 136 +++---- test/Configure/SConscript.py | 160 ++++---- test/Configure/Streamer1.py | 170 ++++---- test/Configure/VariantDir.py | 198 +++++----- test/Configure/basic.py | 182 ++++----- test/Configure/build-fail.py | 194 +++++----- test/Configure/cache-not-ok.py | 204 +++++----- test/Configure/cache-ok.py | 252 ++++++------ test/Configure/clean.py | 168 ++++---- test/Configure/custom-tests.py | 404 ++++++++++---------- test/Configure/from-SConscripts.py | 126 +++--- test/Configure/help.py | 188 ++++----- test/Configure/option--Q.py | 104 ++--- 99 files changed, 8739 insertions(+), 8739 deletions(-) diff --git a/test/AR/ARCOM.py b/test/AR/ARCOM.py index 010b6c584a..ddb5ba90c4 100644 --- a/test/AR/ARCOM.py +++ b/test/AR/ARCOM.py @@ -1,64 +1,64 @@ -#!/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. - -""" -Test the ability to configure the $ARCOM construction variable. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') -test.file_fixture('myrewrite.py') - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['default', 'ar'], - ARCOM = r'%(_python_)s mycompile.py ar $TARGET $SOURCES', - RANLIB = True, - RANLIBCOM = r'%(_python_)s myrewrite.py ranlib $TARGET', - LIBPREFIX = '', - LIBSUFFIX = '.lib') -env.Library(target = 'output', source = ['file.1', 'file.2']) -""" % locals()) - -test.write('file.1', "file.1\n/*ar*/\n/*ranlib*/\n") -test.write('file.2', "file.2\n/*ar*/\n/*ranlib*/\n") - - -test.run(arguments = '.') - -test.must_match('output.lib', "file.1\nfile.2\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to configure the $ARCOM construction variable. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') +test.file_fixture('myrewrite.py') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['default', 'ar'], + ARCOM = r'%(_python_)s mycompile.py ar $TARGET $SOURCES', + RANLIB = True, + RANLIBCOM = r'%(_python_)s myrewrite.py ranlib $TARGET', + LIBPREFIX = '', + LIBSUFFIX = '.lib') +env.Library(target = 'output', source = ['file.1', 'file.2']) +""" % locals()) + +test.write('file.1', "file.1\n/*ar*/\n/*ranlib*/\n") +test.write('file.2', "file.2\n/*ar*/\n/*ranlib*/\n") + + +test.run(arguments = '.') + +test.must_match('output.lib', "file.1\nfile.2\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AR/ARCOMSTR.py b/test/AR/ARCOMSTR.py index 95a0cd6ff6..593949ff88 100644 --- a/test/AR/ARCOMSTR.py +++ b/test/AR/ARCOMSTR.py @@ -1,67 +1,67 @@ -#!/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. - -""" -Test that the $ARCOMSTR construction variable allows you to customize -the displayed archiver string. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') -test.file_fixture('myrewrite.py') - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['default', 'ar'], - ARCOM = r'%(_python_)s mycompile.py ar $TARGET $SOURCES', - ARCOMSTR = 'Archiving $TARGET from $SOURCES', - RANLIB = True, - RANLIBCOM = r'%(_python_)s myrewrite.py ranlib $TARGET', - LIBPREFIX = '', - LIBSUFFIX = '.lib') -env.Library(target = 'output', source = ['file.1', 'file.2']) -""" % locals()) - -test.write('file.1', "file.1\n/*ar*/\n/*ranlib*/\n") -test.write('file.2', "file.2\n/*ar*/\n/*ranlib*/\n") - -test.run() - -expect = 'Archiving output.lib from file.1 file.2' -test.must_contain_all_lines(test.stdout(), [expect]) -test.must_match('output.lib', "file.1\nfile.2\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $ARCOMSTR construction variable allows you to customize +the displayed archiver string. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') +test.file_fixture('myrewrite.py') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['default', 'ar'], + ARCOM = r'%(_python_)s mycompile.py ar $TARGET $SOURCES', + ARCOMSTR = 'Archiving $TARGET from $SOURCES', + RANLIB = True, + RANLIBCOM = r'%(_python_)s myrewrite.py ranlib $TARGET', + LIBPREFIX = '', + LIBSUFFIX = '.lib') +env.Library(target = 'output', source = ['file.1', 'file.2']) +""" % locals()) + +test.write('file.1', "file.1\n/*ar*/\n/*ranlib*/\n") +test.write('file.2', "file.2\n/*ar*/\n/*ranlib*/\n") + +test.run() + +expect = 'Archiving output.lib from file.1 file.2' +test.must_contain_all_lines(test.stdout(), [expect]) +test.must_match('output.lib', "file.1\nfile.2\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AS/ASCOM.py b/test/AS/ASCOM.py index 14291cf82c..01add8cafa 100644 --- a/test/AS/ASCOM.py +++ b/test/AS/ASCOM.py @@ -1,90 +1,90 @@ -#!/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. - -""" -Test the ability to configure the $ASCOM construction variable. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.s') == os.path.normcase('.S'): - alt_s_suffix = '.S' - alt_asm_suffix = '.ASM' -else: - alt_s_suffix = '.s' - alt_asm_suffix = '.asm' - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['as'], - ASCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', - OBJSUFFIX = '.obj', - SHOBJPREFIX = '', - SHOBJSUFFIX = '.shobj') -env.Object(target = 'test1', source = 'test1.s') -env.Object(target = 'test2', source = 'test2%(alt_s_suffix)s') -env.Object(target = 'test3', source = 'test3.asm') -env.Object(target = 'test4', source = 'test4%(alt_asm_suffix)s') -env.SharedObject(target = 'test5', source = 'test5.s') -env.SharedObject(target = 'test6', source = 'test6%(alt_s_suffix)s') -env.SharedObject(target = 'test7', source = 'test7.asm') -env.SharedObject(target = 'test8', source = 'test8%(alt_asm_suffix)s') -""" % locals()) - -test.write('test1.s', "test1.s\n/*as*/\n") -test.write('test2'+alt_s_suffix, "test2.S\n/*as*/\n") -test.write('test3.asm', "test3.asm\n/*as*/\n") -test.write('test4'+alt_asm_suffix, "test4.ASM\n/*as*/\n") -test.write('test5.s', "test5.s\n/*as*/\n") -test.write('test6'+alt_s_suffix, "test6.S\n/*as*/\n") -test.write('test7.asm', "test7.asm\n/*as*/\n") -test.write('test8'+alt_asm_suffix, "test8.ASM\n/*as*/\n") - -test.run(arguments = '.') - -test.must_match('test1.obj', "test1.s\n") -test.must_match('test2.obj', "test2.S\n") -test.must_match('test3.obj', "test3.asm\n") -test.must_match('test4.obj', "test4.ASM\n") -test.must_match('test5.shobj', "test5.s\n") -test.must_match('test6.shobj', "test6.S\n") -test.must_match('test7.shobj', "test7.asm\n") -test.must_match('test8.shobj', "test8.ASM\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to configure the $ASCOM construction variable. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.s') == os.path.normcase('.S'): + alt_s_suffix = '.S' + alt_asm_suffix = '.ASM' +else: + alt_s_suffix = '.s' + alt_asm_suffix = '.asm' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['as'], + ASCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', + OBJSUFFIX = '.obj', + SHOBJPREFIX = '', + SHOBJSUFFIX = '.shobj') +env.Object(target = 'test1', source = 'test1.s') +env.Object(target = 'test2', source = 'test2%(alt_s_suffix)s') +env.Object(target = 'test3', source = 'test3.asm') +env.Object(target = 'test4', source = 'test4%(alt_asm_suffix)s') +env.SharedObject(target = 'test5', source = 'test5.s') +env.SharedObject(target = 'test6', source = 'test6%(alt_s_suffix)s') +env.SharedObject(target = 'test7', source = 'test7.asm') +env.SharedObject(target = 'test8', source = 'test8%(alt_asm_suffix)s') +""" % locals()) + +test.write('test1.s', "test1.s\n/*as*/\n") +test.write('test2'+alt_s_suffix, "test2.S\n/*as*/\n") +test.write('test3.asm', "test3.asm\n/*as*/\n") +test.write('test4'+alt_asm_suffix, "test4.ASM\n/*as*/\n") +test.write('test5.s', "test5.s\n/*as*/\n") +test.write('test6'+alt_s_suffix, "test6.S\n/*as*/\n") +test.write('test7.asm', "test7.asm\n/*as*/\n") +test.write('test8'+alt_asm_suffix, "test8.ASM\n/*as*/\n") + +test.run(arguments = '.') + +test.must_match('test1.obj', "test1.s\n") +test.must_match('test2.obj', "test2.S\n") +test.must_match('test3.obj', "test3.asm\n") +test.must_match('test4.obj', "test4.ASM\n") +test.must_match('test5.shobj', "test5.s\n") +test.must_match('test6.shobj', "test6.S\n") +test.must_match('test7.shobj', "test7.asm\n") +test.must_match('test8.shobj', "test8.ASM\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AS/ASCOMSTR.py b/test/AS/ASCOMSTR.py index a97c285775..956c97cb6d 100644 --- a/test/AS/ASCOMSTR.py +++ b/test/AS/ASCOMSTR.py @@ -1,83 +1,83 @@ -#!/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. - -""" -Test that the $ASCOMSTR construction variable allows you to configure -the assembly output. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.s') == os.path.normcase('.S'): - alt_s_suffix = '.S' - alt_asm_suffix = '.ASM' -else: - alt_s_suffix = '.s' - alt_asm_suffix = '.asm' - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['as'], - ASCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', - ASCOMSTR = 'Assembling $TARGET from $SOURCE', - OBJSUFFIX = '.obj') -env.Object(target = 'test1', source = 'test1.s') -env.Object(target = 'test2', source = 'test2%(alt_s_suffix)s') -env.Object(target = 'test3', source = 'test3.asm') -env.Object(target = 'test4', source = 'test4%(alt_asm_suffix)s') -""" % locals()) - -test.write('test1.s', "test1.s\n/*as*/\n") -test.write('test2'+alt_s_suffix, "test2.S\n/*as*/\n") -test.write('test3.asm', "test3.asm\n/*as*/\n") -test.write('test4'+alt_asm_suffix, "test4.ASM\n/*as*/\n") - -test.run(stdout = test.wrap_stdout("""\ -Assembling test1.obj from test1.s -Assembling test2.obj from test2%(alt_s_suffix)s -Assembling test3.obj from test3.asm -Assembling test4.obj from test4%(alt_asm_suffix)s -""" % locals())) - -test.must_match('test1.obj', "test1.s\n") -test.must_match('test2.obj', "test2.S\n") -test.must_match('test3.obj', "test3.asm\n") -test.must_match('test4.obj', "test4.ASM\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $ASCOMSTR construction variable allows you to configure +the assembly output. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.s') == os.path.normcase('.S'): + alt_s_suffix = '.S' + alt_asm_suffix = '.ASM' +else: + alt_s_suffix = '.s' + alt_asm_suffix = '.asm' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['as'], + ASCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', + ASCOMSTR = 'Assembling $TARGET from $SOURCE', + OBJSUFFIX = '.obj') +env.Object(target = 'test1', source = 'test1.s') +env.Object(target = 'test2', source = 'test2%(alt_s_suffix)s') +env.Object(target = 'test3', source = 'test3.asm') +env.Object(target = 'test4', source = 'test4%(alt_asm_suffix)s') +""" % locals()) + +test.write('test1.s', "test1.s\n/*as*/\n") +test.write('test2'+alt_s_suffix, "test2.S\n/*as*/\n") +test.write('test3.asm', "test3.asm\n/*as*/\n") +test.write('test4'+alt_asm_suffix, "test4.ASM\n/*as*/\n") + +test.run(stdout = test.wrap_stdout("""\ +Assembling test1.obj from test1.s +Assembling test2.obj from test2%(alt_s_suffix)s +Assembling test3.obj from test3.asm +Assembling test4.obj from test4%(alt_asm_suffix)s +""" % locals())) + +test.must_match('test1.obj', "test1.s\n") +test.must_match('test2.obj', "test2.S\n") +test.must_match('test3.obj', "test3.asm\n") +test.must_match('test4.obj', "test4.ASM\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AS/ASPPCOM.py b/test/AS/ASPPCOM.py index 6b704b437f..5c4c7458c3 100644 --- a/test/AS/ASPPCOM.py +++ b/test/AS/ASPPCOM.py @@ -1,69 +1,69 @@ -#!/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. - -""" -Test the ability to configure the $ASPPCOM construction variable. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['as'], - ASPPCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', - OBJSUFFIX = '.obj', - SHOBJPREFIX = '', - SHOBJSUFFIX = '.shobj') -env.Object(target = 'test1', source = 'test1.spp') -env.Object(target = 'test2', source = 'test2.SPP') -env.SharedObject(target = 'test3', source = 'test3.spp') -env.SharedObject(target = 'test4', source = 'test4.SPP') -""" % locals()) - -test.write('test1.spp', "test1.spp\n/*as*/\n") -test.write('test2.SPP', "test2.SPP\n/*as*/\n") -test.write('test3.spp', "test3.spp\n/*as*/\n") -test.write('test4.SPP', "test4.SPP\n/*as*/\n") - -test.run(arguments = '.') - -test.must_match('test1.obj', "test1.spp\n") -test.must_match('test2.obj', "test2.SPP\n") -test.must_match('test3.shobj', "test3.spp\n") -test.must_match('test4.shobj', "test4.SPP\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to configure the $ASPPCOM construction variable. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['as'], + ASPPCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', + OBJSUFFIX = '.obj', + SHOBJPREFIX = '', + SHOBJSUFFIX = '.shobj') +env.Object(target = 'test1', source = 'test1.spp') +env.Object(target = 'test2', source = 'test2.SPP') +env.SharedObject(target = 'test3', source = 'test3.spp') +env.SharedObject(target = 'test4', source = 'test4.SPP') +""" % locals()) + +test.write('test1.spp', "test1.spp\n/*as*/\n") +test.write('test2.SPP', "test2.SPP\n/*as*/\n") +test.write('test3.spp', "test3.spp\n/*as*/\n") +test.write('test4.SPP', "test4.SPP\n/*as*/\n") + +test.run(arguments = '.') + +test.must_match('test1.obj', "test1.spp\n") +test.must_match('test2.obj', "test2.SPP\n") +test.must_match('test3.shobj', "test3.spp\n") +test.must_match('test4.shobj', "test4.SPP\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AS/ASPPCOMSTR.py b/test/AS/ASPPCOMSTR.py index 11a9b468ee..030d8a598a 100644 --- a/test/AS/ASPPCOMSTR.py +++ b/test/AS/ASPPCOMSTR.py @@ -1,66 +1,66 @@ -#!/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. - -""" -Test that the $ASPPCOMSTR construction variable allows you to customize -the displayed assembler string. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['as'], - ASPPCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', - ASPPCOMSTR = 'Assembling $TARGET from $SOURCE', - OBJSUFFIX = '.obj') -env.Object(target = 'test1', source = 'test1.spp') -env.Object(target = 'test2', source = 'test2.SPP') -""" % locals()) - -test.write('test1.spp', "test1.spp\n/*as*/\n") -test.write('test2.SPP', "test2.SPP\n/*as*/\n") - -test.run(stdout = test.wrap_stdout("""\ -Assembling test1.obj from test1.spp -Assembling test2.obj from test2.SPP -""")) - -test.must_match('test1.obj', "test1.spp\n") -test.must_match('test2.obj', "test2.SPP\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $ASPPCOMSTR construction variable allows you to customize +the displayed assembler string. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['as'], + ASPPCOM = r'%(_python_)s mycompile.py as $TARGET $SOURCE', + ASPPCOMSTR = 'Assembling $TARGET from $SOURCE', + OBJSUFFIX = '.obj') +env.Object(target = 'test1', source = 'test1.spp') +env.Object(target = 'test2', source = 'test2.SPP') +""" % locals()) + +test.write('test1.spp', "test1.spp\n/*as*/\n") +test.write('test2.SPP', "test2.SPP\n/*as*/\n") + +test.run(stdout = test.wrap_stdout("""\ +Assembling test1.obj from test1.spp +Assembling test2.obj from test2.SPP +""")) + +test.must_match('test1.obj', "test1.spp\n") +test.must_match('test2.obj', "test2.SPP\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AS/ml.py b/test/AS/ml.py index 76b76a9bd1..5684fa87d7 100644 --- a/test/AS/ml.py +++ b/test/AS/ml.py @@ -1,121 +1,121 @@ -#!/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 correct use of the live 'ml' assembler. -""" - -import sys - -import TestSCons - -_python_ = TestSCons._python_ -_exe = TestSCons._exe - -test = TestSCons.TestSCons() - -if sys.platform != 'win32': - test.skip_test("Skipping ml test on non-win32 platform '%s'\n" % sys.platform) - -ml = test.where_is('ml') - -if not ml: - test.skip_test("ml not found; skipping test\n") - -test.file_fixture('wrapper.py') - -test.write('SConstruct', """ -import os -ccc = Environment(tools = ['msvc', 'mslink', 'masm'], - ASFLAGS = '/nologo /coff') -ccc['ENV']['PATH'] = ccc['ENV']['PATH'] + os.pathsep + os.environ['PATH'] -ddd = ccc.Clone(AS = r'%(_python_)s wrapper.py ' + ccc['AS']) -ccc.Program(target = 'ccc', source = ['ccc.asm', 'ccc_main.c']) -ddd.Program(target = 'ddd', source = ['ddd.asm', 'ddd_main.c']) -""" % locals()) - -test.write('ccc.asm', -""" -DSEG segment - PUBLIC _name -_name byte "ccc.asm",0 -DSEG ends - end -""") - -test.write('ddd.asm', -""" -DSEG segment - PUBLIC _name -_name byte "ddd.asm",0 -DSEG ends - end -""") - -test.write('ccc_main.c', r""" -extern char name[]; - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("ccc_main.c %s\n", name); - exit (0); -} -""") - -test.write('ddd_main.c', r""" -extern char name[]; - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("ddd_main.c %s\n", name); - exit (0); -} -""") - -test.run(arguments = 'ccc' + _exe, stderr = None) - -test.run(program = test.workpath('ccc'), stdout = "ccc_main.c ccc.asm\n") - -test.must_not_exist('wrapper.out') - -test.run(arguments = 'ddd' + _exe) - -test.run(program = test.workpath('ddd'), stdout = "ddd_main.c ddd.asm\n") - -test.must_match('wrapper.out', "wrapper.py\n") - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 correct use of the live 'ml' assembler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +if sys.platform != 'win32': + test.skip_test("Skipping ml test on non-win32 platform '%s'\n" % sys.platform) + +ml = test.where_is('ml') + +if not ml: + test.skip_test("ml not found; skipping test\n") + +test.file_fixture('wrapper.py') + +test.write('SConstruct', """ +import os +ccc = Environment(tools = ['msvc', 'mslink', 'masm'], + ASFLAGS = '/nologo /coff') +ccc['ENV']['PATH'] = ccc['ENV']['PATH'] + os.pathsep + os.environ['PATH'] +ddd = ccc.Clone(AS = r'%(_python_)s wrapper.py ' + ccc['AS']) +ccc.Program(target = 'ccc', source = ['ccc.asm', 'ccc_main.c']) +ddd.Program(target = 'ddd', source = ['ddd.asm', 'ddd_main.c']) +""" % locals()) + +test.write('ccc.asm', +""" +DSEG segment + PUBLIC _name +_name byte "ccc.asm",0 +DSEG ends + end +""") + +test.write('ddd.asm', +""" +DSEG segment + PUBLIC _name +_name byte "ddd.asm",0 +DSEG ends + end +""") + +test.write('ccc_main.c', r""" +extern char name[]; + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("ccc_main.c %s\n", name); + exit (0); +} +""") + +test.write('ddd_main.c', r""" +extern char name[]; + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("ddd_main.c %s\n", name); + exit (0); +} +""") + +test.run(arguments = 'ccc' + _exe, stderr = None) + +test.run(program = test.workpath('ccc'), stdout = "ccc_main.c ccc.asm\n") + +test.must_not_exist('wrapper.out') + +test.run(arguments = 'ddd' + _exe) + +test.run(program = test.workpath('ddd'), stdout = "ddd_main.c ddd.asm\n") + +test.must_match('wrapper.out', "wrapper.py\n") + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Actions/addpost-link.py b/test/Actions/addpost-link.py index 3aa8352504..65a59b3897 100644 --- a/test/Actions/addpost-link.py +++ b/test/Actions/addpost-link.py @@ -1,75 +1,75 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that AddPostAction() on a program target doesn't interfere with -linking. - -This is a test for fix of Issue 1004, reported by Matt Doar and -packaged by Gary Oberbrunner. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.dir_fixture('addpost-link-fixture') - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment() - -mylib = env.StaticLibrary('mytest', 'test_lib.c') - -myprog = env.Program('test1.c', - LIBPATH = ['.'], - LIBS = ['mytest'], - OBJSUFFIX = '.obj', - PROGSUFFIX = '.exe') -if ARGUMENTS['case']=='2': - AddPostAction(myprog, Action(r'%(_python_)s strip.py ' + myprog[0].get_abspath())) -""" % locals()) - -test.run(arguments="-Q case=1", stderr=None) - -test.run(arguments="-Q -c case=1") - -test.must_not_exist('test1.obj') - -test.run(arguments="-Q case=2", stderr=None) - -expect = 'strip.py: %s' % test.workpath('test1.exe') -test.must_contain_all_lines(test.stdout(), [expect]) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that AddPostAction() on a program target doesn't interfere with +linking. + +This is a test for fix of Issue 1004, reported by Matt Doar and +packaged by Gary Oberbrunner. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.dir_fixture('addpost-link-fixture') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment() + +mylib = env.StaticLibrary('mytest', 'test_lib.c') + +myprog = env.Program('test1.c', + LIBPATH = ['.'], + LIBS = ['mytest'], + OBJSUFFIX = '.obj', + PROGSUFFIX = '.exe') +if ARGUMENTS['case']=='2': + AddPostAction(myprog, Action(r'%(_python_)s strip.py ' + myprog[0].get_abspath())) +""" % locals()) + +test.run(arguments="-Q case=1", stderr=None) + +test.run(arguments="-Q -c case=1") + +test.must_not_exist('test1.obj') + +test.run(arguments="-Q case=2", stderr=None) + +expect = 'strip.py: %s' % test.workpath('test1.exe') +test.must_contain_all_lines(test.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/Actions/exitstatfunc.py b/test/Actions/exitstatfunc.py index e14b86b32f..7d5b2b976d 100644 --- a/test/Actions/exitstatfunc.py +++ b/test/Actions/exitstatfunc.py @@ -1,73 +1,73 @@ -#!/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 that setting exitstatfunc on an Action works as advertised. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -def always_succeed(s): - # Always return 0, which indicates success. - return 0 - -def copy_fail(target, source, env): - with open(str(source[0]), 'rb') as infp, open(str(target[0]), 'wb') as f: - f.write(infp.read()) - return 2 - -a = Action(copy_fail, exitstatfunc=always_succeed) -Alias('test1', Command('test1.out', 'test1.in', a)) - -def fail(target, source, env): - return 2 - -t2 = Command('test2.out', 'test2.in', Copy('$TARGET', '$SOURCE')) -AddPostAction(t2, Action(fail, exitstatfunc=always_succeed)) -Alias('test2', t2) -""") - -test.write('test1.in', "test1.in\n") -test.write('test2.in', "test2.in\n") - -test.run(arguments = 'test1') - -test.must_match('test1.out', "test1.in\n") - -test.run(arguments = 'test2') - -test.must_match('test2.out', "test2.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that setting exitstatfunc on an Action works as advertised. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def always_succeed(s): + # Always return 0, which indicates success. + return 0 + +def copy_fail(target, source, env): + with open(str(source[0]), 'rb') as infp, open(str(target[0]), 'wb') as f: + f.write(infp.read()) + return 2 + +a = Action(copy_fail, exitstatfunc=always_succeed) +Alias('test1', Command('test1.out', 'test1.in', a)) + +def fail(target, source, env): + return 2 + +t2 = Command('test2.out', 'test2.in', Copy('$TARGET', '$SOURCE')) +AddPostAction(t2, Action(fail, exitstatfunc=always_succeed)) +Alias('test2', t2) +""") + +test.write('test1.in', "test1.in\n") +test.write('test2.in', "test2.in\n") + +test.run(arguments = 'test1') + +test.must_match('test1.out', "test1.in\n") + +test.run(arguments = 'test2') + +test.must_match('test2.out', "test2.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AddOption/basic.py b/test/AddOption/basic.py index b087ac0aba..64fb7ba1c1 100644 --- a/test/AddOption/basic.py +++ b/test/AddOption/basic.py @@ -1,74 +1,74 @@ -#!/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 the help text when the AddOption() function is used (and when -it's not). -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -AddOption('--force', - action="store_true", - help='force installation (overwrite any existing files)') -AddOption('--prefix', - nargs=1, - dest='prefix', - action='store', - type='string', - metavar='DIR', - help='installation prefix') -f = GetOption('force') -if f: - f = "True" -print(f) -print(GetOption('prefix')) -""") - -test.run('-Q -q .', - stdout="None\nNone\n") - -test.run('-Q -q . --force', - stdout="True\nNone\n") - -test.run('-Q -q . --prefix=/home/foo', - stdout="None\n/home/foo\n") - -test.run('-Q -q . -- --prefix=/home/foo --force', - status=1, - stdout="None\nNone\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the help text when the AddOption() function is used (and when +it's not). +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +AddOption('--force', + action="store_true", + help='force installation (overwrite any existing files)') +AddOption('--prefix', + nargs=1, + dest='prefix', + action='store', + type='string', + metavar='DIR', + help='installation prefix') +f = GetOption('force') +if f: + f = "True" +print(f) +print(GetOption('prefix')) +""") + +test.run('-Q -q .', + stdout="None\nNone\n") + +test.run('-Q -q . --force', + stdout="True\nNone\n") + +test.run('-Q -q . --prefix=/home/foo', + stdout="None\n/home/foo\n") + +test.run('-Q -q . -- --prefix=/home/foo --force', + status=1, + stdout="None\nNone\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AddOption/help.py b/test/AddOption/help.py index 81bb8983fb..6d855bd2d2 100644 --- a/test/AddOption/help.py +++ b/test/AddOption/help.py @@ -1,81 +1,81 @@ -#!/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. - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -AddOption('--force', - action="store_true", - help='force installation (overwrite existing files)') -AddOption('--prefix', - nargs=1, - dest='prefix', - action='store', - type='string', - metavar='DIR', - help='installation prefix') -""") - -expected_lines = [ - 'Local Options:', - ' --force force installation (overwrite existing files)', - ' --prefix=DIR installation prefix', -] - -test.run(arguments = '-h') -lines = test.stdout().split('\n') -missing = [e for e in expected_lines if e not in lines] - -if missing: - print("====== STDOUT:") - print(test.stdout()) - print("====== Missing the following lines in the above AddOption() help output:") - print("\n".join(missing)) - test.fail_test() - -test.unlink('SConstruct') - -test.run(arguments = '-h') -lines = test.stdout().split('\n') -unexpected = [e for e in expected_lines if e in lines] - -if unexpected: - print("====== STDOUT:") - print(test.stdout()) - print("====== Unexpected lines in the above non-AddOption() help output:") - print("\n".join(unexpected)) - test.fail_test() - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +AddOption('--force', + action="store_true", + help='force installation (overwrite existing files)') +AddOption('--prefix', + nargs=1, + dest='prefix', + action='store', + type='string', + metavar='DIR', + help='installation prefix') +""") + +expected_lines = [ + 'Local Options:', + ' --force force installation (overwrite existing files)', + ' --prefix=DIR installation prefix', +] + +test.run(arguments = '-h') +lines = test.stdout().split('\n') +missing = [e for e in expected_lines if e not in lines] + +if missing: + print("====== STDOUT:") + print(test.stdout()) + print("====== Missing the following lines in the above AddOption() help output:") + print("\n".join(missing)) + test.fail_test() + +test.unlink('SConstruct') + +test.run(arguments = '-h') +lines = test.stdout().split('\n') +unexpected = [e for e in expected_lines if e in lines] + +if unexpected: + print("====== STDOUT:") + print(test.stdout()) + print("====== Unexpected lines in the above non-AddOption() help output:") + print("\n".join(unexpected)) + test.fail_test() + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/AddOption/optional-arg.py b/test/AddOption/optional-arg.py index 1f597bf43e..362da00ec4 100644 --- a/test/AddOption/optional-arg.py +++ b/test/AddOption/optional-arg.py @@ -1,107 +1,107 @@ -#!/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 use of the nargs='?' keyword argument to specify a long -command-line option with an optional argument value. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -AddOption('--install', - nargs='?', - dest='install', - default='/default/directory', - const='/called/default/directory', - action='store', - type='string', - metavar='DIR', - help='installation directory') -print(GetOption('install')) -""") - -test.run('-Q -q', - stdout="/default/directory\n") - -test.run('-Q -q next-arg', - stdout="/default/directory\n", - status=1) - -test.run('-Q -q . --install', - stdout="/called/default/directory\n") - -test.run('-Q -q . --install next-arg', - stdout="/called/default/directory\n", - status=1) - -test.run('-Q -q . first-arg --install', - stdout="/called/default/directory\n", - status=1) - -test.run('-Q -q . first-arg --install next-arg', - stdout="/called/default/directory\n", - status=1) - -test.run('-Q -q . --install=/command/line/directory', - stdout="/command/line/directory\n") - -test.run('-Q -q . --install=/command/line/directory next-arg', - stdout="/command/line/directory\n", - status=1) - -test.run('-Q -q . first-arg --install=/command/line/directory', - stdout="/command/line/directory\n", - status=1) - -test.run('-Q -q . first-arg --install=/command/line/directory next-arg', - stdout="/command/line/directory\n", - status=1) - - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -AddOption('-X', nargs='?') -""") - -expect = r""" -scons: \*\*\* option -X: nargs='\?' is incompatible with short options -File "[^"]+", line \d+, in \S+ -""" - -test.run(status=2, stderr=expect, match=TestSCons.match_re) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of the nargs='?' keyword argument to specify a long +command-line option with an optional argument value. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +AddOption('--install', + nargs='?', + dest='install', + default='/default/directory', + const='/called/default/directory', + action='store', + type='string', + metavar='DIR', + help='installation directory') +print(GetOption('install')) +""") + +test.run('-Q -q', + stdout="/default/directory\n") + +test.run('-Q -q next-arg', + stdout="/default/directory\n", + status=1) + +test.run('-Q -q . --install', + stdout="/called/default/directory\n") + +test.run('-Q -q . --install next-arg', + stdout="/called/default/directory\n", + status=1) + +test.run('-Q -q . first-arg --install', + stdout="/called/default/directory\n", + status=1) + +test.run('-Q -q . first-arg --install next-arg', + stdout="/called/default/directory\n", + status=1) + +test.run('-Q -q . --install=/command/line/directory', + stdout="/command/line/directory\n") + +test.run('-Q -q . --install=/command/line/directory next-arg', + stdout="/command/line/directory\n", + status=1) + +test.run('-Q -q . first-arg --install=/command/line/directory', + stdout="/command/line/directory\n", + status=1) + +test.run('-Q -q . first-arg --install=/command/line/directory next-arg', + stdout="/command/line/directory\n", + status=1) + + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +AddOption('-X', nargs='?') +""") + +expect = r""" +scons: \*\*\* option -X: nargs='\?' is incompatible with short options +File "[^"]+", line \d+, in \S+ +""" + +test.run(status=2, stderr=expect, match=TestSCons.match_re) + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Alias/Dir-order.py b/test/Alias/Dir-order.py index 6b62cb2020..86d0495c57 100644 --- a/test/Alias/Dir-order.py +++ b/test/Alias/Dir-order.py @@ -1,49 +1,49 @@ -#!/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. - -""" -Validate that calling Dir() for a string after we've used it as an -Alias() expansion works. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -Alias('afoo', 'foo') -f = Dir('foo') -""") - -test.run(arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Validate that calling Dir() for a string after we've used it as an +Alias() expansion works. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +Alias('afoo', 'foo') +f = Dir('foo') +""") + +test.run(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Alias/errors.py b/test/Alias/errors.py index 0bd24909d0..abac5d569d 100644 --- a/test/Alias/errors.py +++ b/test/Alias/errors.py @@ -1,49 +1,49 @@ -#!/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. - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env=Environment(tools=[]) -Decider('content') -env.Alias('C', 'D') -env.Alias('B', 'C') -env.Alias('A', 'B') -""") - -test.run(arguments='A', - stderr="scons: *** [C] Source `D' not found, needed by target `C'.\n", - status=2) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env=Environment(tools=[]) +Decider('content') +env.Alias('C', 'D') +env.Alias('B', 'C') +env.Alias('A', 'B') +""") + +test.run(arguments='A', + stderr="scons: *** [C] Source `D' not found, needed by target `C'.\n", + status=2) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Alias/scanner.py b/test/Alias/scanner.py index 618c052ec1..4f7473b672 100644 --- a/test/Alias/scanner.py +++ b/test/Alias/scanner.py @@ -1,64 +1,64 @@ -#!/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. - -""" -Test that an Alias of a node with a Scanner works. -""" - - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: - for src in source: - with open(str(src), "rb") as ifp: - f.write(ifp.read()) - -XBuilder = Builder(action = cat, src_suffix = '.x', suffix = '.c') -env = Environment(tools=[]) -env.Append(BUILDERS = { 'XBuilder': XBuilder }) -f = env.XBuilder(source = ['file.x'], target = ['file.c']) -env.Alias(target = ['cfiles'], source = f) -Default(['cfiles']) -""") - -test.write('file.x', "file.x\n") - -test.run() - -test.fail_test(test.read('file.c') != b"file.x\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that an Alias of a node with a Scanner works. +""" + + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, "wb") as f: + for src in source: + with open(str(src), "rb") as ifp: + f.write(ifp.read()) + +XBuilder = Builder(action = cat, src_suffix = '.x', suffix = '.c') +env = Environment(tools=[]) +env.Append(BUILDERS = { 'XBuilder': XBuilder }) +f = env.XBuilder(source = ['file.x'], target = ['file.c']) +env.Alias(target = ['cfiles'], source = f) +Default(['cfiles']) +""") + +test.write('file.x', "file.x\n") + +test.run() + +test.fail_test(test.read('file.c') != b"file.x\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/Boolean.py b/test/Batch/Boolean.py index 249599da73..702d24befb 100644 --- a/test/Batch/Boolean.py +++ b/test/Batch/Boolean.py @@ -1,81 +1,81 @@ -#!/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 basic use of batch_key to write a batch builder that handles -arbitrary pairs of target + source files. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) - -def batch_build(target, source, env): - for t, s in zip(target, source): - with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: - f.write(infp.read()) -env = Environment(tools=[]) -bb = Action(batch_build, batch_key=True) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('f1a.out', 'f1a.in') -env1.Batch('f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('f3a.out', 'f3a.in') -env3.Batch('f3b.out', 'f3b.in') -""") - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -batch_build(["f1a.out", "f1b.out"], ["f1a.in", "f1b.in"]) -batch_build(["f2a.out"], ["f2a.in"]) -batch_build(["f3a.out", "f3b.out"], ["f3a.in", "f3b.in"]) -""") - -test.run(stdout = expect) - -test.must_match('f1a.out', "f1a.in\n") -test.must_match('f1b.out', "f1b.in\n") -test.must_match('f2a.out', "f2a.in\n") -test.must_match('f3a.out', "f3a.in\n") -test.must_match('f3b.out', "f3b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 basic use of batch_key to write a batch builder that handles +arbitrary pairs of target + source files. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + +def batch_build(target, source, env): + for t, s in zip(target, source): + with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: + f.write(infp.read()) +env = Environment(tools=[]) +bb = Action(batch_build, batch_key=True) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('f1a.out', 'f1a.in') +env1.Batch('f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('f3a.out', 'f3a.in') +env3.Batch('f3b.out', 'f3b.in') +""") + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +batch_build(["f1a.out", "f1b.out"], ["f1a.in", "f1b.in"]) +batch_build(["f2a.out"], ["f2a.in"]) +batch_build(["f3a.out", "f3b.out"], ["f3a.in", "f3b.in"]) +""") + +test.run(stdout = expect) + +test.must_match('f1a.out', "f1a.in\n") +test.must_match('f1b.out', "f1b.in\n") +test.must_match('f2a.out', "f2a.in\n") +test.must_match('f3a.out', "f3a.in\n") +test.must_match('f3b.out', "f3b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/CHANGED_SOURCES.py b/test/Batch/CHANGED_SOURCES.py index 125a5562e0..5899269194 100644 --- a/test/Batch/CHANGED_SOURCES.py +++ b/test/Batch/CHANGED_SOURCES.py @@ -1,125 +1,125 @@ -#!/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 use of $CHANGED_SOURCES with batch builders correctly decides -to rebuild if any sources of changed, and specifies only the sources -on the rebuild. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.write('batch_build.py', """\ -import os -import sys -dir = sys.argv[1] -for infile in sys.argv[2:]: - inbase = os.path.splitext(os.path.split(infile)[1])[0] - outfile = os.path.join(dir, inbase+'.out') - with open(outfile, 'wb') as f, open(infile, 'rb') as infp: - f.write(infp.read()) -sys.exit(0) -""") - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -env['BATCH_BUILD'] = 'batch_build.py' -env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $CHANGED_SOURCES' -bb = Action('$BATCHCOM', batch_key=True, targets='CHANGED_TARGETS') -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('out1/f1a.out', 'f1a.in') -env1.Batch('out1/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('out2/f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('out3/f3a.out', 'f3a.in') -env3.Batch('out3/f3b.out', 'f3b.in') -""" % locals()) - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - - - -test.write('f1b.in', "f1b.in 2\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - - - -test.write('f3a.in', "f3a.in 2\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out3 f3a.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in 2\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of $CHANGED_SOURCES with batch builders correctly decides +to rebuild if any sources of changed, and specifies only the sources +on the rebuild. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.write('batch_build.py', """\ +import os +import sys +dir = sys.argv[1] +for infile in sys.argv[2:]: + inbase = os.path.splitext(os.path.split(infile)[1])[0] + outfile = os.path.join(dir, inbase+'.out') + with open(outfile, 'wb') as f, open(infile, 'rb') as infp: + f.write(infp.read()) +sys.exit(0) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +env['BATCH_BUILD'] = 'batch_build.py' +env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $CHANGED_SOURCES' +bb = Action('$BATCHCOM', batch_key=True, targets='CHANGED_TARGETS') +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('out1/f1a.out', 'f1a.in') +env1.Batch('out1/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('out2/f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('out3/f3a.out', 'f3a.in') +env3.Batch('out3/f3b.out', 'f3b.in') +""" % locals()) + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + + + +test.write('f1b.in', "f1b.in 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + + + +test.write('f3a.in', "f3a.in 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out3 f3a.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in 2\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/SOURCES.py b/test/Batch/SOURCES.py index 2cbb205cfc..ea17224e4e 100644 --- a/test/Batch/SOURCES.py +++ b/test/Batch/SOURCES.py @@ -1,127 +1,127 @@ -#!/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 use of $SOURCES with batch builders correctly decides to -rebuild if any sources of changed, and specifies all the sources -on the rebuild. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.write('batch_build.py', """\ -import os -import sys -dir = sys.argv[1] -for infile in sys.argv[2:]: - inbase = os.path.splitext(os.path.split(infile)[1])[0] - outfile = os.path.join(dir, inbase+'.out') - with open(outfile, 'wb') as f, open(infile, 'rb') as infp: - f.write(infp.read()) -sys.exit(0) -""") - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -env['BATCH_BUILD'] = 'batch_build.py' -env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $SOURCES' -bb = Action('$BATCHCOM', batch_key=True) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('out1/f1a.out', 'f1a.in') -env1.Batch('out1/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('out2/f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('out3/f3a.out', 'f3a.in') -env3.Batch('out3/f3b.out', 'f3b.in') -""" % locals()) - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.up_to_date(options = '--debug=explain', arguments = '.') - - - - -test.write('f1b.in', "f1b.in 2\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - - -test.write('f3a.in', "f3a.in 2\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in 2\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of $SOURCES with batch builders correctly decides to +rebuild if any sources of changed, and specifies all the sources +on the rebuild. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.write('batch_build.py', """\ +import os +import sys +dir = sys.argv[1] +for infile in sys.argv[2:]: + inbase = os.path.splitext(os.path.split(infile)[1])[0] + outfile = os.path.join(dir, inbase+'.out') + with open(outfile, 'wb') as f, open(infile, 'rb') as infp: + f.write(infp.read()) +sys.exit(0) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +env['BATCH_BUILD'] = 'batch_build.py' +env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $SOURCES' +bb = Action('$BATCHCOM', batch_key=True) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('out1/f1a.out', 'f1a.in') +env1.Batch('out1/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('out2/f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('out3/f3a.out', 'f3a.in') +env3.Batch('out3/f3b.out', 'f3b.in') +""" % locals()) + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.up_to_date(options = '--debug=explain', arguments = '.') + + + + +test.write('f1b.in', "f1b.in 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + + +test.write('f3a.in', "f3a.in 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in 2\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in 2\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/action-changed.py b/test/Batch/action-changed.py index bbe7da3b3c..771b876250 100644 --- a/test/Batch/action-changed.py +++ b/test/Batch/action-changed.py @@ -1,104 +1,104 @@ -#!/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 that targets in a batch builder are rebuilt when the -build action changes. -""" - -import os - -import TestSCons - -# swap slashes because on py3 on win32 inside the generated build.py -# the backslashes are getting interpretted as unicode which is -# invalid. -python = TestSCons.python.replace('\\','//') -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -build_py_contents = """\ -#!/usr/bin/env %s -import sys -sep = sys.argv.index('--') -targets = sys.argv[1:sep] -sources = sys.argv[sep+1:] -for t, s in zip(targets, sources): - with open(t, 'wb') as ofp, open(s, 'rb') as ifp: - ofp.write(bytearray('%s\\n', 'utf-8')) - ofp.write(ifp.read()) -sys.exit(0) -""" - -test.write('build.py', build_py_contents % (python, 'one')) -os.chmod(test.workpath('build.py'), 0o755) - -build_py_workpath = test.workpath('build.py') - -# Provide IMPLICIT_COMMAND_DEPENDENCIES=2 so we take a dependency on build.py. -# Without that, we only scan the first entry in the action string. -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=[], - IMPLICIT_COMMAND_DEPENDENCIES=2) -env.PrependENVPath('PATHEXT', '.PY') -bb = Action(r'%(_python_)s "%(build_py_workpath)s" $CHANGED_TARGETS -- $CHANGED_SOURCES', - batch_key=True, - targets='CHANGED_TARGETS') -env['BUILDERS']['Batch'] = Builder(action=bb) -env.Batch('f1.out', 'f1.in') -env.Batch('f2.out', 'f2.in') -env.Batch('f3.out', 'f3.in') -""" % locals()) - -test.write('f1.in', "f1.in\n") -test.write('f2.in', "f2.in\n") -test.write('f3.in', "f3.in\n") - -test.run(arguments = '.') - -test.must_match('f1.out', "one\nf1.in\n") -test.must_match('f2.out', "one\nf2.in\n") -test.must_match('f3.out', "one\nf3.in\n") - -test.up_to_date(arguments = '.') - -test.write('build.py', build_py_contents % (python, 'two')) -os.chmod(test.workpath('build.py'), 0o755) - -test.not_up_to_date(arguments = '.') - -test.must_match('f1.out', "two\nf1.in\n") -test.must_match('f2.out', "two\nf2.in\n") -test.must_match('f3.out', "two\nf3.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that targets in a batch builder are rebuilt when the +build action changes. +""" + +import os + +import TestSCons + +# swap slashes because on py3 on win32 inside the generated build.py +# the backslashes are getting interpretted as unicode which is +# invalid. +python = TestSCons.python.replace('\\','//') +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +build_py_contents = """\ +#!/usr/bin/env %s +import sys +sep = sys.argv.index('--') +targets = sys.argv[1:sep] +sources = sys.argv[sep+1:] +for t, s in zip(targets, sources): + with open(t, 'wb') as ofp, open(s, 'rb') as ifp: + ofp.write(bytearray('%s\\n', 'utf-8')) + ofp.write(ifp.read()) +sys.exit(0) +""" + +test.write('build.py', build_py_contents % (python, 'one')) +os.chmod(test.workpath('build.py'), 0o755) + +build_py_workpath = test.workpath('build.py') + +# Provide IMPLICIT_COMMAND_DEPENDENCIES=2 so we take a dependency on build.py. +# Without that, we only scan the first entry in the action string. +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[], + IMPLICIT_COMMAND_DEPENDENCIES=2) +env.PrependENVPath('PATHEXT', '.PY') +bb = Action(r'%(_python_)s "%(build_py_workpath)s" $CHANGED_TARGETS -- $CHANGED_SOURCES', + batch_key=True, + targets='CHANGED_TARGETS') +env['BUILDERS']['Batch'] = Builder(action=bb) +env.Batch('f1.out', 'f1.in') +env.Batch('f2.out', 'f2.in') +env.Batch('f3.out', 'f3.in') +""" % locals()) + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") +test.write('f3.in', "f3.in\n") + +test.run(arguments = '.') + +test.must_match('f1.out', "one\nf1.in\n") +test.must_match('f2.out', "one\nf2.in\n") +test.must_match('f3.out', "one\nf3.in\n") + +test.up_to_date(arguments = '.') + +test.write('build.py', build_py_contents % (python, 'two')) +os.chmod(test.workpath('build.py'), 0o755) + +test.not_up_to_date(arguments = '.') + +test.must_match('f1.out', "two\nf1.in\n") +test.must_match('f2.out', "two\nf2.in\n") +test.must_match('f3.out', "two\nf3.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/callable.py b/test/Batch/callable.py index 9f9a119a2a..5c4d816d3c 100644 --- a/test/Batch/callable.py +++ b/test/Batch/callable.py @@ -1,110 +1,110 @@ -#!/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 passing in a batch_key callable for more control over how -batch builders behave. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('sub1', 'sub2') - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -def batch_build(target, source, env): - for t, s in zip(target, source): - with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: - f.write(infp.read()) -if ARGUMENTS.get('BATCH_CALLABLE'): - def batch_key(action, env, target, source): - return (id(action), id(env), target[0].dir) -else: - batch_key=True -env = Environment(tools=[]) -bb = Action(batch_build, batch_key=batch_key) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('sub1/f1a.out', 'f1a.in') -env1.Batch('sub2/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('sub1/f2a.out', 'f2a.in') -env2.Batch('sub2/f2b.out', 'f2b.in') -""") - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f2b.in', "f2b.in\n") - -sub1_f1a_out = os.path.join('sub1', 'f1a.out') -sub2_f1b_out = os.path.join('sub2', 'f1b.out') -sub1_f2a_out = os.path.join('sub1', 'f2a.out') -sub2_f2b_out = os.path.join('sub2', 'f2b.out') - -expect = test.wrap_stdout("""\ -batch_build(["%(sub1_f1a_out)s", "%(sub2_f1b_out)s"], ["f1a.in", "f1b.in"]) -batch_build(["%(sub1_f2a_out)s", "%(sub2_f2b_out)s"], ["f2a.in", "f2b.in"]) -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['sub1', 'f1a.out'], "f1a.in\n") -test.must_match(['sub2', 'f1b.out'], "f1b.in\n") -test.must_match(['sub1', 'f2a.out'], "f2a.in\n") -test.must_match(['sub2', 'f2b.out'], "f2b.in\n") - -test.run(arguments = '-c') - -test.must_not_exist(['sub1', 'f1a.out']) -test.must_not_exist(['sub2', 'f1b.out']) -test.must_not_exist(['sub1', 'f2a.out']) -test.must_not_exist(['sub2', 'f2b.out']) - -expect = test.wrap_stdout("""\ -batch_build(["%(sub1_f1a_out)s"], ["f1a.in"]) -batch_build(["%(sub1_f2a_out)s"], ["f2a.in"]) -batch_build(["%(sub2_f1b_out)s"], ["f1b.in"]) -batch_build(["%(sub2_f2b_out)s"], ["f2b.in"]) -""" % locals()) - -test.run(arguments = 'BATCH_CALLABLE=1', stdout = expect) - -test.must_match(['sub1', 'f1a.out'], "f1a.in\n") -test.must_match(['sub2', 'f1b.out'], "f1b.in\n") -test.must_match(['sub1', 'f2a.out'], "f2a.in\n") -test.must_match(['sub2', 'f2b.out'], "f2b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 passing in a batch_key callable for more control over how +batch builders behave. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('sub1', 'sub2') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +def batch_build(target, source, env): + for t, s in zip(target, source): + with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: + f.write(infp.read()) +if ARGUMENTS.get('BATCH_CALLABLE'): + def batch_key(action, env, target, source): + return (id(action), id(env), target[0].dir) +else: + batch_key=True +env = Environment(tools=[]) +bb = Action(batch_build, batch_key=batch_key) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('sub1/f1a.out', 'f1a.in') +env1.Batch('sub2/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('sub1/f2a.out', 'f2a.in') +env2.Batch('sub2/f2b.out', 'f2b.in') +""") + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f2b.in', "f2b.in\n") + +sub1_f1a_out = os.path.join('sub1', 'f1a.out') +sub2_f1b_out = os.path.join('sub2', 'f1b.out') +sub1_f2a_out = os.path.join('sub1', 'f2a.out') +sub2_f2b_out = os.path.join('sub2', 'f2b.out') + +expect = test.wrap_stdout("""\ +batch_build(["%(sub1_f1a_out)s", "%(sub2_f1b_out)s"], ["f1a.in", "f1b.in"]) +batch_build(["%(sub1_f2a_out)s", "%(sub2_f2b_out)s"], ["f2a.in", "f2b.in"]) +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['sub1', 'f1a.out'], "f1a.in\n") +test.must_match(['sub2', 'f1b.out'], "f1b.in\n") +test.must_match(['sub1', 'f2a.out'], "f2a.in\n") +test.must_match(['sub2', 'f2b.out'], "f2b.in\n") + +test.run(arguments = '-c') + +test.must_not_exist(['sub1', 'f1a.out']) +test.must_not_exist(['sub2', 'f1b.out']) +test.must_not_exist(['sub1', 'f2a.out']) +test.must_not_exist(['sub2', 'f2b.out']) + +expect = test.wrap_stdout("""\ +batch_build(["%(sub1_f1a_out)s"], ["f1a.in"]) +batch_build(["%(sub1_f2a_out)s"], ["f2a.in"]) +batch_build(["%(sub2_f1b_out)s"], ["f1b.in"]) +batch_build(["%(sub2_f2b_out)s"], ["f2b.in"]) +""" % locals()) + +test.run(arguments = 'BATCH_CALLABLE=1', stdout = expect) + +test.must_match(['sub1', 'f1a.out'], "f1a.in\n") +test.must_match(['sub2', 'f1b.out'], "f1b.in\n") +test.must_match(['sub1', 'f2a.out'], "f2a.in\n") +test.must_match(['sub2', 'f2b.out'], "f2b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/changed_sources_alwaysbuild.py b/test/Batch/changed_sources_alwaysbuild.py index 06a2e02c2b..5e278a5236 100644 --- a/test/Batch/changed_sources_alwaysbuild.py +++ b/test/Batch/changed_sources_alwaysbuild.py @@ -1,53 +1,53 @@ -#!/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 that files marked AlwaysBuild also get put into CHANGED_SOURCES. -Tigris bug 2622 -""" - -import TestSCons - -test = TestSCons.TestSCons() -test.file_fixture('SConstruct_changed_sources_alwaysBuild','SConstruct') -test.file_fixture('changed_sources_main.cpp') -# always works on first run -test.run() - -# On second run prior to fix the file hasn't changed and so never -# makes it into CHANGED_SOURCES. -# Compile is triggered because SCons knows it needs to build it. -# This tests that on second run the source file is in the scons -# output. Also prior to fix the compile would fail because -# it would produce a compile command line lacking a source file. -test.run() -test.must_contain_all_lines(test.stdout(),['changed_sources_main.cpp']) -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that files marked AlwaysBuild also get put into CHANGED_SOURCES. +Tigris bug 2622 +""" + +import TestSCons + +test = TestSCons.TestSCons() +test.file_fixture('SConstruct_changed_sources_alwaysBuild','SConstruct') +test.file_fixture('changed_sources_main.cpp') +# always works on first run +test.run() + +# On second run prior to fix the file hasn't changed and so never +# makes it into CHANGED_SOURCES. +# Compile is triggered because SCons knows it needs to build it. +# This tests that on second run the source file is in the scons +# output. Also prior to fix the compile would fail because +# it would produce a compile command line lacking a source file. +test.run() +test.must_contain_all_lines(test.stdout(),['changed_sources_main.cpp']) +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/generated.py b/test/Batch/generated.py index 91b4609f2d..cb76ff2b7c 100644 --- a/test/Batch/generated.py +++ b/test/Batch/generated.py @@ -1,85 +1,85 @@ -#!/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 use of a batch builder when one of the later targets in the -list the list depends on a generated file. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) - -def batch_build(target, source, env): - for t, s in zip(target, source): - with open(str(t), 'wb') as fp: - if str(t) == 'f3.out': - with open('f3.include', 'rb') as f: - fp.write(f.read()) - with open(str(s), 'rb') as f: - fp.write(f.read()) -env = Environment(tools=[]) -bb = Action(batch_build, batch_key=True) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('f1.out', 'f1.in') -env1.Batch('f2.out', 'f2.mid') -f3_out = env1.Batch('f3.out', 'f3.in') - -env2 = env.Clone() -env2.Batch('f2.mid', 'f2.in') - -f3_include = env.Batch('f3.include', 'f3.include.in') -env.Depends(f3_out, f3_include) -""") - -test.write('f1.in', "f1.in\n") -test.write('f2.in', "f2.in\n") -test.write('f3.in', "f3.in\n") -test.write('f3.include.in', "f3.include.in\n") - -expect = test.wrap_stdout("""\ -batch_build(["f2.mid"], ["f2.in"]) -batch_build(["f3.include"], ["f3.include.in"]) -batch_build(["f1.out", "f2.out", "f3.out"], ["f1.in", "f2.mid", "f3.in"]) -""") - -test.run(stdout = expect) - -test.must_match('f1.out', "f1.in\n") -test.must_match('f2.out', "f2.in\n") - -test.up_to_date(arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of a batch builder when one of the later targets in the +list the list depends on a generated file. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + +def batch_build(target, source, env): + for t, s in zip(target, source): + with open(str(t), 'wb') as fp: + if str(t) == 'f3.out': + with open('f3.include', 'rb') as f: + fp.write(f.read()) + with open(str(s), 'rb') as f: + fp.write(f.read()) +env = Environment(tools=[]) +bb = Action(batch_build, batch_key=True) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('f1.out', 'f1.in') +env1.Batch('f2.out', 'f2.mid') +f3_out = env1.Batch('f3.out', 'f3.in') + +env2 = env.Clone() +env2.Batch('f2.mid', 'f2.in') + +f3_include = env.Batch('f3.include', 'f3.include.in') +env.Depends(f3_out, f3_include) +""") + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") +test.write('f3.in', "f3.in\n") +test.write('f3.include.in', "f3.include.in\n") + +expect = test.wrap_stdout("""\ +batch_build(["f2.mid"], ["f2.in"]) +batch_build(["f3.include"], ["f3.include.in"]) +batch_build(["f1.out", "f2.out", "f3.out"], ["f1.in", "f2.mid", "f3.in"]) +""") + +test.run(stdout = expect) + +test.must_match('f1.out', "f1.in\n") +test.must_match('f2.out', "f2.in\n") + +test.up_to_date(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/removed.py b/test/Batch/removed.py index b64958dda3..60c2fbf852 100644 --- a/test/Batch/removed.py +++ b/test/Batch/removed.py @@ -1,112 +1,112 @@ -#!/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 correct batch builder $CHANGED_SOURCES behavior when some of -the targets have been removed. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.write('batch_build.py', """\ -import os -import sys -dir = sys.argv[1] -for infile in sys.argv[2:]: - inbase = os.path.splitext(os.path.split(infile)[1])[0] - outfile = os.path.join(dir, inbase+'.out') - with open(outfile, 'wb') as f, open(infile, 'rb') as infp: - f.write(infp.read()) -sys.exit(0) -""") - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -env['BATCH_BUILD'] = 'batch_build.py' -env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $CHANGED_SOURCES' -bb = Action('$BATCHCOM', batch_key=True, targets='CHANGED_TARGETS') -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('out1/f1a.out', 'f1a.in') -env1.Batch('out1/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('out2/f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('out3/f3a.out', 'f3a.in') -env3.Batch('out3/f3b.out', 'f3b.in') -""" % locals()) - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.up_to_date(options = '--debug=explain', arguments = '.') - -test.unlink(['out1', 'f1b.out']) -test.unlink(['out2', 'f2a.out']) -test.unlink(['out3', 'f3a.out']) - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in -""" % locals()) - -test.run(arguments = '.', stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 correct batch builder $CHANGED_SOURCES behavior when some of +the targets have been removed. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.write('batch_build.py', """\ +import os +import sys +dir = sys.argv[1] +for infile in sys.argv[2:]: + inbase = os.path.splitext(os.path.split(infile)[1])[0] + outfile = os.path.join(dir, inbase+'.out') + with open(outfile, 'wb') as f, open(infile, 'rb') as infp: + f.write(infp.read()) +sys.exit(0) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +env['BATCH_BUILD'] = 'batch_build.py' +env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $CHANGED_SOURCES' +bb = Action('$BATCHCOM', batch_key=True, targets='CHANGED_TARGETS') +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('out1/f1a.out', 'f1a.in') +env1.Batch('out1/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('out2/f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('out3/f3a.out', 'f3a.in') +env3.Batch('out3/f3b.out', 'f3b.in') +""" % locals()) + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.up_to_date(options = '--debug=explain', arguments = '.') + +test.unlink(['out1', 'f1b.out']) +test.unlink(['out2', 'f2a.out']) +test.unlink(['out3', 'f3a.out']) + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in +""" % locals()) + +test.run(arguments = '.', stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Batch/up_to_date.py b/test/Batch/up_to_date.py index 4ba304b6c0..ae73afbced 100644 --- a/test/Batch/up_to_date.py +++ b/test/Batch/up_to_date.py @@ -1,94 +1,94 @@ -#!/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 simple use of $SOURCES with batch builders correctly decide -that files are up to date on a rebuild. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.write('batch_build.py', """\ -import os -import sys -dir = sys.argv[1] -for infile in sys.argv[2:]: - inbase = os.path.splitext(os.path.split(infile)[1])[0] - outfile = os.path.join(dir, inbase+'.out') - with open(outfile, 'wb') as f, open(infile, 'rb') as infp: - f.write(infp.read()) -sys.exit(0) -""") - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -env['BATCH_BUILD'] = 'batch_build.py' -env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $SOURCES' -bb = Action('$BATCHCOM', batch_key=True) -env['BUILDERS']['Batch'] = Builder(action=bb) -env1 = env.Clone() -env1.Batch('out1/f1a.out', 'f1a.in') -env1.Batch('out1/f1b.out', 'f1b.in') -env2 = env.Clone() -env2.Batch('out2/f2a.out', 'f2a.in') -env3 = env.Clone() -env3.Batch('out3/f3a.out', 'f3a.in') -env3.Batch('out3/f3b.out', 'f3b.in') -""" % locals()) - -test.write('f1a.in', "f1a.in\n") -test.write('f1b.in', "f1b.in\n") -test.write('f2a.in', "f2a.in\n") -test.write('f3a.in', "f3a.in\n") -test.write('f3b.in', "f3b.in\n") - -expect = test.wrap_stdout("""\ -%(_python_)s batch_build.py out1 f1a.in f1b.in -%(_python_)s batch_build.py out2 f2a.in -%(_python_)s batch_build.py out3 f3a.in f3b.in -""" % locals()) - -test.run(stdout = expect) - -test.must_match(['out1', 'f1a.out'], "f1a.in\n") -test.must_match(['out1', 'f1b.out'], "f1b.in\n") -test.must_match(['out2', 'f2a.out'], "f2a.in\n") -test.must_match(['out3', 'f3a.out'], "f3a.in\n") -test.must_match(['out3', 'f3b.out'], "f3b.in\n") - -test.up_to_date(options = '--debug=explain', arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 simple use of $SOURCES with batch builders correctly decide +that files are up to date on a rebuild. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.write('batch_build.py', """\ +import os +import sys +dir = sys.argv[1] +for infile in sys.argv[2:]: + inbase = os.path.splitext(os.path.split(infile)[1])[0] + outfile = os.path.join(dir, inbase+'.out') + with open(outfile, 'wb') as f, open(infile, 'rb') as infp: + f.write(infp.read()) +sys.exit(0) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +env['BATCH_BUILD'] = 'batch_build.py' +env['BATCHCOM'] = r'%(_python_)s $BATCH_BUILD ${TARGET.dir} $SOURCES' +bb = Action('$BATCHCOM', batch_key=True) +env['BUILDERS']['Batch'] = Builder(action=bb) +env1 = env.Clone() +env1.Batch('out1/f1a.out', 'f1a.in') +env1.Batch('out1/f1b.out', 'f1b.in') +env2 = env.Clone() +env2.Batch('out2/f2a.out', 'f2a.in') +env3 = env.Clone() +env3.Batch('out3/f3a.out', 'f3a.in') +env3.Batch('out3/f3b.out', 'f3b.in') +""" % locals()) + +test.write('f1a.in', "f1a.in\n") +test.write('f1b.in', "f1b.in\n") +test.write('f2a.in', "f2a.in\n") +test.write('f3a.in', "f3a.in\n") +test.write('f3b.in', "f3b.in\n") + +expect = test.wrap_stdout("""\ +%(_python_)s batch_build.py out1 f1a.in f1b.in +%(_python_)s batch_build.py out2 f2a.in +%(_python_)s batch_build.py out3 f3a.in f3b.in +""" % locals()) + +test.run(stdout = expect) + +test.must_match(['out1', 'f1a.out'], "f1a.in\n") +test.must_match(['out1', 'f1b.out'], "f1b.in\n") +test.must_match(['out2', 'f2a.out'], "f2a.in\n") +test.must_match(['out3', 'f3a.out'], "f3a.in\n") +test.must_match(['out3', 'f3b.out'], "f3b.in\n") + +test.up_to_date(options = '--debug=explain', arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/TargetSubst.py b/test/Builder/TargetSubst.py index 3983eee174..de4d724f7d 100644 --- a/test/Builder/TargetSubst.py +++ b/test/Builder/TargetSubst.py @@ -1,52 +1,52 @@ -#!/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 that the ensure_suffix argument to causes us to add the suffix -configured for the Builder even if it looks like the target already has -a different suffix. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -builder = Builder(action=Copy('$TARGET', '$SOURCE')) -tgt = builder(env, target="${SOURCE}.out", source="infile") -""") - -test.write('infile', "infile\n") -test.run(arguments = '.') -test.must_match('infile.out', "infile\n") -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the ensure_suffix argument to causes us to add the suffix +configured for the Builder even if it looks like the target already has +a different suffix. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +builder = Builder(action=Copy('$TARGET', '$SOURCE')) +tgt = builder(env, target="${SOURCE}.out", source="infile") +""") + +test.write('infile', "infile\n") +test.run(arguments = '.') +test.must_match('infile.out', "infile\n") +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/add_src_builder.py b/test/Builder/add_src_builder.py index 78e30069c4..733d1b92a2 100644 --- a/test/Builder/add_src_builder.py +++ b/test/Builder/add_src_builder.py @@ -1,71 +1,71 @@ -#!/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 that we can call add_src_builder() to add a builder to -another on the fly. - -This used to trigger infinite recursion (issue 1681) because the -same src_builder list object was being re-used between all Builder -objects that weren't initialized with a separate src_builder. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -copy_out = Builder(action = Copy('$TARGET', '$SOURCE'), - suffix = '.out', - src_suffix = '.mid') - -copy_mid = Builder(action = Copy('$TARGET', '$SOURCE'), - suffix = '.mid', \ - src_suffix = '.in') - -env = Environment(tools=[]) -env['BUILDERS']['CopyOut'] = copy_out -env['BUILDERS']['CopyMid'] = copy_mid - -copy_out.add_src_builder(copy_mid) - -env.CopyOut('file1.out', 'file1.in') -""") - -test.write('file1.in', "file1.in\n") - -test.run() - -test.must_match('file1.mid', "file1.in\n") -test.must_match('file1.out', "file1.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that we can call add_src_builder() to add a builder to +another on the fly. + +This used to trigger infinite recursion (issue 1681) because the +same src_builder list object was being re-used between all Builder +objects that weren't initialized with a separate src_builder. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +copy_out = Builder(action = Copy('$TARGET', '$SOURCE'), + suffix = '.out', + src_suffix = '.mid') + +copy_mid = Builder(action = Copy('$TARGET', '$SOURCE'), + suffix = '.mid', \ + src_suffix = '.in') + +env = Environment(tools=[]) +env['BUILDERS']['CopyOut'] = copy_out +env['BUILDERS']['CopyMid'] = copy_mid + +copy_out.add_src_builder(copy_mid) + +env.CopyOut('file1.out', 'file1.in') +""") + +test.write('file1.in', "file1.in\n") + +test.run() + +test.must_match('file1.mid', "file1.in\n") +test.must_match('file1.out', "file1.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/different-actions.py b/test/Builder/different-actions.py index c7191bf097..457639121a 100644 --- a/test/Builder/different-actions.py +++ b/test/Builder/different-actions.py @@ -1,58 +1,58 @@ -#!/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 that two builders in two environments with different -actions generate an error. -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -e1 = Environment(tools=[]) -e2 = Environment(tools=[]) - -e1.Command('out.txt', [], 'echo 1 > $TARGET') -e2.Command('out.txt', [], 'echo 2 > $TARGET') -""",'w') - -expect = TestSCons.re_escape(""" -scons: *** Two environments with different actions were specified for the same target: out.txt -(action 1: echo 1 > out.txt) -(action 2: echo 2 > out.txt) -""") + TestSCons.file_expr - -test.run(arguments='out.txt', status=2, stderr=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that two builders in two environments with different +actions generate an error. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +e1 = Environment(tools=[]) +e2 = Environment(tools=[]) + +e1.Command('out.txt', [], 'echo 1 > $TARGET') +e2.Command('out.txt', [], 'echo 2 > $TARGET') +""",'w') + +expect = TestSCons.re_escape(""" +scons: *** Two environments with different actions were specified for the same target: out.txt +(action 1: echo 1 > out.txt) +(action 2: echo 2 > out.txt) +""") + TestSCons.file_expr + +test.run(arguments='out.txt', status=2, stderr=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/Builder/ensure_suffix.py b/test/Builder/ensure_suffix.py index 94a7e1f5ed..08cf16e354 100644 --- a/test/Builder/ensure_suffix.py +++ b/test/Builder/ensure_suffix.py @@ -1,61 +1,61 @@ -#!/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 that the ensure_suffix argument to causes us to add the suffix -configured for the Builder even if it looks like the target already has -a different suffix. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) - -tbuilder = Builder(action=Copy('$TARGET', '$SOURCE'), - suffix='.dll', - ensure_suffix=True) - -env['BUILDERS']['TBuilder'] = tbuilder - -env.TBuilder("aa.bb.cc.dd","aa.aa.txt") -""") - -test.write('aa.aa.txt', "clean test\n") - -test.run(arguments = '.') - -test.must_match('aa.bb.cc.dd.dll', "clean test\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the ensure_suffix argument to causes us to add the suffix +configured for the Builder even if it looks like the target already has +a different suffix. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) + +tbuilder = Builder(action=Copy('$TARGET', '$SOURCE'), + suffix='.dll', + ensure_suffix=True) + +env['BUILDERS']['TBuilder'] = tbuilder + +env.TBuilder("aa.bb.cc.dd","aa.aa.txt") +""") + +test.write('aa.aa.txt', "clean test\n") + +test.run(arguments = '.') + +test.must_match('aa.bb.cc.dd.dll', "clean test\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/non-multi.py b/test/Builder/non-multi.py index cdbd5b044f..d540f6bf8b 100644 --- a/test/Builder/non-multi.py +++ b/test/Builder/non-multi.py @@ -1,67 +1,67 @@ -#!/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 that a builder without "multi" set can still be called multiple -times if the calls are the same. -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) - -def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: - f.write(infp.read()) - -B = Builder(action=build, multi=0) -env = Environment(tools=[], BUILDERS = { 'B' : B }) -env.B(target = 'file7.out', source = 'file7.in') -env.B(target = 'file7.out', source = 'file7.in') -env.B(target = 'file8.out', source = 'file8.in', arg=1) -env.B(target = 'file8.out', source = 'file8.in', arg=1) -""") - -test.write('file7.in', 'file7.in\n') -test.write('file8.in', 'file8.in\n') - -test.run(arguments='file7.out') -test.run(arguments='file8.out') - -test.must_match('file7.out', "file7.in\n") -test.must_match('file8.out', "file8.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that a builder without "multi" set can still be called multiple +times if the calls are the same. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + +def build(env, target, source): + with open(str(target[0]), 'wb') as f: + for s in source: + with open(str(s), 'rb') as infp: + f.write(infp.read()) + +B = Builder(action=build, multi=0) +env = Environment(tools=[], BUILDERS = { 'B' : B }) +env.B(target = 'file7.out', source = 'file7.in') +env.B(target = 'file7.out', source = 'file7.in') +env.B(target = 'file8.out', source = 'file8.in', arg=1) +env.B(target = 'file8.out', source = 'file8.in', arg=1) +""") + +test.write('file7.in', 'file7.in\n') +test.write('file8.in', 'file8.in\n') + +test.run(arguments='file7.out') +test.run(arguments='file8.out') + +test.must_match('file7.out', "file7.in\n") +test.must_match('file8.out', "file8.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/not-a-Builder.py b/test/Builder/not-a-Builder.py index 2cee0aaef7..f0a16b3654 100644 --- a/test/Builder/not-a-Builder.py +++ b/test/Builder/not-a-Builder.py @@ -1,60 +1,60 @@ -#!/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. - -""" -Test the error when trying to configure a Builder with a non-Builder object. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -SConstruct_path = test.workpath('SConstruct') - -test.write(SConstruct_path, """\ -def mkdir(env, target, source): - return None -mkdir = 1 -env = Environment(BUILDERS={'mkdir': 1}) -env.mkdir(env.Dir('src'), None) -""") - -expect_stderr = """\ - -scons: *** 1 is not a Builder. -""" + test.python_file_line(SConstruct_path, 4) - -test.run(arguments='.', - stderr=expect_stderr, - status=2) - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the error when trying to configure a Builder with a non-Builder object. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +SConstruct_path = test.workpath('SConstruct') + +test.write(SConstruct_path, """\ +def mkdir(env, target, source): + return None +mkdir = 1 +env = Environment(BUILDERS={'mkdir': 1}) +env.mkdir(env.Dir('src'), None) +""") + +expect_stderr = """\ + +scons: *** 1 is not a Builder. +""" + test.python_file_line(SConstruct_path, 4) + +test.run(arguments='.', + stderr=expect_stderr, + status=2) + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/same-actions-diff-envs.py b/test/Builder/same-actions-diff-envs.py index f3bff437c3..709b10850f 100644 --- a/test/Builder/same-actions-diff-envs.py +++ b/test/Builder/same-actions-diff-envs.py @@ -1,63 +1,63 @@ -#!/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 that two builders in two environments with the same actions generate -a warning -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) - -def build(env, target, source): - with open(str(target[0]), 'w') as f: - f.write('1') - -B = Builder(action=build) -env = Environment(tools=[], BUILDERS = { 'B' : B }) -env2 = Environment(tools=[], BUILDERS = { 'B' : B }) -env.B('out.txt', []) -env2.B('out.txt', []) -""") - -expect = TestSCons.re_escape(""" -scons: warning: Two different environments were specified for target out.txt, -\tbut they appear to have the same action: build(target, source, env) -""") + TestSCons.file_expr - -test.run(arguments='out.txt', status=0, stderr=expect) -test.must_match('out.txt', '1') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that two builders in two environments with the same actions generate +a warning +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + +def build(env, target, source): + with open(str(target[0]), 'w') as f: + f.write('1') + +B = Builder(action=build) +env = Environment(tools=[], BUILDERS = { 'B' : B }) +env2 = Environment(tools=[], BUILDERS = { 'B' : B }) +env.B('out.txt', []) +env2.B('out.txt', []) +""") + +expect = TestSCons.re_escape(""" +scons: warning: Two different environments were specified for target out.txt, +\tbut they appear to have the same action: build(target, source, env) +""") + TestSCons.file_expr + +test.run(arguments='out.txt', status=0, stderr=expect) +test.must_match('out.txt', '1') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/same-actions-diff-overrides.py b/test/Builder/same-actions-diff-overrides.py index 596c8687f2..ba3ee9078d 100644 --- a/test/Builder/same-actions-diff-overrides.py +++ b/test/Builder/same-actions-diff-overrides.py @@ -1,62 +1,62 @@ -#!/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 that two calls to a builder with different overrides, but the same -action, generate a warning -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) - -def build(env, target, source): - with open(str(target[0]), 'w') as f: - f.write('1') - -B = Builder(action=build) -env = Environment(tools=[], BUILDERS = { 'B' : B }) -env.B('out.txt', [], arg=1) -env.B('out.txt', [], arg=2) -""") - -expect = TestSCons.re_escape(""" -scons: warning: Two different environments were specified for target out.txt, -\tbut they appear to have the same action: build(target, source, env) -""") + TestSCons.file_expr - -test.run(arguments='out.txt', status=0, stderr=expect) -test.must_match('out.txt', '1') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that two calls to a builder with different overrides, but the same +action, generate a warning +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + +def build(env, target, source): + with open(str(target[0]), 'w') as f: + f.write('1') + +B = Builder(action=build) +env = Environment(tools=[], BUILDERS = { 'B' : B }) +env.B('out.txt', [], arg=1) +env.B('out.txt', [], arg=2) +""") + +expect = TestSCons.re_escape(""" +scons: warning: Two different environments were specified for target out.txt, +\tbut they appear to have the same action: build(target, source, env) +""") + TestSCons.file_expr + +test.run(arguments='out.txt', status=0, stderr=expect) +test.must_match('out.txt', '1') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/srcdir.py b/test/Builder/srcdir.py index 0ce6b5e0a0..d084f9380c 100644 --- a/test/Builder/srcdir.py +++ b/test/Builder/srcdir.py @@ -1,83 +1,83 @@ -#!/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 that specifying a srcdir when calling a Builder correctly -prefixes each relative-path string with the specified srcdir. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.subdir('src', ['src', 'foo']) - -file3 = test.workpath('file3') - -test.write(['src', 'cat.py'], """\ -import sys -with open(sys.argv[1], 'wb') as o: - for f in sys.argv[2:]: - with open(f, 'rb') as i: - o.write(i.read()) -""") - -test.write(['src', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) - -Command('output', - ['file1', File('file2'), r'%(file3)s', 'file4'], - r'%(_python_)s cat.py $TARGET $SOURCES', - srcdir='foo') -""" % locals()) - -test.write(['src', 'foo', 'file1'], "file1\n") - -test.write(['src', 'file2'], "file2\n") - -test.write(file3, "file3\n") - -test.write(['src', 'foo', 'file4'], "file4\n") - -test.run(chdir = 'src', arguments = '.') - -expected = """\ -file1 -file2 -file3 -file4 -""" - -test.must_match(['src', 'output'], expected) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that specifying a srcdir when calling a Builder correctly +prefixes each relative-path string with the specified srcdir. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.subdir('src', ['src', 'foo']) + +file3 = test.workpath('file3') + +test.write(['src', 'cat.py'], """\ +import sys +with open(sys.argv[1], 'wb') as o: + for f in sys.argv[2:]: + with open(f, 'rb') as i: + o.write(i.read()) +""") + +test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) + +Command('output', + ['file1', File('file2'), r'%(file3)s', 'file4'], + r'%(_python_)s cat.py $TARGET $SOURCES', + srcdir='foo') +""" % locals()) + +test.write(['src', 'foo', 'file1'], "file1\n") + +test.write(['src', 'file2'], "file2\n") + +test.write(file3, "file3\n") + +test.write(['src', 'foo', 'file4'], "file4\n") + +test.run(chdir = 'src', arguments = '.') + +expected = """\ +file1 +file2 +file3 +file4 +""" + +test.must_match(['src', 'output'], expected) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Builder/wrapper.py b/test/Builder/wrapper.py index d68a6cab61..60f8b21baa 100644 --- a/test/Builder/wrapper.py +++ b/test/Builder/wrapper.py @@ -1,77 +1,77 @@ -#!/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. - -""" -Test the ability to use a direct Python function to wrap -calls to other Builder(s). -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) - -import os.path -import string -def cat(target, source, env): - with open(str(target[0]), 'wb') as fp: - for s in map(str, source): - with open(s, 'rb') as infp: - fp.write(infp.read()) -Cat = Builder(action=cat) -def Wrapper(env, target, source): - if not target: - target = [str(source[0]).replace('.in', '.wout')] - t1 = 't1-'+str(target[0]) - source = 's-'+str(source[0]) - env.Cat(t1, source) - t2 = 't2-'+str(target[0]) - env.Cat(t2, source) -env = Environment(tools=[], - BUILDERS = {'Cat' : Cat, - 'Wrapper' : Wrapper}) -env.Wrapper('f1.out', 'f1.in') -env.Wrapper('f2.in') -""") - -test.write('s-f1.in', "s-f1.in\n") -test.write('s-f2.in', "s-f2.in\n") - -test.run() - -test.must_match('t1-f1.out', "s-f1.in\n") -test.must_match('t1-f2.wout', "s-f2.in\n") -test.must_match('t2-f1.out', "s-f1.in\n") -test.must_match('t2-f2.wout', "s-f2.in\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to use a direct Python function to wrap +calls to other Builder(s). +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) + +import os.path +import string +def cat(target, source, env): + with open(str(target[0]), 'wb') as fp: + for s in map(str, source): + with open(s, 'rb') as infp: + fp.write(infp.read()) +Cat = Builder(action=cat) +def Wrapper(env, target, source): + if not target: + target = [str(source[0]).replace('.in', '.wout')] + t1 = 't1-'+str(target[0]) + source = 's-'+str(source[0]) + env.Cat(t1, source) + t2 = 't2-'+str(target[0]) + env.Cat(t2, source) +env = Environment(tools=[], + BUILDERS = {'Cat' : Cat, + 'Wrapper' : Wrapper}) +env.Wrapper('f1.out', 'f1.in') +env.Wrapper('f2.in') +""") + +test.write('s-f1.in', "s-f1.in\n") +test.write('s-f2.in', "s-f2.in\n") + +test.run() + +test.must_match('t1-f1.out', "s-f1.in\n") +test.must_match('t1-f2.wout', "s-f2.in\n") +test.must_match('t2-f1.out', "s-f1.in\n") +test.must_match('t2-f2.wout', "s-f2.in\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/CCCOM.py b/test/CC/CCCOM.py index 717b36f984..9ee49b446b 100644 --- a/test/CC/CCCOM.py +++ b/test/CC/CCCOM.py @@ -1,72 +1,72 @@ -#!/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. - -""" -Test the ability to configure the $CCCOM construction variable. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.c') == os.path.normcase('.C'): - alt_c_suffix = '.C' -else: - alt_c_suffix = '.c' - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['cc'], - CCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', - OBJSUFFIX='.obj') -env.Object(target = 'test1', source = 'test1.c') -env.Object(target = 'test2', source = 'test2%(alt_c_suffix)s') -""" % locals()) - -test.write('test1.c', 'test1.c\n/*cc*/\n') - -test.write('test2'+alt_c_suffix, """\ -test2.C -/*cc*/ -""") - -test.run() - -test.must_match('test1.obj', "test1.c\n") -test.must_match('test2.obj', "test2.C\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the ability to configure the $CCCOM construction variable. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.c') == os.path.normcase('.C'): + alt_c_suffix = '.C' +else: + alt_c_suffix = '.c' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['cc'], + CCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', + OBJSUFFIX='.obj') +env.Object(target = 'test1', source = 'test1.c') +env.Object(target = 'test2', source = 'test2%(alt_c_suffix)s') +""" % locals()) + +test.write('test1.c', 'test1.c\n/*cc*/\n') + +test.write('test2'+alt_c_suffix, """\ +test2.C +/*cc*/ +""") + +test.run() + +test.must_match('test1.obj', "test1.c\n") +test.must_match('test2.obj', "test2.C\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/CCCOMSTR.py b/test/CC/CCCOMSTR.py index 55d1db26d7..da07dd3d44 100644 --- a/test/CC/CCCOMSTR.py +++ b/test/CC/CCCOMSTR.py @@ -1,77 +1,77 @@ -#!/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. - -""" -Test that the $CCCOMSTR construction variable allows you to configure -the C compilation output. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.c') == os.path.normcase('.C'): - alt_c_suffix = '.C' -else: - alt_c_suffix = '.c' - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['cc'], - CCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', - CCCOMSTR = 'Building $TARGET from $SOURCE', - OBJSUFFIX='.obj') -env.Object(target = 'test1', source = 'test1.c') -env.Object(target = 'test2', source = 'test2%(alt_c_suffix)s') -""" % locals()) - -test.write('test1.c', 'test1.c\n/*cc*/\n') - -test.write('test2'+alt_c_suffix, """\ -test2.C -/*cc*/ -""") - -test.run(stdout = test.wrap_stdout("""\ -Building test1.obj from test1.c -Building test2.obj from test2%(alt_c_suffix)s -""" % locals())) - -test.must_match('test1.obj', "test1.c\n") -test.must_match('test2.obj', "test2.C\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $CCCOMSTR construction variable allows you to configure +the C compilation output. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.c') == os.path.normcase('.C'): + alt_c_suffix = '.C' +else: + alt_c_suffix = '.c' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['cc'], + CCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', + CCCOMSTR = 'Building $TARGET from $SOURCE', + OBJSUFFIX='.obj') +env.Object(target = 'test1', source = 'test1.c') +env.Object(target = 'test2', source = 'test2%(alt_c_suffix)s') +""" % locals()) + +test.write('test1.c', 'test1.c\n/*cc*/\n') + +test.write('test2'+alt_c_suffix, """\ +test2.C +/*cc*/ +""") + +test.run(stdout = test.wrap_stdout("""\ +Building test1.obj from test1.c +Building test2.obj from test2%(alt_c_suffix)s +""" % locals())) + +test.must_match('test1.obj', "test1.c\n") +test.must_match('test2.obj', "test2.C\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/CCFLAGS.py b/test/CC/CCFLAGS.py index 8afa3b342f..7269b9cf24 100644 --- a/test/CC/CCFLAGS.py +++ b/test/CC/CCFLAGS.py @@ -1,111 +1,111 @@ -#!/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. - -import sys -import TestSCons - -_obj = TestSCons._obj - -if sys.platform == 'win32': - import SCons.Tool.MSCommon as msc - - if not msc.msvc_exists(): - fooflags = '-DFOO' - barflags = '-DBAR' - else: - fooflags = '/nologo -DFOO' - barflags = '/nologo -DBAR' -else: - fooflags = '-DFOO' - barflags = '-DBAR' - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tool=[]) -foo = Environment(CCFLAGS = '%s') -bar = Environment(CCFLAGS = '%s') -foo.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -foo.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -foo.Program(target = 'prog', source = 'prog.c', - CCFLAGS = '$CCFLAGS -DBAR $BAZ', BAZ = '-DBAZ') -""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) - -test.write('prog.c', r""" -#include -#include - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; -#ifdef FOO - printf("prog.c: FOO\n"); -#endif -#ifdef BAR - printf("prog.c: BAR\n"); -#endif -#ifdef BAZ - printf("prog.c: BAZ\n"); -#endif - exit (0); -} -""") - - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('prog'), stdout = """\ -prog.c: FOO -prog.c: BAR -prog.c: BAZ -""") - -test.write('SConstruct', """ -DefaultEnvironment(tool=[]) - -bar = Environment(CCFLAGS = '%s') -bar.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -bar.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -""" % (barflags, _obj, _obj, _obj, _obj)) - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import sys +import TestSCons + +_obj = TestSCons._obj + +if sys.platform == 'win32': + import SCons.Tool.MSCommon as msc + + if not msc.msvc_exists(): + fooflags = '-DFOO' + barflags = '-DBAR' + else: + fooflags = '/nologo -DFOO' + barflags = '/nologo -DBAR' +else: + fooflags = '-DFOO' + barflags = '-DBAR' + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tool=[]) +foo = Environment(CCFLAGS = '%s') +bar = Environment(CCFLAGS = '%s') +foo.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +foo.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +foo.Program(target = 'prog', source = 'prog.c', + CCFLAGS = '$CCFLAGS -DBAR $BAZ', BAZ = '-DBAZ') +""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) + +test.write('prog.c', r""" +#include +#include + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; +#ifdef FOO + printf("prog.c: FOO\n"); +#endif +#ifdef BAR + printf("prog.c: BAR\n"); +#endif +#ifdef BAZ + printf("prog.c: BAZ\n"); +#endif + exit (0); +} +""") + + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('prog'), stdout = """\ +prog.c: FOO +prog.c: BAR +prog.c: BAZ +""") + +test.write('SConstruct', """ +DefaultEnvironment(tool=[]) + +bar = Environment(CCFLAGS = '%s') +bar.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +bar.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +""" % (barflags, _obj, _obj, _obj, _obj)) + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/CFLAGS.py b/test/CC/CFLAGS.py index 59ac137b6c..19ae2641df 100644 --- a/test/CC/CFLAGS.py +++ b/test/CC/CFLAGS.py @@ -1,124 +1,124 @@ -#!/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. - -import sys -import TestSCons - -test = TestSCons.TestSCons() - -# Make sure CFLAGS is not passed to CXX by just expanding CXXCOM -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(CFLAGS='-xyz', CCFLAGS='-abc') -print(env.subst('$CXXCOM')) -print(env.subst('$CXXCOMSTR')) -print(env.subst('$SHCXXCOM')) -print(env.subst('$SHCXXCOMSTR')) -""") -test.run(arguments = '.') -test.must_not_contain_any_line(test.stdout(), ["-xyz"]) -test.must_contain_all_lines(test.stdout(), ["-abc"]) - -_obj = TestSCons._obj - -# Test passing CFLAGS to C compiler by actually compiling programs -if sys.platform == 'win32': - import SCons.Tool.MSCommon as msc - - if not msc.msvc_exists(): - fooflags = '-DFOO' - barflags = '-DBAR' - else: - fooflags = '/nologo -DFOO' - barflags = '/nologo -DBAR' -else: - fooflags = '-DFOO' - barflags = '-DBAR' - - -test.write('SConstruct', """ -foo = Environment(CFLAGS = '%s') -bar = Environment(CFLAGS = '%s') -foo.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -foo.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -foo.Program(target = 'prog', source = 'prog.c', - CFLAGS = '$CFLAGS -DBAR $BAZ', BAZ = '-DBAZ') -""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) - -test.write('prog.c', r""" -#include -#include - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; -#ifdef FOO - printf("prog.c: FOO\n"); -#endif -#ifdef BAR - printf("prog.c: BAR\n"); -#endif -#ifdef BAZ - printf("prog.c: BAZ\n"); -#endif - exit (0); -} -""") - - - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('prog'), stdout = """\ -prog.c: FOO -prog.c: BAR -prog.c: BAZ -""") - -test.write('SConstruct', """ -bar = Environment(CFLAGS = '%s') -bar.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -bar.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -""" % (barflags, _obj, _obj, _obj, _obj)) - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import sys +import TestSCons + +test = TestSCons.TestSCons() + +# Make sure CFLAGS is not passed to CXX by just expanding CXXCOM +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CFLAGS='-xyz', CCFLAGS='-abc') +print(env.subst('$CXXCOM')) +print(env.subst('$CXXCOMSTR')) +print(env.subst('$SHCXXCOM')) +print(env.subst('$SHCXXCOMSTR')) +""") +test.run(arguments = '.') +test.must_not_contain_any_line(test.stdout(), ["-xyz"]) +test.must_contain_all_lines(test.stdout(), ["-abc"]) + +_obj = TestSCons._obj + +# Test passing CFLAGS to C compiler by actually compiling programs +if sys.platform == 'win32': + import SCons.Tool.MSCommon as msc + + if not msc.msvc_exists(): + fooflags = '-DFOO' + barflags = '-DBAR' + else: + fooflags = '/nologo -DFOO' + barflags = '/nologo -DBAR' +else: + fooflags = '-DFOO' + barflags = '-DBAR' + + +test.write('SConstruct', """ +foo = Environment(CFLAGS = '%s') +bar = Environment(CFLAGS = '%s') +foo.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +foo.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +foo.Program(target = 'prog', source = 'prog.c', + CFLAGS = '$CFLAGS -DBAR $BAZ', BAZ = '-DBAZ') +""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) + +test.write('prog.c', r""" +#include +#include + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; +#ifdef FOO + printf("prog.c: FOO\n"); +#endif +#ifdef BAR + printf("prog.c: BAR\n"); +#endif +#ifdef BAZ + printf("prog.c: BAZ\n"); +#endif + exit (0); +} +""") + + + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('prog'), stdout = """\ +prog.c: FOO +prog.c: BAR +prog.c: BAZ +""") + +test.write('SConstruct', """ +bar = Environment(CFLAGS = '%s') +bar.Object(target = 'foo%s', source = 'prog.c') +bar.Object(target = 'bar%s', source = 'prog.c') +bar.Program(target = 'foo', source = 'foo%s') +bar.Program(target = 'bar', source = 'bar%s') +""" % (barflags, _obj, _obj, _obj, _obj)) + +test.run(arguments = '.') + +test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/SHCCCOM.py b/test/CC/SHCCCOM.py index 55ff1e1ff4..eb05e9cd02 100644 --- a/test/CC/SHCCCOM.py +++ b/test/CC/SHCCCOM.py @@ -1,72 +1,72 @@ -#!/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. - -import os - -import TestSCons - -""" -Test the ability to configure the $SHCCCOM construction variable. -""" - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.c') == os.path.normcase('.C'): - alt_c_suffix = '.C' -else: - alt_c_suffix = '.c' - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(SHCCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', - SHOBJPREFIX='', - SHOBJSUFFIX='.obj') -env.SharedObject(target = 'test1', source = 'test1.c') -env.SharedObject(target = 'test2', source = 'test2%(alt_c_suffix)s') -""" % locals()) - -test.write('test1.c', 'test1.c\n/*cc*/\n') - -test.write('test2'+alt_c_suffix, """\ -test2.C -/*cc*/ -""") - -test.run() - -test.must_match('test1.obj', "test1.c\n") -test.must_match('test2.obj', "test2.C\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import os + +import TestSCons + +""" +Test the ability to configure the $SHCCCOM construction variable. +""" + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.c') == os.path.normcase('.C'): + alt_c_suffix = '.C' +else: + alt_c_suffix = '.c' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(SHCCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', + SHOBJPREFIX='', + SHOBJSUFFIX='.obj') +env.SharedObject(target = 'test1', source = 'test1.c') +env.SharedObject(target = 'test2', source = 'test2%(alt_c_suffix)s') +""" % locals()) + +test.write('test1.c', 'test1.c\n/*cc*/\n') + +test.write('test2'+alt_c_suffix, """\ +test2.C +/*cc*/ +""") + +test.run() + +test.must_match('test1.obj', "test1.c\n") +test.must_match('test2.obj', "test2.C\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/SHCCCOMSTR.py b/test/CC/SHCCCOMSTR.py index e41de80849..c24f9a93eb 100644 --- a/test/CC/SHCCCOMSTR.py +++ b/test/CC/SHCCCOMSTR.py @@ -1,79 +1,79 @@ -#!/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. - -""" -Test that the $SHCCCOMSTR construction variable allows you to customize -the shared object C compilation output. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.file_fixture('mycompile.py') - -if os.path.normcase('.c') == os.path.normcase('.C'): - alt_c_suffix = '.C' -else: - alt_c_suffix = '.c' - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(SHCCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', - SHCCCOMSTR = 'Building $TARGET from $SOURCE', - SHOBJPREFIX='', - SHOBJSUFFIX='.obj') -env.SharedObject(target = 'test1', source = 'test1.c') -env.SharedObject(target = 'test2', source = 'test2%(alt_c_suffix)s') -""" % locals()) - -test.write('test1.c', 'test1.c\n/*cc*/\n') - -test.write('test2'+alt_c_suffix, """\ -test2.C -/*cc*/ -""") - -test.run(stdout = test.wrap_stdout("""\ -Building test1.obj from test1.c -Building test2.obj from test2%(alt_c_suffix)s -""" % locals())) - -test.must_match('test1.obj', "test1.c\n") -test.must_match('test2.obj', "test2.C\n") - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the $SHCCCOMSTR construction variable allows you to customize +the shared object C compilation output. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('mycompile.py') + +if os.path.normcase('.c') == os.path.normcase('.C'): + alt_c_suffix = '.C' +else: + alt_c_suffix = '.c' + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(SHCCCOM = r'%(_python_)s mycompile.py cc $TARGET $SOURCE', + SHCCCOMSTR = 'Building $TARGET from $SOURCE', + SHOBJPREFIX='', + SHOBJSUFFIX='.obj') +env.SharedObject(target = 'test1', source = 'test1.c') +env.SharedObject(target = 'test2', source = 'test2%(alt_c_suffix)s') +""" % locals()) + +test.write('test1.c', 'test1.c\n/*cc*/\n') + +test.write('test2'+alt_c_suffix, """\ +test2.C +/*cc*/ +""") + +test.run(stdout = test.wrap_stdout("""\ +Building test1.obj from test1.c +Building test2.obj from test2%(alt_c_suffix)s +""" % locals())) + +test.must_match('test1.obj', "test1.c\n") +test.must_match('test2.obj', "test2.C\n") + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/SHCFLAGS.py b/test/CC/SHCFLAGS.py index b110f59de9..e143713c56 100644 --- a/test/CC/SHCFLAGS.py +++ b/test/CC/SHCFLAGS.py @@ -1,138 +1,138 @@ -#!/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. -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import sys -import TestSCons -import os - -test = TestSCons.TestSCons() - -e = test.Environment() -fooflags = e['SHCFLAGS'] + ' -DFOO' -barflags = e['SHCFLAGS'] + ' -DBAR' - -if os.name == 'posix': - os.environ['LD_LIBRARY_PATH'] = '.' -if sys.platform.find('irix') > -1: - os.environ['LD_LIBRARYN32_PATH'] = '.' - -test.write('SConstruct', f""" -DefaultEnvironment(tools=[]) -foo = Environment(SHCFLAGS = '{fooflags}', WINDOWS_INSERT_DEF=1) -bar = Environment(SHCFLAGS = '{barflags}', WINDOWS_INSERT_DEF=1) - -foo_obj = foo.SharedObject(target = 'foo', source = 'prog.c') -foo.SharedLibrary(target = 'foo', source = foo_obj) - -bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') -bar.SharedLibrary(target = 'bar', source = bar_obj) - -fooMain = foo.Clone(LIBS='foo', LIBPATH='.') -foomain_obj = fooMain.Object(target='foomain', source='main.c') -fooMain.Program(target='fooprog', source=foomain_obj) - -barMain = bar.Clone(LIBS='bar', LIBPATH='.') -barmain_obj = barMain.Object(target='barmain', source='main.c') -barMain.Program(target='barprog', source=barmain_obj) -""") - -test.write('foo.def', r""" -LIBRARY "foo" -DESCRIPTION "Foo Shared Library" - -EXPORTS - doIt -""") - -test.write('bar.def', r""" -LIBRARY "bar" -DESCRIPTION "Bar Shared Library" - -EXPORTS - doIt -""") - -test.write('prog.c', r""" -#include - -void -doIt() -{ -#ifdef FOO - printf("prog.c: FOO\n"); -#endif -#ifdef BAR - printf("prog.c: BAR\n"); -#endif -} -""") - -test.write('main.c', r""" - -void doIt(); - -int -main(int argc, char* argv[]) -{ - doIt(); - return 0; -} -""") - -test.run(arguments = '.') - -test.run(program = test.workpath('fooprog'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") - -test.write('SConstruct', f""" -DefaultEnvironment(tools=[]) -bar = Environment(SHCFLAGS = '{barflags}', WINDOWS_INSERT_DEF=1) - -foo_obj = bar.SharedObject(target = 'foo', source = 'prog.c') -bar.SharedLibrary(target = 'foo', source = foo_obj) - -bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') -bar.SharedLibrary(target = 'bar', source = bar_obj) - -barMain = bar.Clone(LIBS='bar', LIBPATH='.') -foomain_obj = barMain.Object(target='foomain', source='main.c') -barmain_obj = barMain.Object(target='barmain', source='main.c') -barMain.Program(target='barprog', source=foomain_obj) -barMain.Program(target='fooprog', source=barmain_obj) -""") - -test.run(arguments = '.') - -test.run(program = test.workpath('fooprog'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys +import TestSCons +import os + +test = TestSCons.TestSCons() + +e = test.Environment() +fooflags = e['SHCFLAGS'] + ' -DFOO' +barflags = e['SHCFLAGS'] + ' -DBAR' + +if os.name == 'posix': + os.environ['LD_LIBRARY_PATH'] = '.' +if sys.platform.find('irix') > -1: + os.environ['LD_LIBRARYN32_PATH'] = '.' + +test.write('SConstruct', f""" +DefaultEnvironment(tools=[]) +foo = Environment(SHCFLAGS = '{fooflags}', WINDOWS_INSERT_DEF=1) +bar = Environment(SHCFLAGS = '{barflags}', WINDOWS_INSERT_DEF=1) + +foo_obj = foo.SharedObject(target = 'foo', source = 'prog.c') +foo.SharedLibrary(target = 'foo', source = foo_obj) + +bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') +bar.SharedLibrary(target = 'bar', source = bar_obj) + +fooMain = foo.Clone(LIBS='foo', LIBPATH='.') +foomain_obj = fooMain.Object(target='foomain', source='main.c') +fooMain.Program(target='fooprog', source=foomain_obj) + +barMain = bar.Clone(LIBS='bar', LIBPATH='.') +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=barmain_obj) +""") + +test.write('foo.def', r""" +LIBRARY "foo" +DESCRIPTION "Foo Shared Library" + +EXPORTS + doIt +""") + +test.write('bar.def', r""" +LIBRARY "bar" +DESCRIPTION "Bar Shared Library" + +EXPORTS + doIt +""") + +test.write('prog.c', r""" +#include + +void +doIt() +{ +#ifdef FOO + printf("prog.c: FOO\n"); +#endif +#ifdef BAR + printf("prog.c: BAR\n"); +#endif +} +""") + +test.write('main.c', r""" + +void doIt(); + +int +main(int argc, char* argv[]) +{ + doIt(); + return 0; +} +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('fooprog'), stdout = "prog.c: FOO\n") +test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") + +test.write('SConstruct', f""" +DefaultEnvironment(tools=[]) +bar = Environment(SHCFLAGS = '{barflags}', WINDOWS_INSERT_DEF=1) + +foo_obj = bar.SharedObject(target = 'foo', source = 'prog.c') +bar.SharedLibrary(target = 'foo', source = foo_obj) + +bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') +bar.SharedLibrary(target = 'bar', source = bar_obj) + +barMain = bar.Clone(LIBS='bar', LIBPATH='.') +foomain_obj = barMain.Object(target='foomain', source='main.c') +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=foomain_obj) +barMain.Program(target='fooprog', source=barmain_obj) +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('fooprog'), stdout = "prog.c: BAR\n") +test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/Dir.py b/test/CPPPATH/Dir.py index 8275c0e243..a0b1b32ac4 100644 --- a/test/CPPPATH/Dir.py +++ b/test/CPPPATH/Dir.py @@ -1,85 +1,85 @@ -#!/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 that CPPPATH values with Dir nodes work correctly. -""" - -import TestSCons - -_exe = TestSCons._exe - -test = TestSCons.TestSCons() - -test.subdir('inc1', 'inc2', 'inc3', ['inc3', 'subdir']) - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir'], - INC2 = Dir('inc2'), - INC3 = Dir('inc3')) -env.Program('prog.c') -""") - -test.write('prog.c', """\ -#include -#include - -#include "one.h" -#include "two.h" -#include "three.h" -int -main(int argc, char *argv[]) -{ - printf("%s\\n", ONE); - printf("%s\\n", TWO); - printf("%s\\n", THREE); - return (0); -} -""") - -test.write(['inc1', 'one.h'], """\ -#define ONE "1" -""") - -test.write(['inc2', 'two.h'], """\ -#define TWO "2" -""") - -test.write(['inc3', 'subdir', 'three.h'], """\ -#define THREE "3" -""") - -test.run(arguments = '.') - -test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that CPPPATH values with Dir nodes work correctly. +""" + +import TestSCons + +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.subdir('inc1', 'inc2', 'inc3', ['inc3', 'subdir']) + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir'], + INC2 = Dir('inc2'), + INC3 = Dir('inc3')) +env.Program('prog.c') +""") + +test.write('prog.c', """\ +#include +#include + +#include "one.h" +#include "two.h" +#include "three.h" +int +main(int argc, char *argv[]) +{ + printf("%s\\n", ONE); + printf("%s\\n", TWO); + printf("%s\\n", THREE); + return (0); +} +""") + +test.write(['inc1', 'one.h'], """\ +#define ONE "1" +""") + +test.write(['inc2', 'two.h'], """\ +#define TWO "2" +""") + +test.write(['inc3', 'subdir', 'three.h'], """\ +#define THREE "3" +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/absolute-path.py b/test/CPPPATH/absolute-path.py index a73f4fcec8..dca01f0336 100644 --- a/test/CPPPATH/absolute-path.py +++ b/test/CPPPATH/absolute-path.py @@ -1,100 +1,100 @@ -#!/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 the ability to #include a file with an absolute path name. (Which -is not strictly a test of using $CPPPATH, but it's in the ball park...) -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('include', 'work') - -inc1_h = test.workpath('include', 'inc1.h') -inc2_h = test.workpath('include', 'inc2.h') -does_not_exist_h = test.workpath('include', 'does_not_exist.h') - -# Verify that including an absolute path still works even if they -# double the separators in the input file. This can happen especially -# on Windows if they use \\ to represent an escaped backslash. -inc2_h = inc2_h.replace(os.sep, os.sep+os.sep) - -test.write(['work', 'SConstruct'], """\ -Program('prog.c') -""") - -test.write(['work', 'prog.c'], """\ -#include -#include "%(inc1_h)s" -#include "%(inc2_h)s" -#if 0 -#include "%(does_not_exist_h)s" -#endif - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("%%s\\n", STRING1); - printf("%%s\\n", STRING2); - return 0; -} -""" % locals()) - -test.write(['include', 'inc1.h'], """\ -#define STRING1 "include/inc1.h A\\n" -""") - -test.write(['include', 'inc2.h'], """\ -#define STRING2 "include/inc2.h A\\n" -""") - -test.run(chdir = 'work', arguments = '.') - -test.up_to_date(chdir = 'work', arguments = '.') - -test.write(['include', 'inc1.h'], """\ -#define STRING1 "include/inc1.h B\\n" -""") - -test.not_up_to_date(chdir = 'work', arguments = '.') - -test.write(['include', 'inc2.h'], """\ -#define STRING2 "include/inc2.h B\\n" -""") - -test.not_up_to_date(chdir = 'work', arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the ability to #include a file with an absolute path name. (Which +is not strictly a test of using $CPPPATH, but it's in the ball park...) +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('include', 'work') + +inc1_h = test.workpath('include', 'inc1.h') +inc2_h = test.workpath('include', 'inc2.h') +does_not_exist_h = test.workpath('include', 'does_not_exist.h') + +# Verify that including an absolute path still works even if they +# double the separators in the input file. This can happen especially +# on Windows if they use \\ to represent an escaped backslash. +inc2_h = inc2_h.replace(os.sep, os.sep+os.sep) + +test.write(['work', 'SConstruct'], """\ +Program('prog.c') +""") + +test.write(['work', 'prog.c'], """\ +#include +#include "%(inc1_h)s" +#include "%(inc2_h)s" +#if 0 +#include "%(does_not_exist_h)s" +#endif + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("%%s\\n", STRING1); + printf("%%s\\n", STRING2); + return 0; +} +""" % locals()) + +test.write(['include', 'inc1.h'], """\ +#define STRING1 "include/inc1.h A\\n" +""") + +test.write(['include', 'inc2.h'], """\ +#define STRING2 "include/inc2.h A\\n" +""") + +test.run(chdir = 'work', arguments = '.') + +test.up_to_date(chdir = 'work', arguments = '.') + +test.write(['include', 'inc1.h'], """\ +#define STRING1 "include/inc1.h B\\n" +""") + +test.not_up_to_date(chdir = 'work', arguments = '.') + +test.write(['include', 'inc2.h'], """\ +#define STRING2 "include/inc2.h B\\n" +""") + +test.not_up_to_date(chdir = 'work', arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/expand-object.py b/test/CPPPATH/expand-object.py index 5b751bb823..eec4ac815b 100644 --- a/test/CPPPATH/expand-object.py +++ b/test/CPPPATH/expand-object.py @@ -1,69 +1,69 @@ -#!/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 - -""" -Make sure that $CPPPATH expands correctly if one of the subsidiary -expansions contains a stringable non-Node object. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -class XXX: - def __init__(self, value): - self.value = value - def __str__(self): - return 'XXX-' + self.value + '-XXX' -env = Environment(CPPPATH = ['#', - '$BUILDDIR', - '/tmp/xyzzy'], - BUILDDIR = '#scons_build/$EXPANSION', - EXPANSION = XXX('win32')) -env.Object('foo.c') -""") - -test.write('foo.c', """\ -#include -void -foo(void) -{ - printf("foo.c\\n"); -} -""") - -test.run(arguments = '.') - -test.must_exist(test.workpath('foo' + TestSCons._obj)) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Make sure that $CPPPATH expands correctly if one of the subsidiary +expansions contains a stringable non-Node object. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +class XXX: + def __init__(self, value): + self.value = value + def __str__(self): + return 'XXX-' + self.value + '-XXX' +env = Environment(CPPPATH = ['#', + '$BUILDDIR', + '/tmp/xyzzy'], + BUILDDIR = '#scons_build/$EXPANSION', + EXPANSION = XXX('win32')) +env.Object('foo.c') +""") + +test.write('foo.c', """\ +#include +void +foo(void) +{ + printf("foo.c\\n"); +} +""") + +test.run(arguments = '.') + +test.must_exist(test.workpath('foo' + TestSCons._obj)) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/function-expansion.py b/test/CPPPATH/function-expansion.py index b9bc5be8db..3fb999fa4c 100644 --- a/test/CPPPATH/function-expansion.py +++ b/test/CPPPATH/function-expansion.py @@ -1,142 +1,142 @@ -#!/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 that expansion of construction variables whose values are functions -(that return lists that contain Nodes, even) works as expected within -a $CPPPATH list definition. - -This used to cause TypeErrors when the _concat expansion tried to -join the Nodes with the strings. - -Test courtesy Konstantin Bozhikov. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('list_inc1', - 'list_inc2', - 'inc1', - 'inc2', - 'inc3', - ['inc3', 'subdir']) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -def my_cpppaths( target, source, env, for_signature ): - return [ Dir('list_inc1'), Dir('list_inc2') ] - -env = Environment( CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir', '$MY_CPPPATHS' ], - INC2 = Dir('inc2'), - INC3 = Dir('inc3'), - MY_CPPPATHS = my_cpppaths ) - -env.Program('prog.c') -""") - -test.write('prog.c', """\ -#include -#include -#include "string_list_1.h" -#include "string_list_2.h" -#include "string_1.h" -#include "string_2.h" -#include "string_3.h" - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("prog.c\\n"); - printf("%s\\n", STRING_LIST_1); - printf("%s\\n", STRING_LIST_2); - printf("%s\\n", STRING_1); - printf("%s\\n", STRING_2); - printf("%s\\n", STRING_3); - exit (0); -} -""") - -test.write(['list_inc1', 'string_list_1.h'], """\ -#define STRING_LIST_1 "list_inc1/string_list_1.h" -""") - -test.write(['list_inc2', 'string_list_2.h'], """\ -#define STRING_LIST_2 "list_inc2/string_list_2.h" -""") - -test.write(['inc1', 'string_1.h'], """\ -#define STRING_1 "inc1/string_1.h" -""") - -test.write(['inc2', 'string_2.h'], """\ -#define STRING_2 "inc2/string_2.h" -""") - -test.write(['inc3', 'subdir', 'string_3.h'], """\ -#define STRING_3 "inc3/subdir/string_3.h" -""") - -test.run() - -test.up_to_date(arguments = '.') - -expect = """\ -prog.c -list_inc1/string_list_1.h -list_inc2/string_list_2.h -inc1/string_1.h -inc2/string_2.h -inc3/subdir/string_3.h -""" - -test.run(program = test.workpath('prog' + TestSCons._exe), stdout=expect) - -test.write(['inc3', 'subdir', 'string_3.h'], """\ -#define STRING_3 "inc3/subdir/string_3.h 2" -""") - -test.not_up_to_date(arguments = '.') - -expect = """\ -prog.c -list_inc1/string_list_1.h -list_inc2/string_list_2.h -inc1/string_1.h -inc2/string_2.h -inc3/subdir/string_3.h 2 -""" - -test.run(program = test.workpath('prog' + TestSCons._exe), stdout=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that expansion of construction variables whose values are functions +(that return lists that contain Nodes, even) works as expected within +a $CPPPATH list definition. + +This used to cause TypeErrors when the _concat expansion tried to +join the Nodes with the strings. + +Test courtesy Konstantin Bozhikov. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('list_inc1', + 'list_inc2', + 'inc1', + 'inc2', + 'inc3', + ['inc3', 'subdir']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def my_cpppaths( target, source, env, for_signature ): + return [ Dir('list_inc1'), Dir('list_inc2') ] + +env = Environment( CPPPATH = [Dir('inc1'), '$INC2', '$INC3/subdir', '$MY_CPPPATHS' ], + INC2 = Dir('inc2'), + INC3 = Dir('inc3'), + MY_CPPPATHS = my_cpppaths ) + +env.Program('prog.c') +""") + +test.write('prog.c', """\ +#include +#include +#include "string_list_1.h" +#include "string_list_2.h" +#include "string_1.h" +#include "string_2.h" +#include "string_3.h" + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("prog.c\\n"); + printf("%s\\n", STRING_LIST_1); + printf("%s\\n", STRING_LIST_2); + printf("%s\\n", STRING_1); + printf("%s\\n", STRING_2); + printf("%s\\n", STRING_3); + exit (0); +} +""") + +test.write(['list_inc1', 'string_list_1.h'], """\ +#define STRING_LIST_1 "list_inc1/string_list_1.h" +""") + +test.write(['list_inc2', 'string_list_2.h'], """\ +#define STRING_LIST_2 "list_inc2/string_list_2.h" +""") + +test.write(['inc1', 'string_1.h'], """\ +#define STRING_1 "inc1/string_1.h" +""") + +test.write(['inc2', 'string_2.h'], """\ +#define STRING_2 "inc2/string_2.h" +""") + +test.write(['inc3', 'subdir', 'string_3.h'], """\ +#define STRING_3 "inc3/subdir/string_3.h" +""") + +test.run() + +test.up_to_date(arguments = '.') + +expect = """\ +prog.c +list_inc1/string_list_1.h +list_inc2/string_list_2.h +inc1/string_1.h +inc2/string_2.h +inc3/subdir/string_3.h +""" + +test.run(program = test.workpath('prog' + TestSCons._exe), stdout=expect) + +test.write(['inc3', 'subdir', 'string_3.h'], """\ +#define STRING_3 "inc3/subdir/string_3.h 2" +""") + +test.not_up_to_date(arguments = '.') + +expect = """\ +prog.c +list_inc1/string_list_1.h +list_inc2/string_list_2.h +inc1/string_1.h +inc2/string_2.h +inc3/subdir/string_3.h 2 +""" + +test.run(program = test.workpath('prog' + TestSCons._exe), 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/CPPPATH/list-expansion.py b/test/CPPPATH/list-expansion.py index ad41ba24eb..5e88320042 100644 --- a/test/CPPPATH/list-expansion.py +++ b/test/CPPPATH/list-expansion.py @@ -1,138 +1,138 @@ -#!/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 that expansion of construction variables whose values are -lists works as expected within a $CPPPATH list definition. - -Previously, the stringification of the expansion of the individual -variables would turn a list like ['sub1', 'sub2'] below into "-Isub1 sub2" -on the command line. - -Test case courtesy Daniel Svensson. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('sub1', 'sub2', 'sub3', 'sub4') - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -class _inc_test: - def __init__(self, name): - self.name = name - - def __call__(self, target, source, env, for_signature): - return env.something[self.name] - -env = Environment() - -env.something = {} -env.something['test'] = ['sub1', 'sub2'] - -env['INC_PATHS1'] = _inc_test - -env['INC_PATHS2'] = ['sub3', 'sub4'] - -env.Append(CPPPATH = ['${INC_PATHS1("test")}', '$INC_PATHS2']) -env.Program('test', 'test.c') -""") - -test.write('test.c', """\ -#include -#include -#include "string1.h" -#include "string2.h" -#include "string3.h" -#include "string4.h" - -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("test.c\\n"); - printf("%s\\n", STRING1); - printf("%s\\n", STRING2); - printf("%s\\n", STRING3); - printf("%s\\n", STRING4); - exit (0); -} -""") - -test.write(['sub1', 'string1.h'], """\ -#define STRING1 "sub1/string1.h" -""") - -test.write(['sub2', 'string2.h'], """\ -#define STRING2 "sub2/string2.h" -""") - -test.write(['sub3', 'string3.h'], """\ -#define STRING3 "sub3/string3.h" -""") - -test.write(['sub4', 'string4.h'], """\ -#define STRING4 "sub4/string4.h" -""") - -test.run() - -test.up_to_date(arguments = '.') - -expect = """\ -test.c -sub1/string1.h -sub2/string2.h -sub3/string3.h -sub4/string4.h -""" - -test.run(program = test.workpath('test' + TestSCons._exe), stdout=expect) - -test.write(['sub2', 'string2.h'], """\ -#define STRING2 "sub2/string2.h 2" -""") - -test.not_up_to_date(arguments = '.') - -expect = """\ -test.c -sub1/string1.h -sub2/string2.h 2 -sub3/string3.h -sub4/string4.h -""" - -test.run(program = test.workpath('test' + TestSCons._exe), stdout=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that expansion of construction variables whose values are +lists works as expected within a $CPPPATH list definition. + +Previously, the stringification of the expansion of the individual +variables would turn a list like ['sub1', 'sub2'] below into "-Isub1 sub2" +on the command line. + +Test case courtesy Daniel Svensson. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('sub1', 'sub2', 'sub3', 'sub4') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +class _inc_test: + def __init__(self, name): + self.name = name + + def __call__(self, target, source, env, for_signature): + return env.something[self.name] + +env = Environment() + +env.something = {} +env.something['test'] = ['sub1', 'sub2'] + +env['INC_PATHS1'] = _inc_test + +env['INC_PATHS2'] = ['sub3', 'sub4'] + +env.Append(CPPPATH = ['${INC_PATHS1("test")}', '$INC_PATHS2']) +env.Program('test', 'test.c') +""") + +test.write('test.c', """\ +#include +#include +#include "string1.h" +#include "string2.h" +#include "string3.h" +#include "string4.h" + +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("test.c\\n"); + printf("%s\\n", STRING1); + printf("%s\\n", STRING2); + printf("%s\\n", STRING3); + printf("%s\\n", STRING4); + exit (0); +} +""") + +test.write(['sub1', 'string1.h'], """\ +#define STRING1 "sub1/string1.h" +""") + +test.write(['sub2', 'string2.h'], """\ +#define STRING2 "sub2/string2.h" +""") + +test.write(['sub3', 'string3.h'], """\ +#define STRING3 "sub3/string3.h" +""") + +test.write(['sub4', 'string4.h'], """\ +#define STRING4 "sub4/string4.h" +""") + +test.run() + +test.up_to_date(arguments = '.') + +expect = """\ +test.c +sub1/string1.h +sub2/string2.h +sub3/string3.h +sub4/string4.h +""" + +test.run(program = test.workpath('test' + TestSCons._exe), stdout=expect) + +test.write(['sub2', 'string2.h'], """\ +#define STRING2 "sub2/string2.h 2" +""") + +test.not_up_to_date(arguments = '.') + +expect = """\ +test.c +sub1/string1.h +sub2/string2.h 2 +sub3/string3.h +sub4/string4.h +""" + +test.run(program = test.workpath('test' + TestSCons._exe), 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/CPPPATH/match-dir.py b/test/CPPPATH/match-dir.py index fdbdfee901..41f071d7cc 100644 --- a/test/CPPPATH/match-dir.py +++ b/test/CPPPATH/match-dir.py @@ -1,74 +1,74 @@ -#!/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 that we don't blow up if there's a directory name within -$CPPPATH that matches a #include file name. -""" - -import sys - -import TestSCons - -test = TestSCons.TestSCons() - -# TODO(sgk): get this to work everywhere by using fake compilers -if sys.platform.find('sunos') != -1: - msg = 'SunOS C compiler does not handle this case; skipping test.\n' - test.skip_test(msg) - -test.subdir(['src'], - ['src', 'inc'], - ['src', 'inc', 'inc2']) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -SConscript('src/SConscript', variant_dir = 'build', duplicate = 0) -""") - -test.write(['src', 'SConscript'], """\ -env = Environment(CPPPATH = ['#build/inc', '#build/inc/inc2']) -env.Object('foo.c') -""") - -test.write(['src', 'foo.c'], """\ -#include "inc1" -""") - -test.subdir(['src', 'inc', 'inc1']) - -test.write(['src', 'inc', 'inc2', 'inc1'], "\n") - -test.run(arguments = '.') - -test.up_to_date(arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that we don't blow up if there's a directory name within +$CPPPATH that matches a #include file name. +""" + +import sys + +import TestSCons + +test = TestSCons.TestSCons() + +# TODO(sgk): get this to work everywhere by using fake compilers +if sys.platform.find('sunos') != -1: + msg = 'SunOS C compiler does not handle this case; skipping test.\n' + test.skip_test(msg) + +test.subdir(['src'], + ['src', 'inc'], + ['src', 'inc', 'inc2']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +SConscript('src/SConscript', variant_dir = 'build', duplicate = 0) +""") + +test.write(['src', 'SConscript'], """\ +env = Environment(CPPPATH = ['#build/inc', '#build/inc/inc2']) +env.Object('foo.c') +""") + +test.write(['src', 'foo.c'], """\ +#include "inc1" +""") + +test.subdir(['src', 'inc', 'inc1']) + +test.write(['src', 'inc', 'inc2', 'inc1'], "\n") + +test.run(arguments = '.') + +test.up_to_date(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/nested-lists.py b/test/CPPPATH/nested-lists.py index f47829d64c..86fb69b107 100644 --- a/test/CPPPATH/nested-lists.py +++ b/test/CPPPATH/nested-lists.py @@ -1,83 +1,83 @@ -#!/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 that CPPPATH values consisting of nested lists work correctly. -""" - -import TestSCons - -_exe = TestSCons._exe - -test = TestSCons.TestSCons() - -test.subdir('inc1', 'inc2', 'inc3') - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(CPPPATH = ['inc1', ['inc2', ['inc3']]]) -env.Program('prog.c') -""") - -test.write('prog.c', """\ -#include -#include - -#include "one.h" -#include "two.h" -#include "three.h" -int -main(int argc, char *argv[]) -{ - printf("%s\\n", ONE); - printf("%s\\n", TWO); - printf("%s\\n", THREE); - return (0); -} -""") - -test.write(['inc1', 'one.h'], """\ -#define ONE "1" -""") - -test.write(['inc2', 'two.h'], """\ -#define TWO "2" -""") - -test.write(['inc3', 'three.h'], """\ -#define THREE "3" -""") - -test.run(arguments = '.') - -test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that CPPPATH values consisting of nested lists work correctly. +""" + +import TestSCons + +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.subdir('inc1', 'inc2', 'inc3') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CPPPATH = ['inc1', ['inc2', ['inc3']]]) +env.Program('prog.c') +""") + +test.write('prog.c', """\ +#include +#include + +#include "one.h" +#include "two.h" +#include "three.h" +int +main(int argc, char *argv[]) +{ + printf("%s\\n", ONE); + printf("%s\\n", TWO); + printf("%s\\n", THREE); + return (0); +} +""") + +test.write(['inc1', 'one.h'], """\ +#define ONE "1" +""") + +test.write(['inc2', 'two.h'], """\ +#define TWO "2" +""") + +test.write(['inc3', 'three.h'], """\ +#define THREE "3" +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('prog' + _exe), stdout = "1\n2\n3\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/null.py b/test/CPPPATH/null.py index 17f0a8c9cc..b8c6841bbe 100644 --- a/test/CPPPATH/null.py +++ b/test/CPPPATH/null.py @@ -1,61 +1,61 @@ -#!/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 SOFTWAR - -""" -Verify that neither a null-string CPPPATH nor a -a CPPPATH containing null values blows up. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(CPPPATH = '') -env.Library('one', source = 'empty1.c') -env = Environment(CPPPATH = [None]) -env.Library('two', source = 'empty2.c') -env = Environment(CPPPATH = ['']) -env.Library('three', source = 'empty3.c') -""") - -test.write('empty1.c', "int a=0;\n") -test.write('empty2.c', "int b=0;\n") -test.write('empty3.c', "int c=0;\n") - -test.run(arguments = '.', - stderr=TestSCons.noisy_ar, - match=TestSCons.match_re_dotall) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 SOFTWAR + +""" +Verify that neither a null-string CPPPATH nor a +a CPPPATH containing null values blows up. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CPPPATH = '') +env.Library('one', source = 'empty1.c') +env = Environment(CPPPATH = [None]) +env.Library('two', source = 'empty2.c') +env = Environment(CPPPATH = ['']) +env.Library('three', source = 'empty3.c') +""") + +test.write('empty1.c', "int a=0;\n") +test.write('empty2.c', "int b=0;\n") +test.write('empty3.c', "int c=0;\n") + +test.run(arguments = '.', + stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CPPPATH/subdir-as-include.py b/test/CPPPATH/subdir-as-include.py index 27e687fd83..25ecd23521 100644 --- a/test/CPPPATH/subdir-as-include.py +++ b/test/CPPPATH/subdir-as-include.py @@ -1,98 +1,98 @@ -#!/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 SOFTWAR - -""" -This is an obscure test case. When a file without a suffix is included in -a c++ build and there is a directory with the same name as that file in a -sub-build directory, verify that an Errno 21 is not thrown upon trying to -recursively scan the contents of includes. The Errno 21 indicates that -the directory with the same name was trying to be scanned as the include -file, which it clearly is not. -""" - -import TestSCons - -_exe = TestSCons._exe - -test = TestSCons.TestSCons() - -test.subdir('inc1', ['inc1', 'iterator']) - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(CPPPATH = [Dir('inc1')]) -env.Program('prog.cpp') - -Export('env') -SConscript('inc1/SConscript', variant_dir='inc1/build', duplicate=0) -""") - -test.write('prog.cpp', """\ -#include -#include - -#include "one.h" -#include -int main(int argc, char* argv[]) -{ - printf("%s\\n", ONE); - return 0; -} -""") - -test.write(['inc1', 'SConscript'], """\ -Import('env') -oneenv = env.Clone() -oneenv.Program('one.cpp') -""") - -test.write(['inc1', 'one.h'], """\ -#define ONE "1" -""") - -test.write(['inc1', 'one.cpp'], """\ -#include -#include -#include "one.h" - -int main(int argc, char* argv[]) -{ - printf("%s\\n", ONE); - return 0; -} -""") - -test.run(arguments = '.') - -test.run(program = test.workpath('prog' + _exe), stdout = "1\n") -test.run(program = test.workpath('inc1/build/one' + _exe), stdout = "1\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 SOFTWAR + +""" +This is an obscure test case. When a file without a suffix is included in +a c++ build and there is a directory with the same name as that file in a +sub-build directory, verify that an Errno 21 is not thrown upon trying to +recursively scan the contents of includes. The Errno 21 indicates that +the directory with the same name was trying to be scanned as the include +file, which it clearly is not. +""" + +import TestSCons + +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.subdir('inc1', ['inc1', 'iterator']) + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(CPPPATH = [Dir('inc1')]) +env.Program('prog.cpp') + +Export('env') +SConscript('inc1/SConscript', variant_dir='inc1/build', duplicate=0) +""") + +test.write('prog.cpp', """\ +#include +#include + +#include "one.h" +#include +int main(int argc, char* argv[]) +{ + printf("%s\\n", ONE); + return 0; +} +""") + +test.write(['inc1', 'SConscript'], """\ +Import('env') +oneenv = env.Clone() +oneenv.Program('one.cpp') +""") + +test.write(['inc1', 'one.h'], """\ +#define ONE "1" +""") + +test.write(['inc1', 'one.cpp'], """\ +#include +#include +#include "one.h" + +int main(int argc, char* argv[]) +{ + printf("%s\\n", ONE); + return 0; +} +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('prog' + _exe), stdout = "1\n") +test.run(program = test.workpath('inc1/build/one' + _exe), stdout = "1\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/CacheDir.py b/test/CacheDir/CacheDir.py index f59da13ea9..bd8d674af3 100644 --- a/test/CacheDir/CacheDir.py +++ b/test/CacheDir/CacheDir.py @@ -1,164 +1,164 @@ -#!/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. - -""" -Test retrieving derived files from a CacheDir. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -cache = test.workpath('cache') - -src_aaa_out = test.workpath('src', 'aaa.out') -src_bbb_out = test.workpath('src', 'bbb.out') -src_ccc_out = test.workpath('src', 'ccc.out') -src_cat_out = test.workpath('src', 'cat.out') -src_all = test.workpath('src', 'all') - -test.subdir('cache', 'src') - -test.write(['src', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -CacheDir(r'%(cache)s') -SConscript('SConscript') -""" % locals()) - -test.write(['src', 'SConscript'], """\ -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -""") - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# Verify that building with -n and an empty cache reports that proper -# build operations would be taken, but that nothing is actually built -# and that the cache is still empty. -test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ -cat(["aaa.out"], ["aaa.in"]) -cat(["bbb.out"], ["bbb.in"]) -cat(["ccc.out"], ["ccc.in"]) -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_not_exist(src_aaa_out) -test.must_not_exist(src_bbb_out) -test.must_not_exist(src_ccc_out) -test.must_not_exist(src_all) -# Even if you do -n, the cache will be configured. -test.fail_test(os.listdir(cache) != ['config']) - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run(chdir = 'src', arguments = '.') - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(['src', 'cat.out']) - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. Then clean up. -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.must_not_exist(src_cat_out) - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') - -# Verify that rebuilding with -n reports that everything was retrieved -# from the cache, but that nothing really was. -test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.must_not_exist(src_aaa_out) -test.must_not_exist(src_bbb_out) -test.must_not_exist(src_ccc_out) -test.must_not_exist(src_all) - -# Verify that rebuilding with -s retrieves everything from the cache -# even though it doesn't report anything. -test.run(chdir = 'src', arguments = '-s .', stdout = "") - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_not_exist(src_cat_out) - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') - -# Verify that updating one input file builds its derived file and -# dependency but that the other files are retrieved from cache. -test.write(['src', 'bbb.in'], "bbb.in 2\n") - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -cat(["bbb.out"], ["bbb.in"]) -Retrieved `ccc.out' from cache -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') -test.must_match(['src', 'cat.out'], "bbb.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test retrieving derived files from a CacheDir. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +cache = test.workpath('cache') + +src_aaa_out = test.workpath('src', 'aaa.out') +src_bbb_out = test.workpath('src', 'bbb.out') +src_ccc_out = test.workpath('src', 'ccc.out') +src_cat_out = test.workpath('src', 'cat.out') +src_all = test.workpath('src', 'all') + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +CacheDir(r'%(cache)s') +SConscript('SConscript') +""" % locals()) + +test.write(['src', 'SConscript'], """\ +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that building with -n and an empty cache reports that proper +# build operations would be taken, but that nothing is actually built +# and that the cache is still empty. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +cat(["bbb.out"], ["bbb.in"]) +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) +# Even if you do -n, the cache will be configured. +test.fail_test(os.listdir(cache) != ['config']) + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.must_not_exist(src_cat_out) + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') + +# Verify that rebuilding with -n reports that everything was retrieved +# from the cache, but that nothing really was. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) + +# Verify that rebuilding with -s retrieves everything from the cache +# even though it doesn't report anything. +test.run(chdir = 'src', arguments = '-s .', stdout = "") + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_not_exist(src_cat_out) + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') + +# Verify that updating one input file builds its derived file and +# dependency but that the other files are retrieved from cache. +test.write(['src', 'bbb.in'], "bbb.in 2\n") + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') +test.must_match(['src', 'cat.out'], "bbb.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/SideEffect.py b/test/CacheDir/SideEffect.py index c93485f817..61c9bbcf6e 100644 --- a/test/CacheDir/SideEffect.py +++ b/test/CacheDir/SideEffect.py @@ -1,114 +1,114 @@ -#!/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._"_" - -""" -Test that use of SideEffect() doesn't interfere with CacheDir. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'work') - -cache = test.workpath('cache') - -test.write(['work', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -def copy(source, target): - with open(target, "w") as f, open(source, "r") as f2: - f.write(f2.read()) - -def build(env, source, target): - s = str(source[0]) - t = str(target[0]) - copy(s, t) - if target[0].side_effects: - with open(str(target[0].side_effects[0]), "a") as side_effect: - side_effect.write(s + ' -> ' + t + '\\n') - -CacheDir(r'%(cache)s') - -Build = Builder(action=build) -env = Environment(tools=[], BUILDERS={'Build':Build}, SUBDIR='subdir') -env.Build('f1.out', 'f1.in') -env.Build('f2.out', 'f2.in') -env.Build('f3.out', 'f3.in') -SideEffect('log.txt', ['f1.out', 'f2.out', 'f3.out']) -""" % locals()) - -test.write(['work', 'f1.in'], 'f1.in\n') -test.write(['work', 'f2.in'], 'f2.in\n') -test.write(['work', 'f3.in'], 'f3.in\n') - -test.run(chdir='work', arguments='f1.out f2.out') - -expect = """\ -f1.in -> f1.out -f2.in -> f2.out -""" - -test.must_match(['work', 'log.txt'], expect, mode='r') - - - -test.write(['work', 'f2.in'], 'f2.in 2 \n') - -test.run(chdir='work', arguments='log.txt') - -expect = """\ -f1.in -> f1.out -f2.in -> f2.out -f2.in -> f2.out -f3.in -> f3.out -""" - -test.must_match(['work', 'log.txt'], expect, mode='r') - - - -test.write(['work', 'f1.in'], 'f1.in 2 \n') - -test.run(chdir='work', arguments=".") - -expect = """\ -f1.in -> f1.out -f2.in -> f2.out -f2.in -> f2.out -f3.in -> f3.out -f1.in -> f1.out -""" - -test.must_match(['work', 'log.txt'], expect, mode='r') - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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._"_" + +""" +Test that use of SideEffect() doesn't interfere with CacheDir. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'work') + +cache = test.workpath('cache') + +test.write(['work', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +def copy(source, target): + with open(target, "w") as f, open(source, "r") as f2: + f.write(f2.read()) + +def build(env, source, target): + s = str(source[0]) + t = str(target[0]) + copy(s, t) + if target[0].side_effects: + with open(str(target[0].side_effects[0]), "a") as side_effect: + side_effect.write(s + ' -> ' + t + '\\n') + +CacheDir(r'%(cache)s') + +Build = Builder(action=build) +env = Environment(tools=[], BUILDERS={'Build':Build}, SUBDIR='subdir') +env.Build('f1.out', 'f1.in') +env.Build('f2.out', 'f2.in') +env.Build('f3.out', 'f3.in') +SideEffect('log.txt', ['f1.out', 'f2.out', 'f3.out']) +""" % locals()) + +test.write(['work', 'f1.in'], 'f1.in\n') +test.write(['work', 'f2.in'], 'f2.in\n') +test.write(['work', 'f3.in'], 'f3.in\n') + +test.run(chdir='work', arguments='f1.out f2.out') + +expect = """\ +f1.in -> f1.out +f2.in -> f2.out +""" + +test.must_match(['work', 'log.txt'], expect, mode='r') + + + +test.write(['work', 'f2.in'], 'f2.in 2 \n') + +test.run(chdir='work', arguments='log.txt') + +expect = """\ +f1.in -> f1.out +f2.in -> f2.out +f2.in -> f2.out +f3.in -> f3.out +""" + +test.must_match(['work', 'log.txt'], expect, mode='r') + + + +test.write(['work', 'f1.in'], 'f1.in 2 \n') + +test.run(chdir='work', arguments=".") + +expect = """\ +f1.in -> f1.out +f2.in -> f2.out +f2.in -> f2.out +f3.in -> f3.out +f1.in -> f1.out +""" + +test.must_match(['work', 'log.txt'], expect, mode='r') + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/VariantDir.py b/test/CacheDir/VariantDir.py index 77cd117dd1..9fc82c9cb0 100644 --- a/test/CacheDir/VariantDir.py +++ b/test/CacheDir/VariantDir.py @@ -1,156 +1,156 @@ -#!/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._" - -""" -Test retrieving derived files from a CacheDir when a VariantDir is used. -""" - -import os.path - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'src') - -cache = test.workpath('cache') -cat_out = test.workpath('cat.out') - -test.write(['src', 'SConscript'], """\ -DefaultEnvironment(tools=[]) - -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -""") - -build_aaa_out = os.path.join('build', 'aaa.out') -build_bbb_out = os.path.join('build', 'bbb.out') -build_ccc_out = os.path.join('build', 'ccc.out') -build_all = os.path.join('build', 'all') - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[], TWO = '2') -CacheDir(r'%s') -VariantDir('build', 'src', duplicate=0) -SConscript('build/SConscript') -""" % test.workpath('cache${TWO}')) - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run() - -test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_match('cat.out', "%s\n%s\n%s\n%s\n" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all), mode='r') - -test.up_to_date(arguments = '.') - -test.run(arguments = '-c .') -test.unlink('cat.out') - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. Then clean up. -test.run(stdout = test.wrap_stdout("""\ -Retrieved `%s' from cache -Retrieved `%s' from cache -Retrieved `%s' from cache -Retrieved `%s' from cache -""" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))) - -test.must_not_exist(cat_out) - -test.up_to_date(arguments = '.') - -test.run(arguments = '-c .') - -# Verify that rebuilding with -n reports that everything was retrieved -# from the cache, but that nothing really was. -test.run(arguments = '-n .', stdout = test.wrap_stdout("""\ -Retrieved `%s' from cache -Retrieved `%s' from cache -Retrieved `%s' from cache -Retrieved `%s' from cache -""" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))) - -test.must_not_exist(test.workpath('build', 'aaa.out')) -test.must_not_exist(test.workpath('build', 'bbb.out')) -test.must_not_exist(test.workpath('build', 'ccc.out')) -test.must_not_exist(test.workpath('build', 'all')) - -# Verify that rebuilding with -s retrieves everything from the cache -# even though it doesn't report anything. -test.run(arguments = '-s .', stdout = "") - -test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_not_exist(cat_out) - -test.up_to_date(arguments = '.') - -test.run(arguments = '-c .') - -# Verify that updating one input file builds its derived file and -# dependency but that the other files are retrieved from cache. -test.write(['src', 'bbb.in'], "bbb.in 2\n") - -test.run(stdout = test.wrap_stdout("""\ -Retrieved `%s' from cache -cat(["%s"], ["%s"]) -Retrieved `%s' from cache -cat(["%s"], ["%s", "%s", "%s"]) -""" % (build_aaa_out, - build_bbb_out, os.path.join('src', 'bbb.in'), - build_ccc_out, - build_all, build_aaa_out, build_bbb_out, build_ccc_out))) - -test.must_match(['build', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') -test.must_match('cat.out', "%s\n%s\n" % (build_bbb_out, build_all), mode='r') - -test.up_to_date(arguments = '.') - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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._" + +""" +Test retrieving derived files from a CacheDir when a VariantDir is used. +""" + +import os.path + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'src') + +cache = test.workpath('cache') +cat_out = test.workpath('cat.out') + +test.write(['src', 'SConscript'], """\ +DefaultEnvironment(tools=[]) + +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +build_aaa_out = os.path.join('build', 'aaa.out') +build_bbb_out = os.path.join('build', 'bbb.out') +build_ccc_out = os.path.join('build', 'ccc.out') +build_all = os.path.join('build', 'all') + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[], TWO = '2') +CacheDir(r'%s') +VariantDir('build', 'src', duplicate=0) +SConscript('build/SConscript') +""" % test.workpath('cache${TWO}')) + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run() + +test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_match('cat.out', "%s\n%s\n%s\n%s\n" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all), mode='r') + +test.up_to_date(arguments = '.') + +test.run(arguments = '-c .') +test.unlink('cat.out') + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(stdout = test.wrap_stdout("""\ +Retrieved `%s' from cache +Retrieved `%s' from cache +Retrieved `%s' from cache +Retrieved `%s' from cache +""" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))) + +test.must_not_exist(cat_out) + +test.up_to_date(arguments = '.') + +test.run(arguments = '-c .') + +# Verify that rebuilding with -n reports that everything was retrieved +# from the cache, but that nothing really was. +test.run(arguments = '-n .', stdout = test.wrap_stdout("""\ +Retrieved `%s' from cache +Retrieved `%s' from cache +Retrieved `%s' from cache +Retrieved `%s' from cache +""" % (build_aaa_out, build_bbb_out, build_ccc_out, build_all))) + +test.must_not_exist(test.workpath('build', 'aaa.out')) +test.must_not_exist(test.workpath('build', 'bbb.out')) +test.must_not_exist(test.workpath('build', 'ccc.out')) +test.must_not_exist(test.workpath('build', 'all')) + +# Verify that rebuilding with -s retrieves everything from the cache +# even though it doesn't report anything. +test.run(arguments = '-s .', stdout = "") + +test.must_match(['build', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_not_exist(cat_out) + +test.up_to_date(arguments = '.') + +test.run(arguments = '-c .') + +# Verify that updating one input file builds its derived file and +# dependency but that the other files are retrieved from cache. +test.write(['src', 'bbb.in'], "bbb.in 2\n") + +test.run(stdout = test.wrap_stdout("""\ +Retrieved `%s' from cache +cat(["%s"], ["%s"]) +Retrieved `%s' from cache +cat(["%s"], ["%s", "%s", "%s"]) +""" % (build_aaa_out, + build_bbb_out, os.path.join('src', 'bbb.in'), + build_ccc_out, + build_all, build_aaa_out, build_bbb_out, build_ccc_out))) + +test.must_match(['build', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') +test.must_match('cat.out', "%s\n%s\n" % (build_bbb_out, build_all), mode='r') + +test.up_to_date(arguments = '.') + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/debug.py b/test/CacheDir/debug.py index 2c7ff0dd54..0ba6bbba2c 100644 --- a/test/CacheDir/debug.py +++ b/test/CacheDir/debug.py @@ -1,201 +1,201 @@ -#!/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. - -""" -Test the --cache-debug option to see if it prints the expected messages. - -Note that we don't check for the "race condition" message when someone -else's build populates the CacheDir with a file in between the time we -to build it because it doesn't exist in the CacheDir, and the time our -build of the file completes and we push it out. -""" - -import TestSCons - -test = TestSCons.TestSCons(match=TestSCons.match_re) - -test.subdir('cache', 'src') - -cache = test.workpath('cache') -debug_out = test.workpath('cache-debug.out') - - - -test.write(['src', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -CacheDir(r'%(cache)s') -SConscript('SConscript') -""" % locals()) - -test.write(['src', 'SConscript'], """\ -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -""") - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - - - -# Test for messages about files not being in CacheDir, with -n (don't -# actually build or push) and sendinig the message to a file. - -expect = \ -r"""cat\(\["aaa.out"\], \["aaa.in"\]\) -cat\(\["bbb.out"\], \["bbb.in"\]\) -cat\(\["ccc.out"\], \["ccc.in"\]\) -cat\(\["all"\], \["aaa.out", "bbb.out", "ccc.out"\]\) -""" - -test.run(chdir='src', - arguments='-n -Q --cache-debug=%s .' % debug_out, - stdout=expect) - -expect = \ -r"""CacheRetrieve\(aaa.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(bbb.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(ccc.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(all\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -""" - -test.must_match(debug_out, expect, mode='r') - - - -# Test for messages about actually pushing to the cache, without -n -# and to standard ouput. - -expect = \ -r"""CacheRetrieve\(aaa.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -cat\(\["aaa.out"\], \["aaa.in"\]\) -CachePush\(aaa.out\): pushing to [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(bbb.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -cat\(\["bbb.out"\], \["bbb.in"\]\) -CachePush\(bbb.out\): pushing to [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(ccc.out\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -cat\(\["ccc.out"\], \["ccc.in"\]\) -CachePush\(ccc.out\): pushing to [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(all\): [0-9a-fA-F]+ not in cache -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -cat\(\["all"\], \["aaa.out", "bbb.out", "ccc.out"\]\) -CachePush\(all\): pushing to [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -""" - -test.run(chdir='src', - arguments='-Q --cache-debug=- .', - stdout=expect) - - - -# Clean up the local targets. - -test.run(chdir='src', arguments='-c --cache-debug=%s .' % debug_out) -test.unlink(['src', 'cat.out']) - - - -# Test for messages about retrieving files from CacheDir, with -n -# and sending the messages to standard output. - -expect = \ -r"""Retrieved `aaa.out' from cache -CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -Retrieved `bbb.out' from cache -CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -Retrieved `ccc.out' from cache -CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -Retrieved `all' from cache -CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -""" - -test.run(chdir='src', - arguments='-n -Q --cache-debug=- .', - stdout=expect) - - - -# And finally test for message about retrieving file from CacheDir -# *without* -n and sending the message to a file. - -expect = \ -r"""Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""" - -test.run(chdir='src', - arguments='-Q --cache-debug=%s .' % debug_out, - stdout=expect) - -expect = \ -r"""CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+ -requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% -""" - -test.must_match(debug_out, expect, mode='r') - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test the --cache-debug option to see if it prints the expected messages. + +Note that we don't check for the "race condition" message when someone +else's build populates the CacheDir with a file in between the time we +to build it because it doesn't exist in the CacheDir, and the time our +build of the file completes and we push it out. +""" + +import TestSCons + +test = TestSCons.TestSCons(match=TestSCons.match_re) + +test.subdir('cache', 'src') + +cache = test.workpath('cache') +debug_out = test.workpath('cache-debug.out') + + + +test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +CacheDir(r'%(cache)s') +SConscript('SConscript') +""" % locals()) + +test.write(['src', 'SConscript'], """\ +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + + + +# Test for messages about files not being in CacheDir, with -n (don't +# actually build or push) and sendinig the message to a file. + +expect = \ +r"""cat\(\["aaa.out"\], \["aaa.in"\]\) +cat\(\["bbb.out"\], \["bbb.in"\]\) +cat\(\["ccc.out"\], \["ccc.in"\]\) +cat\(\["all"\], \["aaa.out", "bbb.out", "ccc.out"\]\) +""" + +test.run(chdir='src', + arguments='-n -Q --cache-debug=%s .' % debug_out, + stdout=expect) + +expect = \ +r"""CacheRetrieve\(aaa.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(bbb.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(ccc.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(all\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +""" + +test.must_match(debug_out, expect, mode='r') + + + +# Test for messages about actually pushing to the cache, without -n +# and to standard ouput. + +expect = \ +r"""CacheRetrieve\(aaa.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +cat\(\["aaa.out"\], \["aaa.in"\]\) +CachePush\(aaa.out\): pushing to [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(bbb.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +cat\(\["bbb.out"\], \["bbb.in"\]\) +CachePush\(bbb.out\): pushing to [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(ccc.out\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +cat\(\["ccc.out"\], \["ccc.in"\]\) +CachePush\(ccc.out\): pushing to [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(all\): [0-9a-fA-F]+ not in cache +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +cat\(\["all"\], \["aaa.out", "bbb.out", "ccc.out"\]\) +CachePush\(all\): pushing to [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +""" + +test.run(chdir='src', + arguments='-Q --cache-debug=- .', + stdout=expect) + + + +# Clean up the local targets. + +test.run(chdir='src', arguments='-c --cache-debug=%s .' % debug_out) +test.unlink(['src', 'cat.out']) + + + +# Test for messages about retrieving files from CacheDir, with -n +# and sending the messages to standard output. + +expect = \ +r"""Retrieved `aaa.out' from cache +CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +Retrieved `bbb.out' from cache +CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +Retrieved `ccc.out' from cache +CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +Retrieved `all' from cache +CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +""" + +test.run(chdir='src', + arguments='-n -Q --cache-debug=- .', + stdout=expect) + + + +# And finally test for message about retrieving file from CacheDir +# *without* -n and sending the message to a file. + +expect = \ +r"""Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""" + +test.run(chdir='src', + arguments='-Q --cache-debug=%s .' % debug_out, + stdout=expect) + +expect = \ +r"""CacheRetrieve\(aaa.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(bbb.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(ccc.out\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +CacheRetrieve\(all\): retrieving from [0-9a-fA-F]+ +requests: [0-9]+, hits: [0-9]+, misses: [0-9]+, hit rate: [0-9]+\.[0-9]{2,}% +""" + +test.must_match(debug_out, expect, mode='r') + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/environment.py b/test/CacheDir/environment.py index 60c52ebfa5..17a4f26e0b 100644 --- a/test/CacheDir/environment.py +++ b/test/CacheDir/environment.py @@ -1,170 +1,170 @@ -#!/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 that whether or not a target gets retrieved from a CacheDir -is configurable by construction environment. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -cache = test.workpath('cache') - -src_aaa_out = test.workpath('src', 'aaa.out') -src_bbb_out = test.workpath('src', 'bbb.out') -src_ccc_out = test.workpath('src', 'ccc.out') -src_cat_out = test.workpath('src', 'cat.out') -src_all = test.workpath('src', 'all') - -test.subdir('cache', 'src') - -test.write(['src', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -CacheDir(r'%(cache)s') -SConscript('SConscript') -""" % locals()) - -test.write(['src', 'SConscript'], """\ -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env_cache = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env_nocache = env_cache.Clone() -env_nocache.CacheDir(None) -env_cache.Cat('aaa.out', 'aaa.in') -env_nocache.Cat('bbb.out', 'bbb.in') -env_cache.Cat('ccc.out', 'ccc.in') -env_nocache.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -""") - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# Verify that building with -n and an empty cache reports that proper -# build operations would be taken, but that nothing is actually built -# and that the cache is still empty. -test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ -cat(["aaa.out"], ["aaa.in"]) -cat(["bbb.out"], ["bbb.in"]) -cat(["ccc.out"], ["ccc.in"]) -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_not_exist(src_aaa_out) -test.must_not_exist(src_bbb_out) -test.must_not_exist(src_ccc_out) -test.must_not_exist(src_all) -# Even if you do -n, the cache will be configured. -test.fail_test(os.listdir(cache) != ['config']) - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run(chdir = 'src', arguments = '.') - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') -test.must_match(src_cat_out, "aaa.out\nbbb.out\nccc.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(src_cat_out) - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. Then clean up. -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -cat(["bbb.out"], ["bbb.in"]) -Retrieved `ccc.out' from cache -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(src_cat_out) - -# Verify that rebuilding with -n reports that files were retrieved -# from the cache, but that nothing really was. -test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -cat(["bbb.out"], ["bbb.in"]) -Retrieved `ccc.out' from cache -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_not_exist(src_aaa_out) -test.must_not_exist(src_bbb_out) -test.must_not_exist(src_ccc_out) -test.must_not_exist(src_all) - -# Verify that rebuilding with -s retrieves everything from the cache -# even though it doesn't report anything. -test.run(chdir = 'src', arguments = '-s .', stdout = "") - -test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') - -test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(src_cat_out) - -# Verify that updating one input file builds its derived file and -# dependency but that the other files are retrieved from cache. -test.write(['src', 'bbb.in'], "bbb.in 2\n") - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -cat(["bbb.out"], ["bbb.in"]) -Retrieved `ccc.out' from cache -cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) -""")) - -test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') -test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') - -test.up_to_date(chdir = 'src', arguments = '.') - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that whether or not a target gets retrieved from a CacheDir +is configurable by construction environment. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +cache = test.workpath('cache') + +src_aaa_out = test.workpath('src', 'aaa.out') +src_bbb_out = test.workpath('src', 'bbb.out') +src_ccc_out = test.workpath('src', 'ccc.out') +src_cat_out = test.workpath('src', 'cat.out') +src_all = test.workpath('src', 'all') + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +CacheDir(r'%(cache)s') +SConscript('SConscript') +""" % locals()) + +test.write(['src', 'SConscript'], """\ +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env_cache = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env_nocache = env_cache.Clone() +env_nocache.CacheDir(None) +env_cache.Cat('aaa.out', 'aaa.in') +env_nocache.Cat('bbb.out', 'bbb.in') +env_cache.Cat('ccc.out', 'ccc.in') +env_nocache.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +""") + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that building with -n and an empty cache reports that proper +# build operations would be taken, but that nothing is actually built +# and that the cache is still empty. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +cat(["bbb.out"], ["bbb.in"]) +cat(["ccc.out"], ["ccc.in"]) +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) +# Even if you do -n, the cache will be configured. +test.fail_test(os.listdir(cache) != ['config']) + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') +test.must_match(src_cat_out, "aaa.out\nbbb.out\nccc.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(src_cat_out) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(src_cat_out) + +# Verify that rebuilding with -n reports that files were retrieved +# from the cache, but that nothing really was. +test.run(chdir = 'src', arguments = '-n .', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_not_exist(src_aaa_out) +test.must_not_exist(src_bbb_out) +test.must_not_exist(src_ccc_out) +test.must_not_exist(src_all) + +# Verify that rebuilding with -s retrieves everything from the cache +# even though it doesn't report anything. +test.run(chdir = 'src', arguments = '-s .', stdout = "") + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n", mode='r') + +test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(src_cat_out) + +# Verify that updating one input file builds its derived file and +# dependency but that the other files are retrieved from cache. +test.write(['src', 'bbb.in'], "bbb.in 2\n") + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +cat(["bbb.out"], ["bbb.in"]) +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in 2\nccc.in\n", mode='r') +test.must_match(src_cat_out, "bbb.out\nall\n", mode='r') + +test.up_to_date(chdir = 'src', arguments = '.') + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/multi-targets.py b/test/CacheDir/multi-targets.py index ed11f779ca..72c7e66a2b 100644 --- a/test/CacheDir/multi-targets.py +++ b/test/CacheDir/multi-targets.py @@ -1,78 +1,78 @@ -#!/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. - -""" -Test that multiple target files get retrieved from a CacheDir correctly. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'multiple') - -cache = test.workpath('cache') - -multiple_bar = test.workpath('multiple', 'bar') -multiple_foo = test.workpath('multiple', 'foo') - -test.write(['multiple', 'SConstruct'], """\ -DefaultEnvironment(tools=[]) -def touch(env, source, target): - with open('foo', 'w') as f: - f.write("") - with open('bar', 'w') as f: - f.write("") -CacheDir(r'%(cache)s') -env = Environment(tools=[]) -env.Command(['foo', 'bar'], ['input'], touch) -""" % locals()) - -test.write(['multiple', 'input'], "multiple/input\n") - -test.run(chdir = 'multiple') - -test.must_exist(multiple_foo) -test.must_exist(multiple_bar) - -test.run(chdir = 'multiple', arguments = '-c') - -test.must_not_exist(multiple_foo) -test.must_not_exist(multiple_bar) - -test.run(chdir = 'multiple') - -test.must_exist(multiple_foo) -test.must_exist(multiple_bar) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that multiple target files get retrieved from a CacheDir correctly. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'multiple') + +cache = test.workpath('cache') + +multiple_bar = test.workpath('multiple', 'bar') +multiple_foo = test.workpath('multiple', 'foo') + +test.write(['multiple', 'SConstruct'], """\ +DefaultEnvironment(tools=[]) +def touch(env, source, target): + with open('foo', 'w') as f: + f.write("") + with open('bar', 'w') as f: + f.write("") +CacheDir(r'%(cache)s') +env = Environment(tools=[]) +env.Command(['foo', 'bar'], ['input'], touch) +""" % locals()) + +test.write(['multiple', 'input'], "multiple/input\n") + +test.run(chdir = 'multiple') + +test.must_exist(multiple_foo) +test.must_exist(multiple_bar) + +test.run(chdir = 'multiple', arguments = '-c') + +test.must_not_exist(multiple_foo) +test.must_not_exist(multiple_bar) + +test.run(chdir = 'multiple') + +test.must_exist(multiple_foo) +test.must_exist(multiple_bar) + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/option--cf.py b/test/CacheDir/option--cf.py index f93b4db4f7..4750b40b99 100644 --- a/test/CacheDir/option--cf.py +++ b/test/CacheDir/option--cf.py @@ -1,131 +1,131 @@ -#!/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." - -""" -Test populating a CacheDir with the --cache-force option. -""" - -import os.path -import shutil - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'src') - -test.write(['src', 'SConstruct'], """ -DefaultEnvironment(tools=[]) -def cat(env, source, target): - target = str(target[0]) - with open('cat.out', 'a') as f: - f.write(target + "\\n") - with open(target, "w") as f: - for src in source: - with open(str(src), "r") as f2: - f.write(f2.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('aaa.out', 'aaa.in') -env.Cat('bbb.out', 'bbb.in') -env.Cat('ccc.out', 'ccc.in') -env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) -CacheDir(r'%s') -""" % test.workpath('cache')) - -test.write(['src', 'aaa.in'], "aaa.in\n") -test.write(['src', 'bbb.in'], "bbb.in\n") -test.write(['src', 'ccc.in'], "ccc.in\n") - -# Verify that a normal build works correctly, and clean up. -# This should populate the cache with our derived files. -test.run(chdir = 'src', arguments = '.') - -test.must_match(['src','all'],"aaa.in\nbbb.in\nccc.in\n", mode='r') -# test.fail_test(test.read(['src', 'all']) != "aaa.in\nbbb.in\nccc.in\n") -test.must_match(['src','cat.out'],"aaa.out\nbbb.out\nccc.out\nall\n", mode='r') -# test.fail_test(test.read(['src', 'cat.out']) != "aaa.out\nbbb.out\nccc.out\nall\n") - -test.up_to_date(chdir = 'src', arguments = '.') - -test.run(chdir = 'src', arguments = '-c .') -test.unlink(['src', 'cat.out']) - -# Verify that we now retrieve the derived files from cache, -# not rebuild them. DO NOT CLEAN UP. -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) - -test.up_to_date(chdir = 'src', arguments = '.') - -# Blow away and recreate the CacheDir, then verify that --cache-force -# repopulates the cache with the local built targets. DO NOT CLEAN UP. -shutil.rmtree(test.workpath('cache')) -test.subdir('cache') - -test.run(chdir = 'src', arguments = '--cache-force .') - -test.run(chdir = 'src', arguments = '-c .') - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) - -# Blow away and recreate the CacheDir, then verify that --cache-populate -# repopulates the cache with the local built targets. DO NOT CLEAN UP. -shutil.rmtree(test.workpath('cache')) -test.subdir('cache') - -test.run(chdir = 'src', arguments = '--cache-populate .') - -test.run(chdir = 'src', arguments = '-c .') - -test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ -Retrieved `aaa.out' from cache -Retrieved `bbb.out' from cache -Retrieved `ccc.out' from cache -Retrieved `all' from cache -""")) - -test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) - -# All done. -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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." + +""" +Test populating a CacheDir with the --cache-force option. +""" + +import os.path +import shutil + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open('cat.out', 'a') as f: + f.write(target + "\\n") + with open(target, "w") as f: + for src in source: + with open(str(src), "r") as f2: + f.write(f2.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +CacheDir(r'%s') +""" % test.workpath('cache')) + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.must_match(['src','all'],"aaa.in\nbbb.in\nccc.in\n", mode='r') +# test.fail_test(test.read(['src', 'all']) != "aaa.in\nbbb.in\nccc.in\n") +test.must_match(['src','cat.out'],"aaa.out\nbbb.out\nccc.out\nall\n", mode='r') +# test.fail_test(test.read(['src', 'cat.out']) != "aaa.out\nbbb.out\nccc.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. DO NOT CLEAN UP. +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) + +test.up_to_date(chdir = 'src', arguments = '.') + +# Blow away and recreate the CacheDir, then verify that --cache-force +# repopulates the cache with the local built targets. DO NOT CLEAN UP. +shutil.rmtree(test.workpath('cache')) +test.subdir('cache') + +test.run(chdir = 'src', arguments = '--cache-force .') + +test.run(chdir = 'src', arguments = '-c .') + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) + +# Blow away and recreate the CacheDir, then verify that --cache-populate +# repopulates the cache with the local built targets. DO NOT CLEAN UP. +shutil.rmtree(test.workpath('cache')) +test.subdir('cache') + +test.run(chdir = 'src', arguments = '--cache-populate .') + +test.run(chdir = 'src', arguments = '-c .') + +test.run(chdir = 'src', arguments = '.', stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.fail_test(os.path.exists(test.workpath('src', 'cat.out'))) + +# All done. +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/symlink.py b/test/CacheDir/symlink.py index 9645f4d2df..36ca4be34f 100644 --- a/test/CacheDir/symlink.py +++ b/test/CacheDir/symlink.py @@ -1,76 +1,76 @@ -#!/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._" -import os -import sys - -import TestSCons - -""" -Verify that we push and retrieve a built symlink to/from a CacheDir() -as an actualy symlink, not by copying the file contents. -""" - - - - -test = TestSCons.TestSCons() - -if not hasattr(os, 'symlink') or sys.platform == 'win32': - # Skip test on windows as well, because this requires permissions which aren't default - import sys - test.skip_test('%s has no os.symlink() method; skipping test\n' % sys.executable) - -test.write('SConstruct', """\ -CacheDir('cache') -import os -Symlink = Action(lambda target, source, env: - os.symlink(str(source[0]), str(target[0])), - "os.symlink($SOURCE, $TARGET)") -Command('file.symlink', 'file.txt', Symlink) -""") - -test.write('file.txt', "file.txt\n") - -test.run(arguments = '.') - -test.fail_test(not os.path.islink('file.symlink')) -test.must_match('file.symlink', "file.txt\n") - -test.run(arguments = '-c .') - -test.must_not_exist('file.symlink') - -test.run(arguments = '.') - -test.fail_test(not os.path.islink('file.symlink')) -test.must_match('file.symlink', "file.txt\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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._" +import os +import sys + +import TestSCons + +""" +Verify that we push and retrieve a built symlink to/from a CacheDir() +as an actualy symlink, not by copying the file contents. +""" + + + + +test = TestSCons.TestSCons() + +if not hasattr(os, 'symlink') or sys.platform == 'win32': + # Skip test on windows as well, because this requires permissions which aren't default + import sys + test.skip_test('%s has no os.symlink() method; skipping test\n' % sys.executable) + +test.write('SConstruct', """\ +CacheDir('cache') +import os +Symlink = Action(lambda target, source, env: + os.symlink(str(source[0]), str(target[0])), + "os.symlink($SOURCE, $TARGET)") +Command('file.symlink', 'file.txt', Symlink) +""") + +test.write('file.txt', "file.txt\n") + +test.run(arguments = '.') + +test.fail_test(not os.path.islink('file.symlink')) +test.must_match('file.symlink', "file.txt\n") + +test.run(arguments = '-c .') + +test.must_not_exist('file.symlink') + +test.run(arguments = '.') + +test.fail_test(not os.path.islink('file.symlink')) +test.must_match('file.symlink', "file.txt\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/up-to-date-q.py b/test/CacheDir/up-to-date-q.py index c25a6777ad..c8fa1e3805 100644 --- a/test/CacheDir/up-to-date-q.py +++ b/test/CacheDir/up-to-date-q.py @@ -1,86 +1,86 @@ -#!/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 that targets retrieved from CacheDir() are reported as -up-to-date by the -q option. - -Thanks to dvitek for the test case. -""" - -# Demonstrate a regression between 0.96.1 and 0.96.93. -# -# SCons would incorrectly believe files are stale if they were retrieved -# from the cache in a previous invocation. -# -# What this script does: -# 1. Set up two identical C project directories called 'alpha' and -# 'beta', which use the same cache -# 2. Invoke scons on 'alpha' -# 3. Invoke scons on 'beta', which successfully draws output -# files from the cache -# 4. Invoke scons again, asserting (with -q) that 'beta' is up to date -# -# Step 4 failed in 0.96.93. In practice, this problem would lead to -# lots of unecessary fetches from the cache during incremental -# builds (because they behaved like non-incremental builds). - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('cache', 'alpha', 'beta') - -foo_c = """ -int main(void){ return 0; } -""" - -sconstruct = """ -CacheDir(r'%s') -Program('foo', 'foo.c') -""" % test.workpath('cache') - -test.write('alpha/foo.c', foo_c) -test.write('alpha/SConstruct', sconstruct) - -test.write('beta/foo.c', foo_c) -test.write('beta/SConstruct', sconstruct) - -# First build, populates the cache -test.run(chdir = 'alpha', arguments = '.') - -# Second build, everything is a cache hit -test.run(chdir = 'beta', arguments = '.') - -# Since we just built 'beta', it ought to be up to date. -test.run(chdir = 'beta', arguments = '. -q') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that targets retrieved from CacheDir() are reported as +up-to-date by the -q option. + +Thanks to dvitek for the test case. +""" + +# Demonstrate a regression between 0.96.1 and 0.96.93. +# +# SCons would incorrectly believe files are stale if they were retrieved +# from the cache in a previous invocation. +# +# What this script does: +# 1. Set up two identical C project directories called 'alpha' and +# 'beta', which use the same cache +# 2. Invoke scons on 'alpha' +# 3. Invoke scons on 'beta', which successfully draws output +# files from the cache +# 4. Invoke scons again, asserting (with -q) that 'beta' is up to date +# +# Step 4 failed in 0.96.93. In practice, this problem would lead to +# lots of unecessary fetches from the cache during incremental +# builds (because they behaved like non-incremental builds). + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'alpha', 'beta') + +foo_c = """ +int main(void){ return 0; } +""" + +sconstruct = """ +CacheDir(r'%s') +Program('foo', 'foo.c') +""" % test.workpath('cache') + +test.write('alpha/foo.c', foo_c) +test.write('alpha/SConstruct', sconstruct) + +test.write('beta/foo.c', foo_c) +test.write('beta/SConstruct', sconstruct) + +# First build, populates the cache +test.run(chdir = 'alpha', arguments = '.') + +# Second build, everything is a cache hit +test.run(chdir = 'beta', arguments = '.') + +# Since we just built 'beta', it ought to be up to date. +test.run(chdir = 'beta', arguments = '. -q') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CacheDir/value_dependencies.py b/test/CacheDir/value_dependencies.py index 73ebc60e04..b34401dc9c 100644 --- a/test/CacheDir/value_dependencies.py +++ b/test/CacheDir/value_dependencies.py @@ -1,51 +1,51 @@ -#!/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 that bwuilds with caching work for an action with a Value as a child -in a variety of cases. Specifically: - -1. A source file that depends on a Value. -2. A source directory that depends on a Value. -3. A scanner that returns a Value and a directory that depends on a Value. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.dir_fixture('value_dependencies') -test.subdir('cache') - -# First build, populates the cache -test.run(arguments='.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that bwuilds with caching work for an action with a Value as a child +in a variety of cases. Specifically: + +1. A source file that depends on a Value. +2. A source directory that depends on a Value. +3. A scanner that returns a Value and a directory that depends on a Value. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.dir_fixture('value_dependencies') +test.subdir('cache') + +# First build, populates the cache +test.run(arguments='.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clang_default_environment.py b/test/Clang/clang_default_environment.py index dc75bd4898..41bb44e15f 100644 --- a/test/Clang/clang_default_environment.py +++ b/test/Clang/clang_default_environment.py @@ -1,62 +1,62 @@ -#!/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. - -import TestSCons - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang', skipping test.\n") - -## This will likely NOT use clang - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clang','link']) -env.Program('foo.c') -""") - -test.write('foo.c', """\ -#include -int main(int argc, char ** argv) { - printf("Hello!"); - return 0; -} -""") - -test.run() - -test.run(program=test.workpath('foo'+_exe)) - -test.fail_test(not test.stdout() == 'Hello!') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang', skipping test.\n") + +## This will likely NOT use clang + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang','link']) +env.Program('foo.c') +""") + +test.write('foo.c', """\ +#include +int main(int argc, char ** argv) { + printf("Hello!"); + return 0; +} +""") + +test.run() + +test.run(program=test.workpath('foo'+_exe)) + +test.fail_test(not test.stdout() == 'Hello!') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clang_specific_environment.py b/test/Clang/clang_specific_environment.py index d0e145d206..10637e6635 100644 --- a/test/Clang/clang_specific_environment.py +++ b/test/Clang/clang_specific_environment.py @@ -1,60 +1,60 @@ -#!/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. - -import TestSCons - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang', skipping test.\n") - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clang', 'link']) -env.Program('foo.c') -""") - -test.write('foo.c', """\ -#include -int main(int argc, char ** argv) { - printf("Hello!"); - return 0; -} -""") - -test.run() - -test.run(program=test.workpath('foo'+_exe)) - -test.fail_test(not test.stdout() == 'Hello!') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang', skipping test.\n") + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang', 'link']) +env.Program('foo.c') +""") + +test.write('foo.c', """\ +#include +int main(int argc, char ** argv) { + printf("Hello!"); + return 0; +} +""") + +test.run() + +test.run(program=test.workpath('foo'+_exe)) + +test.fail_test(not test.stdout() == 'Hello!') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clang_static_library.py b/test/Clang/clang_static_library.py index 9d21def92d..c0d86d19c5 100644 --- a/test/Clang/clang_static_library.py +++ b/test/Clang/clang_static_library.py @@ -1,68 +1,68 @@ -#!/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. - -import TestSCons -from TestCmd import IS_WINDOWS - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang', skipping test.\n") - -if IS_WINDOWS: - foo_lib = 'foo.lib' - archiver = 'mslib' - # TODO: other Windows combinations exist (not depending on - # mslib (lib.exe) from MS Build Tools / Visual Studio). - # Expand this if there is demand. -else: - foo_lib = 'libfoo.a' - archiver = 'ar' - - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['mingw','clang', '%s']) -env.StaticLibrary('foo', 'foo.c') -""" % archiver) - -test.write('foo.c', """\ -int bar() { - return 42; -} -""") - -test.run() - -test.must_exist(test.workpath(foo_lib)) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons +from TestCmd import IS_WINDOWS + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang', skipping test.\n") + +if IS_WINDOWS: + foo_lib = 'foo.lib' + archiver = 'mslib' + # TODO: other Windows combinations exist (not depending on + # mslib (lib.exe) from MS Build Tools / Visual Studio). + # Expand this if there is demand. +else: + foo_lib = 'libfoo.a' + archiver = 'ar' + + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang', '%s']) +env.StaticLibrary('foo', 'foo.c') +""" % archiver) + +test.write('foo.c', """\ +int bar() { + return 42; +} +""") + +test.run() + +test.must_exist(test.workpath(foo_lib)) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clangxx_default_environment.py b/test/Clang/clangxx_default_environment.py index 82d99aa24f..ef5929e7dc 100644 --- a/test/Clang/clangxx_default_environment.py +++ b/test/Clang/clangxx_default_environment.py @@ -1,62 +1,62 @@ -#!/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. - -import TestSCons - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang++', skipping test.\n") - -## This will likely NOT use clang++. - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clangxx','link']) -env.Program('foo.cpp') -""") - -test.write('foo.cpp', """\ -#include -int main(int argc, char ** argv) { - std::cout << "Hello!" << std::endl; - return 0; -} -""") - -test.run() - -test.run(program=test.workpath('foo'+_exe)) - -test.fail_test(not test.stdout() == 'Hello!\n') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang++', skipping test.\n") + +## This will likely NOT use clang++. + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clangxx','link']) +env.Program('foo.cpp') +""") + +test.write('foo.cpp', """\ +#include +int main(int argc, char ** argv) { + std::cout << "Hello!" << std::endl; + return 0; +} +""") + +test.run() + +test.run(program=test.workpath('foo'+_exe)) + +test.fail_test(not test.stdout() == 'Hello!\n') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clangxx_shared_library.py b/test/Clang/clangxx_shared_library.py index dbc4831b9b..8471aa1680 100644 --- a/test/Clang/clangxx_shared_library.py +++ b/test/Clang/clangxx_shared_library.py @@ -1,77 +1,77 @@ -#!/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. - -import TestSCons - -from SCons.Environment import Base - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang++', skipping test.\n") - -platform = Base()['PLATFORM'] -if platform == 'posix': - filename_options = ['foo.os'] - libraryname = 'libfoo.so' -elif platform == 'darwin': - filename_options = ['foo.os'] - libraryname = 'libfoo.dylib' -elif platform == 'win32': - filename_options = ['foo.obj','foo.os'] - libraryname = 'foo.dll' -elif platform == 'sunos': - filename_options = ['foo.pic.o'] - libraryname = 'libfoo.so' -else: - test.fail_test() - - - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clang++', 'link']) -env.SharedLibrary('foo', 'foo.cpp') -""") - -test.write('foo.cpp', """\ -int bar() { - return 42; -} -""") - -test.run() - -test.must_exist_one_of([test.workpath(f) for f in filename_options]) -test.must_exist(test.workpath(libraryname)) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +from SCons.Environment import Base + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang++', skipping test.\n") + +platform = Base()['PLATFORM'] +if platform == 'posix': + filename_options = ['foo.os'] + libraryname = 'libfoo.so' +elif platform == 'darwin': + filename_options = ['foo.os'] + libraryname = 'libfoo.dylib' +elif platform == 'win32': + filename_options = ['foo.obj','foo.os'] + libraryname = 'foo.dll' +elif platform == 'sunos': + filename_options = ['foo.pic.o'] + libraryname = 'libfoo.so' +else: + test.fail_test() + + + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang++', 'link']) +env.SharedLibrary('foo', 'foo.cpp') +""") + +test.write('foo.cpp', """\ +int bar() { + return 42; +} +""") + +test.run() + +test.must_exist_one_of([test.workpath(f) for f in filename_options]) +test.must_exist(test.workpath(libraryname)) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clangxx_specific_environment.py b/test/Clang/clangxx_specific_environment.py index 5ca3c1229e..f26ebf92c8 100644 --- a/test/Clang/clangxx_specific_environment.py +++ b/test/Clang/clangxx_specific_environment.py @@ -1,60 +1,60 @@ -#!/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. - -import TestSCons - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang++', skipping test.\n") - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['clang++', 'link']) -env.Program('foo.cpp') -""") - -test.write('foo.cpp', """\ -#include -int main(int argc, char ** argv) { - std::cout << "Hello!" << std::endl; - return 0; -} -""") - -test.run() - -test.run(program=test.workpath('foo'+_exe)) - -test.fail_test(not test.stdout() == 'Hello!\n') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang++', skipping test.\n") + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['clang++', 'link']) +env.Program('foo.cpp') +""") + +test.write('foo.cpp', """\ +#include +int main(int argc, char ** argv) { + std::cout << "Hello!" << std::endl; + return 0; +} +""") + +test.run() + +test.run(program=test.workpath('foo'+_exe)) + +test.fail_test(not test.stdout() == 'Hello!\n') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clang/clangxx_static_library.py b/test/Clang/clangxx_static_library.py index 62711115ef..d697f1a9f5 100644 --- a/test/Clang/clangxx_static_library.py +++ b/test/Clang/clangxx_static_library.py @@ -1,68 +1,68 @@ -#!/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. - -import TestSCons -from TestCmd import IS_WINDOWS - -_exe = TestSCons._exe -test = TestSCons.TestSCons() - -if not test.where_is('clang'): - test.skip_test("Could not find 'clang++', skipping test.\n") - -if IS_WINDOWS: - foo_lib = 'foo.lib' - archiver = 'mslib' - # TODO: other Windows combinations exist (not depending on - # mslib (lib.exe) from MS Build Tools / Visual Studio). - # Expand this if there is demand. -else: - foo_lib = 'libfoo.a' - archiver = 'ar' - - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=['mingw','clang++', '%s']) -env.StaticLibrary('foo', 'foo.cpp') -""" % archiver) - -test.write('foo.cpp', """\ -int bar() { - return 42; -} -""") - -test.run() - -test.must_exist(test.workpath(foo_lib)) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons +from TestCmd import IS_WINDOWS + +_exe = TestSCons._exe +test = TestSCons.TestSCons() + +if not test.where_is('clang'): + test.skip_test("Could not find 'clang++', skipping test.\n") + +if IS_WINDOWS: + foo_lib = 'foo.lib' + archiver = 'mslib' + # TODO: other Windows combinations exist (not depending on + # mslib (lib.exe) from MS Build Tools / Visual Studio). + # Expand this if there is demand. +else: + foo_lib = 'libfoo.a' + archiver = 'ar' + + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw','clang++', '%s']) +env.StaticLibrary('foo', 'foo.cpp') +""" % archiver) + +test.write('foo.cpp', """\ +int bar() { + return 42; +} +""") + +test.run() + +test.must_exist(test.workpath(foo_lib)) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clean/basic.py b/test/Clean/basic.py index 1fc90efdde..ee510cbbde 100644 --- a/test/Clean/basic.py +++ b/test/Clean/basic.py @@ -1,185 +1,185 @@ -#!/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. - -""" -Test various basic uses of the -c (clean) option. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.write('build.py', r""" -import sys -with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: - ofp.write(ifp.read()) -""") - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') -env = Environment(tools=[], BUILDERS = { 'B' : B }) -env.B(target = 'foo1.out', source = 'foo1.in') -env.B(target = 'foo2.out', source = 'foo2.xxx') -env.B(target = 'foo2.xxx', source = 'foo2.in') -env.B(target = 'foo3.out', source = 'foo3.in') -env.B(target = 'foo4.out', source = 'foo4.in') -env.NoClean('foo4.out') -import os -import sys -if hasattr(os, 'symlink') and sys.platform !='win32': - def symlink1(env, target, source): - # symlink to a file that exists - os.symlink(str(source[0]), str(target[0])) - env.Command(target = 'symlink1', source = 'foo1.in', action = symlink1) - def symlink2(env, target, source): - # force symlink to a file that doesn't exist - os.symlink('does_not_exist', str(target[0])) - env.Command(target = 'symlink2', source = 'foo1.in', action = symlink2) -# Test handling of Builder calls that have multiple targets. -env.Command(['touch1.out', 'touch2.out'], - [], - [Touch('${TARGETS[0]}'), Touch('${TARGETS[1]}')]) -""" % locals()) - -test.write('foo1.in', "foo1.in\n") - -test.write('foo2.in', "foo2.in\n") - -test.write('foo3.in', "foo3.in\n") - -test.write('foo4.in', "foo4.in\n") - -test.run(arguments = 'foo1.out foo2.out foo3.out foo4.out') - -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo4.out'), "foo4.in\n") - -test.run(arguments = '-c foo1.out', - stdout = test.wrap_stdout("Removed foo1.out\n", cleaning=1)) - -test.must_not_exist(test.workpath('foo1.out')) -test.must_exist(test.workpath('foo2.xxx')) -test.must_exist(test.workpath('foo2.out')) -test.must_exist(test.workpath('foo3.out')) -test.must_exist(test.workpath('foo4.out')) - -test.run(arguments = '--clean foo2.out foo2.xxx', - stdout = test.wrap_stdout("Removed foo2.xxx\nRemoved foo2.out\n", - cleaning=1)) - -test.must_not_exist(test.workpath('foo1.out')) -test.must_not_exist(test.workpath('foo2.xxx')) -test.must_not_exist(test.workpath('foo2.out')) -test.must_exist(test.workpath('foo3.out')) -test.must_exist(test.workpath('foo4.out')) - -test.run(arguments = '--remove foo3.out', - stdout = test.wrap_stdout("Removed foo3.out\n", cleaning=1)) - -test.must_not_exist(test.workpath('foo1.out')) -test.must_not_exist(test.workpath('foo2.xxx')) -test.must_not_exist(test.workpath('foo2.out')) -test.must_not_exist(test.workpath('foo3.out')) -test.must_exist(test.workpath('foo4.out')) - -test.run(arguments = '.') - -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo4.out'), "foo4.in\n") -test.must_exist(test.workpath('touch1.out')) -test.must_exist(test.workpath('touch2.out')) - -if test.platform_has_symlink(): - test.fail_test(not os.path.islink(test.workpath('symlink1'))) - test.fail_test(not os.path.islink(test.workpath('symlink2'))) - -test.run(arguments = '-c foo2.xxx', - stdout = test.wrap_stdout("Removed foo2.xxx\n", cleaning=1)) - -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_not_exist(test.workpath('foo2.xxx')) -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo4.out'), "foo4.in\n") -test.must_exist(test.workpath('touch1.out')) -test.must_exist(test.workpath('touch2.out')) - -test.run(arguments = '-c .') - -test.must_not_exist(test.workpath('foo1.out')) -test.must_not_exist(test.workpath('foo2.out')) -test.must_not_exist(test.workpath('foo3.out')) -test.must_exist(test.workpath('foo4.out')) -test.must_not_exist(test.workpath('touch1.out')) -test.must_not_exist(test.workpath('touch2.out')) - -if test.platform_has_symlink(): - test.fail_test(os.path.islink(test.workpath('symlink1'))) - test.fail_test(os.path.islink(test.workpath('symlink2'))) - -args = 'foo1.out foo2.out foo3.out touch1.out' - -expect = test.wrap_stdout("""\ -Removed foo1.out -Removed foo2.xxx -Removed foo2.out -Removed foo3.out -Removed touch1.out -Removed touch2.out -""", cleaning=1) - -test.run(arguments = args) - -test.run(arguments = '-c -n ' + args, stdout = expect) - -test.run(arguments = '-n -c ' + args, stdout = expect) - -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") -test.must_match(test.workpath('foo4.out'), "foo4.in\n") -test.must_exist(test.workpath('touch1.out')) -test.must_exist(test.workpath('touch2.out')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test various basic uses of the -c (clean) option. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('build.py', r""" +import sys +with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: + ofp.write(ifp.read()) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') +env = Environment(tools=[], BUILDERS = { 'B' : B }) +env.B(target = 'foo1.out', source = 'foo1.in') +env.B(target = 'foo2.out', source = 'foo2.xxx') +env.B(target = 'foo2.xxx', source = 'foo2.in') +env.B(target = 'foo3.out', source = 'foo3.in') +env.B(target = 'foo4.out', source = 'foo4.in') +env.NoClean('foo4.out') +import os +import sys +if hasattr(os, 'symlink') and sys.platform !='win32': + def symlink1(env, target, source): + # symlink to a file that exists + os.symlink(str(source[0]), str(target[0])) + env.Command(target = 'symlink1', source = 'foo1.in', action = symlink1) + def symlink2(env, target, source): + # force symlink to a file that doesn't exist + os.symlink('does_not_exist', str(target[0])) + env.Command(target = 'symlink2', source = 'foo1.in', action = symlink2) +# Test handling of Builder calls that have multiple targets. +env.Command(['touch1.out', 'touch2.out'], + [], + [Touch('${TARGETS[0]}'), Touch('${TARGETS[1]}')]) +""" % locals()) + +test.write('foo1.in', "foo1.in\n") + +test.write('foo2.in', "foo2.in\n") + +test.write('foo3.in', "foo3.in\n") + +test.write('foo4.in', "foo4.in\n") + +test.run(arguments = 'foo1.out foo2.out foo3.out foo4.out') + +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo4.out'), "foo4.in\n") + +test.run(arguments = '-c foo1.out', + stdout = test.wrap_stdout("Removed foo1.out\n", cleaning=1)) + +test.must_not_exist(test.workpath('foo1.out')) +test.must_exist(test.workpath('foo2.xxx')) +test.must_exist(test.workpath('foo2.out')) +test.must_exist(test.workpath('foo3.out')) +test.must_exist(test.workpath('foo4.out')) + +test.run(arguments = '--clean foo2.out foo2.xxx', + stdout = test.wrap_stdout("Removed foo2.xxx\nRemoved foo2.out\n", + cleaning=1)) + +test.must_not_exist(test.workpath('foo1.out')) +test.must_not_exist(test.workpath('foo2.xxx')) +test.must_not_exist(test.workpath('foo2.out')) +test.must_exist(test.workpath('foo3.out')) +test.must_exist(test.workpath('foo4.out')) + +test.run(arguments = '--remove foo3.out', + stdout = test.wrap_stdout("Removed foo3.out\n", cleaning=1)) + +test.must_not_exist(test.workpath('foo1.out')) +test.must_not_exist(test.workpath('foo2.xxx')) +test.must_not_exist(test.workpath('foo2.out')) +test.must_not_exist(test.workpath('foo3.out')) +test.must_exist(test.workpath('foo4.out')) + +test.run(arguments = '.') + +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo4.out'), "foo4.in\n") +test.must_exist(test.workpath('touch1.out')) +test.must_exist(test.workpath('touch2.out')) + +if test.platform_has_symlink(): + test.fail_test(not os.path.islink(test.workpath('symlink1'))) + test.fail_test(not os.path.islink(test.workpath('symlink2'))) + +test.run(arguments = '-c foo2.xxx', + stdout = test.wrap_stdout("Removed foo2.xxx\n", cleaning=1)) + +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_not_exist(test.workpath('foo2.xxx')) +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo4.out'), "foo4.in\n") +test.must_exist(test.workpath('touch1.out')) +test.must_exist(test.workpath('touch2.out')) + +test.run(arguments = '-c .') + +test.must_not_exist(test.workpath('foo1.out')) +test.must_not_exist(test.workpath('foo2.out')) +test.must_not_exist(test.workpath('foo3.out')) +test.must_exist(test.workpath('foo4.out')) +test.must_not_exist(test.workpath('touch1.out')) +test.must_not_exist(test.workpath('touch2.out')) + +if test.platform_has_symlink(): + test.fail_test(os.path.islink(test.workpath('symlink1'))) + test.fail_test(os.path.islink(test.workpath('symlink2'))) + +args = 'foo1.out foo2.out foo3.out touch1.out' + +expect = test.wrap_stdout("""\ +Removed foo1.out +Removed foo2.xxx +Removed foo2.out +Removed foo3.out +Removed touch1.out +Removed touch2.out +""", cleaning=1) + +test.run(arguments = args) + +test.run(arguments = '-c -n ' + args, stdout = expect) + +test.run(arguments = '-n -c ' + args, stdout = expect) + +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_match(test.workpath('foo2.xxx'), "foo2.in\n") +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") +test.must_match(test.workpath('foo4.out'), "foo4.in\n") +test.must_exist(test.workpath('touch1.out')) +test.must_exist(test.workpath('touch2.out')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clean/function.py b/test/Clean/function.py index b73fe6757b..2476e61143 100644 --- a/test/Clean/function.py +++ b/test/Clean/function.py @@ -1,119 +1,119 @@ -#!/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 use of the Clean() function. -""" - -import os - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.write('build.py', r""" -import sys -with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: - ofp.write(ifp.read()) -""") - -test.subdir('subd') - -subd_SConscript = os.path.join('subd', 'SConscript') -subd_foon_in = os.path.join('subd', 'foon.in') -subd_foox_in = os.path.join('subd', 'foox.in') - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') -env = Environment(tools=[], BUILDERS = { 'B' : B }, FOO = 'foo2') -env.B(target = 'foo1.out', source = 'foo1.in') -env.B(target = 'foo2.out', source = 'foo2.xxx') -foo2_xxx = env.B(target = 'foo2.xxx', source = 'foo2.in') -env.B(target = 'foo3.out', source = 'foo3.in') -SConscript('subd/SConscript') -Clean(foo2_xxx, ['aux1.x']) -env.Clean(['${FOO}.xxx'], ['aux2.x']) -Clean('.', ['subd']) -""" % locals()) - -test.write(['subd', 'SConscript'], """ -Clean('.', 'foox.in') -""") - -test.write('foo1.in', "foo1.in\n") -test.write('foo2.in', "foo2.in\n") -test.write('foo3.in', "foo3.in\n") -test.write(['subd', 'foon.in'], "foon.in\n") -test.write(['subd', 'foox.in'], "foox.in\n") -test.write('aux1.x', "aux1.x\n") -test.write('aux2.x', "aux2.x\n") - -test.run() - -expect = test.wrap_stdout("""Removed foo2.xxx -Removed aux1.x -Removed aux2.x -""", cleaning=1) -test.run(arguments = '-c foo2.xxx', stdout=expect) -test.must_match(test.workpath('foo1.out'), "foo1.in\n") -test.must_not_exist(test.workpath('foo2.xxx')) -test.must_match(test.workpath('foo2.out'), "foo2.in\n") -test.must_match(test.workpath('foo3.out'), "foo3.in\n") - -expect = test.wrap_stdout("Removed %s\n" % subd_foox_in, cleaning = 1) -test.run(arguments = '-c subd', stdout=expect) -test.must_not_exist(test.workpath('foox.in')) - -expect = test.wrap_stdout("""Removed foo1.out -Removed foo2.xxx -Removed foo2.out -Removed foo3.out -Removed %(subd_SConscript)s -Removed %(subd_foon_in)s -Removed directory subd -""" % locals(), cleaning = 1) -test.run(arguments = '-c -n .', stdout=expect) - -expect = test.wrap_stdout("""Removed foo1.out -Removed foo2.out -Removed foo3.out -Removed %(subd_SConscript)s -Removed %(subd_foon_in)s -Removed directory subd -""" % locals(), cleaning = 1) -test.run(arguments = '-c .', stdout=expect) -test.must_not_exist(test.workpath('subdir', 'foon.in')) -test.must_not_exist(test.workpath('subdir')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 use of the Clean() function. +""" + +import os + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('build.py', r""" +import sys +with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: + ofp.write(ifp.read()) +""") + +test.subdir('subd') + +subd_SConscript = os.path.join('subd', 'SConscript') +subd_foon_in = os.path.join('subd', 'foon.in') +subd_foox_in = os.path.join('subd', 'foox.in') + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES') +env = Environment(tools=[], BUILDERS = { 'B' : B }, FOO = 'foo2') +env.B(target = 'foo1.out', source = 'foo1.in') +env.B(target = 'foo2.out', source = 'foo2.xxx') +foo2_xxx = env.B(target = 'foo2.xxx', source = 'foo2.in') +env.B(target = 'foo3.out', source = 'foo3.in') +SConscript('subd/SConscript') +Clean(foo2_xxx, ['aux1.x']) +env.Clean(['${FOO}.xxx'], ['aux2.x']) +Clean('.', ['subd']) +""" % locals()) + +test.write(['subd', 'SConscript'], """ +Clean('.', 'foox.in') +""") + +test.write('foo1.in', "foo1.in\n") +test.write('foo2.in', "foo2.in\n") +test.write('foo3.in', "foo3.in\n") +test.write(['subd', 'foon.in'], "foon.in\n") +test.write(['subd', 'foox.in'], "foox.in\n") +test.write('aux1.x', "aux1.x\n") +test.write('aux2.x', "aux2.x\n") + +test.run() + +expect = test.wrap_stdout("""Removed foo2.xxx +Removed aux1.x +Removed aux2.x +""", cleaning=1) +test.run(arguments = '-c foo2.xxx', stdout=expect) +test.must_match(test.workpath('foo1.out'), "foo1.in\n") +test.must_not_exist(test.workpath('foo2.xxx')) +test.must_match(test.workpath('foo2.out'), "foo2.in\n") +test.must_match(test.workpath('foo3.out'), "foo3.in\n") + +expect = test.wrap_stdout("Removed %s\n" % subd_foox_in, cleaning = 1) +test.run(arguments = '-c subd', stdout=expect) +test.must_not_exist(test.workpath('foox.in')) + +expect = test.wrap_stdout("""Removed foo1.out +Removed foo2.xxx +Removed foo2.out +Removed foo3.out +Removed %(subd_SConscript)s +Removed %(subd_foon_in)s +Removed directory subd +""" % locals(), cleaning = 1) +test.run(arguments = '-c -n .', stdout=expect) + +expect = test.wrap_stdout("""Removed foo1.out +Removed foo2.out +Removed foo3.out +Removed %(subd_SConscript)s +Removed %(subd_foon_in)s +Removed directory subd +""" % locals(), cleaning = 1) +test.run(arguments = '-c .', stdout=expect) +test.must_not_exist(test.workpath('subdir', 'foon.in')) +test.must_not_exist(test.workpath('subdir')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clean/mkfifo.py b/test/Clean/mkfifo.py index cfe20c6b6b..62d19d0465 100644 --- a/test/Clean/mkfifo.py +++ b/test/Clean/mkfifo.py @@ -1,81 +1,81 @@ -#!/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 that SCons reports an error when cleaning up a target directory -containing a named pipe created with o.mkfifo(). -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -if not hasattr(os, 'mkfifo'): - test.skip_test('No os.mkfifo() function; skipping test\n') - -test_dir_name = 'testdir' -pipe_path = os.path.join(test_dir_name, 'namedpipe') - -test.write('SConstruct', """\ -Execute(Mkdir("{0}")) -dir = Dir("{0}") -Clean(dir, '{0}') -""".format(test_dir_name)) - -test.run(arguments='-Q -q', stdout='Mkdir("{0}")\n'.format(test_dir_name)) - -os.mkfifo(pipe_path) - -test.must_exist(test.workpath(pipe_path)) - -expect1 = """\ -Mkdir("{0}") -Path '{1}' exists but isn't a file or directory. -scons: Could not remove '{0}': Directory not empty -""".format(test_dir_name, pipe_path) - -expect2 = """\ -Mkdir("{0}") -Path '{1}' exists but isn't a file or directory. -scons: Could not remove '{0}': File exists -""".format(test_dir_name, pipe_path) - -test.run(arguments='-c -Q -q') - -test.must_exist(test.workpath(pipe_path)) - -if test.stdout() not in [expect1, expect2]: - test.diff(expect1, test.stdout(), 'STDOUT ') - test.fail_test() - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that SCons reports an error when cleaning up a target directory +containing a named pipe created with o.mkfifo(). +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +if not hasattr(os, 'mkfifo'): + test.skip_test('No os.mkfifo() function; skipping test\n') + +test_dir_name = 'testdir' +pipe_path = os.path.join(test_dir_name, 'namedpipe') + +test.write('SConstruct', """\ +Execute(Mkdir("{0}")) +dir = Dir("{0}") +Clean(dir, '{0}') +""".format(test_dir_name)) + +test.run(arguments='-Q -q', stdout='Mkdir("{0}")\n'.format(test_dir_name)) + +os.mkfifo(pipe_path) + +test.must_exist(test.workpath(pipe_path)) + +expect1 = """\ +Mkdir("{0}") +Path '{1}' exists but isn't a file or directory. +scons: Could not remove '{0}': Directory not empty +""".format(test_dir_name, pipe_path) + +expect2 = """\ +Mkdir("{0}") +Path '{1}' exists but isn't a file or directory. +scons: Could not remove '{0}': File exists +""".format(test_dir_name, pipe_path) + +test.run(arguments='-c -Q -q') + +test.must_exist(test.workpath(pipe_path)) + +if test.stdout() not in [expect1, expect2]: + test.diff(expect1, test.stdout(), 'STDOUT ') + test.fail_test() + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Clean/symlinks.py b/test/Clean/symlinks.py index 2f5a1f8602..05fb08be53 100644 --- a/test/Clean/symlinks.py +++ b/test/Clean/symlinks.py @@ -1,63 +1,63 @@ -#!/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 correct deletion of broken symlinks. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -if not test.platform_has_symlink(): - test.skip_test('No os.symlink() function; skipping test\n') - -test.write('SConstruct', """\ -Execute(Mkdir("testdir")) -dir = Dir("testdir") -Clean(dir, 'testdir') -""") - -test.run(arguments = '-Q -q', stdout='Mkdir("testdir")\n') - -os.symlink('testdir/symlinksrc', 'testdir/symlinkdst') - -expect = """\ -Mkdir("testdir") -Removed %s -Removed directory testdir -""" % os.path.join('testdir', 'symlinkdst') - -test.run(arguments = '-c -Q -q', stdout=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 correct deletion of broken symlinks. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +if not test.platform_has_symlink(): + test.skip_test('No os.symlink() function; skipping test\n') + +test.write('SConstruct', """\ +Execute(Mkdir("testdir")) +dir = Dir("testdir") +Clean(dir, 'testdir') +""") + +test.run(arguments = '-Q -q', stdout='Mkdir("testdir")\n') + +os.symlink('testdir/symlinksrc', 'testdir/symlinkdst') + +expect = """\ +Mkdir("testdir") +Removed %s +Removed directory testdir +""" % os.path.join('testdir', 'symlinkdst') + +test.run(arguments = '-c -Q -q', 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/Climb/U-Default-dir.py b/test/Climb/U-Default-dir.py index 4c0eb6689f..f26fe75fcc 100644 --- a/test/Climb/U-Default-dir.py +++ b/test/Climb/U-Default-dir.py @@ -1,48 +1,48 @@ -#!/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. - -""" -Make sure that a Default() directory doesn't cause an exception -when used with the -U option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -Default('.') -""") - -test.run(arguments = '-U') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure that a Default() directory doesn't cause an exception +when used with the -U option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +Default('.') +""") + +test.run(arguments = '-U') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/U-Default-no-target.py b/test/Climb/U-Default-no-target.py index f580bffb23..331863e210 100644 --- a/test/Climb/U-Default-no-target.py +++ b/test/Climb/U-Default-no-target.py @@ -1,50 +1,50 @@ -#!/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. - -""" -Make sure a Default() target that doesn't exist is handled with -the correct failure when used with the -U option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -Default('not_a_target.in') -""") - -test.run(arguments = '-U', status=2, match=TestSCons.match_re, stderr=\ -r"""scons: \*\*\* Do not know how to make File target `not_a_target.in' \(.*not_a_target.in\). Stop. -""") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure a Default() target that doesn't exist is handled with +the correct failure when used with the -U option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +Default('not_a_target.in') +""") + +test.run(arguments = '-U', status=2, match=TestSCons.match_re, stderr=\ +r"""scons: \*\*\* Do not know how to make File target `not_a_target.in' \(.*not_a_target.in\). Stop. +""") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/U-no-Default.py b/test/Climb/U-no-Default.py index 9cd1ebf157..940414b934 100644 --- a/test/Climb/U-no-Default.py +++ b/test/Climb/U-no-Default.py @@ -1,47 +1,47 @@ -#!/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. - -""" -Make sure no Default() targets in the SConstruct doesn't cause an -exception when used with the -U option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', "\n") - -test.run(arguments = '-U', - stderr = "scons: *** No targets specified and no Default() targets found. Stop.\n", - status = 2) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure no Default() targets in the SConstruct doesn't cause an +exception when used with the -U option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', "\n") + +test.run(arguments = '-U', + stderr = "scons: *** No targets specified and no Default() targets found. Stop.\n", + status = 2) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/explicit-parent--D.py b/test/Climb/explicit-parent--D.py index 51686fe48f..c44dc38401 100644 --- a/test/Climb/explicit-parent--D.py +++ b/test/Climb/explicit-parent--D.py @@ -1,79 +1,79 @@ -#!/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. - -""" -Make sure explicit targets beginning with ../ get built correctly -by the -D option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir(['subdir']) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: - for src in source: - with open(str(src), 'rb') as ifp: - ofp.write(ifp.read()) -env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('f1.out', 'f1.in') -f2 = env.Cat('f2.out', 'f2.in') -Default(f2) -SConscript('subdir/SConscript', "env") -""") - -test.write('f1.in', "f1.in\n") -test.write('f2.in', "f2.in\n") - -test.write(['subdir', 'SConscript'], """\ -Import("env") -f3 = env.Cat('f3.out', 'f3.in') -env.Cat('f4.out', 'f4.in') -Default(f3) -""") - -test.write(['subdir', 'f3.in'], "subdir/f3.in\n") -test.write(['subdir', 'f4.in'], "subdir/f4.in\n") - -test.run(chdir = 'subdir', arguments = '-D ../f1.out') - -test.must_exist(test.workpath('f1.out')) -test.must_not_exist(test.workpath('f2.out')) -test.must_not_exist(test.workpath('dir', 'f3.out')) -test.must_not_exist(test.workpath('dir', 'f4.out')) - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure explicit targets beginning with ../ get built correctly +by the -D option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir(['subdir']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, 'wb') as ofp: + for src in source: + with open(str(src), 'rb') as ifp: + ofp.write(ifp.read()) +env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('f1.out', 'f1.in') +f2 = env.Cat('f2.out', 'f2.in') +Default(f2) +SConscript('subdir/SConscript', "env") +""") + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") + +test.write(['subdir', 'SConscript'], """\ +Import("env") +f3 = env.Cat('f3.out', 'f3.in') +env.Cat('f4.out', 'f4.in') +Default(f3) +""") + +test.write(['subdir', 'f3.in'], "subdir/f3.in\n") +test.write(['subdir', 'f4.in'], "subdir/f4.in\n") + +test.run(chdir = 'subdir', arguments = '-D ../f1.out') + +test.must_exist(test.workpath('f1.out')) +test.must_not_exist(test.workpath('f2.out')) +test.must_not_exist(test.workpath('dir', 'f3.out')) +test.must_not_exist(test.workpath('dir', 'f4.out')) + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/explicit-parent--U.py b/test/Climb/explicit-parent--U.py index 973ce26cc1..a37d5a1608 100644 --- a/test/Climb/explicit-parent--U.py +++ b/test/Climb/explicit-parent--U.py @@ -1,72 +1,72 @@ -#!/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. - -""" -Make sure explicit targets beginning with ../ get built correctly. -by the -U option. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('subdir') - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: - for src in source: - with open(str(src), 'rb') as ifp: - ofp.write(ifp.read()) -env = Environment(tools=[], - BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('foo.out', 'foo.in') -SConscript('subdir/SConscript', "env") -""") - -test.write('foo.in', "foo.in\n") - -test.write(['subdir', 'SConscript'], """\ -Import("env") -bar = env.Cat('bar.out', 'bar.in') -Default(bar) -""") - -test.write(['subdir', 'bar.in'], "subdir/bar.in\n") - -test.run(chdir = 'subdir', arguments = '-U ../foo.out') - -test.must_exist(test.workpath('foo.out')) -test.must_not_exist(test.workpath('subdir', 'bar.out')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Make sure explicit targets beginning with ../ get built correctly. +by the -U option. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('subdir') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, 'wb') as ofp: + for src in source: + with open(str(src), 'rb') as ifp: + ofp.write(ifp.read()) +env = Environment(tools=[], + BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('foo.out', 'foo.in') +SConscript('subdir/SConscript', "env") +""") + +test.write('foo.in', "foo.in\n") + +test.write(['subdir', 'SConscript'], """\ +Import("env") +bar = env.Cat('bar.out', 'bar.in') +Default(bar) +""") + +test.write(['subdir', 'bar.in'], "subdir/bar.in\n") + +test.run(chdir = 'subdir', arguments = '-U ../foo.out') + +test.must_exist(test.workpath('foo.out')) +test.must_not_exist(test.workpath('subdir', 'bar.out')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/explicit-parent-u.py b/test/Climb/explicit-parent-u.py index c42e9db2ab..c8c5da23e0 100644 --- a/test/Climb/explicit-parent-u.py +++ b/test/Climb/explicit-parent-u.py @@ -1,78 +1,78 @@ -#!/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. - -""" -Test that the -u option only builds targets at or below -the current directory. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -# Make sure explicit targets beginning with ../ get built. -test.subdir('subdir') - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: - for src in source: - with open(str(src), 'rb') as ifp: - ofp.write(ifp.read()) -env = Environment(tools=[], - BUILDERS={'Cat':Builder(action=cat)}) -env.Cat('f1.out', 'f1.in') -env.Cat('f2.out', 'f2.in') -SConscript('subdir/SConscript', "env") -""") - -test.write('f1.in', "f1.in\n") -test.write('f2.in', "f2.in\n") - -test.write(['subdir', 'SConscript'], """\ -Import("env") -env.Cat('f3.out', 'f3.in') -env.Cat('f4.out', 'f4.in') -""") - -test.write(['subdir', 'f3.in'], "subdir/f3.in\n") -test.write(['subdir', 'f4.in'], "subdir/f4.in\n") - -test.run(chdir = 'subdir', arguments = '-u ../f2.out') - -test.must_not_exist(test.workpath('f1.out')) -test.must_exist(test.workpath('f2.out')) -test.must_not_exist(test.workpath('dir', 'f3.out')) -test.must_not_exist(test.workpath('dir', 'f4.out')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the -u option only builds targets at or below +the current directory. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +# Make sure explicit targets beginning with ../ get built. +test.subdir('subdir') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, 'wb') as ofp: + for src in source: + with open(str(src), 'rb') as ifp: + ofp.write(ifp.read()) +env = Environment(tools=[], + BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('f1.out', 'f1.in') +env.Cat('f2.out', 'f2.in') +SConscript('subdir/SConscript', "env") +""") + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") + +test.write(['subdir', 'SConscript'], """\ +Import("env") +env.Cat('f3.out', 'f3.in') +env.Cat('f4.out', 'f4.in') +""") + +test.write(['subdir', 'f3.in'], "subdir/f3.in\n") +test.write(['subdir', 'f4.in'], "subdir/f4.in\n") + +test.run(chdir = 'subdir', arguments = '-u ../f2.out') + +test.must_not_exist(test.workpath('f1.out')) +test.must_exist(test.workpath('f2.out')) +test.must_not_exist(test.workpath('dir', 'f3.out')) +test.must_not_exist(test.workpath('dir', 'f4.out')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/filename--D.py b/test/Climb/filename--D.py index c747a81871..38a337b204 100644 --- a/test/Climb/filename--D.py +++ b/test/Climb/filename--D.py @@ -1,79 +1,79 @@ -#!/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 the ability to use the -D option with the -f option to -specify a different top-level file name. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('subdir', 'other') - -test.write('main.scons', """\ -DefaultEnvironment(tools=[]) -print("main.scons") -SConscript('subdir/sub.scons') -""") - -test.write(['subdir', 'sub.scons'], """\ -print("subdir/sub.scons") -""") - - - -read_str = """\ -main.scons -subdir/sub.scons -""" - -expect = "scons: Entering directory `%s'\n" % test.workpath() \ - + test.wrap_stdout(read_str = read_str, - build_str = "scons: `subdir' is up to date.\n") - -test.run(chdir='subdir', arguments='-D -f main.scons .', stdout=expect) - - - -expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", - build_str = "scons: `.' is up to date.\n") - -test.run(chdir='other', arguments='-D -f ../subdir/sub.scons .', stdout=expect) - -test.run(chdir='other', - arguments='-D -f %s .' % test.workpath('subdir', 'sub.scons'), - stdout=expect) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the ability to use the -D option with the -f option to +specify a different top-level file name. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('subdir', 'other') + +test.write('main.scons', """\ +DefaultEnvironment(tools=[]) +print("main.scons") +SConscript('subdir/sub.scons') +""") + +test.write(['subdir', 'sub.scons'], """\ +print("subdir/sub.scons") +""") + + + +read_str = """\ +main.scons +subdir/sub.scons +""" + +expect = "scons: Entering directory `%s'\n" % test.workpath() \ + + test.wrap_stdout(read_str = read_str, + build_str = "scons: `subdir' is up to date.\n") + +test.run(chdir='subdir', arguments='-D -f main.scons .', stdout=expect) + + + +expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", + build_str = "scons: `.' is up to date.\n") + +test.run(chdir='other', arguments='-D -f ../subdir/sub.scons .', stdout=expect) + +test.run(chdir='other', + arguments='-D -f %s .' % test.workpath('subdir', 'sub.scons'), + 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/Climb/filename--U.py b/test/Climb/filename--U.py index 5242add13e..f14593374a 100644 --- a/test/Climb/filename--U.py +++ b/test/Climb/filename--U.py @@ -1,77 +1,77 @@ -#!/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 the ability to use the -U option with the -f option to -specify a different top-level file name. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('subdir', 'other') - -test.write('main.scons', """\ -DefaultEnvironment(tools=[]) -print("main.scons") -SConscript('subdir/sub.scons') -""") - -test.write(['subdir', 'sub.scons'], """\ -print("subdir/sub.scons") -""") - -read_str = """\ -main.scons -subdir/sub.scons -""" - -expect = "scons: Entering directory `%s'\n" % test.workpath() \ - + test.wrap_stdout(read_str = read_str, - build_str = "scons: `subdir' is up to date.\n") - -test.run(chdir='subdir', arguments='-U -f main.scons .', stdout=expect) - - - -expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", - build_str = "scons: `.' is up to date.\n") - -test.run(chdir='other', arguments='-U -f ../subdir/sub.scons .', stdout=expect) - -test.run(chdir='other', - arguments='-U -f %s .' % test.workpath('subdir', 'sub.scons'), - stdout=expect) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the ability to use the -U option with the -f option to +specify a different top-level file name. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('subdir', 'other') + +test.write('main.scons', """\ +DefaultEnvironment(tools=[]) +print("main.scons") +SConscript('subdir/sub.scons') +""") + +test.write(['subdir', 'sub.scons'], """\ +print("subdir/sub.scons") +""") + +read_str = """\ +main.scons +subdir/sub.scons +""" + +expect = "scons: Entering directory `%s'\n" % test.workpath() \ + + test.wrap_stdout(read_str = read_str, + build_str = "scons: `subdir' is up to date.\n") + +test.run(chdir='subdir', arguments='-U -f main.scons .', stdout=expect) + + + +expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", + build_str = "scons: `.' is up to date.\n") + +test.run(chdir='other', arguments='-U -f ../subdir/sub.scons .', stdout=expect) + +test.run(chdir='other', + arguments='-U -f %s .' % test.workpath('subdir', 'sub.scons'), + 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/Climb/filename-u.py b/test/Climb/filename-u.py index 844f2cda1f..4806c16c63 100644 --- a/test/Climb/filename-u.py +++ b/test/Climb/filename-u.py @@ -1,77 +1,77 @@ -#!/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 the ability to use the -u option with the -f option to -specify a different top-level file name. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('subdir', 'other') - -test.write('main.scons', """\ -DefaultEnvironment(tools=[]) -print("main.scons") -SConscript('subdir/sub.scons') -""") - -test.write(['subdir', 'sub.scons'], """\ -print("subdir/sub.scons") -""") - -read_str = """\ -main.scons -subdir/sub.scons -""" - -expect = "scons: Entering directory `%s'\n" % test.workpath() \ - + test.wrap_stdout(read_str = read_str, - build_str = "scons: `subdir' is up to date.\n") - -test.run(chdir='subdir', arguments='-u -f main.scons .', stdout=expect) - - - -expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", - build_str = "scons: `.' is up to date.\n") - -test.run(chdir='other', arguments='-u -f ../subdir/sub.scons .', stdout=expect) - -test.run(chdir='other', - arguments='-u -f %s .' % test.workpath('subdir', 'sub.scons'), - stdout=expect) - - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 the ability to use the -u option with the -f option to +specify a different top-level file name. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('subdir', 'other') + +test.write('main.scons', """\ +DefaultEnvironment(tools=[]) +print("main.scons") +SConscript('subdir/sub.scons') +""") + +test.write(['subdir', 'sub.scons'], """\ +print("subdir/sub.scons") +""") + +read_str = """\ +main.scons +subdir/sub.scons +""" + +expect = "scons: Entering directory `%s'\n" % test.workpath() \ + + test.wrap_stdout(read_str = read_str, + build_str = "scons: `subdir' is up to date.\n") + +test.run(chdir='subdir', arguments='-u -f main.scons .', stdout=expect) + + + +expect = test.wrap_stdout(read_str = "subdir/sub.scons\n", + build_str = "scons: `.' is up to date.\n") + +test.run(chdir='other', arguments='-u -f ../subdir/sub.scons .', stdout=expect) + +test.run(chdir='other', + arguments='-u -f %s .' % test.workpath('subdir', 'sub.scons'), + 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/Climb/option--D.py b/test/Climb/option--D.py index 5b66f6865c..169678aef5 100644 --- a/test/Climb/option--D.py +++ b/test/Climb/option--D.py @@ -1,89 +1,89 @@ -#!/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. - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.subdir('sub1', 'sub2') - -test.write('build.py', r""" -import sys -with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: - ofp.write(ifp.read()) -""") - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -import SCons.Defaults -B = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES') -env = Environment(tools=[]) -env['BUILDERS']['B'] = B -env.B(target = 'sub1/foo.out', source = 'sub1/foo.in') -Export('env') -SConscript('sub1/SConscript') -SConscript('sub2/SConscript') -""" % locals()) - -test.write(['sub1', 'SConscript'], """ -Import('env') -env.B(target = 'foo.out', source = 'foo.in') -Default('.') -""") - -test.write(['sub1', 'foo.in'], "sub1/foo.in") - -test.write(['sub2', 'SConscript'], """ -Import('env') -env.Alias('bar', env.B(target = 'bar.out', source = 'bar.in')) -Default('.') - -""") - -test.write(['sub2', 'bar.in'], "sub2/bar.in") - -test.run(arguments = '-D', chdir = 'sub1') - -test.must_match(['sub1', 'foo.out'], "sub1/foo.in") -test.must_match(['sub2', 'bar.out'], "sub2/bar.in") - -test.unlink(['sub1', 'foo.out']) -test.unlink(['sub2', 'bar.out']) - -test.run(arguments = '-D bar', chdir = 'sub1') - -test.must_not_exist(test.workpath('sub1', 'foo.out')) -test.must_exist(test.workpath('sub2', 'bar.out')) - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.subdir('sub1', 'sub2') + +test.write('build.py', r""" +import sys +with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: + ofp.write(ifp.read()) +""") + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +import SCons.Defaults +B = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES') +env = Environment(tools=[]) +env['BUILDERS']['B'] = B +env.B(target = 'sub1/foo.out', source = 'sub1/foo.in') +Export('env') +SConscript('sub1/SConscript') +SConscript('sub2/SConscript') +""" % locals()) + +test.write(['sub1', 'SConscript'], """ +Import('env') +env.B(target = 'foo.out', source = 'foo.in') +Default('.') +""") + +test.write(['sub1', 'foo.in'], "sub1/foo.in") + +test.write(['sub2', 'SConscript'], """ +Import('env') +env.Alias('bar', env.B(target = 'bar.out', source = 'bar.in')) +Default('.') + +""") + +test.write(['sub2', 'bar.in'], "sub2/bar.in") + +test.run(arguments = '-D', chdir = 'sub1') + +test.must_match(['sub1', 'foo.out'], "sub1/foo.in") +test.must_match(['sub2', 'bar.out'], "sub2/bar.in") + +test.unlink(['sub1', 'foo.out']) +test.unlink(['sub2', 'bar.out']) + +test.run(arguments = '-D bar', chdir = 'sub1') + +test.must_not_exist(test.workpath('sub1', 'foo.out')) +test.must_exist(test.workpath('sub2', 'bar.out')) + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/option--U.py b/test/Climb/option--U.py index 4bf5b3558e..8c34462b45 100644 --- a/test/Climb/option--U.py +++ b/test/Climb/option--U.py @@ -1,144 +1,144 @@ -#!/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. - -import sys - -import TestSCons - -test = TestSCons.TestSCons() - -_python_ = TestSCons._python_ - -test.subdir('sub1', 'sub2', 'sub3') - -test.write('build.py', r""" -import sys -with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: - ofp.write(ifp.read()) -""") - -test.write('SConstruct', r""" -DefaultEnvironment(tools=[]) -import SCons.Defaults -env = Environment(tools=[]) -env['BUILDERS']['B'] = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES', multi=1) -Default(env.B(target = 'sub1/foo.out', source = 'sub1/foo.in')) -Export('env') -SConscript('sub2/SConscript') -Default(env.B(target = 'sub3/baz.out', source = 'sub3/baz.in')) -VariantDir('sub2b', 'sub2') -SConscript('sub2b/SConscript') -Default(env.B(target = 'sub2/xxx.out', source = 'xxx.in')) -SConscript('SConscript') -""" % locals()) - -test.write(['sub2', 'SConscript'], """ -Import('env') -bar = env.B(target = 'bar.out', source = 'bar.in') -Default(bar) -env.Alias('bar', bar) -Default(env.B(target = '../bar.out', source = 'bar.in')) -""") - - -test.write(['sub1', 'foo.in'], "sub1/foo.in\n") -test.write(['sub2', 'bar.in'], "sub2/bar.in\n") -test.write(['sub3', 'baz.in'], "sub3/baz.in\n") -test.write('xxx.in', "xxx.in\n") - -test.write('SConscript', """assert GetLaunchDir() == r'%s'\n"""%test.workpath('sub1')) -test.run(arguments = '-U foo.out', chdir = 'sub1') - -test.must_exist(test.workpath('sub1', 'foo.out')) -test.must_not_exist(test.workpath('sub2', 'bar.out')) -test.must_not_exist(test.workpath('sub2b', 'bar.out')) -test.must_not_exist(test.workpath('sub3', 'baz.out')) -test.must_not_exist(test.workpath('bar.out')) -test.must_not_exist(test.workpath('sub2/xxx.out')) - -test.unlink(['sub1', 'foo.out']) - -test.write('SConscript', """\ -env = Environment(tools=[], ) -assert env.GetLaunchDir() == r'%s' -"""%test.workpath('sub1')) -test.run(arguments = '-U', - chdir = 'sub1', - stderr = "scons: *** No targets specified and no Default() targets found. Stop.\n", - status = 2) -test.must_not_exist(test.workpath('sub1', 'foo.out')) -test.must_not_exist(test.workpath('sub2', 'bar.out')) -test.must_not_exist(test.workpath('sub2b', 'bar.out')) -test.must_not_exist(test.workpath('sub3', 'baz.out')) -test.must_not_exist(test.workpath('bar.out')) -test.must_not_exist(test.workpath('sub2/xxx.out')) - - -if sys.platform == 'win32': - sub2 = 'SUB2' -else: - sub2 = 'sub2' -test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath(sub2)) -test.run(chdir = sub2, arguments = '-U') -test.must_not_exist(test.workpath('sub1', 'foo.out')) -test.must_exist(test.workpath('sub2', 'bar.out')) -test.must_exist(test.workpath('sub2b', 'bar.out')) -test.must_not_exist(test.workpath('sub3', 'baz.out')) -test.must_exist(test.workpath('bar.out')) -test.must_not_exist(test.workpath('sub2/xxx.out')) - -test.unlink(['sub2', 'bar.out']) -test.unlink(['sub2b', 'bar.out']) -test.unlink('bar.out') - -test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath()) -test.run(arguments='-U') -test.must_exist(test.workpath('sub1', 'foo.out')) -test.must_not_exist(test.workpath('sub2', 'bar.out')) -test.must_not_exist(test.workpath('sub2b', 'bar.out')) -test.must_exist(test.workpath('sub3', 'baz.out')) -test.must_not_exist(test.workpath('bar.out')) -test.must_exist(test.workpath('sub2/xxx.out')) - -test.unlink(['sub1', 'foo.out']) -test.unlink(['sub3', 'baz.out']) -test.unlink(['sub2', 'xxx.out']) - -test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath('sub3')) -test.run(chdir = 'sub3', arguments='-U bar') -test.must_not_exist(test.workpath('sub1', 'foo.out')) -test.must_exist(test.workpath('sub2', 'bar.out')) -test.must_exist(test.workpath('sub2b', 'bar.out')) -test.must_not_exist(test.workpath('sub3', 'baz.out')) -test.must_not_exist(test.workpath('bar.out')) -test.must_not_exist(test.workpath('sub2/xxx.out')) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +import sys + +import TestSCons + +test = TestSCons.TestSCons() + +_python_ = TestSCons._python_ + +test.subdir('sub1', 'sub2', 'sub3') + +test.write('build.py', r""" +import sys +with open(sys.argv[1], 'wb') as ofp, open(sys.argv[2], 'rb') as ifp: + ofp.write(ifp.read()) +""") + +test.write('SConstruct', r""" +DefaultEnvironment(tools=[]) +import SCons.Defaults +env = Environment(tools=[]) +env['BUILDERS']['B'] = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES', multi=1) +Default(env.B(target = 'sub1/foo.out', source = 'sub1/foo.in')) +Export('env') +SConscript('sub2/SConscript') +Default(env.B(target = 'sub3/baz.out', source = 'sub3/baz.in')) +VariantDir('sub2b', 'sub2') +SConscript('sub2b/SConscript') +Default(env.B(target = 'sub2/xxx.out', source = 'xxx.in')) +SConscript('SConscript') +""" % locals()) + +test.write(['sub2', 'SConscript'], """ +Import('env') +bar = env.B(target = 'bar.out', source = 'bar.in') +Default(bar) +env.Alias('bar', bar) +Default(env.B(target = '../bar.out', source = 'bar.in')) +""") + + +test.write(['sub1', 'foo.in'], "sub1/foo.in\n") +test.write(['sub2', 'bar.in'], "sub2/bar.in\n") +test.write(['sub3', 'baz.in'], "sub3/baz.in\n") +test.write('xxx.in', "xxx.in\n") + +test.write('SConscript', """assert GetLaunchDir() == r'%s'\n"""%test.workpath('sub1')) +test.run(arguments = '-U foo.out', chdir = 'sub1') + +test.must_exist(test.workpath('sub1', 'foo.out')) +test.must_not_exist(test.workpath('sub2', 'bar.out')) +test.must_not_exist(test.workpath('sub2b', 'bar.out')) +test.must_not_exist(test.workpath('sub3', 'baz.out')) +test.must_not_exist(test.workpath('bar.out')) +test.must_not_exist(test.workpath('sub2/xxx.out')) + +test.unlink(['sub1', 'foo.out']) + +test.write('SConscript', """\ +env = Environment(tools=[], ) +assert env.GetLaunchDir() == r'%s' +"""%test.workpath('sub1')) +test.run(arguments = '-U', + chdir = 'sub1', + stderr = "scons: *** No targets specified and no Default() targets found. Stop.\n", + status = 2) +test.must_not_exist(test.workpath('sub1', 'foo.out')) +test.must_not_exist(test.workpath('sub2', 'bar.out')) +test.must_not_exist(test.workpath('sub2b', 'bar.out')) +test.must_not_exist(test.workpath('sub3', 'baz.out')) +test.must_not_exist(test.workpath('bar.out')) +test.must_not_exist(test.workpath('sub2/xxx.out')) + + +if sys.platform == 'win32': + sub2 = 'SUB2' +else: + sub2 = 'sub2' +test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath(sub2)) +test.run(chdir = sub2, arguments = '-U') +test.must_not_exist(test.workpath('sub1', 'foo.out')) +test.must_exist(test.workpath('sub2', 'bar.out')) +test.must_exist(test.workpath('sub2b', 'bar.out')) +test.must_not_exist(test.workpath('sub3', 'baz.out')) +test.must_exist(test.workpath('bar.out')) +test.must_not_exist(test.workpath('sub2/xxx.out')) + +test.unlink(['sub2', 'bar.out']) +test.unlink(['sub2b', 'bar.out']) +test.unlink('bar.out') + +test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath()) +test.run(arguments='-U') +test.must_exist(test.workpath('sub1', 'foo.out')) +test.must_not_exist(test.workpath('sub2', 'bar.out')) +test.must_not_exist(test.workpath('sub2b', 'bar.out')) +test.must_exist(test.workpath('sub3', 'baz.out')) +test.must_not_exist(test.workpath('bar.out')) +test.must_exist(test.workpath('sub2/xxx.out')) + +test.unlink(['sub1', 'foo.out']) +test.unlink(['sub3', 'baz.out']) +test.unlink(['sub2', 'xxx.out']) + +test.write('SConscript', """assert GetLaunchDir() == r'%s'"""%test.workpath('sub3')) +test.run(chdir = 'sub3', arguments='-U bar') +test.must_not_exist(test.workpath('sub1', 'foo.out')) +test.must_exist(test.workpath('sub2', 'bar.out')) +test.must_exist(test.workpath('sub2b', 'bar.out')) +test.must_not_exist(test.workpath('sub3', 'baz.out')) +test.must_not_exist(test.workpath('bar.out')) +test.must_not_exist(test.workpath('sub2/xxx.out')) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Climb/option-u.py b/test/Climb/option-u.py index f7c1e9110e..b7298d2801 100644 --- a/test/Climb/option-u.py +++ b/test/Climb/option-u.py @@ -1,148 +1,148 @@ -#!/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. - -""" -Test that the -u option only builds targets at or below -the current directory. -""" - -import os - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir('sub1', - 'sub2', ['sub2', 'dir'], - 'sub3', - 'sub4', ['sub4', 'dir']) - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: - for src in source: - with open(str(src), 'rb') as ifp: - ofp.write(ifp.read()) -env = Environment(tools=[]) -env.Append(BUILDERS = {'Cat' : Builder(action=cat)}) -env.Cat(target = 'sub1/f1a.out', source = 'sub1/f1a.in') -env.Cat(target = 'sub1/f1b.out', source = 'sub1/f1b.in') -Export('env') -SConscript('sub2/SConscript') -f3 = env.Cat(target = 'sub3/f3.out', source = 'sub3/f3.in') -env.Alias('my_alias', f3) -VariantDir('build', 'sub4') -SConscript('build/SConscript') -""") - -test.write(['sub2', 'SConscript'], """ -Import('env') -env.Cat(target = 'f2a.out', source = 'f2a.in') -env.Cat(target = 'dir/f2b.out', source = 'dir/f2b.in') -""") - -test.write(['sub4', 'SConscript'], """ -Import('env') -env.Cat(target = 'f4a.out', source = 'f4a.in') -f4b_in = File('dir/f4b.in') -f4b_in.exists() -f4b_in.is_derived() -env.Cat(target = 'dir/f4b.out', source = f4b_in) -""") - -test.write(['sub1', 'f1a.in'], "sub1/f1a.in") -test.write(['sub1', 'f1b.in'], "sub1/f1b.in") -test.write(['sub2', 'f2a.in'], "sub2/f2a.in") -test.write(['sub2', 'dir', 'f2b.in'], "sub2/dir/f2b.in") -test.write(['sub3', 'f3.in'], "sub3/f3.in") -test.write(['sub4', 'f4a.in'], "sub4/f4a.in") -test.write(['sub4', 'dir', 'f4b.in'], "sub4/dir/f4b.in") - -# Verify that we only build the specified local argument. -test.run(chdir = 'sub1', arguments = '-u f1a.out') - -test.must_match(['sub1', 'f1a.out'], "sub1/f1a.in") -test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) -test.must_not_exist(test.workpath('sub2', 'f2a.out')) -test.must_not_exist(test.workpath('sub2', 'dir', 'f2b.out')) -test.must_not_exist(test.workpath('sub3', 'f3.out')) -test.must_not_exist(test.workpath('sub4', 'f4a.out')) -test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) -test.must_not_exist(test.workpath('build', 'f4a.out')) -test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) - -# Verify that we build everything at or below our current directory. -test.run(chdir = 'sub2', arguments = '-u') - -test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) -test.must_match(['sub2', 'f2a.out'], "sub2/f2a.in") -test.must_match(['sub2', 'dir', 'f2b.out'], "sub2/dir/f2b.in") -test.must_not_exist(test.workpath('sub3', 'f3.out')) -test.must_not_exist(test.workpath('sub4', 'f4a.out')) -test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) -test.must_not_exist(test.workpath('build', 'f4a.out')) -test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) - -# Verify that we build a specified alias, regardless of where. -test.run(chdir = 'sub2', arguments = '-u my_alias') - -test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) -test.must_match(['sub3', 'f3.out'], "sub3/f3.in") -test.must_not_exist(test.workpath('sub4', 'f4a.out')) -test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) -test.must_not_exist(test.workpath('build', 'f4a.out')) -test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) - -# Verify that we build things in a linked VariantDir. -f4a_in = os.path.join('build', 'f4a.in') -f4a_out = os.path.join('build', 'f4a.out') -f4b_in = os.path.join('build', 'dir', 'f4b.in') -f4b_out = os.path.join('build', 'dir', 'f4b.out') -test.run(chdir = 'sub4', - arguments = '-u', - stdout = "scons: Entering directory `%s'\n" % test.workpath() + \ - test.wrap_stdout("""\ -scons: building associated VariantDir targets: build -cat(["%s"], ["%s"]) -cat(["%s"], ["%s"]) -scons: `sub4' is up to date. -""" % (f4b_out, f4b_in, f4a_out, f4a_in))) - -test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) -test.must_not_exist(test.workpath('sub4', 'f4a.out')) -test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) -test.must_match(['build', 'f4a.out'], "sub4/f4a.in") -test.must_match(['build', 'dir', 'f4b.out'], "sub4/dir/f4b.in") - - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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. + +""" +Test that the -u option only builds targets at or below +the current directory. +""" + +import os + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('sub1', + 'sub2', ['sub2', 'dir'], + 'sub3', + 'sub4', ['sub4', 'dir']) + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +def cat(env, source, target): + target = str(target[0]) + with open(target, 'wb') as ofp: + for src in source: + with open(str(src), 'rb') as ifp: + ofp.write(ifp.read()) +env = Environment(tools=[]) +env.Append(BUILDERS = {'Cat' : Builder(action=cat)}) +env.Cat(target = 'sub1/f1a.out', source = 'sub1/f1a.in') +env.Cat(target = 'sub1/f1b.out', source = 'sub1/f1b.in') +Export('env') +SConscript('sub2/SConscript') +f3 = env.Cat(target = 'sub3/f3.out', source = 'sub3/f3.in') +env.Alias('my_alias', f3) +VariantDir('build', 'sub4') +SConscript('build/SConscript') +""") + +test.write(['sub2', 'SConscript'], """ +Import('env') +env.Cat(target = 'f2a.out', source = 'f2a.in') +env.Cat(target = 'dir/f2b.out', source = 'dir/f2b.in') +""") + +test.write(['sub4', 'SConscript'], """ +Import('env') +env.Cat(target = 'f4a.out', source = 'f4a.in') +f4b_in = File('dir/f4b.in') +f4b_in.exists() +f4b_in.is_derived() +env.Cat(target = 'dir/f4b.out', source = f4b_in) +""") + +test.write(['sub1', 'f1a.in'], "sub1/f1a.in") +test.write(['sub1', 'f1b.in'], "sub1/f1b.in") +test.write(['sub2', 'f2a.in'], "sub2/f2a.in") +test.write(['sub2', 'dir', 'f2b.in'], "sub2/dir/f2b.in") +test.write(['sub3', 'f3.in'], "sub3/f3.in") +test.write(['sub4', 'f4a.in'], "sub4/f4a.in") +test.write(['sub4', 'dir', 'f4b.in'], "sub4/dir/f4b.in") + +# Verify that we only build the specified local argument. +test.run(chdir = 'sub1', arguments = '-u f1a.out') + +test.must_match(['sub1', 'f1a.out'], "sub1/f1a.in") +test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) +test.must_not_exist(test.workpath('sub2', 'f2a.out')) +test.must_not_exist(test.workpath('sub2', 'dir', 'f2b.out')) +test.must_not_exist(test.workpath('sub3', 'f3.out')) +test.must_not_exist(test.workpath('sub4', 'f4a.out')) +test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) +test.must_not_exist(test.workpath('build', 'f4a.out')) +test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) + +# Verify that we build everything at or below our current directory. +test.run(chdir = 'sub2', arguments = '-u') + +test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) +test.must_match(['sub2', 'f2a.out'], "sub2/f2a.in") +test.must_match(['sub2', 'dir', 'f2b.out'], "sub2/dir/f2b.in") +test.must_not_exist(test.workpath('sub3', 'f3.out')) +test.must_not_exist(test.workpath('sub4', 'f4a.out')) +test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) +test.must_not_exist(test.workpath('build', 'f4a.out')) +test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) + +# Verify that we build a specified alias, regardless of where. +test.run(chdir = 'sub2', arguments = '-u my_alias') + +test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) +test.must_match(['sub3', 'f3.out'], "sub3/f3.in") +test.must_not_exist(test.workpath('sub4', 'f4a.out')) +test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) +test.must_not_exist(test.workpath('build', 'f4a.out')) +test.must_not_exist(test.workpath('build', 'dir', 'f4b.out')) + +# Verify that we build things in a linked VariantDir. +f4a_in = os.path.join('build', 'f4a.in') +f4a_out = os.path.join('build', 'f4a.out') +f4b_in = os.path.join('build', 'dir', 'f4b.in') +f4b_out = os.path.join('build', 'dir', 'f4b.out') +test.run(chdir = 'sub4', + arguments = '-u', + stdout = "scons: Entering directory `%s'\n" % test.workpath() + \ + test.wrap_stdout("""\ +scons: building associated VariantDir targets: build +cat(["%s"], ["%s"]) +cat(["%s"], ["%s"]) +scons: `sub4' is up to date. +""" % (f4b_out, f4b_in, f4a_out, f4a_in))) + +test.must_not_exist(test.workpath('sub1', 'sub1/f1b.out')) +test.must_not_exist(test.workpath('sub4', 'f4a.out')) +test.must_not_exist(test.workpath('sub4', 'dir', 'f4b.out')) +test.must_match(['build', 'f4a.out'], "sub4/f4a.in") +test.must_match(['build', 'dir', 'f4b.out'], "sub4/dir/f4b.in") + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/Action-error.py b/test/Configure/Action-error.py index 69ba7f7045..5021e1cc49 100644 --- a/test/Configure/Action-error.py +++ b/test/Configure/Action-error.py @@ -1,55 +1,55 @@ -#!/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 that calling Configure from an Action results in a readable error. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -def ConfigureAction(target, source, env): - env.Configure() - return 0 -env = Environment(tools=[], - BUILDERS = {'MyAction' : - Builder(action=Action(ConfigureAction))}) -env.MyAction('target', []) -""") - -expect = "scons: *** [target] Calling Configure from Builders is not supported.\n" - -test.run(status=2, stderr=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that calling Configure from an Action results in a readable error. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def ConfigureAction(target, source, env): + env.Configure() + return 0 +env = Environment(tools=[], + BUILDERS = {'MyAction' : + Builder(action=Action(ConfigureAction))}) +env.MyAction('target', []) +""") + +expect = "scons: *** [target] Calling Configure from Builders is not supported.\n" + +test.run(status=2, stderr=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/Configure/Builder-call.py b/test/Configure/Builder-call.py index 3427fafde0..bbe3366f06 100644 --- a/test/Configure/Builder-call.py +++ b/test/Configure/Builder-call.py @@ -1,64 +1,64 @@ -#!/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 that calling normal Builders from an actual Configure -context environment works correctly. -""" - -import TestSCons - -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -test.write('mycommand.py', r""" -import sys -sys.stderr.write( 'Hello World on stderr\n' ) -sys.stdout.write( 'Hello World on stdout\n' ) -with open(sys.argv[1], 'w') as f: - f.write( 'Hello World\n' ) -""") - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -def CustomTest(*args): - return 0 -conf = env.Configure(custom_tests = {'MyTest' : CustomTest}) -if not conf.MyTest(): - env.Command("hello", [], r'%(_python_)s mycommand.py $TARGET') -env = conf.Finish() -""" % locals()) - -test.run(stderr="Hello World on stderr\n") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that calling normal Builders from an actual Configure +context environment works correctly. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('mycommand.py', r""" +import sys +sys.stderr.write( 'Hello World on stderr\n' ) +sys.stdout.write( 'Hello World on stdout\n' ) +with open(sys.argv[1], 'w') as f: + f.write( 'Hello World\n' ) +""") + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +def CustomTest(*args): + return 0 +conf = env.Configure(custom_tests = {'MyTest' : CustomTest}) +if not conf.MyTest(): + env.Command("hello", [], r'%(_python_)s mycommand.py $TARGET') +env = conf.Finish() +""" % locals()) + +test.run(stderr="Hello World on stderr\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/CONFIGUREDIR.py b/test/Configure/CONFIGUREDIR.py index 61d0b56e7e..4777b823fc 100644 --- a/test/Configure/CONFIGUREDIR.py +++ b/test/Configure/CONFIGUREDIR.py @@ -1,57 +1,57 @@ -#!/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 - -""" -Test that the configure context directory can be specified by -setting the $CONFIGUREDIR construction variable. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write("SConstruct", """\ -DefaultEnvironment(tools=[]) -def CustomTest(context): - context.Message('Executing Custom Test ... ') - context.Result(1) - -env = Environment(tools=[], CONFIGUREDIR = 'custom_config_dir') -conf = Configure(env, custom_tests = {'CustomTest' : CustomTest}) -conf.CustomTest(); -env = conf.Finish() -""") - -test.run() - -test.must_exist('custom_config_dir') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Test that the configure context directory can be specified by +setting the $CONFIGUREDIR construction variable. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write("SConstruct", """\ +DefaultEnvironment(tools=[]) +def CustomTest(context): + context.Message('Executing Custom Test ... ') + context.Result(1) + +env = Environment(tools=[], CONFIGUREDIR = 'custom_config_dir') +conf = Configure(env, custom_tests = {'CustomTest' : CustomTest}) +conf.CustomTest(); +env = conf.Finish() +""") + +test.run() + +test.must_exist('custom_config_dir') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/CONFIGURELOG.py b/test/Configure/CONFIGURELOG.py index e10a0aa2fa..7ced290473 100644 --- a/test/Configure/CONFIGURELOG.py +++ b/test/Configure/CONFIGURELOG.py @@ -1,68 +1,68 @@ -#!/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 - -""" -Test that the configure context log file name can be specified by -setting the $CONFIGURELOG construction variable. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -SConstruct_path = test.workpath('SConstruct') - -test.write(SConstruct_path, """\ -DefaultEnvironment(tools=[]) -def CustomTest(context): - context.Message('Executing Custom Test ...') - context.Result(1) - -env = Environment(tools=[], CONFIGURELOG = 'custom.logfile') -conf = Configure(env, custom_tests = {'CustomTest' : CustomTest}) -conf.CustomTest(); -env = conf.Finish() -""") - -test.run() - -expect = """\ -file %(SConstruct_path)s,line 7: -\tConfigure(confdir = .sconf_temp) -scons: Configure: Executing Custom Test ... -scons: Configure: (cached) yes - - -""" % locals() - -test.must_match('custom.logfile', expect, mode='r') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Test that the configure context log file name can be specified by +setting the $CONFIGURELOG construction variable. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +SConstruct_path = test.workpath('SConstruct') + +test.write(SConstruct_path, """\ +DefaultEnvironment(tools=[]) +def CustomTest(context): + context.Message('Executing Custom Test ...') + context.Result(1) + +env = Environment(tools=[], CONFIGURELOG = 'custom.logfile') +conf = Configure(env, custom_tests = {'CustomTest' : CustomTest}) +conf.CustomTest(); +env = conf.Finish() +""") + +test.run() + +expect = """\ +file %(SConstruct_path)s,line 7: +\tConfigure(confdir = .sconf_temp) +scons: Configure: Executing Custom Test ... +scons: Configure: (cached) yes + + +""" % locals() + +test.must_match('custom.logfile', expect, mode='r') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/SConscript.py b/test/Configure/SConscript.py index 4bca8c134d..8378f85560 100644 --- a/test/Configure/SConscript.py +++ b/test/Configure/SConscript.py @@ -1,80 +1,80 @@ -#!/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 that Configure contexts from multiple subsidiary SConscript -files work without error. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.subdir(['dir1'], - ['dir2'], - ['dir2', 'sub1'], - ['dir2', 'sub1', 'sub2']) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment(tools=[]) -SConscript(dirs=['dir1', 'dir2'], exports="env") -""") - -test.write(['dir1', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -""") - -test.write(['dir2', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -SConscript(dirs=['sub1'], exports="env") -""") - -test.write(['dir2', 'sub1', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -SConscript(dirs=['sub2'], exports="env") -""") - -test.write(['dir2', 'sub1', 'sub2', 'SConscript'], """ -Import("env") -conf = env.Configure() -conf.Finish() -""") - -test.run() - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that Configure contexts from multiple subsidiary SConscript +files work without error. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir(['dir1'], + ['dir2'], + ['dir2', 'sub1'], + ['dir2', 'sub1', 'sub2']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(tools=[]) +SConscript(dirs=['dir1', 'dir2'], exports="env") +""") + +test.write(['dir1', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +""") + +test.write(['dir2', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +SConscript(dirs=['sub1'], exports="env") +""") + +test.write(['dir2', 'sub1', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +SConscript(dirs=['sub2'], exports="env") +""") + +test.write(['dir2', 'sub1', 'sub2', 'SConscript'], """ +Import("env") +conf = env.Configure() +conf.Finish() +""") + +test.run() + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/Streamer1.py b/test/Configure/Streamer1.py index 2b29c40adc..c2312be91b 100644 --- a/test/Configure/Streamer1.py +++ b/test/Configure/Streamer1.py @@ -1,85 +1,85 @@ -#!/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 - -""" -Test for BitBucket PR 126: - -SConf doesn't work well with 'io' module on pre-3.0 Python. This is because -io.StringIO (used by SCons.SConf.Streamer) accepts only unicode strings. -Non-unicode input causes it to raise an exception. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -# SConstruct -# -# The CheckHello should return 'yes' if everything works fine. Otherwise it -# returns 'failed'. -# -def hello(target, source, env): - import traceback - try: - print('hello!\\n') # this breaks the script - with open(env.subst('$TARGET', target = target),'w') as f: - f.write('yes') - except: - # write to file, as stdout/stderr is broken - traceback.print_exc(file=open('traceback','w')) - return 0 - -def CheckHello(context): - import sys - context.Display('Checking whether hello works... ') - stat,out = context.TryAction(hello,'','.in') - if stat and out: - context.Result(out) - else: - context.Result('failed') - return out - -env = Environment(tools=[]) -cfg = Configure(env) - -cfg.AddTest('CheckHello', CheckHello) -cfg.CheckHello() - -env = cfg.Finish() -""") - -test.run(arguments = '.') -test.must_contain_all_lines(test.stdout(), ['Checking whether hello works... yes']) -test.must_not_exist('traceback') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Test for BitBucket PR 126: + +SConf doesn't work well with 'io' module on pre-3.0 Python. This is because +io.StringIO (used by SCons.SConf.Streamer) accepts only unicode strings. +Non-unicode input causes it to raise an exception. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +# SConstruct +# +# The CheckHello should return 'yes' if everything works fine. Otherwise it +# returns 'failed'. +# +def hello(target, source, env): + import traceback + try: + print('hello!\\n') # this breaks the script + with open(env.subst('$TARGET', target = target),'w') as f: + f.write('yes') + except: + # write to file, as stdout/stderr is broken + traceback.print_exc(file=open('traceback','w')) + return 0 + +def CheckHello(context): + import sys + context.Display('Checking whether hello works... ') + stat,out = context.TryAction(hello,'','.in') + if stat and out: + context.Result(out) + else: + context.Result('failed') + return out + +env = Environment(tools=[]) +cfg = Configure(env) + +cfg.AddTest('CheckHello', CheckHello) +cfg.CheckHello() + +env = cfg.Finish() +""") + +test.run(arguments = '.') +test.must_contain_all_lines(test.stdout(), ['Checking whether hello works... yes']) +test.must_not_exist('traceback') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/VariantDir.py b/test/Configure/VariantDir.py index 0296d1c7f1..a883d48d32 100644 --- a/test/Configure/VariantDir.py +++ b/test/Configure/VariantDir.py @@ -1,99 +1,99 @@ -#!/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 that Configure contexts work with basic use of VariantDir. -""" - -import os - -import TestSCons - -_obj = TestSCons._obj - -test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) - -env = Environment(LOGFILE='build/config.log') -import os -env.AppendENVPath('PATH', os.environ['PATH']) -VariantDir('build', '.') -conf = env.Configure(conf_dir='build/config.tests', log_file='$LOGFILE') -r1 = conf.CheckCHeader('math.h') -r2 = conf.CheckCHeader('no_std_c_header.h') # leads to compile error -env = conf.Finish() -Export('env') -# with open('build/config.log') as f: -# print f.readlines() -SConscript('build/SConscript') -""") - -test.write('SConscript', """\ -Import('env') -env.Program('TestProgram', 'TestProgram.c') -""") - -test.write('TestProgram.c', """\ -#include - -int main(void) { - printf("Hello\\n"); -} -""") - -test.run() -test.checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))]], - os.path.join("build", "config.log"), - os.path.join("build", "config.tests"), - "SConstruct") - -test.run() -test.checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - os.path.join("build", "config.log"), - os.path.join("build", "config.tests"), - "SConstruct") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that Configure contexts work with basic use of VariantDir. +""" + +import os + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) + +env = Environment(LOGFILE='build/config.log') +import os +env.AppendENVPath('PATH', os.environ['PATH']) +VariantDir('build', '.') +conf = env.Configure(conf_dir='build/config.tests', log_file='$LOGFILE') +r1 = conf.CheckCHeader('math.h') +r2 = conf.CheckCHeader('no_std_c_header.h') # leads to compile error +env = conf.Finish() +Export('env') +# with open('build/config.log') as f: +# print f.readlines() +SConscript('build/SConscript') +""") + +test.write('SConscript', """\ +Import('env') +env.Program('TestProgram', 'TestProgram.c') +""") + +test.write('TestProgram.c', """\ +#include + +int main(void) { + printf("Hello\\n"); +} +""") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))]], + os.path.join("build", "config.log"), + os.path.join("build", "config.tests"), + "SConstruct") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + os.path.join("build", "config.log"), + os.path.join("build", "config.tests"), + "SConstruct") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/basic.py b/test/Configure/basic.py index d2f7111d8e..9d555659f5 100644 --- a/test/Configure/basic.py +++ b/test/Configure/basic.py @@ -1,91 +1,91 @@ -#!/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 that basic builds work with Configure contexts. -""" - -from TestSCons import TestSCons, ConfigCheckInfo, _obj -from TestCmd import IS_WINDOWS - - -test = TestSCons(match = TestSCons.match_re_dotall) - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -# Throw in a bad variable name intentionally used by Ubuntu packaging. -env['ENV']['HASH(0x12345678)'] = 'Bad variable name!' -conf = Configure(env) -r1 = conf.CheckCHeader( 'math.h' ) -r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -env = conf.Finish() -Export( 'env' ) -SConscript( 'SConscript' ) -""") - -test.write('SConscript', """\ -Import( 'env' ) -env.Program( 'TestProgram', 'TestProgram.c' ) -""") - -test.write('TestProgram.c', """\ -#include - -int main(void) { - printf( "Hello\\n" ); -} -""") - -test.run() -test.checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", NCR), (_obj, NCR))], - [((".c", NCR), (_obj, NCF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.run() -test.checkLogAndStdout(["Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... "], - ["yes", "no"], - [[((".c", CR), (_obj, CR))], - [((".c", CR), (_obj, CF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that basic builds work with Configure contexts. +""" + +from TestSCons import TestSCons, ConfigCheckInfo, _obj +from TestCmd import IS_WINDOWS + + +test = TestSCons(match = TestSCons.match_re_dotall) + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +# Throw in a bad variable name intentionally used by Ubuntu packaging. +env['ENV']['HASH(0x12345678)'] = 'Bad variable name!' +conf = Configure(env) +r1 = conf.CheckCHeader( 'math.h' ) +r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +env = conf.Finish() +Export( 'env' ) +SConscript( 'SConscript' ) +""") + +test.write('SConscript', """\ +Import( 'env' ) +env.Program( 'TestProgram', 'TestProgram.c' ) +""") + +test.write('TestProgram.c', """\ +#include + +int main(void) { + printf( "Hello\\n" ); +} +""") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", NCR), (_obj, NCR))], + [((".c", NCR), (_obj, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run() +test.checkLogAndStdout(["Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... "], + ["yes", "no"], + [[((".c", CR), (_obj, CR))], + [((".c", CR), (_obj, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/build-fail.py b/test/Configure/build-fail.py index b835cb7418..21d4b5e079 100644 --- a/test/Configure/build-fail.py +++ b/test/Configure/build-fail.py @@ -1,97 +1,97 @@ -#!/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 that Configure tests work even after an earlier test fails. - -This was broken in 0.98.3 because we'd mark the /usr/bin/g++ compiler -as having failed (because it was on the candidates list as the implicit -command dependency for both the object file and executable generated -for the configuration test) and then avoid trying to rebuild anything -else that used the "failed" Node. - -Thanks to Ben Webb for the test case. -""" - -import os -import re - -import TestSCons - -_obj = TestSCons._obj - -test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) - -test.subdir('a', 'b') - -a_boost_hpp = os.path.join('..', 'a', 'boost.hpp') -b_boost_hpp = os.path.join('..', 'b', 'boost.hpp') - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -import os -def _check(context): - for dir in ['a', 'b']: - inc = os.path.join('..', dir, 'boost.hpp') - result = context.TryRun(''' - #include "%s" - - int main(void) { return 0; } - ''' % inc, '.cpp')[0] - if result: - import sys - sys.stdout.write('%s: ' % inc) - break - context.Result(result) - return result -env = Environment() -conf = env.Configure(custom_tests={'CheckBoost':_check}) -conf.CheckBoost() -conf.Finish() -""") - -test.write(['b', 'boost.hpp'], """#define FILE "b/boost.hpp"\n""") - -expect = test.wrap_stdout(read_str = "%s: yes\n" % re.escape(b_boost_hpp), - build_str = "scons: `.' is up to date.\n") - -test.run(arguments='--config=force', stdout=expect) - -expect = test.wrap_stdout(read_str = "%s: yes\n" % re.escape(a_boost_hpp), - build_str = "scons: `.' is up to date.\n") - -test.write(['a', 'boost.hpp'], """#define FILE "a/boost.hpp"\n""") - -test.run(arguments='--config=force', stdout=expect) - -test.run() - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that Configure tests work even after an earlier test fails. + +This was broken in 0.98.3 because we'd mark the /usr/bin/g++ compiler +as having failed (because it was on the candidates list as the implicit +command dependency for both the object file and executable generated +for the configuration test) and then avoid trying to rebuild anything +else that used the "failed" Node. + +Thanks to Ben Webb for the test case. +""" + +import os +import re + +import TestSCons + +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +test.subdir('a', 'b') + +a_boost_hpp = os.path.join('..', 'a', 'boost.hpp') +b_boost_hpp = os.path.join('..', 'b', 'boost.hpp') + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +import os +def _check(context): + for dir in ['a', 'b']: + inc = os.path.join('..', dir, 'boost.hpp') + result = context.TryRun(''' + #include "%s" + + int main(void) { return 0; } + ''' % inc, '.cpp')[0] + if result: + import sys + sys.stdout.write('%s: ' % inc) + break + context.Result(result) + return result +env = Environment() +conf = env.Configure(custom_tests={'CheckBoost':_check}) +conf.CheckBoost() +conf.Finish() +""") + +test.write(['b', 'boost.hpp'], """#define FILE "b/boost.hpp"\n""") + +expect = test.wrap_stdout(read_str = "%s: yes\n" % re.escape(b_boost_hpp), + build_str = "scons: `.' is up to date.\n") + +test.run(arguments='--config=force', stdout=expect) + +expect = test.wrap_stdout(read_str = "%s: yes\n" % re.escape(a_boost_hpp), + build_str = "scons: `.' is up to date.\n") + +test.write(['a', 'boost.hpp'], """#define FILE "a/boost.hpp"\n""") + +test.run(arguments='--config=force', stdout=expect) + +test.run() + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/cache-not-ok.py b/test/Configure/cache-not-ok.py index 0944996441..04a348658d 100644 --- a/test/Configure/cache-not-ok.py +++ b/test/Configure/cache-not-ok.py @@ -1,102 +1,102 @@ -#!/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 that the cache mechanism works when checks are not ok. -""" - -import TestSCons - -_exe = TestSCons._exe -_obj = TestSCons._obj - -test = TestSCons.TestSCons() - -lib = test.Configure_lib - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -if not int(ARGUMENTS.get('target_signatures_content', 0)): - Decider('timestamp-newer') -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = env.Configure() -r1 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -r2 = conf.CheckLib( 'no_c_library_SAFFDG' ) # leads to link error -env = conf.Finish() -if not (not r1 and not r2): - print("FAIL: ", r1, r2) - Exit(1) -""") - -# Verify correct behavior when we call Decider('timestamp-newer'). - -test.run() -test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", NCR), (_obj, NCF))], - [((".c", NCR), (_obj, NCR), (_exe, NCF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.run() -test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", CR), (_obj, NCF))], - [((".c", CR), (_obj, CR), (_exe, NCF))]], - "config.log", ".sconf_temp", "SConstruct") - -# Same should be true for the default behavior of Decider('content'). - -test.run(arguments='target_signatures_content=1 --config=force') -test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", NCR), (_obj, NCF))], - [((".c", NCR), (_obj, NCR), (_exe, NCF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.run(arguments='target_signatures_content=1') -test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", - "Checking for C library no_c_library_SAFFDG... "], - ["no"]*2, - [[((".c", CR), (_obj, CF))], - [((".c", CR), (_obj, CR), (_exe, CF))]], - "config.log", ".sconf_temp", "SConstruct") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the cache mechanism works when checks are not ok. +""" + +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj + +test = TestSCons.TestSCons() + +lib = test.Configure_lib + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +if not int(ARGUMENTS.get('target_signatures_content', 0)): + Decider('timestamp-newer') +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = env.Configure() +r1 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +r2 = conf.CheckLib( 'no_c_library_SAFFDG' ) # leads to link error +env = conf.Finish() +if not (not r1 and not r2): + print("FAIL: ", r1, r2) + Exit(1) +""") + +# Verify correct behavior when we call Decider('timestamp-newer'). + +test.run() +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", NCR), (_obj, NCF))], + [((".c", NCR), (_obj, NCR), (_exe, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run() +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", CR), (_obj, NCF))], + [((".c", CR), (_obj, CR), (_exe, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +# Same should be true for the default behavior of Decider('content'). + +test.run(arguments='target_signatures_content=1 --config=force') +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", NCR), (_obj, NCF))], + [((".c", NCR), (_obj, NCR), (_exe, NCF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='target_signatures_content=1') +test.checkLogAndStdout(["Checking for C header file no_std_c_header.h... ", + "Checking for C library no_c_library_SAFFDG... "], + ["no"]*2, + [[((".c", CR), (_obj, CF))], + [((".c", CR), (_obj, CR), (_exe, CF))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/cache-ok.py b/test/Configure/cache-ok.py index dc2d0c32b9..a2ef8fa216 100644 --- a/test/Configure/cache-ok.py +++ b/test/Configure/cache-ok.py @@ -1,126 +1,126 @@ -#!/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 that the cache mechanism works when checks are ok. -""" - -import TestSCons - -_exe = TestSCons._exe -_obj = TestSCons._obj - -test = TestSCons.TestSCons(match = TestSCons.match_re) - -lib = test.Configure_lib - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -if not int(ARGUMENTS.get('target_signatures_content', 0)): - Decider('timestamp-newer') -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env) -r1 = conf.CheckLibWithHeader( '%(lib)s', 'math.h', 'c' ) -r2 = conf.CheckLibWithHeader( None, 'math.h', 'c' ) -r3 = conf.CheckLib( '%(lib)s', autoadd=0 ) -r4 = conf.CheckLib( None, autoadd=0 ) -r5 = conf.CheckCHeader( 'math.h' ) -r6 = conf.CheckCXXHeader( 'vector' ) -env = conf.Finish() -if not (r1 and r2 and r3 and r4 and r5 and r6): - Exit(1) -""" % locals()) - -# Verify correct behavior when we call Decider('timestamp-newer') - -test.run() -test.checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + - [[((".c", NCR), (_obj, NCR))]] + - [[((".cpp", NCR), (_obj, NCR))]], - "config.log", ".sconf_temp", "SConstruct") - - -test.run() -test.checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + - [[((".c", CR), (_obj, CR))]] + - [[((".cpp", CR), (_obj, CR))]], - "config.log", ".sconf_temp", "SConstruct") - -# same should be true for the default behavior of Decider('content') - -test.run(arguments='target_signatures_content=1 --config=force') -test.checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + - [[((".c", NCR), (_obj, NCR))]] + - [[((".cpp", NCR), (_obj, NCR))]], - "config.log", ".sconf_temp", "SConstruct") - -test.run(arguments='target_signatures_content=1') -test.checkLogAndStdout(["Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C library %s... " % lib, - "Checking for C library None... ", - "Checking for C header file math.h... ", - "Checking for C++ header file vector... "], - ["yes"]*6, - [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + - [[((".c", CR), (_obj, CR))]] + - [[((".cpp", CR), (_obj, CR))]], - "config.log", ".sconf_temp", "SConstruct") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the cache mechanism works when checks are ok. +""" + +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj + +test = TestSCons.TestSCons(match = TestSCons.match_re) + +lib = test.Configure_lib + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +if not int(ARGUMENTS.get('target_signatures_content', 0)): + Decider('timestamp-newer') +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env) +r1 = conf.CheckLibWithHeader( '%(lib)s', 'math.h', 'c' ) +r2 = conf.CheckLibWithHeader( None, 'math.h', 'c' ) +r3 = conf.CheckLib( '%(lib)s', autoadd=0 ) +r4 = conf.CheckLib( None, autoadd=0 ) +r5 = conf.CheckCHeader( 'math.h' ) +r6 = conf.CheckCXXHeader( 'vector' ) +env = conf.Finish() +if not (r1 and r2 and r3 and r4 and r5 and r6): + Exit(1) +""" % locals()) + +# Verify correct behavior when we call Decider('timestamp-newer') + +test.run() +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + + [[((".c", NCR), (_obj, NCR))]] + + [[((".cpp", NCR), (_obj, NCR))]], + "config.log", ".sconf_temp", "SConstruct") + + +test.run() +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + + [[((".c", CR), (_obj, CR))]] + + [[((".cpp", CR), (_obj, CR))]], + "config.log", ".sconf_temp", "SConstruct") + +# same should be true for the default behavior of Decider('content') + +test.run(arguments='target_signatures_content=1 --config=force') +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", NCR), (_obj, NCR), (_exe, NCR))]]*4 + + [[((".c", NCR), (_obj, NCR))]] + + [[((".cpp", NCR), (_obj, NCR))]], + "config.log", ".sconf_temp", "SConstruct") + +test.run(arguments='target_signatures_content=1') +test.checkLogAndStdout(["Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C library %s... " % lib, + "Checking for C library None... ", + "Checking for C header file math.h... ", + "Checking for C++ header file vector... "], + ["yes"]*6, + [[((".c", CR), (_obj, CR), (_exe, CR))]]*4 + + [[((".c", CR), (_obj, CR))]] + + [[((".cpp", CR), (_obj, CR))]], + "config.log", ".sconf_temp", "SConstruct") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/clean.py b/test/Configure/clean.py index 3370cd251c..19e4879a2a 100644 --- a/test/Configure/clean.py +++ b/test/Configure/clean.py @@ -1,84 +1,84 @@ -#!/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 that we don't perform Configure context actions when the --c or --clean options have been specified. -""" - -import TestSCons - -test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env, clean=int(ARGUMENTS['clean'])) -r1 = conf.CheckCHeader( 'math.h' ) -r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -env = conf.Finish() -Export( 'env' ) -SConscript( 'SConscript' ) -""") - -test.write('SConscript', """\ -Import( 'env' ) -env.Program( 'TestProgram', 'TestProgram.c' ) -""") - -test.write('TestProgram.c', """\ -#include - -int main(void) { - printf( "Hello\\n" ); -} -""") - -lines = [ - "Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... " -] - -test.run(arguments = '-c clean=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '-c clean=1') -test.must_contain_all_lines(test.stdout(), lines) - -test.run(arguments = '--clean clean=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '--clean clean=1') -test.must_contain_all_lines(test.stdout(), lines) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that we don't perform Configure context actions when the +-c or --clean options have been specified. +""" + +import TestSCons + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env, clean=int(ARGUMENTS['clean'])) +r1 = conf.CheckCHeader( 'math.h' ) +r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +env = conf.Finish() +Export( 'env' ) +SConscript( 'SConscript' ) +""") + +test.write('SConscript', """\ +Import( 'env' ) +env.Program( 'TestProgram', 'TestProgram.c' ) +""") + +test.write('TestProgram.c', """\ +#include + +int main(void) { + printf( "Hello\\n" ); +} +""") + +lines = [ + "Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... " +] + +test.run(arguments = '-c clean=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '-c clean=1') +test.must_contain_all_lines(test.stdout(), lines) + +test.run(arguments = '--clean clean=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '--clean clean=1') +test.must_contain_all_lines(test.stdout(), lines) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/custom-tests.py b/test/Configure/custom-tests.py index a4c29c78e1..3f5bcb6db3 100644 --- a/test/Configure/custom-tests.py +++ b/test/Configure/custom-tests.py @@ -1,202 +1,202 @@ -#!/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 execution of custom test cases. -""" - - -import TestSCons - -_exe = TestSCons._exe -_obj = TestSCons._obj -_python_ = TestSCons._python_ - -test = TestSCons.TestSCons() - -NCR = test.NCR # non-cached rebuild -CR = test.CR # cached rebuild (up to date) -NCF = test.NCF # non-cached build failure -CF = test.CF # cached build failure - -compileOK = '#include \\nint main(void) {printf("Hello");return 0;}' -compileFAIL = "syntax error" -linkOK = compileOK -linkFAIL = "void myFunc(); int main(void) { myFunc(); }" -runOK = compileOK -runFAIL = "int main(void) { return 1; }" - -test.write('pyAct.py', """\ -import sys -print(sys.argv[1]) -sys.exit(int(sys.argv[1])) -""") - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -def CheckCustom(test): - test.Message( 'Executing MyTest ... ' ) - retCompileOK = test.TryCompile( '%(compileOK)s', '.c' ) - retCompileFAIL = test.TryCompile( '%(compileFAIL)s', '.c' ) - retLinkOK = test.TryLink( '%(linkOK)s', '.c' ) - retLinkFAIL = test.TryLink( '%(linkFAIL)s', '.c' ) - (retRunOK, outputRunOK) = test.TryRun( '%(runOK)s', '.c' ) - (retRunFAIL, outputRunFAIL) = test.TryRun( '%(runFAIL)s', '.c' ) - (retActOK, outputActOK) = test.TryAction( r'%(_python_)s pyAct.py 0 > $TARGET' ) - (retActFAIL, outputActFAIL) = test.TryAction( r'%(_python_)s pyAct.py 1 > $TARGET' ) - resOK = retCompileOK and retLinkOK and retRunOK and outputRunOK=="Hello" - resOK = resOK and retActOK and int(outputActOK)==0 - resFAIL = retCompileFAIL or retLinkFAIL or retRunFAIL or outputRunFAIL!="" - resFAIL = resFAIL or retActFAIL or outputActFAIL!="" - test.Result( resOK and not resFAIL ) - return resOK and not resFAIL - -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure( env, custom_tests={'CheckCustom' : CheckCustom} ) -conf.CheckCustom() -env = conf.Finish() -""" % locals()) - -test.run() - -test.checkLogAndStdout(["Executing MyTest ... "], - ["yes"], - [[(('.c', NCR), (_obj, NCR)), - (('.c', NCR), (_obj, NCF)), - (('.c', NCR), (_obj, NCR), (_exe, NCR)), - (('.c', NCR), (_obj, NCR), (_exe, NCF)), - (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCR)), - (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCF)), - (('', NCR),), - (('', NCF),)]], - "config.log", ".sconf_temp", "SConstruct") - -test.run() - -# Try again to check caching -test.checkLogAndStdout(["Executing MyTest ... "], - ["yes"], - [[(('.c', CR), (_obj, CR)), - (('.c', CR), (_obj, CF)), - (('.c', CR), (_obj, CR), (_exe, CR)), - (('.c', CR), (_obj, CR), (_exe, CF)), - (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CR)), - (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CF)), - (('', CR),), - (('', CF),)]], - "config.log", ".sconf_temp", "SConstruct") - -# Test other customs: -test.write('SConstruct', """\ -def CheckList(test): - test.Message( 'Display of list ...' ) - res = [1, 2, 3, 4] - test.Result( res ) - return res - -def CheckEmptyList(test): - test.Message( 'Display of empty list ...' ) - res = list() - test.Result( res ) - return res - -def CheckRandomStr(test): - test.Message( 'Display of random string ...' ) - res = "a random string" - test.Result( res ) - return res - -def CheckEmptyStr(test): - test.Message( 'Display of empty string ...' ) - res = "" - test.Result( res ) - return res - -def CheckDict(test): - test.Message( 'Display of dictionary ...' ) - res = {"key1" : 1, "key2" : "text"} - test.Result( res ) - return res - -def CheckEmptyDict(test): - test.Message( 'Display of empty dictionary ...' ) - res = dict - test.Result( res ) - return res - -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure( env, custom_tests={'CheckList' : CheckList, - 'CheckEmptyList' : CheckEmptyList, - 'CheckRandomStr' : CheckRandomStr, - 'CheckEmptyStr' : CheckEmptyStr, - 'CheckDict' : CheckDict, - 'CheckEmptyDict' : CheckEmptyDict} ) -conf.CheckList() -conf.CheckEmptyList() -conf.CheckRandomStr() -conf.CheckEmptyStr() -conf.CheckDict() -conf.CheckEmptyDict() -env = conf.Finish() -""" % locals()) - -test.run() - -test.must_match('config.log', -r""".* -.* -scons: Configure: Display of list ... -scons: Configure: \(cached\) yes - -scons: Configure: Display of empty list ... -scons: Configure: \(cached\) no - -scons: Configure: Display of random string ... -scons: Configure: \(cached\) a random string - -scons: Configure: Display of empty string ... -scons: Configure: \(cached\) * - -scons: Configure: Display of dictionary ... -scons: Configure: \(cached\) yes - -scons: Configure: Display of empty dictionary ... -scons: Configure: \(cached\) yes - - -""", -match=TestSCons.match_re) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 execution of custom test cases. +""" + + +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +NCR = test.NCR # non-cached rebuild +CR = test.CR # cached rebuild (up to date) +NCF = test.NCF # non-cached build failure +CF = test.CF # cached build failure + +compileOK = '#include \\nint main(void) {printf("Hello");return 0;}' +compileFAIL = "syntax error" +linkOK = compileOK +linkFAIL = "void myFunc(); int main(void) { myFunc(); }" +runOK = compileOK +runFAIL = "int main(void) { return 1; }" + +test.write('pyAct.py', """\ +import sys +print(sys.argv[1]) +sys.exit(int(sys.argv[1])) +""") + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +def CheckCustom(test): + test.Message( 'Executing MyTest ... ' ) + retCompileOK = test.TryCompile( '%(compileOK)s', '.c' ) + retCompileFAIL = test.TryCompile( '%(compileFAIL)s', '.c' ) + retLinkOK = test.TryLink( '%(linkOK)s', '.c' ) + retLinkFAIL = test.TryLink( '%(linkFAIL)s', '.c' ) + (retRunOK, outputRunOK) = test.TryRun( '%(runOK)s', '.c' ) + (retRunFAIL, outputRunFAIL) = test.TryRun( '%(runFAIL)s', '.c' ) + (retActOK, outputActOK) = test.TryAction( r'%(_python_)s pyAct.py 0 > $TARGET' ) + (retActFAIL, outputActFAIL) = test.TryAction( r'%(_python_)s pyAct.py 1 > $TARGET' ) + resOK = retCompileOK and retLinkOK and retRunOK and outputRunOK=="Hello" + resOK = resOK and retActOK and int(outputActOK)==0 + resFAIL = retCompileFAIL or retLinkFAIL or retRunFAIL or outputRunFAIL!="" + resFAIL = resFAIL or retActFAIL or outputActFAIL!="" + test.Result( resOK and not resFAIL ) + return resOK and not resFAIL + +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure( env, custom_tests={'CheckCustom' : CheckCustom} ) +conf.CheckCustom() +env = conf.Finish() +""" % locals()) + +test.run() + +test.checkLogAndStdout(["Executing MyTest ... "], + ["yes"], + [[(('.c', NCR), (_obj, NCR)), + (('.c', NCR), (_obj, NCF)), + (('.c', NCR), (_obj, NCR), (_exe, NCR)), + (('.c', NCR), (_obj, NCR), (_exe, NCF)), + (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCR)), + (('.c', NCR), (_obj, NCR), (_exe, NCR), (_exe + '.out', NCF)), + (('', NCR),), + (('', NCF),)]], + "config.log", ".sconf_temp", "SConstruct") + +test.run() + +# Try again to check caching +test.checkLogAndStdout(["Executing MyTest ... "], + ["yes"], + [[(('.c', CR), (_obj, CR)), + (('.c', CR), (_obj, CF)), + (('.c', CR), (_obj, CR), (_exe, CR)), + (('.c', CR), (_obj, CR), (_exe, CF)), + (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CR)), + (('.c', CR), (_obj, CR), (_exe, CR), (_exe + '.out', CF)), + (('', CR),), + (('', CF),)]], + "config.log", ".sconf_temp", "SConstruct") + +# Test other customs: +test.write('SConstruct', """\ +def CheckList(test): + test.Message( 'Display of list ...' ) + res = [1, 2, 3, 4] + test.Result( res ) + return res + +def CheckEmptyList(test): + test.Message( 'Display of empty list ...' ) + res = list() + test.Result( res ) + return res + +def CheckRandomStr(test): + test.Message( 'Display of random string ...' ) + res = "a random string" + test.Result( res ) + return res + +def CheckEmptyStr(test): + test.Message( 'Display of empty string ...' ) + res = "" + test.Result( res ) + return res + +def CheckDict(test): + test.Message( 'Display of dictionary ...' ) + res = {"key1" : 1, "key2" : "text"} + test.Result( res ) + return res + +def CheckEmptyDict(test): + test.Message( 'Display of empty dictionary ...' ) + res = dict + test.Result( res ) + return res + +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure( env, custom_tests={'CheckList' : CheckList, + 'CheckEmptyList' : CheckEmptyList, + 'CheckRandomStr' : CheckRandomStr, + 'CheckEmptyStr' : CheckEmptyStr, + 'CheckDict' : CheckDict, + 'CheckEmptyDict' : CheckEmptyDict} ) +conf.CheckList() +conf.CheckEmptyList() +conf.CheckRandomStr() +conf.CheckEmptyStr() +conf.CheckDict() +conf.CheckEmptyDict() +env = conf.Finish() +""" % locals()) + +test.run() + +test.must_match('config.log', +r""".* +.* +scons: Configure: Display of list ... +scons: Configure: \(cached\) yes + +scons: Configure: Display of empty list ... +scons: Configure: \(cached\) no + +scons: Configure: Display of random string ... +scons: Configure: \(cached\) a random string + +scons: Configure: Display of empty string ... +scons: Configure: \(cached\) * + +scons: Configure: Display of dictionary ... +scons: Configure: \(cached\) yes + +scons: Configure: Display of empty dictionary ... +scons: Configure: \(cached\) yes + + +""", +match=TestSCons.match_re) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/from-SConscripts.py b/test/Configure/from-SConscripts.py index 5d16592be4..17d975b0e7 100644 --- a/test/Configure/from-SConscripts.py +++ b/test/Configure/from-SConscripts.py @@ -1,63 +1,63 @@ -#!/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 - -""" -Make sure we can call Configure() from subsidiary SConscript calls. - -This was broken at one point when we were using the internal -sconscript_reading flag (which is basically a hint for whether or not -we're in a Builder call) as a semaphore, not a counter. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = SConscript('x.scons') -""") - -test.write('x.scons', """\ -env = SConscript('y.scons') -config = env.Configure() -env = config.Finish() -Return('env') -""") - -test.write('y.scons', """\ -env = Environment(tools=[]) -Return('env') -""") - -test.run(arguments = '.') - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 + +""" +Make sure we can call Configure() from subsidiary SConscript calls. + +This was broken at one point when we were using the internal +sconscript_reading flag (which is basically a hint for whether or not +we're in a Builder call) as a semaphore, not a counter. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = SConscript('x.scons') +""") + +test.write('x.scons', """\ +env = SConscript('y.scons') +config = env.Configure() +env = config.Finish() +Return('env') +""") + +test.write('y.scons', """\ +env = Environment(tools=[]) +Return('env') +""") + +test.run(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/help.py b/test/Configure/help.py index abe446d13c..b7184eea93 100644 --- a/test/Configure/help.py +++ b/test/Configure/help.py @@ -1,94 +1,94 @@ -#!/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 that we don't perform Configure context actions when the --H, -h or --help options have been specified. -""" - -import TestSCons - -test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env, help=int(ARGUMENTS['help'])) -r1 = conf.CheckCHeader( 'math.h' ) -r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error -env = conf.Finish() -Export( 'env' ) -SConscript( 'SConscript' ) -""") - -test.write('SConscript', """\ -Import( 'env' ) -env.Program( 'TestProgram', 'TestProgram.c' ) -""") - -test.write('TestProgram.c', """\ -#include - -int main(void) { - printf( "Hello\\n" ); -} -""") - -lines = [ - "Checking for C header file math.h... ", - "Checking for C header file no_std_c_header.h... " -] - -# The help setting should have no effect on -H, so the -H output -# should never contain the lines. -test.run(arguments = '-H help=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '-H help=1') -test.must_not_contain_any_line(test.stdout(), lines) - -# For -h and --help, the lines appear or not depending on how Configure() -# is initialized. -test.run(arguments = '-h help=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '-h help=1') -test.must_contain_all_lines(test.stdout(), lines) - -test.run(arguments = '--help help=0') -test.must_not_contain_any_line(test.stdout(), lines) - -test.run(arguments = '--help help=1') -test.must_contain_all_lines(test.stdout(), lines) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that we don't perform Configure context actions when the +-H, -h or --help options have been specified. +""" + +import TestSCons + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env, help=int(ARGUMENTS['help'])) +r1 = conf.CheckCHeader( 'math.h' ) +r2 = conf.CheckCHeader( 'no_std_c_header.h' ) # leads to compile error +env = conf.Finish() +Export( 'env' ) +SConscript( 'SConscript' ) +""") + +test.write('SConscript', """\ +Import( 'env' ) +env.Program( 'TestProgram', 'TestProgram.c' ) +""") + +test.write('TestProgram.c', """\ +#include + +int main(void) { + printf( "Hello\\n" ); +} +""") + +lines = [ + "Checking for C header file math.h... ", + "Checking for C header file no_std_c_header.h... " +] + +# The help setting should have no effect on -H, so the -H output +# should never contain the lines. +test.run(arguments = '-H help=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '-H help=1') +test.must_not_contain_any_line(test.stdout(), lines) + +# For -h and --help, the lines appear or not depending on how Configure() +# is initialized. +test.run(arguments = '-h help=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '-h help=1') +test.must_contain_all_lines(test.stdout(), lines) + +test.run(arguments = '--help help=0') +test.must_not_contain_any_line(test.stdout(), lines) + +test.run(arguments = '--help help=1') +test.must_contain_all_lines(test.stdout(), lines) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Configure/option--Q.py b/test/Configure/option--Q.py index 4602a50f68..8316ab0268 100644 --- a/test/Configure/option--Q.py +++ b/test/Configure/option--Q.py @@ -1,52 +1,52 @@ -#!/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 that the -Q option suppresses Configure context output. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) -env = Environment() -import os -env.AppendENVPath('PATH', os.environ['PATH']) -conf = Configure(env) -r1 = conf.CheckCHeader('stdio.h') -env = conf.Finish() -""") - -test.run(arguments='-Q', stdout="scons: `.' is up to date.\n", stderr="") - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: +#!/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 that the -Q option suppresses Configure context output. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment() +import os +env.AppendENVPath('PATH', os.environ['PATH']) +conf = Configure(env) +r1 = conf.CheckCHeader('stdio.h') +env = conf.Finish() +""") + +test.run(arguments='-Q', stdout="scons: `.' is up to date.\n", stderr="") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: From 09c4a1250f0c73a1c04a8e78d129bfac80692357 Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Thu, 30 May 2024 16:57:28 -0500 Subject: [PATCH 053/386] eol commit to `.git-blame-ignore-revs` --- .git-blame-ignore-revs | 2 ++ CHANGES.txt | 1 + RELEASE.txt | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 538cb23c92..a798490c6e 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,2 +1,4 @@ # files reformatted from DOS line-endings 1277d8e5ab6457ed18d291100539f31d1bdb2d7c +# enforced .editorconfig eol settings +fbb026ef1145fe29e0ec3c1b66a3e99cac51e18d diff --git a/CHANGES.txt b/CHANGES.txt index e5e6f5de25..4dff9dad4b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -13,6 +13,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - GetSConsVersion() to grab the latest SCons version without needing to access SCons internals. - Migrate setup.cfg logic to pyproject.toml; remove setup.cfg. + - Update .gitattributes to match .editorconfig; enforce eol settings. From Raymond Li: - Fix issue #3935: OSErrors are now no longer hidden during execution of diff --git a/RELEASE.txt b/RELEASE.txt index a8d121ef36..fd269d65b1 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -88,6 +88,10 @@ DEVELOPMENT by adding SKIP_PDF=1. This should help with distro packaging of SCons, which now does not need "fop" and other tools to be set up in order to build pdf versions which are then ignored. +- .gitattributes has been setup to mirror .editorconfig's eol settings. + The repo-wide line-ending is now `lf`, with the exception of a few + Windows-only files using `crlf` instead. Any files not already fitting + this format have been explicitly converted. Thanks to the following contributors listed below for their contributions to this release. From 112118151dc8e555f06e573380523dbc78a14a1b Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Fri, 31 May 2024 12:05:43 -0500 Subject: [PATCH 054/386] Replace `black`/`flake8` with `ruff` --- .flake8 | 22 ---------------------- CHANGES.txt | 1 + RELEASE.txt | 3 +++ pyproject.toml | 31 +++++++++++++++++++++++++------ 4 files changed, 29 insertions(+), 28 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index d10d72d2ab..0000000000 --- a/.flake8 +++ /dev/null @@ -1,22 +0,0 @@ -[flake8] -show-source = True -# don't complain about work black has done -max-line-length = 88 -extend-exclude = - bin, - bench, - doc, - src, - template, - testing, - test, - timings, - SCons/Tool/docbook/docbook-xsl-1.76.1, - bootstrap.py, - runtest.py -extend-ignore = - E302, - E305 -per-file-ignores = - # module symbols made available for compat - ignore "unused" warns - SCons/Util/__init__.py: F401 diff --git a/CHANGES.txt b/CHANGES.txt index 4dff9dad4b..889a7aef11 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -14,6 +14,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER access SCons internals. - Migrate setup.cfg logic to pyproject.toml; remove setup.cfg. - Update .gitattributes to match .editorconfig; enforce eol settings. + - Replace black/flake8 with ruff for more efficient formatting & linting. From Raymond Li: - Fix issue #3935: OSErrors are now no longer hidden during execution of diff --git a/RELEASE.txt b/RELEASE.txt index fd269d65b1..5d5c2ebe81 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -92,6 +92,9 @@ DEVELOPMENT The repo-wide line-ending is now `lf`, with the exception of a few Windows-only files using `crlf` instead. Any files not already fitting this format have been explicitly converted. +- Repository linter/formatter changed from flake8/black to ruff, as the + latter grants an insane speed boost without compromising functionality. + Existing settings were migrated 1-to-1 where possible. Thanks to the following contributors listed below for their contributions to this release. diff --git a/pyproject.toml b/pyproject.toml index 8d755dcb2b..94f62a40f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,11 +66,30 @@ dist-dir = "build/dist" [tool.distutils.bdist_wheel] dist-dir = "build/dist" -# for black and mypy, set the lowest Python version supported -[tool.black] -quiet = true -target-version = ['py36'] -skip-string-normalization = true +[tool.ruff] +target-version = "py37" # Lowest python version supported +extend-include = ["SConstruct", "SConscript"] +extend-exclude = [ + "bench", + "bin", + "doc", + "src", + "template", + "test", + "testing", + "timings", + "SCons/Tool/docbook/docbook-xsl-1.76.1", + "bootstrap.py", + "runtest.py", +] + +[tool.ruff.format] +quote-style = "preserve" # Equivalent to black's "skip-string-normalization" + +[tool.ruff.lint.per-file-ignores] +"SCons/Util/__init__.py" = [ + "F401", # Module imported but unused +] [tool.mypy] -python_version = "3.6" +python_version = "3.6" # Lowest python version supported (v0.971 and below) From 94ec4684be18aeb6fa8fd9c74d7bfa1baf83f926 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 2 Jun 2024 13:42:08 -0600 Subject: [PATCH 055/386] Make is_valid_construction_var really be a bool function Signed-off-by: Mats Wichmann --- CHANGES.txt | 13 +++++++++++-- SCons/Environment.py | 4 ++-- SCons/EnvironmentTests.py | 34 +++++++++++++++++----------------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4dff9dad4b..5be451f1dd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,8 +4,11 @@ Change Log -NOTE: The 4.0.0 Release of SCons dropped Python 2.7 Support -NOTE: 4.3.0 now requires Python 3.6.0 and above. Python 3.5.x is no longer supported +NOTE: The 4.0.0 release of SCons dropped Python 2.7 support. Use 3.1.2 if + Python 2.7 support is required (but note old SCons releases are unsupported). +NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. +NOTE: Python 3.6 support is deprecated and will be dropped in a future reease. + python.org no longer supports 3.6 or 3.7, and will drop 3.8 in Oct. 2024. RELEASE VERSION/DATE TO BE FILLED IN LATER @@ -76,6 +79,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Framework for scons-time tests adjusted so a path with a long username Windows has squashed doesn't get re-expanded. Fixes a problem seen on GitHub Windows runner which uses a name "runneradmin". + - SCons.Environment.is_valid_construction_var now returns a boolean to + match the convention that functions beginnig with "is" have yes/no + answers (previously returned either None or an re.match object). + Now matches the annotation and docstring (which were prematurely + updated in 4.6). All SCons usage except unit test was already fully + consistent with a bool. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/SCons/Environment.py b/SCons/Environment.py index 5bf763d91a..ae44414154 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -512,9 +512,9 @@ def update(self, mapping) -> None: _is_valid_var = re.compile(r'[_a-zA-Z]\w*$') -def is_valid_construction_var(varstr) -> bool: +def is_valid_construction_var(varstr: str) -> bool: """Return True if *varstr* is a legitimate construction variable.""" - return _is_valid_var.match(varstr) + return bool(_is_valid_var.match(varstr)) class SubstitutionEnvironment: diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index 9d1229c44d..d213099d1a 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -2306,7 +2306,7 @@ def my_depends(target, dependency, tlist=tlist, dlist=dlist) -> None: exc_caught = None try: - env.ParseDepends(test.workpath('does_not_exist'), must_exist=1) + env.ParseDepends(test.workpath('does_not_exist'), must_exist=True) except IOError: exc_caught = 1 assert exc_caught, "did not catch expected IOError" @@ -2314,7 +2314,7 @@ def my_depends(target, dependency, tlist=tlist, dlist=dlist) -> None: del tlist[:] del dlist[:] - env.ParseDepends('$SINGLE', only_one=1) + env.ParseDepends('$SINGLE', only_one=True) t = list(map(str, tlist)) d = list(map(str, dlist)) assert t == ['f0'], t @@ -2331,7 +2331,7 @@ def my_depends(target, dependency, tlist=tlist, dlist=dlist) -> None: exc_caught = None try: - env.ParseDepends(test.workpath('multiple'), only_one=1) + env.ParseDepends(test.workpath('multiple'), only_one=True) except SCons.Errors.UserError: exc_caught = 1 assert exc_caught, "did not catch expected UserError" @@ -4147,33 +4147,33 @@ class EnvironmentVariableTestCase(unittest.TestCase): def test_is_valid_construction_var(self) -> None: """Testing is_valid_construction_var()""" r = is_valid_construction_var("_a") - assert r is not None, r + assert r, r r = is_valid_construction_var("z_") - assert r is not None, r + assert r, r r = is_valid_construction_var("X_") - assert r is not None, r + assert r, r r = is_valid_construction_var("2a") - assert r is None, r + assert not r, r r = is_valid_construction_var("a2_") - assert r is not None, r + assert r, r r = is_valid_construction_var("/") - assert r is None, r + assert not r, r r = is_valid_construction_var("_/") - assert r is None, r + assert not r, r r = is_valid_construction_var("a/") - assert r is None, r + assert not r, r r = is_valid_construction_var(".b") - assert r is None, r + assert not r, r r = is_valid_construction_var("_.b") - assert r is None, r + assert not r, r r = is_valid_construction_var("b1._") - assert r is None, r + assert not r, r r = is_valid_construction_var("-b") - assert r is None, r + assert not r, r r = is_valid_construction_var("_-b") - assert r is None, r + assert not r, r r = is_valid_construction_var("b1-_") - assert r is None, r + assert not r, r From a217d6ad0a166053d01b3b1f823e5989a89c7c17 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 3 Jun 2024 09:40:01 +0200 Subject: [PATCH 056/386] Fixed typo, added RELEASE.txt --- CHANGES.txt | 4 ++-- RELEASE.txt | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5be451f1dd..ab13ef074b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -79,8 +79,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Framework for scons-time tests adjusted so a path with a long username Windows has squashed doesn't get re-expanded. Fixes a problem seen on GitHub Windows runner which uses a name "runneradmin". - - SCons.Environment.is_valid_construction_var now returns a boolean to - match the convention that functions beginnig with "is" have yes/no + - SCons.Environment.is_valid_construction_var() now returns a boolean to + match the convention that functions beginning with "is" have yes/no answers (previously returned either None or an re.match object). Now matches the annotation and docstring (which were prematurely updated in 4.6). All SCons usage except unit test was already fully diff --git a/RELEASE.txt b/RELEASE.txt index fd269d65b1..b9c253f4d4 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -44,6 +44,12 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY of CCFLAGS; the latter variable could cause a compiler warning. - The implementation of Variables was slightly refactored, there should not be user-visible changes. +- SCons.Environment.is_valid_construction_var() now returns a boolean to + match the convention that functions beginning with "is" have yes/no + answers (previously returned either None or an re.match object). + Now matches the annotation and docstring (which were prematurely + updated in 4.6). All SCons usage except unit test was already fully + consistent with a bool. FIXES ----- From 18b45e456412379c9182dd9cb4c8b30ca1e841b8 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 3 Jun 2024 07:18:54 -0600 Subject: [PATCH 057/386] Allow a Variable to not be substituted New parameter do_subst added to the variables Add method, if false indicates the variable value should not be substituted by the Variables logic. The default is True. Fixes #4241. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 +++ RELEASE.txt | 3 +++ SCons/Tool/yacc.xml | 4 ++-- SCons/Variables/PathVariable.py | 2 +- SCons/Variables/__init__.py | 26 ++++++++++++++++++-------- doc/generated/variables.gen | 4 ++-- doc/man/scons.xml | 17 ++++++++++++++++- 7 files changed, 45 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ab13ef074b..8850bfe9d2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -85,6 +85,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Now matches the annotation and docstring (which were prematurely updated in 4.6). All SCons usage except unit test was already fully consistent with a bool. + - When a variable is added to a Variables object, it can now be flagged + as "don't perform substitution". This allows variables to contain + characters which would otherwise cause expansion. Fixes #4241. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index b9c253f4d4..ec22f1057a 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -50,6 +50,9 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY Now matches the annotation and docstring (which were prematurely updated in 4.6). All SCons usage except unit test was already fully consistent with a bool. +- The Variables object Add method now accepts a do_subst keyword argument + (defaults to True) which can be set to inhibit substitution prior to + calling the variable's converter and validator. FIXES ----- diff --git a/SCons/Tool/yacc.xml b/SCons/Tool/yacc.xml index 82725dbade..729c408286 100644 --- a/SCons/Tool/yacc.xml +++ b/SCons/Tool/yacc.xml @@ -236,7 +236,7 @@ The value is used only if &cv-YACC_GRAPH_FILE_SUFFIX; is not set. The default value is .gv. -Changed in version 4.X.Y: deprecated. The default value +Changed in version 4.6.0: deprecated. The default value changed from .vcg (&bison; stopped generating .vcg output with version 2.4, in 2006). @@ -261,7 +261,7 @@ Various yacc tools have emitted various formats at different times. Set this to match what your parser generator produces. -New in version 4.X.Y. +New in version 4.6.0.
diff --git a/SCons/Variables/PathVariable.py b/SCons/Variables/PathVariable.py index 6ea4e6bc93..4a827c5e12 100644 --- a/SCons/Variables/PathVariable.py +++ b/SCons/Variables/PathVariable.py @@ -141,7 +141,7 @@ def PathExists(key, val, env) -> None: # lint: W0622: Redefining built-in 'help' (redefined-builtin) def __call__( - self, key, help: str, default, validator: Optional[Callable] = None + self, key: str, help: str, default, validator: Optional[Callable] = None ) -> Tuple[str, str, str, Callable, None]: """Return a tuple describing a path list SCons Variable. diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 867493d7c8..03f7ef3b50 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -49,7 +49,7 @@ class Variable: """A Build Variable.""" - __slots__ = ('key', 'aliases', 'help', 'default', 'validator', 'converter') + __slots__ = ('key', 'aliases', 'help', 'default', 'validator', 'converter', 'do_subst') def __lt__(self, other): """Comparison fuction so Variable instances sort.""" @@ -87,9 +87,9 @@ def __init__( ) -> None: self.options: List[Variable] = [] self.args = args if args is not None else {} - if not SCons.Util.is_List(files): + if not SCons.Util.is_Sequence(files): files = [files] if files else [] - self.files = files + self.files: Sequence[str] = files self.unknown: Dict[str, str] = {} def __str__(self) -> str: @@ -132,6 +132,7 @@ def _do_add( option.default = default option.validator = validator option.converter = converter + option.do_subst = kwargs.get("subst", True) self.options.append(option) @@ -171,8 +172,11 @@ def Add( value before putting it in the environment. (default: ``None``) """ if SCons.Util.is_Sequence(key): - if not (len(args) or len(kwargs)): - return self._do_add(*key) + # If no other positional args (and no fundamental kwargs), + # unpack key, and pass the kwargs on: + known_kw = {'help', 'default', 'validator', 'converter'} + if not args and not known_kw.intersection(kwargs.keys()): + return self._do_add(*key, **kwargs) return self._do_add(key, *args, **kwargs) @@ -247,7 +251,10 @@ def Update(self, env, args: Optional[dict] = None) -> None: # apply converters for option in self.options: if option.converter and option.key in values: - value = env.subst(f'${option.key}') + if option.do_subst: + value = env.subst(f'${option.key}') + else: + value = env[option.key] try: try: env[option.key] = option.converter(value) @@ -262,7 +269,11 @@ def Update(self, env, args: Optional[dict] = None) -> None: # apply validators for option in self.options: if option.validator and option.key in values: - option.validator(option.key, env.subst(f'${option.key}'), env) + if option.do_subst: + value = env.subst('${%s}'%option.key) + else: + value = env[option.key] + option.validator(option.key, value, env) def UnknownVariables(self) -> dict: """Return dict of unknown variables. @@ -340,7 +351,6 @@ def GenerateHelpText(self, env, sort: Union[bool, Callable] = False) -> str: # removed so now we have to convert to a key. if callable(sort): options = sorted(self.options, key=cmp_to_key(lambda x, y: sort(x.key, y.key))) - elif sort is True: options = sorted(self.options) else: diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 8c89616d35..fad7d5d4ae 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -10668,7 +10668,7 @@ Various yacc tools have emitted various formats at different times. Set this to match what your parser generator produces. -New in version 4.X.Y. +New in version 4.6.0. @@ -10826,7 +10826,7 @@ The value is used only if &cv-YACC_GRAPH_FILE_SUFFIX; is not set. The default value is .gv. -Changed in version 4.X.Y: deprecated. The default value +Changed in version 4.6.0: deprecated. The default value changed from .vcg (&bison; stopped generating .vcg output with version 2.4, in 2006). diff --git a/doc/man/scons.xml b/doc/man/scons.xml index cdaaa44ac9..eb02a23248 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -4835,7 +4835,7 @@ not to any stored-values files. - vars.Add(key, [help, default, validator, converter]) + vars.Add(key, [help, default, validator, converter, do_subst]) Add a customizable &consvar; to the &Variables; object. key @@ -4887,6 +4887,16 @@ or there is no separate validator it can raise a ValueError. + +Substitution will be performed on the variable value +as it is added, before the converter and validator are called, +unless the optional do_subst parameter +is false (default True). +Suppressing substitution may be useful if the variable value +looks like a &consvar; reference ($VAR) +to be expanded later. + + As a special case, if key is a sequence and is the only @@ -4919,6 +4929,11 @@ def valid_color(key, val, env): vars.Add('COLOR', validator=valid_color) + + +Changed in version 4.8.0: +added the do_subst parameter. + From 9033b66adb098852c1f89b8d008297cb279debe9 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 3 Jun 2024 08:15:43 -0600 Subject: [PATCH 058/386] runtest.py now recognizes exit code 5 The unittest module's main() can exit with a code of 5, which means no tests were run. SCons/Script/MainTests.py currently has no tests, so this particular error code is expected - should not cause runtest to give up with an "unknown error code". Signed-off-by: Mats Wichmann --- CHANGES.txt | 4 ++++ runtest.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index ab13ef074b..aaa689d7e3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -85,6 +85,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Now matches the annotation and docstring (which were prematurely updated in 4.6). All SCons usage except unit test was already fully consistent with a bool. + - The test runner now recognizes the unittest module's return code of 5, + which means no tests were run. SCons/Script/MainTests.py currently + has no tests, so this particular error code is expected - should not + cause runtest to give up with an "unknown error code". RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/runtest.py b/runtest.py index 1922ccff79..220b490333 100755 --- a/runtest.py +++ b/runtest.py @@ -417,7 +417,7 @@ class SystemExecutor(RuntestBase): def execute(self, env): self.stderr, self.stdout, s = spawn_it(self.command_args, env) self.status = s - if s < 0 or s > 2: + if s < 0 or s > 2 and s != 5: sys.stdout.write("Unexpected exit status %d\n" % s) From 272d72beefb6b1d4efce68ec4c0ea2404f6fcb31 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 3 Jun 2024 10:38:21 -0600 Subject: [PATCH 059/386] Adding testcase for Add(..., subst) Signed-off-by: Mats Wichmann --- RELEASE.txt | 2 +- SCons/Variables/VariablesTests.py | 22 ++++++++++++++++++++++ SCons/Variables/__init__.py | 7 +++++-- doc/man/scons.xml | 12 ++++++------ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/RELEASE.txt b/RELEASE.txt index ec22f1057a..063c116290 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -50,7 +50,7 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY Now matches the annotation and docstring (which were prematurely updated in 4.6). All SCons usage except unit test was already fully consistent with a bool. -- The Variables object Add method now accepts a do_subst keyword argument +- The Variables object Add method now accepts a subst keyword argument (defaults to True) which can be set to inhibit substitution prior to calling the variable's converter and validator. diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index 145bee31f3..866b2ff427 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -150,6 +150,28 @@ def test_Update(self) -> None: opts.Update(env, {}) assert env['ANSWER'] == 54 + # Test that the value is not substituted if 'subst' is False + def check_subst(key, value, env) -> None: + """Check that variable was not substituted before we get called.""" + assert value == "$ORIGIN", \ + f"Validator: '$ORIGIN' was substituted to {value!r}" + + def conv_subst(value) -> None: + """Check that variable was not substituted before we get called.""" + assert value == "$ORIGIN", \ + f"Converter: '$ORIGIN' was substituted to {value!r}" + return value + + opts.Add('NOSUB', + help='Variable whose value will not be substituted', + default='$ORIGIN', + validator=check_subst, + converter=conv_subst, + subst=False) + env = Environment() + opts.Update(env) + assert env['NOSUB'] == "$ORIGIN" + # Test that a bad value from the file is used and # validation fails correctly. test = TestSCons.TestSCons() diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 03f7ef3b50..80cda2b95b 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -114,6 +114,9 @@ def _do_add( """Create a Variable and add it to the list. Internal routine, not public API. + + .. versionadded:: 4.8.0 + *subst* keyword argument is now recognized. """ option = Variable() @@ -252,7 +255,7 @@ def Update(self, env, args: Optional[dict] = None) -> None: for option in self.options: if option.converter and option.key in values: if option.do_subst: - value = env.subst(f'${option.key}') + value = env.subst('${%s}' % option.key) else: value = env[option.key] try: @@ -270,7 +273,7 @@ def Update(self, env, args: Optional[dict] = None) -> None: for option in self.options: if option.validator and option.key in values: if option.do_subst: - value = env.subst('${%s}'%option.key) + value = env.subst('${%s}' % option.key) else: value = env[option.key] option.validator(option.key, value, env) diff --git a/doc/man/scons.xml b/doc/man/scons.xml index eb02a23248..57d38e8ee0 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -4835,7 +4835,7 @@ not to any stored-values files. - vars.Add(key, [help, default, validator, converter, do_subst]) + vars.Add(key, [help, default, validator, converter, subst]) Add a customizable &consvar; to the &Variables; object. key @@ -4889,12 +4889,12 @@ it can raise a ValueError. Substitution will be performed on the variable value -as it is added, before the converter and validator are called, -unless the optional do_subst parameter +before the converter and validator are called, +unless the optional subst parameter is false (default True). Suppressing substitution may be useful if the variable value -looks like a &consvar; reference ($VAR) -to be expanded later. +looks like a &consvar; reference (e.g. $VAR) +and the validator and/or converter should see it unexpanded. @@ -4932,7 +4932,7 @@ vars.Add('COLOR', validator=valid_color) Changed in version 4.8.0: -added the do_subst parameter. +added the subst parameter. From 91b6bcc272292756e47cee9548158cccfaff1755 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 3 Jun 2024 11:38:12 -0600 Subject: [PATCH 060/386] Additional testcase for subst on Variables This tests the other side of the coin: when vars.Add(..., subst=True) is used, substitution *is* performaed. Signed-off-by: Mats Wichmann --- SCons/Variables/VariablesTests.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index 866b2ff427..7c6eaab171 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -151,26 +151,46 @@ def test_Update(self) -> None: assert env['ANSWER'] == 54 # Test that the value is not substituted if 'subst' is False - def check_subst(key, value, env) -> None: + # and that it is if 'subst' is True. + def check_no_subst(key, value, env) -> None: """Check that variable was not substituted before we get called.""" assert value == "$ORIGIN", \ f"Validator: '$ORIGIN' was substituted to {value!r}" - def conv_subst(value) -> None: + def conv_no_subst(value) -> None: """Check that variable was not substituted before we get called.""" assert value == "$ORIGIN", \ f"Converter: '$ORIGIN' was substituted to {value!r}" return value + def check_subst(key, value, env) -> None: + """Check that variable was substituted before we get called.""" + assert value == "Value", \ + f"Validator: '$SUB' was not substituted {value!r} instead of 'Value'" + + def conv_subst(value) -> None: + """Check that variable was not substituted before we get called.""" + assert value == "Value", \ + f"Converter: '$SUB' was substituted to {value!r} instead of 'Value'" + return value + opts.Add('NOSUB', help='Variable whose value will not be substituted', default='$ORIGIN', + validator=check_no_subst, + converter=conv_no_subst, + subst=False) + opts.Add('SUB', + help='Variable whose value will be substituted', + default='$VAR', validator=check_subst, converter=conv_subst, - subst=False) + subst=True) env = Environment() + env['VAR'] = "Value" opts.Update(env) - assert env['NOSUB'] == "$ORIGIN" + assert env['NOSUB'] == "$ORIGIN", env['NOSUB'] + assert env['SUB'] == env['VAR'], env['SUB'] # Test that a bad value from the file is used and # validation fails correctly. From a7f4ab10e2a20f15ef18db6bef9ed6b682468c55 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 3 Jun 2024 12:13:34 -0600 Subject: [PATCH 061/386] Small docstring update for vars.Add Signed-off-by: Mats Wichmann --- SCons/Variables/__init__.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 80cda2b95b..9c9c3f4961 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -135,7 +135,8 @@ def _do_add( option.default = default option.validator = validator option.converter = converter - option.do_subst = kwargs.get("subst", True) + option.do_subst = kwargs.pop("subst", True) + # TODO should any remaining kwargs be saved in the Variable? self.options.append(option) @@ -158,21 +159,26 @@ def Add( Arguments: key: the name of the variable, or a 5-tuple (or list). If *key* is a tuple, and there are no additional positional - arguments, it is unpacked into the variable name plus the four - listed keyword arguments from below. + arguments, it is unpacked into the variable name plus the + *help*, *default*, *validator* and *converter keyword args. If *key* is a tuple and there are additional positional arguments, the first word of the tuple is taken as the variable name, and the remainder as aliases. - args: optional positional arguments, corresponding to the four - listed keyword arguments. + args: optional positional arguments, corresponding to the + *help*, *default*, *validator* and *converter keyword args. kwargs: arbitrary keyword arguments used by the variable itself. Keyword Args: - help: help text for the variable (default: ``""``) + help: help text for the variable (default: empty string) default: default value for variable (default: ``None``) validator: function called to validate the value (default: ``None``) converter: function to be called to convert the variable's value before putting it in the environment. (default: ``None``) + subst: if true perform substitution on the value before the converter + and validator functions (if any) are called (default: ``True``) + + .. versionadded:: 4.8.0 + The *subst* keyword argument is now specially recognized. """ if SCons.Util.is_Sequence(key): # If no other positional args (and no fundamental kwargs), From 265046d40a5b4433bffd3d8da932ea5c7a4fb874 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 3 Jun 2024 12:30:40 -0600 Subject: [PATCH 062/386] Even more vars.Add() docstring tweaking. Signed-off-by: Mats Wichmann --- SCons/Variables/__init__.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 9c9c3f4961..34fb68b08d 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -113,7 +113,8 @@ def _do_add( ) -> None: """Create a Variable and add it to the list. - Internal routine, not public API. + This is the internal implementation for :meth:`Add` and + :meth:`AddVariables`. Not part of the public API. .. versionadded:: 4.8.0 *subst* keyword argument is now recognized. @@ -157,15 +158,16 @@ def Add( """Add a Build Variable. Arguments: - key: the name of the variable, or a 5-tuple (or list). - If *key* is a tuple, and there are no additional positional - arguments, it is unpacked into the variable name plus the - *help*, *default*, *validator* and *converter keyword args. - If *key* is a tuple and there are additional positional arguments, - the first word of the tuple is taken as the variable name, - and the remainder as aliases. + key: the name of the variable, or a 5-tuple (or other sequence). + If *key* is a tuple, and there are no additional arguments + except the *help*, *default*, *validator* and *converter* + keyword arguments, *key* is unpacked into the variable name + plus the *help*, *default*, *validator* and *converter* + arguments; if there are additional arguments, the first + elements of *key* is taken as the variable name, and the + remainder as aliases. args: optional positional arguments, corresponding to the - *help*, *default*, *validator* and *converter keyword args. + *help*, *default*, *validator* and *converter* keyword args. kwargs: arbitrary keyword arguments used by the variable itself. Keyword Args: @@ -174,7 +176,7 @@ def Add( validator: function called to validate the value (default: ``None``) converter: function to be called to convert the variable's value before putting it in the environment. (default: ``None``) - subst: if true perform substitution on the value before the converter + subst: perform substitution on the value before the converter and validator functions (if any) are called (default: ``True``) .. versionadded:: 4.8.0 From 4c668368a211527e2f2c4ad4e462eeb3bb2a3a35 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 4 Jun 2024 07:47:29 -0600 Subject: [PATCH 063/386] Reproducible builds info updated Just tweaked the readmes (top-level and in the packaging/etc dir) and the site_init script sample itself. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + README.rst | 22 +++++++++++++--------- packaging/etc/README.txt | 24 ++++++++++++++++-------- packaging/etc/reproducible_site_init.py | 20 +++++++++++++------- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ab13ef074b..df2425eab7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -85,6 +85,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Now matches the annotation and docstring (which were prematurely updated in 4.6). All SCons usage except unit test was already fully consistent with a bool. + - Updated the notes about reproducible builds with SCons and the example. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/README.rst b/README.rst index ae3c0f4a28..3da4fc0734 100755 --- a/README.rst +++ b/README.rst @@ -249,6 +249,19 @@ notifications and other GitHub events (``#github-update``), if those are of interest. See the website for more contact information: https://scons.org/contact.html. +Reproducible Builds +=================== +SCons itself is set up to do "reproducible builds" +(see (https://reproducible-builds.org/specs/source-date-epoch/) +if environment variables ``SOURCE_DATE_EPOCH`` is set - that is, +fields in the package which could change each time the package is +constructed are forced to constant values. + +To support other projects which wish to do the same, a sample script +is provided which can be placed in a site directory, which imports +``SOURCE_DATE_EPOCH`` and sets it in the execution environment of +every created construction envirionment. There's also an installer +script (POSIX shell only). See packaging/etc/README.txt for more details. Donations ========= @@ -258,15 +271,6 @@ software, or hardware) to support continued work on the project. Information is available at https://www.scons.org/donate.html or the GitHub Sponsors button on https://github.com/scons/scons. -Reproducible Builds -=================== -In order to suppor those users who which to produce reproducible builds -(https://reproducible-builds.org/specs/source-date-epoch/) we're now including -logic to force SCons to propagate SOURCE_DATE_EPOCH from your shell environment for -all SCons builds to support reproducible builds we're now providing an example -site_init.py and a script to install it in your ~/.scons. See packaging/etc/README.txt -for more info - For More Information ==================== diff --git a/packaging/etc/README.txt b/packaging/etc/README.txt index ffb9cc10b2..11f1eab309 100644 --- a/packaging/etc/README.txt +++ b/packaging/etc/README.txt @@ -1,10 +1,18 @@ -This directory contains a number of scripts/files useful when building/packageing SCons +This directory contains helpers for doing reproducible builds with SCons. + +To force SCons to propagate SOURCE_DATE_EPOCH from the shell running SCons, +the reproducible_site_init.py file can be installed (as site_init.py) +in any site directory - either in the project itself, or more globally. +See the manpage for default site directories or how to set your own path: +https://scons.org/doc/production/HTML/scons-man.html#opt-site-dir. +This code will make sure SOURCE_DATE_EPOCH is set in the execution +environment, meaning any external commands run by SCons will have it +in their environment. Any logic in your build system itself will still +need to examine this variable. + +The shell script reproducible_install.sh can be used to install the +Python site file in your user site directory ($HOME/.scons/site_scons). +It is careful to not overwrite any existing site_init.py there. This +only works for a POSIX shell. -To force SCons to propagate SOURCE_DATE_EPOCH from the shell running SCons we're providing -a script to create a ~/.scons/site_scons/site_init.py. -Note that reproducible_install.sh will NOT overwite an existing ~/.scons/site_scons/site_init.py This supports https://reproducible-builds.org/specs/source-date-epoch/ -If you wanted to include this in your build tree you would place in site_scons/site_init.py relative -to your SConstruct. -* reproducible_install.sh -* reproducible_site_init.py \ No newline at end of file diff --git a/packaging/etc/reproducible_site_init.py b/packaging/etc/reproducible_site_init.py index 2b6b42a91f..5f7513ba19 100644 --- a/packaging/etc/reproducible_site_init.py +++ b/packaging/etc/reproducible_site_init.py @@ -1,5 +1,6 @@ """ -Use this file as your ~/.site_scons/scons_init.py to enable reprodicble builds as described at +Use this file as your site_init.py in a site directory, +to enable reprodicble builds as described at https://reproducible-builds.org/specs/source-date-epoch/ """ @@ -8,17 +9,22 @@ old_init = SCons.Environment.Base.__init__ -print("Adding logic to propagate SOURCE_DATE_EPOCH from the shell environment when building with SCons") +print( + "Adding logic to propagate SOURCE_DATE_EPOCH from the shell environment when building with SCons" +) def new_init(self, **kw): - """ - This logic will add SOURCE_DATE_EPOCH to the execution environment used to run - all the build commands. + """Replacement Environment initializer. + + When this is monkey-patched into :class:`SCons.Environment.Base` it adds + ``SOURCE_DATE_EPOCH`` to the execution environment used to run + all external build commands; the original iinitializer is called first. """ old_init(self, **kw) - if 'SOURCE_DATE_EPOCH' in os.environ: - self._dict['ENV']['SOURCE_DATE_EPOCH'] = os.environ['SOURCE_DATE_EPOCH'] + epoch = os.environ.get("SOURCE_DATE_EPOCH") + if epoch is not None: + self._dict["ENV"]["SOURCE_DATE_EPOCH"] = epoch SCons.Environment.Base.__init__ = new_init From 68eb8859776439714c6e38a6e2ef53031199afbc Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 4 Jun 2024 09:44:18 -0600 Subject: [PATCH 064/386] Teach Clone() to respect the variables= kwarg. Previously, "variables" would just be set as a construction var, now it has the same meaning as an Environment() call. Docs updated and test added. Fixes #3590 Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 1 + SCons/Environment.py | 28 +++++++++++++----- SCons/Environment.xml | 32 +++++++++++++++------ test/Clone-Variables.py | 63 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 test/Clone-Variables.py diff --git a/CHANGES.txt b/CHANGES.txt index ab13ef074b..0f827a7943 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -85,6 +85,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Now matches the annotation and docstring (which were prematurely updated in 4.6). All SCons usage except unit test was already fully consistent with a bool. + - The Clone() method now respects the variables argument (fixes #3590) RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index b9c253f4d4..c994041623 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -58,6 +58,7 @@ FIXES - Improved the conversion of a "foreign" exception from an action into BuildError by making sure our defaults get applied even in corner cases. Fixes Issue #4530 +- The Clone() method now respects the variables argument (fixes #3590) IMPROVEMENTS ------------ diff --git a/SCons/Environment.py b/SCons/Environment.py index ae44414154..952ffc17c8 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -1568,16 +1568,28 @@ def AppendUnique(self, delete_existing: bool=False, **kw) -> None: self._dict[key] = dk + val self.scanner_map_delete(kw) - def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw): + def Clone(self, tools=[], toolpath=None, variables=None, parse_flags=None, **kw): """Return a copy of a construction Environment. - The copy is like a Python "deep copy"--that is, independent - copies are made recursively of each objects--except that - a reference is copied when an object is not deep-copyable - (like a function). There are no references to any mutable - objects in the original Environment. - """ + The copy is like a Python "deep copy": independent copies are made + recursively of each object, except that a reference is copied when + an object is not deep-copyable (like a function). There are no + references to any mutable objects in the original environment. + + Unrecognized keyword arguments are taken as construction variable + assignments. + Arguments: + tools: list of tools to initialize. + toolpath: list of paths to search for tools. + variables: a :class:`~SCons.Variables.Variables` object to + use to populate construction variables from command-line + variables. + parse_flags: option strings to parse into construction variables. + + .. versionadded:: 4.8.0 + The optional *variables* parameter was added. + """ builders = self._dict.get('BUILDERS', {}) clone = copy.copy(self) @@ -1603,6 +1615,8 @@ def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw): for key, value in kw.items(): new[key] = SCons.Subst.scons_subst_once(value, self, key) clone.Replace(**new) + if variables: + variables.Update(clone) apply_tools(clone, tools, toolpath) diff --git a/SCons/Environment.xml b/SCons/Environment.xml index 5f152f22ee..c09d3848d6 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -1079,11 +1079,12 @@ Clean(docdir, os.path.join(docdir, projectname)) -Returns a separate copy of a construction environment. -If there are any keyword arguments specified, -they are added to the returned copy, +Returns an independent copy of a &consenv;. +If there are any unrecognized keyword arguments specified, +they are added as &consvars; in the copy, overwriting any existing values -for the keywords. +for those keywords. +See the manpage section "Construction Environments" for more details. @@ -1096,8 +1097,9 @@ env3 = env.Clone(CCFLAGS='-g') -Additionally, a list of tools and a toolpath may be specified, as in -the &f-link-Environment; constructor: +A list of tools +and a toolpath may be specified, +as in the &f-link-Environment; constructor: @@ -1110,7 +1112,7 @@ env4 = env.Clone(tools=['msvc', MyTool]) The parse_flags -keyword argument is also recognized to allow merging command-line +keyword argument is also recognized, to allow merging command-line style arguments into the appropriate construction variables (see &f-link-env-MergeFlags;). @@ -1119,6 +1121,17 @@ variables (see &f-link-env-MergeFlags;). # create an environment for compiling programs that use wxWidgets wx_env = env.Clone(parse_flags='!wx-config --cflags --cxxflags') + + +The variables +keyword argument is also recognized, to allow (re)initializing +&consvars; from a Variables object. + + + +Changed in version 4.8.0: +the variables parameter was added. + @@ -1760,7 +1773,7 @@ will print: -Return a new construction environment +Return a new &consenv; initialized with the specified key=value pairs. @@ -1770,7 +1783,8 @@ The keyword arguments toolpath, tools and variables -are also specially recognized. +are specially recognized and do not lead to +&consvar; creation. See the manpage section "Construction Environments" for more details. diff --git a/test/Clone-Variables.py b/test/Clone-Variables.py new file mode 100644 index 0000000000..383caeafbb --- /dev/null +++ b/test/Clone-Variables.py @@ -0,0 +1,63 @@ +#!/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 that Clone() respects the variables kwarg. + +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +vars = Variables() +vars.Add(BoolVariable('MYTEST', 'help', default=False)) + +_ = DefaultEnvironment(tools=[]) +env = Environment(variables=vars, tools=[]) +print(f"MYTEST={env.Dictionary('MYTEST')}") +env.Replace(MYTEST=True) +print(f"MYTEST={env.Dictionary('MYTEST')}") +env1 = env.Clone(variables=vars) +print(f"MYTEST={env1.Dictionary('MYTEST')}") +""") + +expect = """\ +MYTEST=False +MYTEST=True +MYTEST=False +""" + +test.run(arguments = '-q -Q', stdout=expect) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: From 3f2df31eca2b962c1fa03a40351c9103d9edbc78 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 6 Jun 2024 18:27:58 +0200 Subject: [PATCH 065/386] Added blurb to RELEASE.txt --- RELEASE.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE.txt b/RELEASE.txt index b9c253f4d4..e7e9ff409b 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -82,6 +82,7 @@ DOCUMENTATION - Restructured API Docs build so main package contents are listed before contents of package submodules. - Updated manpage description of Command "builder" and function. +- Updated the notes about reproducible builds with SCons and the example. From 7852c2691e4e30464bd951e44367d15bb1b475a1 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 6 Jun 2024 18:30:01 +0200 Subject: [PATCH 066/386] Added blurb to RELEASE.txt --- RELEASE.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE.txt b/RELEASE.txt index b9c253f4d4..6d49a48748 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -98,6 +98,10 @@ DEVELOPMENT The repo-wide line-ending is now `lf`, with the exception of a few Windows-only files using `crlf` instead. Any files not already fitting this format have been explicitly converted. +- The test runner now recognizes the unittest module's return code of 5, + which means no tests were run. SCons/Script/MainTests.py currently + has no tests, so this particular error code is expected - should not + cause runtest to give up with an "unknown error code". Thanks to the following contributors listed below for their contributions to this release. From b78cfe95487433df7c9f3305571acab755fcdc1b Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 6 Jun 2024 18:37:12 +0200 Subject: [PATCH 067/386] added reference to new arg 'subst' in the blurb in CHANGES.txt --- CHANGES.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7fe6f8a517..66b149e203 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -86,8 +86,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER updated in 4.6). All SCons usage except unit test was already fully consistent with a bool. - When a variable is added to a Variables object, it can now be flagged - as "don't perform substitution". This allows variables to contain - characters which would otherwise cause expansion. Fixes #4241. + as "don't perform substitution" by setting the argument subst. + This allows variables to contain characters which would otherwise + cause expansion. Fixes #4241. - The test runner now recognizes the unittest module's return code of 5, which means no tests were run. SCons/Script/MainTests.py currently has no tests, so this particular error code is expected - should not From d01c21a666090a93875b5398fc7243cc9315e71a Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 6 Jun 2024 10:57:50 -0600 Subject: [PATCH 068/386] Move environment var checker to SCons.Util is_valid_construction_var was defined in Environment, but also used in Variables. This meant a possibility of import loops, since Variables used Environment for this function, and Environment uses Variables to update from an object in Environment() and Clone(). According to breadcumbs in a benchmark script it used to be in Util at some point in the past. Signed-off-by: Mats Wichmann --- CHANGES.txt | 8 ++++++-- SCons/Environment.py | 10 ++-------- SCons/EnvironmentTests.py | 35 ----------------------------------- SCons/Util/UtilTests.py | 36 ++++++++++++++++++++++++++++++++++++ SCons/Util/__init__.py | 1 + SCons/Util/envs.py | 7 +++++++ SCons/Variables/__init__.py | 15 ++++++++------- bench/env.__setitem__.py | 26 +++++++++++++------------- 8 files changed, 73 insertions(+), 65 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 66b149e203..07dcaa48e3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -86,8 +86,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER updated in 4.6). All SCons usage except unit test was already fully consistent with a bool. - When a variable is added to a Variables object, it can now be flagged - as "don't perform substitution" by setting the argument subst. - This allows variables to contain characters which would otherwise + as "don't perform substitution" by setting the argument subst. + This allows variables to contain characters which would otherwise cause expansion. Fixes #4241. - The test runner now recognizes the unittest module's return code of 5, which means no tests were run. SCons/Script/MainTests.py currently @@ -95,6 +95,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER cause runtest to give up with an "unknown error code". - Updated the notes about reproducible builds with SCons and the example. - The Clone() method now respects the variables argument (fixes #3590) + - is_valid_construction_var (not past of the public API) moved from + Environment.py to Util to avoid the chance of import loops. Variables + and Environment both use the routine and Environment() uses a Variables() + object so better to move to a safter location. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/SCons/Environment.py b/SCons/Environment.py index 952ffc17c8..6669bf8ada 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -76,6 +76,7 @@ to_String_for_subst, uniquer_hashables, ) +from SCons.Util.envs import is_valid_construction_var from SCons.Util.sctyping import ExecutorType class _Null: @@ -510,13 +511,6 @@ def update(self, mapping) -> None: self.__setitem__(i, v) -_is_valid_var = re.compile(r'[_a-zA-Z]\w*$') - -def is_valid_construction_var(varstr: str) -> bool: - """Return True if *varstr* is a legitimate construction variable.""" - return bool(_is_valid_var.match(varstr)) - - class SubstitutionEnvironment: """Base class for different flavors of construction environments. @@ -605,7 +599,7 @@ def __setitem__(self, key, value): # key and we don't need to check. If we do check, using a # global, pre-compiled regular expression directly is more # efficient than calling another function or a method. - if key not in self._dict and not _is_valid_var.match(key): + if key not in self._dict and not is_valid_construction_var(key): raise UserError("Illegal construction variable `%s'" % key) self._dict[key] = value diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index d213099d1a..8d90d4d171 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -38,7 +38,6 @@ NoSubstitutionProxy, OverrideEnvironment, SubstitutionEnvironment, - is_valid_construction_var, ) from SCons.Util import CLVar from SCons.SConsign import current_sconsign_filename @@ -4142,40 +4141,6 @@ def test_subst_target_source(self) -> None: x = proxy.subst_target_source(*args, **kw) assert x == ' ttt sss ', x -class EnvironmentVariableTestCase(unittest.TestCase): - - def test_is_valid_construction_var(self) -> None: - """Testing is_valid_construction_var()""" - r = is_valid_construction_var("_a") - assert r, r - r = is_valid_construction_var("z_") - assert r, r - r = is_valid_construction_var("X_") - assert r, r - r = is_valid_construction_var("2a") - assert not r, r - r = is_valid_construction_var("a2_") - assert r, r - r = is_valid_construction_var("/") - assert not r, r - r = is_valid_construction_var("_/") - assert not r, r - r = is_valid_construction_var("a/") - assert not r, r - r = is_valid_construction_var(".b") - assert not r, r - r = is_valid_construction_var("_.b") - assert not r, r - r = is_valid_construction_var("b1._") - assert not r, r - r = is_valid_construction_var("-b") - assert not r, r - r = is_valid_construction_var("_-b") - assert not r, r - r = is_valid_construction_var("b1-_") - assert not r, r - - if __name__ == "__main__": unittest.main() diff --git a/SCons/Util/UtilTests.py b/SCons/Util/UtilTests.py index ff32bab8d2..b1c01086e4 100644 --- a/SCons/Util/UtilTests.py +++ b/SCons/Util/UtilTests.py @@ -73,6 +73,7 @@ to_bytes, to_str, ) +from SCons.Util.envs import is_valid_construction_var from SCons.Util.hashes import ( _attempt_init_of_python_3_9_hash_object, _attempt_get_hash_function, @@ -1206,6 +1207,41 @@ def test_default(self) -> None: assert var is False, 'var should be False, not %s' % repr(var) +class EnvironmentVariableTestCase(unittest.TestCase): + + def test_is_valid_construction_var(self) -> None: + """Testing is_valid_construction_var()""" + r = is_valid_construction_var("_a") + assert r, r + r = is_valid_construction_var("z_") + assert r, r + r = is_valid_construction_var("X_") + assert r, r + r = is_valid_construction_var("2a") + assert not r, r + r = is_valid_construction_var("a2_") + assert r, r + r = is_valid_construction_var("/") + assert not r, r + r = is_valid_construction_var("_/") + assert not r, r + r = is_valid_construction_var("a/") + assert not r, r + r = is_valid_construction_var(".b") + assert not r, r + r = is_valid_construction_var("_.b") + assert not r, r + r = is_valid_construction_var("b1._") + assert not r, r + r = is_valid_construction_var("-b") + assert not r, r + r = is_valid_construction_var("_-b") + assert not r, r + r = is_valid_construction_var("b1-_") + assert not r, r + + + if __name__ == "__main__": unittest.main() diff --git a/SCons/Util/__init__.py b/SCons/Util/__init__.py index 95c1b9978d..28565da797 100644 --- a/SCons/Util/__init__.py +++ b/SCons/Util/__init__.py @@ -107,6 +107,7 @@ AppendPath, AddPathIfNotExists, AddMethod, + is_valid_construction_var, ) from .filelock import FileLock, SConsLockFailure diff --git a/SCons/Util/envs.py b/SCons/Util/envs.py index db4d65ab94..68a40488c6 100644 --- a/SCons/Util/envs.py +++ b/SCons/Util/envs.py @@ -9,6 +9,7 @@ that don't need the specifics of the Environment class. """ +import re import os from types import MethodType, FunctionType from typing import Union, Callable, Optional, Any @@ -329,6 +330,12 @@ def AddMethod(obj, function: Callable, name: Optional[str] = None) -> None: setattr(obj, name, method) +_is_valid_var_re = re.compile(r'[_a-zA-Z]\w*$') + +def is_valid_construction_var(varstr: str) -> bool: + """Return True if *varstr* is a legitimate construction variable.""" + return bool(_is_valid_var_re.match(varstr)) + # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 34fb68b08d..1c41130025 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -28,7 +28,6 @@ from functools import cmp_to_key from typing import Callable, Dict, List, Optional, Sequence, Union -import SCons.Environment import SCons.Errors import SCons.Util import SCons.Warnings @@ -70,13 +69,15 @@ class Variables: files: string or list of strings naming variable config scripts (default ``None``) args: dictionary to override values set from *files*. (default ``None``) - is_global: if true, return a global singleton Variables object instead - of a fresh instance. Currently inoperable (default ``False``) + is_global: if true, return a global singleton :class:`Variables` object + instead of a fresh instance. Currently inoperable (default ``False``) .. versionchanged:: 4.8.0 - The default for *is_global* changed to ``False`` (previously - ``True`` but it had no effect due to an implementation error). - *is_global* is deprecated. + The default for *is_global* changed to ``False`` (previously + ``True`` but it had no effect due to an implementation error). + + .. deprecated:: 4.8.0 + *is_global* is deprecated. """ def __init__( @@ -130,7 +131,7 @@ def _do_add( option.key = key # TODO: normalize to not include key in aliases. Currently breaks tests. option.aliases = [key,] - if not SCons.Environment.is_valid_construction_var(option.key): + if not SCons.Util.is_valid_construction_var(option.key): raise SCons.Errors.UserError(f"Illegal Variables key {option.key!r}") option.help = help option.default = default diff --git a/bench/env.__setitem__.py b/bench/env.__setitem__.py index f9fe0c0cd1..2cd0da826f 100644 --- a/bench/env.__setitem__.py +++ b/bench/env.__setitem__.py @@ -1,8 +1,7 @@ # __COPYRIGHT__ # # Benchmarks for testing various possible implementations of the -# env.__setitem__() method(s) in the src/engine/SCons/Environment.py -# module. +# env.__setitem__() method(s) in the SCons/Environment.py module. from __future__ import print_function @@ -25,10 +24,10 @@ def __init__(self, name, num, init, statement): self.name = name self.statement = statement self.__result = None - + def timeit(self): self.__result = self.__timer.timeit(self.__num) - + def getResult(self): return self.__result @@ -62,8 +61,9 @@ def times(num=1000000, init='', title='Results:', **statements): import SCons.Errors import SCons.Environment +import SCons.Util -is_valid_construction_var = SCons.Environment.is_valid_construction_var +is_valid_construction_var = SCons.Util.is_valid_construction_var global_valid_var = re.compile(r'[_a-zA-Z]\w*$') # The classes with different __setitem__() implementations that we're @@ -80,7 +80,7 @@ def times(num=1000000, init='', title='Results:', **statements): # # The env_Original subclass contains the original implementation (which # actually had the is_valid_construction_var() function in SCons.Util -# originally). +# originally). Update: it has moved back, to avoid import loop with Variables. # # The other subclasses (except for env_Best) each contain *one* # significant change from the env_Original implementation. The doc string @@ -116,7 +116,7 @@ def __setitem__(self, key, value): if special: special(self, key, value) else: - if not SCons.Environment.is_valid_construction_var(key): + if not SCons.Util.is_valid_construction_var(key): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value @@ -176,7 +176,7 @@ def __setitem__(self, key, value): if key in self._special_set: self._special_set[key](self, key, value) else: - if not SCons.Environment.is_valid_construction_var(key): + if not SCons.Util.is_valid_construction_var(key): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value @@ -186,7 +186,7 @@ def __setitem__(self, key, value): if key in ('BUILDERS', 'SCANNERS', 'TARGET', 'TARGETS', 'SOURCE', 'SOURCES'): self._special_set[key](self, key, value) else: - if not SCons.Environment.is_valid_construction_var(key): + if not SCons.Util.is_valid_construction_var(key): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value @@ -196,7 +196,7 @@ def __setitem__(self, key, value): if key in ['BUILDERS', 'SCANNERS', 'TARGET', 'TARGETS', 'SOURCE', 'SOURCES']: self._special_set[key](self, key, value) else: - if not SCons.Environment.is_valid_construction_var(key): + if not SCons.Util.is_valid_construction_var(key): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value @@ -206,7 +206,7 @@ def __setitem__(self, key, value): if key in self._special_set_keys: self._special_set[key](self, key, value) else: - if not SCons.Environment.is_valid_construction_var(key): + if not SCons.Util.is_valid_construction_var(key): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value @@ -220,7 +220,7 @@ def __setitem__(self, key, value): try: self._dict[key] except KeyError: - if not SCons.Environment.is_valid_construction_var(key): + if not SCons.Util.is_valid_construction_var(key): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value @@ -232,7 +232,7 @@ def __setitem__(self, key, value): special(self, key, value) else: if key not in self._dict \ - and not SCons.Environment.is_valid_construction_var(key): + and not SCons.Util.is_valid_construction_var(key): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value From 0fe8822467af809932c0e6101fc59744a9d5b3a5 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 7 Jun 2024 10:08:10 +0200 Subject: [PATCH 069/386] fix some spelling issues and add blurb to RELEASE --- CHANGES.txt | 6 +++--- RELEASE.txt | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 07dcaa48e3..d0eb245f54 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -95,10 +95,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER cause runtest to give up with an "unknown error code". - Updated the notes about reproducible builds with SCons and the example. - The Clone() method now respects the variables argument (fixes #3590) - - is_valid_construction_var (not past of the public API) moved from - Environment.py to Util to avoid the chance of import loops. Variables + - is_valid_construction_var() (not part of the public API) moved from + SCons.Environment to SCons.Util to avoid the chance of import loops. Variables and Environment both use the routine and Environment() uses a Variables() - object so better to move to a safter location. + object so better to move to a safer location. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 02ea1b1c6c..8955e465d0 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -107,7 +107,10 @@ DEVELOPMENT which means no tests were run. SCons/Script/MainTests.py currently has no tests, so this particular error code is expected - should not cause runtest to give up with an "unknown error code". - +- is_valid_construction_var() (not part of the public API) moved from + SCons.Environment to SCons.Util to avoid the chance of import loops. Variables + and Environment both use the routine and Environment() uses a Variables() + object so better to move to a safer location. Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== From 1ad6d783c455ee26558497b942305099cfd9fdf9 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 10 Jun 2024 11:01:30 -0600 Subject: [PATCH 070/386] ListVariable now has a separate validator Previously the converter did both conversion and validation, making it difficult to write a custom validator. Signed-off-by: Mats Wichmann --- CHANGES.txt | 11 +++- RELEASE.txt | 5 ++ SCons/Variables/ListVariable.py | 92 ++++++++++++++++++++++------ SCons/Variables/ListVariableTests.py | 22 +++++-- doc/man/scons.xml | 12 +++- test/Variables/BoolVariable.py | 10 ++- test/Variables/EnumVariable.py | 13 ++-- test/Variables/ListVariable.py | 35 +++++------ test/Variables/PackageVariable.py | 9 ++- test/Variables/PathVariable.py | 10 ++- test/Variables/Variables.py | 13 +++- test/Variables/chdir.py | 3 +- test/Variables/help.py | 3 +- test/Variables/import.py | 3 +- 14 files changed, 161 insertions(+), 80 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d0eb245f54..e8004c75f7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -96,9 +96,14 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Updated the notes about reproducible builds with SCons and the example. - The Clone() method now respects the variables argument (fixes #3590) - is_valid_construction_var() (not part of the public API) moved from - SCons.Environment to SCons.Util to avoid the chance of import loops. Variables - and Environment both use the routine and Environment() uses a Variables() - object so better to move to a safer location. + SCons.Environment to SCons.Util to avoid the chance of import loops. + Variables and Environment both use the routine and Environment() uses + a Variables() object so better to move to a safer location. + - ListVariable now has a separate validator, with the functionality + that was previously part of the converter. The main effect is to + allow a developer to supply a custom validator, which previously + could be inhibited by the converter failing before the validator + is reached. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 8955e465d0..fc066809af 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -53,6 +53,11 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - The Variables object Add method now accepts a subst keyword argument (defaults to True) which can be set to inhibit substitution prior to calling the variable's converter and validator. +- ListVariable now has a separate validator, with the functionality + that was previously part of the converter. The main effect is to + allow a developer to supply a custom validator, which previously + could be inhibited by the converter failing before the validator + is reached. FIXES ----- diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index 0c9fa13289..bbecbaf948 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -23,10 +23,11 @@ """Variable type for List Variables. -A list variable may given as 'all', 'none' or a list of names -separated by comma. After the variable has been processed, the variable -value holds either the named list elements, all list elements or no -list elements at all. +A list variable allows selecting one or more from a supplied set of +allowable values, as well as from an optional mapping of alternate names +(such as aliases and abbreviations) and the special names ``'all'`` and +``'none'``. Specified values are converted during processing into values +only from the allowable values set. Usage example:: @@ -63,12 +64,18 @@ class _ListVariable(collections.UserList): """Internal class holding the data for a List Variable. - The initializer accepts two arguments, the list of actual values - given, and the list of allowable values. Not normally instantiated - by hand, but rather by the ListVariable converter function. + This is normally not directly instantiated, rather the ListVariable + converter callback "converts" string input (or the default value + if none) into an instance and stores it. + + Args: + initlist: the list of actual values given. + allowedElems: the list of allowable values. """ - def __init__(self, initlist=None, allowedElems=None) -> None: + def __init__( + self, initlist: Optional[list] = None, allowedElems: Optional[list] = None + ) -> None: if initlist is None: initlist = [] if allowedElems is None: @@ -106,7 +113,12 @@ def prepare_to_store(self): return str(self) def _converter(val, allowedElems, mapdict) -> _ListVariable: - """Convert list variables.""" + """Callback to convert list variables into a suitable form. + + The arguments *allowedElems* and *mapdict* are non-standard + for a :class:`Variables` converter: the lambda in the + :func:`ListVariable` function arranges for us to be called correctly. + """ if val == 'none': val = [] elif val == 'all': @@ -114,18 +126,47 @@ def _converter(val, allowedElems, mapdict) -> _ListVariable: else: val = [_f for _f in val.split(',') if _f] val = [mapdict.get(v, v) for v in val] - notAllowed = [v for v in val if v not in allowedElems] - if notAllowed: - raise ValueError( - f"Invalid value(s) for option: {','.join(notAllowed)}" - ) return _ListVariable(val, allowedElems) -# def _validator(key, val, env) -> None: -# """ """ -# # TODO: write validator for list variable -# pass +def _validator(key, val, env) -> None: + """Callback to validate supplied value(s) for a ListVariable. + + Validation means "is *val* in the allowed list"? *val* has + been subject to substitution before the validator is called. The + converter created a :class:`_ListVariable` container which is stored + in *env* after it runs; this includes the allowable elements list. + Substitution makes a string made out of the values (only), + so we need to fish the allowed elements list out of the environment + to complete the validation. + + Note that since 18b45e456, whether or not ``subst`` has been + called is conditional on the value of the *subst* argument to + :meth:`~SCons.Variables.Variables.Add`, so we have to account for + possible different types of *val*. + + Raises: + UserError: if validation failed. + + .. versionadded:: 4.8.0 + ``_validator`` split off from :func:`_converter` with an additional + check for whether *val* has been substituted before the call. + """ + allowedElems = env[key].allowedElems + if isinstance(val, _ListVariable): # not substituted, use .data + notAllowed = [v for v in val.data if v not in allowedElems] + else: # val will be a string + notAllowed = [v for v in val.split() if v not in allowedElems] + if notAllowed: + # Converter only synthesized 'all' and 'none', they are never + # in the allowed list, so we need to add those to the error message + # (as is done for the help msg). + valid = ','.join(allowedElems + ['all', 'none']) + msg = ( + f"Invalid value(s) for variable {key!r}: {','.join(notAllowed)!r}. " + f"Valid values are: {valid}" + ) + raise SCons.Errors.UserError(msg) from None # lint: W0622: Redefining built-in 'help' (redefined-builtin) @@ -136,6 +177,7 @@ def ListVariable( default: Union[str, List[str]], names: List[str], map: Optional[dict] = None, + validator: Optional[Callable] = None, ) -> Tuple[str, str, str, None, Callable]: """Return a tuple describing a list variable. @@ -149,25 +191,35 @@ def ListVariable( the allowable values (not including any extra names from *map*). default: the default value(s) for the list variable. Can be given as string (possibly comma-separated), or as a list of strings. - ``all`` or ``none`` are allowed as *default*. + ``all`` or ``none`` are allowed as *default*. You can also simulate + a must-specify ListVariable by giving a *default* that is not part + of *names*, it will fail validation if not supplied. names: the allowable values. Must be a list of strings. map: optional dictionary to map alternative names to the ones in *names*, providing a form of alias. The converter will make the replacement, names from *map* are not stored and will not appear in the help message. + validator: optional callback to validate supplied values. + The default validator is used if not specified. Returns: A tuple including the correct converter and validator. The result is usable as input to :meth:`~SCons.Variables.Variables.Add`. + + .. versionchanged:: 4.8.0 + The validation step was split from the converter to allow for + custom validators. The *validator* keyword argument was added. """ if map is None: map = {} + if validator is None: + validator = _validator names_str = f"allowed names: {' '.join(names)}" if SCons.Util.is_List(default): default = ','.join(default) help = '\n '.join( (help, '(all|none|comma-separated list of names)', names_str)) - return key, help, default, None, lambda val: _converter(val, names, map) + return key, help, default, validator, lambda val: _converter(val, names, map) # Local Variables: # tab-width:4 diff --git a/SCons/Variables/ListVariableTests.py b/SCons/Variables/ListVariableTests.py index 54aad051d1..62ce879b75 100644 --- a/SCons/Variables/ListVariableTests.py +++ b/SCons/Variables/ListVariableTests.py @@ -38,7 +38,7 @@ def test_ListVariable(self) -> None: assert o.key == 'test', o.key assert o.help == 'test option help\n (all|none|comma-separated list of names)\n allowed names: one two three', repr(o.help) assert o.default == 'all', o.default - assert o.validator is None, o.validator + assert o.validator is not None, o.validator assert o.converter is not None, o.converter opts = SCons.Variables.Variables() @@ -52,9 +52,15 @@ def test_ListVariable(self) -> None: def test_converter(self) -> None: """Test the ListVariable converter""" opts = SCons.Variables.Variables() - opts.Add(SCons.Variables.ListVariable('test', 'test option help', 'all', - ['one', 'two', 'three'], - {'ONE':'one', 'TWO':'two'})) + opts.Add( + SCons.Variables.ListVariable( + 'test', + 'test option help', + 'all', + ['one', 'two', 'three'], + {'ONE': 'one', 'TWO': 'two'}, + ) + ) o = opts.options[0] @@ -101,8 +107,12 @@ def test_converter(self) -> None: x = o.converter('three,ONE,TWO') assert str(x) == 'all', x - with self.assertRaises(ValueError): - x = o.converter('no_match') + # invalid value should convert (no change) without error + x = o.converter('no_match') + assert str(x) == 'no_match', x + # ... and fail to validate + with self.assertRaises(SCons.Errors.UserError): + z = o.validator('test', 'no_match', {"test": x}) def test_copy(self) -> None: """Test copying a ListVariable like an Environment would""" diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 57d38e8ee0..c51936832f 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -5203,7 +5203,7 @@ converted to lower case. - ListVariable(key, help, default, names, [map]) + ListVariable(key, help, default, names, [map, validator]) Set up a variable @@ -5225,6 +5225,8 @@ separated by commas. default may be specified either as a string of comma-separated value, or as a list of values. + + The optional map argument is a dictionary @@ -5236,6 +5238,14 @@ list. (Note that the additional values accepted through the use of a map are not reflected in the generated help message). + +The optional validator argument +can be used to specify a custom validator callback function, +as described for Add. +The default is to use an internal validator routine. + +New in 4.8.0: validator. + diff --git a/test/Variables/BoolVariable.py b/test/Variables/BoolVariable.py index c63322b29a..aa01a30a14 100644 --- a/test/Variables/BoolVariable.py +++ b/test/Variables/BoolVariable.py @@ -39,10 +39,7 @@ def check(expect): test.write(SConstruct_path, """\ -from SCons.Variables.BoolVariable import BoolVariable - -BV = BoolVariable - +from SCons.Variables.BoolVariable import BoolVariable as BV from SCons.Variables import BoolVariable opts = Variables(args=ARGUMENTS) @@ -51,7 +48,8 @@ def check(expect): BV('profile', 'create profiling informations', False), ) -env = Environment(variables=opts) +_ = DefaultEnvironment(tools=[]) +env = Environment(variables=opts, tools=[]) Help(opts.GenerateHelpText(env)) print(env['warnings']) @@ -69,7 +67,7 @@ def check(expect): expect_stderr = """ scons: *** Error converting option: 'warnings' Invalid value for boolean variable: 'irgendwas' -""" + test.python_file_line(SConstruct_path, 13) +""" + test.python_file_line(SConstruct_path, 11) test.run(arguments='warnings=irgendwas', stderr=expect_stderr, status=2) diff --git a/test/Variables/EnumVariable.py b/test/Variables/EnumVariable.py index 111fff3504..066df741be 100644 --- a/test/Variables/EnumVariable.py +++ b/test/Variables/EnumVariable.py @@ -38,9 +38,7 @@ def check(expect): assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect) test.write(SConstruct_path, """\ -from SCons.Variables.EnumVariable import EnumVariable -EV = EnumVariable - +from SCons.Variables.EnumVariable import EnumVariable as EV from SCons.Variables import EnumVariable list_of_libs = Split('x11 gl qt ical') @@ -58,7 +56,8 @@ def check(expect): map={}, ignorecase=2), # make lowercase ) -env = Environment(variables=opts) +_ = DefaultEnvironment(tools=[]) +env = Environment(variables=opts, tools=[]) Help(opts.GenerateHelpText(env)) print(env['debug']) @@ -78,19 +77,19 @@ def check(expect): expect_stderr = """ scons: *** Invalid value for enum variable 'debug': 'FULL'. Valid values are: ('yes', 'no', 'full') -""" + test.python_file_line(SConstruct_path, 21) +""" + test.python_file_line(SConstruct_path, 20) test.run(arguments='debug=FULL', stderr=expect_stderr, status=2) expect_stderr = """ scons: *** Invalid value for enum variable 'guilib': 'irgendwas'. Valid values are: ('motif', 'gtk', 'kde') -""" + test.python_file_line(SConstruct_path, 21) +""" + test.python_file_line(SConstruct_path, 20) test.run(arguments='guilib=IrGeNdwas', stderr=expect_stderr, status=2) expect_stderr = """ scons: *** Invalid value for enum variable 'some': 'irgendwas'. Valid values are: ('xaver', 'eins') -""" + test.python_file_line(SConstruct_path, 21) +""" + test.python_file_line(SConstruct_path, 20) test.run(arguments='some=IrGeNdwas', stderr=expect_stderr, status=2) diff --git a/test/Variables/ListVariable.py b/test/Variables/ListVariable.py index 2bd032733f..52f1bc56ef 100644 --- a/test/Variables/ListVariable.py +++ b/test/Variables/ListVariable.py @@ -43,9 +43,7 @@ def check(expect): test.write(SConstruct_path, """\ -from SCons.Variables.ListVariable import ListVariable -LV = ListVariable - +from SCons.Variables.ListVariable import ListVariable as LV from SCons.Variables import ListVariable list_of_libs = Split('x11 gl qt ical') @@ -59,10 +57,10 @@ def check(expect): names = list_of_libs, map = {'GL':'gl', 'QT':'qt'}), LV('listvariable', 'listvariable help', 'all', names=['l1', 'l2', 'l3']) - ) +) -DefaultEnvironment(tools=[]) # test speedup -env = Environment(variables=opts) +_ = DefaultEnvironment(tools=[]) # test speedup +env = Environment(variables=opts, tools=[]) opts.Save(optsfile, env) Help(opts.GenerateHelpText(env)) @@ -113,39 +111,34 @@ def check(expect): expect_stderr = """ -scons: *** Error converting option: 'shared' -Invalid value(s) for option: foo -""" + test.python_file_line(SConstruct_path, 20) +scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,x11,all,none +""" + test.python_file_line(SConstruct_path, 18) test.run(arguments='shared=foo', stderr=expect_stderr, status=2) # be paranoid in testing some more combinations expect_stderr = """ -scons: *** Error converting option: 'shared' -Invalid value(s) for option: foo -""" + test.python_file_line(SConstruct_path, 20) +scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,x11,all,none +""" + test.python_file_line(SConstruct_path, 18) test.run(arguments='shared=foo,ical', stderr=expect_stderr, status=2) expect_stderr = """ -scons: *** Error converting option: 'shared' -Invalid value(s) for option: foo -""" + test.python_file_line(SConstruct_path, 20) +scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,x11,all,none +""" + test.python_file_line(SConstruct_path, 18) test.run(arguments='shared=ical,foo', stderr=expect_stderr, status=2) expect_stderr = """ -scons: *** Error converting option: 'shared' -Invalid value(s) for option: foo -""" + test.python_file_line(SConstruct_path, 20) +scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,x11,all,none +""" + test.python_file_line(SConstruct_path, 18) test.run(arguments='shared=ical,foo,x11', stderr=expect_stderr, status=2) expect_stderr = """ -scons: *** Error converting option: 'shared' -Invalid value(s) for option: foo,bar -""" + test.python_file_line(SConstruct_path, 20) +scons: *** Invalid value(s) for variable 'shared': 'foo,bar'. Valid values are: gl,ical,qt,x11,all,none +""" + test.python_file_line(SConstruct_path, 18) test.run(arguments='shared=foo,x11,,,bar', stderr=expect_stderr, status=2) diff --git a/test/Variables/PackageVariable.py b/test/Variables/PackageVariable.py index 47ee01bcc9..64e0fa878c 100644 --- a/test/Variables/PackageVariable.py +++ b/test/Variables/PackageVariable.py @@ -39,9 +39,7 @@ def check(expect): assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect) test.write(SConstruct_path, """\ -from SCons.Variables.PackageVariable import PackageVariable -PV = PackageVariable - +from SCons.Variables.PackageVariable import PackageVariable as PV from SCons.Variables import PackageVariable opts = Variables(args=ARGUMENTS) @@ -52,7 +50,8 @@ def check(expect): PV('package', 'help for package', 'yes'), ) -env = Environment(variables=opts) +_ = DefaultEnvironment(tools=[]) +env = Environment(variables=opts, tools=[]) Help(opts.GenerateHelpText(env)) print(env['x11']) @@ -73,7 +72,7 @@ def check(expect): expect_stderr = """ scons: *** Path does not exist for variable 'x11': '/non/existing/path/' -""" + test.python_file_line(SConstruct_path, 14) +""" + test.python_file_line(SConstruct_path, 13) test.run(arguments='x11=/non/existing/path/', stderr=expect_stderr, status=2) diff --git a/test/Variables/PathVariable.py b/test/Variables/PathVariable.py index 265f48f52f..61b21b3573 100644 --- a/test/Variables/PathVariable.py +++ b/test/Variables/PathVariable.py @@ -47,9 +47,7 @@ def check(expect): libpath = os.path.join(workpath, 'lib') test.write(SConstruct_path, """\ -from SCons.Variables.PathVariable import PathVariable -PV = PathVariable - +from SCons.Variables.PathVariable import PathVariable as PV from SCons.Variables import PathVariable qtdir = r'%s' @@ -60,8 +58,8 @@ def check(expect): PV('qt_libraries', 'where the Qt library is installed', r'%s'), ) -DefaultEnvironment(tools=[]) # test speedup -env = Environment(variables=opts) +_ = DefaultEnvironment(tools=[]) # test speedup +env = Environment(variables=opts, tools=[]) Help(opts.GenerateHelpText(env)) print(env['qtdir']) @@ -92,7 +90,7 @@ def check(expect): check([qtpath, libpath, libpath]) qtpath = os.path.join(workpath, 'non', 'existing', 'path') -SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 15)[:-1] +SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 13)[:-1] expect_stderr = """ scons: *** Path for variable 'qtdir' does not exist: %(qtpath)s diff --git a/test/Variables/Variables.py b/test/Variables/Variables.py index 77426c5913..d585b57a42 100644 --- a/test/Variables/Variables.py +++ b/test/Variables/Variables.py @@ -23,12 +23,21 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test general behavior of Variables including save files. + +Note this test is coded to expect a compiler tool to have run +so that CC and CCFLAGS are set. The first test "run" collects +those values and uses them as a baseline for the actual tests. +We should be able to mock that in some way. +""" + import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) # test speedup +_ = DefaultEnvironment(tools=[]) # test speedup env = Environment() print(env['CC']) print(" ".join(env['CCFLAGS'])) @@ -277,7 +286,7 @@ def checkSave(file, expected): names = ['a','b','c',]) DefaultEnvironment(tools=[]) # test speedup -env = Environment(variables=opts) +env = Environment(variables=opts, tools=[]) print(env['RELEASE_BUILD']) print(env['DEBUG_BUILD']) diff --git a/test/Variables/chdir.py b/test/Variables/chdir.py index 2c553e2374..ed7cf2be3e 100644 --- a/test/Variables/chdir.py +++ b/test/Variables/chdir.py @@ -43,7 +43,8 @@ SConscript_contents = """\ Import("opts") -env = Environment() +_ = DefaultEnvironment(tools=[]) +env = Environment(tools=[]) opts.Update(env) print("VARIABLE = "+repr(env['VARIABLE'])) """ diff --git a/test/Variables/help.py b/test/Variables/help.py index 186a173d7b..84a405a9d3 100644 --- a/test/Variables/help.py +++ b/test/Variables/help.py @@ -92,7 +92,8 @@ PathVariable('qt_libraries', 'where the Qt library is installed', r'%(libdirvar)s'), ) -env = Environment(variables=opts) +_ = DefaultEnvironment(tools=[]) +env = Environment(variables=opts, tools=[]) Help(opts.GenerateHelpText(env)) print(env['warnings']) diff --git a/test/Variables/import.py b/test/Variables/import.py index 8db7624476..833b299c9f 100644 --- a/test/Variables/import.py +++ b/test/Variables/import.py @@ -45,7 +45,8 @@ SConscript_contents = """\ Import("opts") -env = Environment() +_ = DefaultEnvironment(tools=[]) +env = Environment(tools=[]) opts.Update(env) print("VARIABLE = %s"%env.get('VARIABLE')) """ From 2303fb98830df908322edce751717c51ef82062d Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 15 Jun 2024 12:53:50 -0600 Subject: [PATCH 071/386] Update to current fastest env setitem Updated and re-ran benchmark tests for SubstitutionEnvironment.__setitem__. Added test for new (Python 3.0) string.isidentifier() method. That's actually exactly what we want, and it times out as the fastest method when combined with a membership test if the variable is already defined. Tweaked some comments about this performance consideration, and did other updates in bench/. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 ++ RELEASE.txt | 3 ++ SCons/Environment.py | 37 ++++++++-------- SCons/Util/envs.py | 5 ++- SCons/Variables/__init__.py | 2 +- bench/bench.py | 20 ++++----- bench/env.__setitem__.py | 32 +++++++++++--- bench/is_types.py | 85 ++++++++++++------------------------- bench/lvars-gvars.py | 10 ++--- 9 files changed, 97 insertions(+), 100 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d0eb245f54..5efbe84c90 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -99,6 +99,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER SCons.Environment to SCons.Util to avoid the chance of import loops. Variables and Environment both use the routine and Environment() uses a Variables() object so better to move to a safer location. + - Performance tweak: the __setitem__ method of an Environment, used for + setting construction variables, now uses the string method isidentifier + to validate the name (updated from microbenchmark results). RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 8955e465d0..df6f7c1963 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -69,6 +69,9 @@ IMPROVEMENTS - Make the testing framework a little more resilient: the temporary directory for tests now includes a component named "scons" which can be given to antivirus software to exclude. +- Performance tweak: the __setitem__ method of an Environment, used for + setting construction variables, now uses the string method isidentifier + to validate the name (updated from microbenchmark results). PACKAGING --------- diff --git a/SCons/Environment.py b/SCons/Environment.py index 6669bf8ada..9322450627 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -76,7 +76,6 @@ to_String_for_subst, uniquer_hashables, ) -from SCons.Util.envs import is_valid_construction_var from SCons.Util.sctyping import ExecutorType class _Null: @@ -581,26 +580,20 @@ def __getitem__(self, key): return self._dict[key] def __setitem__(self, key, value): - # This is heavily used. This implementation is the best we have - # according to the timings in bench/env.__setitem__.py. - # - # The "key in self._special_set_keys" test here seems to perform - # pretty well for the number of keys we have. A hard-coded - # list worked a little better in Python 2.5, but that has the - # disadvantage of maybe getting out of sync if we ever add more - # variable names. - # So right now it seems like a good trade-off, but feel free to - # revisit this with bench/env.__setitem__.py as needed (and - # as newer versions of Python come out). if key in self._special_set_keys: self._special_set[key](self, key, value) else: - # If we already have the entry, then it's obviously a valid - # key and we don't need to check. If we do check, using a - # global, pre-compiled regular expression directly is more - # efficient than calling another function or a method. - if key not in self._dict and not is_valid_construction_var(key): - raise UserError("Illegal construction variable `%s'" % key) + # Performance: since this is heavily used, try to avoid checking + # if the variable is valid unless necessary. bench/__setitem__.py + # times a bunch of different approaches. Based the most recent + # run, against Python 3.6-3.13(beta), the best we can do across + # different combinations of actions is to use a membership test + # to see if we already have the variable, if so it must already + # have been checked, so skip; if we do check, "isidentifier()" + # (new in Python 3 so wasn't in benchmark until recently) + # on the key is the best. + if key not in self._dict and not key.isidentifier(): + raise UserError(f"Illegal construction variable {key!r}") self._dict[key] = value def get(self, key, default=None): @@ -2579,8 +2572,12 @@ def __getitem__(self, key): return self.__dict__['__subject'].__getitem__(key) def __setitem__(self, key, value): - if not is_valid_construction_var(key): - raise UserError("Illegal construction variable `%s'" % key) + # This doesn't have the same performance equation as a "real" + # environment: in an override you're basically just writing + # new stuff; it's not a common case to be changing values already + # set in the override dict, so don't spend time checking for existance. + if not key.isidentifier(): + raise UserError(f"Illegal construction variable {key!r}") self.__dict__['overrides'][key] = value def __delitem__(self, key): diff --git a/SCons/Util/envs.py b/SCons/Util/envs.py index 68a40488c6..2640ef5c19 100644 --- a/SCons/Util/envs.py +++ b/SCons/Util/envs.py @@ -330,10 +330,13 @@ def AddMethod(obj, function: Callable, name: Optional[str] = None) -> None: setattr(obj, name, method) +# This routine is used to validate that a construction var name can be used +# as a Python identifier, which we require. However, Python 3 introduced an +# isidentifier() string method so there's really not any need for it now. _is_valid_var_re = re.compile(r'[_a-zA-Z]\w*$') def is_valid_construction_var(varstr: str) -> bool: - """Return True if *varstr* is a legitimate construction variable.""" + """Return True if *varstr* is a legitimate name of a construction variable.""" return bool(_is_valid_var_re.match(varstr)) # Local Variables: diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 1c41130025..1337d14f6e 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -131,7 +131,7 @@ def _do_add( option.key = key # TODO: normalize to not include key in aliases. Currently breaks tests. option.aliases = [key,] - if not SCons.Util.is_valid_construction_var(option.key): + if not option.key.isidentifier(): raise SCons.Errors.UserError(f"Illegal Variables key {option.key!r}") option.help = help option.default = default diff --git a/bench/bench.py b/bench/bench.py index 5adac230cd..bafe5a29d4 100644 --- a/bench/bench.py +++ b/bench/bench.py @@ -62,10 +62,10 @@ -h, --help Display this help and exit -i ITER, --iterations ITER Run each code snippet ITER times --time Use the time.time function - -r RUNS, --runs RUNS Average times for RUNS invocations of + -r RUNS, --runs RUNS Average times for RUNS invocations of """ -# How many times each snippet of code will be (or should be) run by the +# How many times each snippet of code will be (or should be) run by the # functions under test to gather the time (the "inner loop"). Iterations = 1000 @@ -191,19 +191,17 @@ def display(func, result_label, results): # pprint(results_dict) -# breakpoint() + tests = [label for label, args, kw in Data] -columns = ['Python Version', 'Implementation', 'Test'] + tests +columns = ['Python Version', 'Implementation'] + tests with open(results_filename, 'a') as r: - print("Python Version,%s" % ".".join(columns), file=r) - # print("Python Version,%s" % ".".join(columns)) + print(",".join(columns), file=r) - for implementation in results_dict.keys(): + for implementation in results_dict: + print(f'{py_ver_string},"{implementation}"', file=r, end='') for test in tests: - print(f'{py_ver_string},"{implementation}","{test}",%8.3f' % results_dict[implementation][test], file=r) - # print(f'{py_ver_string},"{implementation}","{test}",%8.3f' % results_dict[implementation][test]) - - + print(',%8.3f' % results_dict[implementation][test], file=r, end='') + print(file=r) # Local Variables: # tab-width:4 diff --git a/bench/env.__setitem__.py b/bench/env.__setitem__.py index 2cd0da826f..e915e46338 100644 --- a/bench/env.__setitem__.py +++ b/bench/env.__setitem__.py @@ -57,14 +57,14 @@ def times(num=1000000, init='', title='Results:', **statements): script_dir = os.path.split(filename)[0] if script_dir: script_dir = script_dir + '/' -sys.path = [os.path.abspath(script_dir + '../src/engine')] + sys.path +sys.path = [os.path.abspath(script_dir + '..')] + sys.path import SCons.Errors import SCons.Environment import SCons.Util is_valid_construction_var = SCons.Util.is_valid_construction_var -global_valid_var = re.compile(r'[_a-zA-Z]\w*$') +global_valid_var = SCons.Util.envs._is_valid_var_re # The classes with different __setitem__() implementations that we're # going to horse-race. @@ -105,7 +105,7 @@ class Environment: 'SOURCES' : None, } _special_set_keys = list(_special_set.keys()) - _valid_var = re.compile(r'[_a-zA-Z]\w*$') + _valid_var = global_valid_var def __init__(self, **kw): self._dict = kw @@ -247,8 +247,8 @@ def __setitem__(self, key, value): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value -class env_Best_has_key(Environment): - """Best __setitem__(), with has_key""" +class env_Best_has_key_global_regex(Environment): + """Best __setitem__(), with membership test and global regex""" def __setitem__(self, key, value): if key in self._special_set: self._special_set[key](self, key, value) @@ -258,6 +258,18 @@ def __setitem__(self, key, value): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value +class env_Best_has_key_function(Environment): + """Best __setitem__(), with membership_test and validator function""" + def __setitem__(self, key, value): + if key in self._special_set: + self._special_set[key](self, key, value) + else: + if key not in self._dict \ + and not is_valid_construction_var(key): + raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) + self._dict[key] = value + + class env_Best_list(Environment): """Best __setitem__(), with a list""" def __setitem__(self, key, value): @@ -284,6 +296,16 @@ def __setitem__(self, key, value): raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) self._dict[key] = value +class env_Best_isidentifier(Environment): + """Best __setitem__(), with membership test and isidentifier""" + def __setitem__(self, key, value): + if key in self._special_set: + self._special_set[key](self, key, value) + else: + if key not in self._dict and not key.isidentifier(): + raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) + self._dict[key] = value + # We'll use the names of all the env_* classes we find later to build # the dictionary of statements to be timed, and the import statement # that the timer will use to get at these classes. diff --git a/bench/is_types.py b/bench/is_types.py index 01db768ca9..257fa915c4 100644 --- a/bench/is_types.py +++ b/bench/is_types.py @@ -2,10 +2,11 @@ # # Benchmarks for testing various possible implementations # of the is_Dict(), is_List() and is_String() functions in -# src/engine/SCons/Util.py. +# SCons/Util.py. import types -from collections import UserDict, UserList, UserString +from collections import UserDict, UserList, UserString, deque +from collections.abc import Iterable, MappingView, MutableMapping, MutableSequence DictType = dict ListType = list @@ -28,19 +29,20 @@ def original_is_String(e): # New candidates that explicitly check for whether the object is an # InstanceType before calling isinstance() on the corresponding User* -# type. -# InstanceType was only for old-style classes, so absent in Python 3 -# this this is no different than the previous +# type. Update: meaningless in Python 3, InstanceType was only for +# old-style classes, so these are just removed. +# XXX -def checkInstanceType_is_Dict(e): - return isinstance(e, (dict, UserDict)) +# New candidates that try more generic names from collections: -def checkInstanceType_is_List(e): - return isinstance(e, (list, UserList)) +def new_is_Dict(e): + return isinstance(e, MutableMapping) -def checkInstanceType_is_String(e): - return isinstance(e, (str, UserString)) +def new_is_List(e): + return isinstance(e, MutableSequence) +def new_is_String(e): + return isinstance(e, (str, UserString)) # Improved candidates that cache the type(e) result in a variable # before doing any checks. @@ -51,7 +53,7 @@ def cache_type_e_is_Dict(e): def cache_type_e_is_List(e): t = type(e) - return t is list or isinstance(e, UserList) + return t is list or isinstance(e, UserList or isinstance(e, deque)) def cache_type_e_is_String(e): t = type(e) @@ -68,7 +70,7 @@ def global_cache_type_e_is_Dict(e): def global_cache_type_e_is_List(e): t = type(e) - return t is ListType or isinstance(e, UserList) + return t is ListType or isinstance(e, (UserList, deque)) def global_cache_type_e_is_String(e): t = type(e) @@ -77,30 +79,10 @@ def global_cache_type_e_is_String(e): # Alternative that uses a myType() function to map the User* objects # to their corresponding underlying types. - -instanceTypeMap = { - UserDict : dict, - UserList : list, - UserString : str, -} - -def myType(obj): - t = type(obj) - if t is types.InstanceType: - t = instanceTypeMap.get(obj.__class__, t) - return t - -def myType_is_Dict(e): - return myType(e) is dict - -def myType_is_List(e): - return myType(e) is list - -def myType_is_String(e): - return myType(e) is str - +# Again, since this used InstanceType, it's not useful for Python 3. +# These are the actual test entry points def Func01(obj): """original_is_String""" @@ -118,19 +100,19 @@ def Func03(obj): original_is_Dict(obj) def Func04(obj): - """checkInstanceType_is_String""" + """new_is_String""" for i in IterationList: - checkInstanceType_is_String(obj) + new_is_String(obj) def Func05(obj): - """checkInstanceType_is_List""" + """new_is_List""" for i in IterationList: - checkInstanceType_is_List(obj) + new_is_List(obj) def Func06(obj): - """checkInstanceType_is_Dict""" + """new_is_Dict""" for i in IterationList: - checkInstanceType_is_Dict(obj) + new_is_Dict(obj) def Func07(obj): """cache_type_e_is_String""" @@ -162,22 +144,6 @@ def Func12(obj): for i in IterationList: global_cache_type_e_is_Dict(obj) -#def Func13(obj): -# """myType_is_String""" -# for i in IterationList: -# myType_is_String(obj) -# -#def Func14(obj): -# """myType_is_List""" -# for i in IterationList: -# myType_is_List(obj) -# -#def Func15(obj): -# """myType_is_Dict""" -# for i in IterationList: -# myType_is_Dict(obj) - - # Data to pass to the functions on each run. Each entry is a # three-element tuple: @@ -217,6 +183,11 @@ class A: (UserList([]),), {}, ), + ( + "deque", + (deque([]),), + {}, + ), ( "UserDict", (UserDict({}),), diff --git a/bench/lvars-gvars.py b/bench/lvars-gvars.py index 0a81337581..91d76a0c43 100644 --- a/bench/lvars-gvars.py +++ b/bench/lvars-gvars.py @@ -23,9 +23,9 @@ """ Functions and data for timing different idioms for fetching a keyword -value from a pair of dictionaries for localand global values. This was +value from a pair of dictionaries for local and global values. This was used to select how to most efficiently expand single $KEYWORD strings -in src/engine/SCons/Subst.py. +in SCons/Subst.py (StringSubber and ListSubber). """ def Func1(var, gvars, lvars): @@ -40,7 +40,7 @@ def Func1(var, gvars, lvars): x = '' def Func2(var, gvars, lvars): - """lvars has_key(), gvars try:-except:""" + """lvars membership test, gvars try:-except:""" for i in IterationList: if var in lvars: x = lvars[var] @@ -51,7 +51,7 @@ def Func2(var, gvars, lvars): x = '' def Func3(var, gvars, lvars): - """lvars has_key(), gvars has_key()""" + """lvars membership test, gvars membership test)""" for i in IterationList: if var in lvars: x = lvars[var] @@ -71,7 +71,7 @@ def Func4(var, gvars, lvars): def Func5(var, gvars, lvars): """Chained get with default values""" for i in IterationList: - x = lvars.get(var,gvars.get(var,'')) + x = lvars.get(var, gvars.get(var, '')) # Data to pass to the functions on each run. Each entry is a From 78a26bb2cd5c9e794dd491821c11d7ba33c452ed Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 15 Jun 2024 13:26:35 -0600 Subject: [PATCH 072/386] Fix test for bad construction var Didn't notice that the changes for env.__setitem__ caused a quote mark to flip in the error output, failing one test (a back-quote went to a single regular quote). Updated the test, and snuck in the tools-performance-hack. Signed-off-by: Mats Wichmann --- test/bad-variables.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/test/bad-variables.py b/test/bad-variables.py index 11f2be1af7..06f106447c 100644 --- a/test/bad-variables.py +++ b/test/bad-variables.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__" """ Test that setting illegal construction variables fails in ways that are @@ -37,13 +36,14 @@ SConscript_path = test.workpath('SConscript') test.write(SConstruct_path, """\ -env = Environment() +_ = DefaultEnvironment(tools=[]) +env = Environment(tools=[]) env['foo-bar'] = 1 """) expect_stderr = """ -scons: *** Illegal construction variable `foo-bar' -""" + test.python_file_line(SConstruct_path, 2) +scons: *** Illegal construction variable 'foo-bar' +""" + test.python_file_line(SConstruct_path, 3) test.run(arguments='.', status=2, stderr=expect_stderr) @@ -54,14 +54,15 @@ """) test.write('SConscript', """\ -env = Environment() +_ = DefaultEnvironment(tools=[]) +env = Environment(tools=[]) env['foo(bar)'] = 1 """) expect_stderr = """ -scons: *** Illegal construction variable `foo(bar)' -""" + test.python_file_line(SConscript_path, 2) +scons: *** Illegal construction variable 'foo(bar)' +""" + test.python_file_line(SConscript_path, 3) test.run(arguments='.', status=2, stderr=expect_stderr) From 2093f7a1bd5d2cd5a0e603b02ad672fb0fb3fafe Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 17 Jun 2024 14:57:09 -0600 Subject: [PATCH 073/386] AddOption now recognizes "settable" option AddOption and the internal add_local_option which AddOption calls now recognize a "settable" keyword argument to indicate a project-added option can also be modified using SetOption. There was a TODO in SCons/Script/SConsOptions.py about removing three hardcoded ninja options in the "settable" list once this change was made. When that was done it turned out one test failed, because it called the SetOption before the ninja tool was initialized. Logic reordered in the test, but this is a thing to watch out for. Closes #3983. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 + RELEASE.txt | 3 + SCons/Script/Main.py | 43 ++++-- SCons/Script/Main.xml | 40 ++++-- SCons/Script/SConsOptions.py | 136 ++++++++++-------- SCons/Tool/ninja/Utils.py | 3 + doc/user/command-line.xml | 20 +-- test/AddOption/basic.py | 57 ++++---- test/AddOption/help.py | 29 ++-- test/GetOption/BadSetOption.py | 9 +- .../sconstruct_ninja_determinism | 4 +- 11 files changed, 212 insertions(+), 135 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d0eb245f54..8005b68d92 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -99,6 +99,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER SCons.Environment to SCons.Util to avoid the chance of import loops. Variables and Environment both use the routine and Environment() uses a Variables() object so better to move to a safer location. + - AddOption and the internal add_local_option which AddOption calls now + recognize a "settable" keyword argument to indicate a project-added + option can also be modified using SetOption. Fixes #3983. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 8955e465d0..4d61af3674 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -53,6 +53,9 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - The Variables object Add method now accepts a subst keyword argument (defaults to True) which can be set to inhibit substitution prior to calling the variable's converter and validator. +- AddOption and the internal add_local_option which AddOption calls now + recognize a "settable" keyword argument to indicate a project-added + option can also be modified using SetOption. FIXES ----- diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index d00da42cc8..a5f8f42c2d 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -41,7 +41,7 @@ import traceback import platform import threading -from typing import Optional, List +from typing import Optional, List, TYPE_CHECKING import SCons.CacheDir import SCons.Debug @@ -59,6 +59,8 @@ import SCons.Util import SCons.Warnings import SCons.Script.Interactive +if TYPE_CHECKING: + from SCons.Script import SConsOption from SCons.Util.stats import count_stats, memory_stats, time_stats, ENABLE_JSON, write_scons_stats_file, JSON_OUTPUT_FILE from SCons import __version__ as SConsVersion @@ -174,6 +176,7 @@ def __call__(self, node) -> None: ProgressObject = SCons.Util.Null() def Progress(*args, **kw) -> None: + """Show progress during building - Public API.""" global ProgressObject ProgressObject = Progressor(*args, **kw) @@ -501,29 +504,47 @@ def __getattr__(self, attr): # TODO: to quiet checkers, FakeOptionParser should also define # raise_exception_on_error, preserve_unknown_options, largs and parse_args - def add_local_option(self, *args, **kw) -> None: + def add_local_option(self, *args, **kw) -> "SConsOption": pass OptionsParser = FakeOptionParser() -def AddOption(*args, **kw): +def AddOption(*args, settable: bool = False, **kw) -> "SConsOption": + """Add a local option to the option parser - Public API. + + If the *settable* parameter is true, the option will be included in the + list of settable options; all other keyword arguments are passed on to + :meth:`~SCons.Script.SConsOptions.SConsOptionParser.add_local_option`. + + .. versionchanged:: 4.8.0 + The *settable* parameter added to allow including the new option + to the table of options eligible to use :func:`SetOption`. + + """ if 'default' not in kw: kw['default'] = None + kw['settable'] = settable result = OptionsParser.add_local_option(*args, **kw) return result -def GetOption(name): +def GetOption(name: str): + """Get the value from an option - Public API.""" return getattr(OptionsParser.values, name) -def SetOption(name, value): +def SetOption(name: str, value): + """Set the value of an option - Public API.""" return OptionsParser.values.set_option(name, value) -def DebugOptions(json=None): - """ - API to allow specifying options to SCons debug logic - Currently only json is supported which changes the - json file written by --debug=json from the default +def DebugOptions(json: Optional[str] = None) -> None: + """Specify options to SCons debug logic - Public API. + + Currently only *json* is supported, which changes the JSON file + written to if the ``--debug=json`` command-line option is specified + to the value supplied. + + .. versionadded:: 4.6.0 + """ if json is not None: json_node = SCons.Defaults.DefaultEnvironment().arg2nodes(json) @@ -540,7 +561,7 @@ def DebugOptions(json=None): raise SCons.Errors.UserError(f"Unable to create directory for JSON debug output file: {SCons.Util.stats.JSON_OUTPUT_FILE}") -def ValidateOptions(throw_exception: bool=False) -> None: +def ValidateOptions(throw_exception: bool = False) -> None: """Validate options passed to SCons on the command line. Checks that all options given on the command line are known to this diff --git a/SCons/Script/Main.xml b/SCons/Script/Main.xml index 36e7d305c5..6b63e91bfc 100644 --- a/SCons/Script/Main.xml +++ b/SCons/Script/Main.xml @@ -93,21 +93,25 @@ the option value may be accessed using &f-link-GetOption; or &f-link-env-GetOption;. -&f-link-SetOption; is not currently supported for -options added with &f-AddOption;. - +override a value set in an SConscript file. + + + +Changed in 4.8.0: added the +settable keyword argument +to enable an added option to be settable via &SetOption;. @@ -196,6 +200,7 @@ Allows setting options for SCons debug options. Currently the only supported val DebugOptions(json='#/build/output/scons_stats.json') +New in version 4.6.0.
@@ -334,9 +339,10 @@ atexit.register(print_build_failures) Query the value of settable options which may have been set -on the command line, or by using the &f-link-SetOption; function. +on the command line, via option defaults, +or by using the &f-link-SetOption; function. The value of the option is returned in a type matching how the -option was declared - see the documentation for the +option was declared - see the documentation of the corresponding command line option for information about each specific option. @@ -801,6 +807,16 @@ are not settable using &f-SetOption; since those files must be read in order to find the &f-SetOption; call in the first place. + +For project-specific options (sometimes called +local options) +added via an &f-link-AddOption; call, +&f-SetOption; is available only after the +&f-AddOption; call has completed successfully, +and only if that call included the +settable=True argument. + + The settable variables with their associated command-line options are: diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index e8e5cbf4e9..780c83ba25 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -66,17 +66,18 @@ def diskcheck_convert(value): class SConsValues(optparse.Values): - """ - Holder class for uniform access to SCons options, regardless - of whether they can be set on the command line or in the - SConscript files (using the SetOption() function). + """Holder class for uniform access to SCons options. + + Usable whether or not of whether they can be set on the command line + or in the SConscript files (using the :func:`~SComs.Main.SetOption` + function). A SCons option value can originate three different ways: - 1) set on the command line; - 2) set in an SConscript file; - 3) the default setting (from the the op.add_option() - calls in the Parser() function, below). + 1) set on the command line; + 2) set in an SConscript file; + 3) the default setting (from the the ``op.add_option`` + calls in the :func:`Parser` function, below). The command line always overrides a value set in a SConscript file, which in turn always overrides default settings. Because we want @@ -87,15 +88,16 @@ class SConsValues(optparse.Values): The solution implemented in this class is to keep these different sets of settings separate (command line, SConscript file, and default) - and to override the __getattr__() method to check them in turn. + and to override the :meth:`__getattr__` method to check them in turn + (a little similar in concept to a ChainMap). This should allow the rest of the code to just fetch values as attributes of an instance of this class, without having to worry about where they came from. Note that not all command line options are settable from SConscript files, and the ones that are must be explicitly added to the - "settable" list in this class, and optionally validated and coerced - in the set_option() method. + :attr:`settable` list in this class, and optionally validated and coerced + in the :meth:`set_option` method. """ def __init__(self, defaults) -> None: @@ -103,10 +105,11 @@ def __init__(self, defaults) -> None: self.__SConscript_settings__ = {} def __getattr__(self, attr): - """ - Fetches an options value, checking first for explicit settings - from the command line (which are direct attributes), then the - SConscript file settings, then the default values. + """Fetch an options value, respecting priority rules. + + Check first for explicit settings from the command line + (which are direct attributes), then the SConscript file settings, + then the default values. """ try: return self.__dict__[attr] @@ -148,26 +151,24 @@ def __getattr__(self, attr): 'silent', 'stack_size', 'warn', - - # TODO: Remove these once we update the AddOption() API to allow setting - # added flag as settable. - # Requested settable flag in : https://github.com/SCons/scons/issues/3983 - # From experimental ninja - 'disable_execute_ninja', - 'disable_ninja', - 'skip_ninja_regen' ] def set_option(self, name, value): - """Sets an option from an SConscript file. + """Sets an option *name* from an SConscript file. + + Any necessary validation steps are in-line here. Validation should + be along the same lines as for options processed from the command + line - it's kind of a pain to have to duplicate. On the other + hand, we can hope that the build maintainer will be more careful + about correct SetOption values and it's not as big a deal to + have validation for everything. Raises: - UserError: invalid or malformed option ("error in your script") + UserError: the option is not settable. """ - if name not in self.settable: raise SCons.Errors.UserError( - "This option is not settable from a SConscript file: %s" % name + f"This option is not settable from an SConscript file: {name!r}" ) # the following are for options that need some extra processing @@ -247,7 +248,6 @@ def convert_value(self, opt, value): return tuple([self.check_value(opt, v) for v in value]) def process(self, opt, value, values, parser): - # First, convert the value(s) to the right type. Howl if any # value(s) are bogus. value = self.convert_value(opt, value) @@ -397,46 +397,46 @@ def _process_long_opt(self, rargs, values): option.process(opt, value, values, self) def reparse_local_options(self) -> None: - """ Re-parse the leftover command-line options. + """Re-parse the leftover command-line options. - Parse options stored in `self.largs`, so that any value + Parse options are stored in ``self.largs``, so that any value overridden on the command line is immediately available - if the user turns around and does a :func:`GetOption` right away. + if the user turns around and does a :func:`~SCons.Script.Main.GetOption` + right away. We mimic the processing of the single args in the original OptionParser :func:`_process_args`, but here we allow exact matches for long-opts only (no partial argument names!). - Otherwise there could be problems in :func:`add_local_option` + Otherwise there could be problems in :meth:`add_local_option` below. When called from there, we try to reparse the command-line arguments that 1. haven't been processed so far (`self.largs`), but 2. are possibly not added to the list of options yet. - So, when we only have a value for "--myargument" so far, - a command-line argument of "--myarg=test" would set it, + So, when we only have a value for ``--myargument`` so far, + a command-line argument of ``--myarg=test`` would set it, per the behaviour of :func:`_match_long_opt`, which allows for partial matches of the option name, as long as the common prefix appears to be unique. This would lead to further confusion, because we might want - to add another option "--myarg" later on (see issue #2929). - + to add another option ``--myarg`` later on (see issue #2929). """ rargs = [] largs_restore = [] # Loop over all remaining arguments skip = False - for l in self.largs: + for larg in self.largs: if skip: # Accept all remaining arguments as they are - largs_restore.append(l) + largs_restore.append(larg) else: - if len(l) > 2 and l[0:2] == "--": + if len(larg) > 2 and larg[0:2] == "--": # Check long option - lopt = (l,) - if "=" in l: + lopt = [larg] + if "=" in larg: # Split into option and value - lopt = l.split("=", 1) + lopt = larg.split("=", 1) if lopt[0] in self._long_opt: # Argument is already known @@ -445,26 +445,35 @@ def reparse_local_options(self) -> None: # Not known yet, so reject for now largs_restore.append('='.join(lopt)) else: - if l == "--" or l == "-": + if larg in("--", "-"): # Stop normal processing and don't # process the rest of the command-line opts - largs_restore.append(l) + largs_restore.append(larg) skip = True else: - rargs.append(l) + rargs.append(larg) # Parse the filtered list self.parse_args(rargs, self.values) - # Restore the list of remaining arguments for the + # Restore the list of leftover arguments for the # next call of AddOption/add_local_option... self.largs = self.largs + largs_restore - def add_local_option(self, *args, **kw): + def add_local_option(self, *args, **kw) -> SConsOption: """ Adds a local option to the parser. - This is initiated by an :func:`AddOption` call to add a user-defined - command-line option. We add the option to a separate option - group for the local options, creating the group if necessary. + This is initiated by an :func:`~SCons.Script.Main.AddOption` call to + add a user-defined command-line option. Add the option to a separate + option group for the local options, creating the group if necessary. + + The keyword argument *settable* is recognized specially (and + removed from *kw*). If true, the option is marked as modifiable; + by default "local" (project-added) options are not eligible for + for :func:`~SCons.Script.Main.SetOption` calls. + + .. versionchanged:: 4.8.0 + Added special handling of *settable*. + """ try: group = self.local_option_group @@ -473,6 +482,7 @@ def add_local_option(self, *args, **kw): group = self.add_option_group(group) self.local_option_group = group + settable = kw.pop('settable') result = group.add_option(*args, **kw) if result: # The option was added successfully. We now have to add the @@ -485,6 +495,8 @@ def add_local_option(self, *args, **kw): # right away. setattr(self.values.__defaults__, result.dest, result.default) self.reparse_local_options() + if settable: + SConsValues.settable.append(result.dest) return result @@ -614,7 +626,8 @@ def store_local_option_strings(self, parser, group): """Local-only version of store_option_strings. We need to replicate this so the formatter will be set up - properly if we didn't go through the "normal" store_option_strings + properly if we didn't go through the "normal" + :math:`~optparse.HelpFormatter.store_option_strings`. .. versionadded:: 4.6.0 """ @@ -633,15 +646,18 @@ def Parser(version): """Returns a parser object initialized with the standard SCons options. Add options in the order we want them to show up in the ``-H`` help - text, basically alphabetical. Each ``op.add_option()`` call - should have a consistent format:: - - op.add_option("-L", "--long-option-name", - nargs=1, type="string", - dest="long_option_name", default='foo', - action="callback", callback=opt_long_option, - help="help text goes here", - metavar="VAR") + text, basically alphabetical. For readability, Each + :meth:`~optparse.OptionContainer.add_option` call should have a + consistent format:: + + op.add_option( + "-L", "--long-option-name", + nargs=1, type="string", + dest="long_option_name", default='foo', + action="callback", callback=opt_long_option, + help="help text goes here", + metavar="VAR" + ) Even though the :mod:`optparse` module constructs reasonable default destination names from the long option names, we're going to be diff --git a/SCons/Tool/ninja/Utils.py b/SCons/Tool/ninja/Utils.py index 126d5de0a9..cdce92895c 100644 --- a/SCons/Tool/ninja/Utils.py +++ b/SCons/Tool/ninja/Utils.py @@ -45,6 +45,7 @@ def ninja_add_command_line_options() -> None: metavar='BOOL', action="store_true", default=False, + settable=True, help='Disable automatically running ninja after scons') AddOption('--disable-ninja', @@ -52,6 +53,7 @@ def ninja_add_command_line_options() -> None: metavar='BOOL', action="store_true", default=False, + settable=True, help='Disable ninja generation and build with scons even if tool is loaded. '+ 'Also used by ninja to build targets which only scons can build.') @@ -60,6 +62,7 @@ def ninja_add_command_line_options() -> None: metavar='BOOL', action="store_true", default=False, + settable=True, help='Allow scons to skip regeneration of the ninja file and restarting of the daemon. ' + 'Care should be taken in cases where Glob is in use or SCons generated files are used in ' + 'command lines.') diff --git a/doc/user/command-line.xml b/doc/user/command-line.xml index fea1af1336..d4bb83d00d 100644 --- a/doc/user/command-line.xml +++ b/doc/user/command-line.xml @@ -417,8 +417,10 @@ foo.in - &SetOption; is not currently supported for - options added with &AddOption;. + &SetOption; works for options added with &AddOption;, + but only if they were created with + settable=True in the call to &AddOption; + (only available in SCons 4.8.0 and later). @@ -660,14 +662,12 @@ foo.in - &SetOption; is not currently supported for - options added with &AddOption;. - + + &f-link-SetOption; works for options added with &AddOption;, + but only if they were created with + settable=True in the call to &AddOption; + (only available in SCons 4.8.0 and later). + diff --git a/test/AddOption/basic.py b/test/AddOption/basic.py index 64fb7ba1c1..9fc3c4d9f5 100644 --- a/test/AddOption/basic.py +++ b/test/AddOption/basic.py @@ -24,8 +24,8 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -Verify the help text when the AddOption() function is used (and when -it's not). +Verify added options give the expected default/command line values +when fetched with GetOption. """ import TestSCons @@ -35,35 +35,44 @@ test.write('SConstruct', """\ DefaultEnvironment(tools=[]) env = Environment(tools=[]) -AddOption('--force', - action="store_true", - help='force installation (overwrite any existing files)') -AddOption('--prefix', - nargs=1, - dest='prefix', - action='store', - type='string', - metavar='DIR', - help='installation prefix') +AddOption( + '--force', + action="store_true", + help='force installation (overwrite any existing files)', +) +AddOption( + '--prefix', + nargs=1, + dest='prefix', + action='store', + type='string', + metavar='DIR', + settable=True, + help='installation prefix', +) +AddOption( + '--set', + action="store_true", + help="try SetOption of 'prefix' to '/opt/share'" +) f = GetOption('force') if f: f = "True" print(f) print(GetOption('prefix')) +if GetOption('set'): + SetOption('prefix', '/opt/share') + print(GetOption('prefix')) """) -test.run('-Q -q .', - stdout="None\nNone\n") - -test.run('-Q -q . --force', - stdout="True\nNone\n") - -test.run('-Q -q . --prefix=/home/foo', - stdout="None\n/home/foo\n") - -test.run('-Q -q . -- --prefix=/home/foo --force', - status=1, - stdout="None\nNone\n") +test.run('-Q -q .', stdout="None\nNone\n") +test.run('-Q -q . --force', stdout="True\nNone\n") +test.run('-Q -q . --prefix=/home/foo', stdout="None\n/home/foo\n") +test.run('-Q -q . -- --prefix=/home/foo --force', status=1, stdout="None\nNone\n") +# check that SetOption works on prefix... +test.run('-Q -q . --set', stdout="None\nNone\n/opt/share\n") +# but the "command line wins" rule is not violated +test.run('-Q -q . --set --prefix=/home/foo', stdout="None\n/home/foo\n/home/foo\n") test.pass_test() diff --git a/test/AddOption/help.py b/test/AddOption/help.py index 6d855bd2d2..772a381d63 100644 --- a/test/AddOption/help.py +++ b/test/AddOption/help.py @@ -23,6 +23,11 @@ # 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 the help text when the AddOption() function is used (and when +it's not). +""" + import TestSCons test = TestSCons.TestSCons() @@ -30,16 +35,20 @@ test.write('SConstruct', """\ DefaultEnvironment(tools=[]) env = Environment(tools=[]) -AddOption('--force', - action="store_true", - help='force installation (overwrite existing files)') -AddOption('--prefix', - nargs=1, - dest='prefix', - action='store', - type='string', - metavar='DIR', - help='installation prefix') +AddOption( + '--force', + action="store_true", + help='force installation (overwrite existing files)', +) +AddOption( + '--prefix', + nargs=1, + dest='prefix', + action='store', + type='string', + metavar='DIR', + help='installation prefix', +) """) expected_lines = [ diff --git a/test/GetOption/BadSetOption.py b/test/GetOption/BadSetOption.py index 7b0a33dcaf..f408e39022 100644 --- a/test/GetOption/BadSetOption.py +++ b/test/GetOption/BadSetOption.py @@ -32,7 +32,7 @@ test = TestSCons.TestSCons() badopts = ( - ("no_such_var", True, "This option is not settable from a SConscript file: no_such_var"), + ("no_such_var", True, "This option is not settable from an SConscript file: 'no_such_var'"), ("num_jobs", -22, "A positive integer is required: -22"), ("max_drift", "'Foo'", "An integer is required: 'Foo'"), ("duplicate", "'cookie'", "Not a valid duplication style: cookie"), @@ -45,11 +45,10 @@ SConstruct = "SC-" + opt test.write( SConstruct, - """\ + f"""\ DefaultEnvironment(tools=[]) -SetOption("%(opt)s", %(value)s) -""" - % locals(), +SetOption("{opt}", {value}) +""", ) expect = r"scons: *** %s" % expect test.run(arguments='-Q -f %s .' % SConstruct, stderr=None, status=2) diff --git a/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism b/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism index a7982d4067..6d206364ca 100644 --- a/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism +++ b/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism @@ -5,12 +5,10 @@ import random SetOption('experimental', 'ninja') -SetOption('skip_ninja_regen', True) DefaultEnvironment(tools=[]) - env = Environment(tools=[]) - env.Tool('ninja') +SetOption('skip_ninja_regen', True) # must wait until tool creates the option # make the dependency list vary in order. Ninja tool should sort them to be deterministic. for i in range(1, 10): From 5ca36e89419a5635a54ce66e241a48b335ba6bcf Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 19 Jun 2024 12:55:45 -0600 Subject: [PATCH 074/386] Semi-phony commit to see if AppVeyor triggers Signed-off-by: Mats Wichmann --- .appveyor.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 07b6fe1826..30783a0b55 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,7 +6,7 @@ image: # linux builds done in Travis CI for now - - Visual Studio 2017 + # - Visual Studio 2017 - Visual Studio 2019 - Visual Studio 2022 diff --git a/pyproject.toml b/pyproject.toml index 8d755dcb2b..be4988bf16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,4 +73,4 @@ target-version = ['py36'] skip-string-normalization = true [tool.mypy] -python_version = "3.6" +python_version = "3.8" From 66d0f8f2c05c4f43c0e1ec3d471b7004501d3219 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 20 Jun 2024 08:57:56 -0600 Subject: [PATCH 075/386] Tweak some docstrings for Options Also change the build matrix on AppVeyor so we can hopefully get to a pass. Signed-off-by: Mats Wichmann --- .appveyor.yml | 4 +-- SCons/Script/SConsOptions.py | 62 +++++++++++++++--------------------- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 30783a0b55..e7596da4ae 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -6,9 +6,9 @@ image: # linux builds done in Travis CI for now - # - Visual Studio 2017 + - Visual Studio 2017 - Visual Studio 2019 - - Visual Studio 2022 + #- Visual Studio 2022 XXX Temporary disable while failing on .10 versions cache: - downloads -> appveyor.yml diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index 780c83ba25..d4cc992682 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -68,15 +68,11 @@ def diskcheck_convert(value): class SConsValues(optparse.Values): """Holder class for uniform access to SCons options. - Usable whether or not of whether they can be set on the command line - or in the SConscript files (using the :func:`~SComs.Main.SetOption` - function). - A SCons option value can originate three different ways: - 1) set on the command line; - 2) set in an SConscript file; - 3) the default setting (from the the ``op.add_option`` + 1. set on the command line. + 2. set in an SConscript file via :func:`~SCons.Script.Main.SetOption`. + 3. the default setting (from the the ``op.add_option()`` calls in the :func:`Parser` function, below). The command line always overrides a value set in a SConscript file, @@ -88,11 +84,10 @@ class SConsValues(optparse.Values): The solution implemented in this class is to keep these different sets of settings separate (command line, SConscript file, and default) - and to override the :meth:`__getattr__` method to check them in turn - (a little similar in concept to a ChainMap). - This should allow the rest of the code to just fetch values as - attributes of an instance of this class, without having to worry - about where they came from. + and to override the :meth:`__getattr__` method to check them in turn. + This allows the rest of the code to just fetch values as attributes of + an instance of this class, without having to worry about where they + came from (the scheme is similar to a ``ChainMap``). Note that not all command line options are settable from SConscript files, and the ones that are must be explicitly added to the @@ -107,27 +102,22 @@ def __init__(self, defaults) -> None: def __getattr__(self, attr): """Fetch an options value, respecting priority rules. - Check first for explicit settings from the command line - (which are direct attributes), then the SConscript file settings, - then the default values. + This is a little tricky: since we're answering questions + about outselves, we have avoid lookups that would send us into + into infinite recursion, thus the ``__dict__`` stuff. """ try: - return self.__dict__[attr] + return self.__dict__[attr] except KeyError: try: return self.__dict__['__SConscript_settings__'][attr] except KeyError: try: return getattr(self.__dict__['__defaults__'], attr) - except KeyError: - # Added because with py3 this is a new class, - # not a classic class, and due to the way - # In that case it will create an object without - # __defaults__, and then query for __setstate__ - # which will throw an exception of KeyError - # deepcopy() is expecting AttributeError if __setstate__ - # is not available. - raise AttributeError(attr) + except KeyError as exc: + # Need to respond with AttributeError because + # deepcopy expects that if __setstate__ is not available. + raise AttributeError(attr) from exc # keep this list in sync with the SetOption doc in SCons/Script/Main.xml # search for UPDATE_SETOPTION_DOCS there. @@ -153,15 +143,17 @@ def __getattr__(self, attr): 'warn', ] - def set_option(self, name, value): + def set_option(self, name: str, value) -> None: """Sets an option *name* from an SConscript file. - Any necessary validation steps are in-line here. Validation should - be along the same lines as for options processed from the command - line - it's kind of a pain to have to duplicate. On the other - hand, we can hope that the build maintainer will be more careful - about correct SetOption values and it's not as big a deal to - have validation for everything. + Vvalidation steps for known (that is, defined in SCons itself) + options are in-line here. Validation should be along the same + lines as for options processed from the command line - + it's kind of a pain to have to duplicate. Project-defined options + can specify callbacks for the command-line version, but will have + no inbuilt validation here. It's up to the build system maintainer + to make sure :func:`~SCons.Script.Main.SetOption` is being used + correctly, we can't really do any better here. Raises: UserError: the option is not settable. @@ -313,9 +305,7 @@ class SConsOptionParser(optparse.OptionParser): raise_exception_on_error = False def error(self, msg): - """ - overridden OptionValueError exception handler - """ + """Overridden OptionValueError exception handler.""" if self.raise_exception_on_error: raise SConsBadOptionError(msg, self) else: @@ -399,7 +389,7 @@ def _process_long_opt(self, rargs, values): def reparse_local_options(self) -> None: """Re-parse the leftover command-line options. - Parse options are stored in ``self.largs``, so that any value + Leftover options are stored in ``self.largs``, so that any value overridden on the command line is immediately available if the user turns around and does a :func:`~SCons.Script.Main.GetOption` right away. From 8609d78eb8681778bafd4fba9383cce322ea6c1c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Jun 2024 15:31:35 -0700 Subject: [PATCH 076/386] [ci skip] fix bad merge of CHANGES blurb --- CHANGES.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index aa306222f5..d5f6f65d64 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -102,9 +102,6 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - AddOption and the internal add_local_option which AddOption calls now recognize a "settable" keyword argument to indicate a project-added option can also be modified using SetOption. Fixes #3983. - SCons.Environment to SCons.Util to avoid the chance of import loops. - Variables and Environment both use the routine and Environment() uses - a Variables() object so better to move to a safer location. - ListVariable now has a separate validator, with the functionality that was previously part of the converter. The main effect is to allow a developer to supply a custom validator, which previously From 25c015ce10b68e40720af616237411c0b5e26953 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 23 Jun 2024 15:34:24 -0700 Subject: [PATCH 077/386] [ci skip] Add note that this change may break SetOption() + ninja usage with fix --- CHANGES.txt | 4 ++++ RELEASE.txt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index d5f6f65d64..e2c11c5f62 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -102,6 +102,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - AddOption and the internal add_local_option which AddOption calls now recognize a "settable" keyword argument to indicate a project-added option can also be modified using SetOption. Fixes #3983. + NOTE: If you were using ninja and using SetOption() for ninja options + in your SConscripts prior to loading the ninja tool, you will now + see an error. The fix is to move the SetOption() to after you've loaded + the ninja tool. - ListVariable now has a separate validator, with the functionality that was previously part of the converter. The main effect is to allow a developer to supply a custom validator, which previously diff --git a/RELEASE.txt b/RELEASE.txt index 5dac018289..d6fa72926a 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -56,6 +56,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - AddOption and the internal add_local_option which AddOption calls now recognize a "settable" keyword argument to indicate a project-added option can also be modified using SetOption. + NOTE: If you were using ninja and using SetOption() for ninja options + in your SConscripts prior to loading the ninja tool, you will now + see an error. The fix is to move the SetOption() to after you've loaded + the ninja tool. - ListVariable now has a separate validator, with the functionality that was previously part of the converter. The main effect is to allow a developer to supply a custom validator, which previously From 6253094147a4044b0686c595eed465c3e7e9c5dd Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 24 Jun 2024 09:36:28 -0600 Subject: [PATCH 078/386] Adjust AppVeyor build The VS2017 x Python 3.6 combo is failing one test using xsltproc. We don't know why, but try moving the Python vesion in case it's involved (perhaps something in urllib and/or the certs/ssl that go with 3.6, since it seems to be failing to fetch a file). Signed-off-by: Mats Wichmann --- .appveyor.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index e7596da4ae..1674bade2a 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -8,7 +8,7 @@ image: # linux builds done in Travis CI for now - Visual Studio 2017 - Visual Studio 2019 - #- Visual Studio 2022 XXX Temporary disable while failing on .10 versions + #- Visual Studio 2022 # Temporary disable while failing on 14.40 build tools cache: - downloads -> appveyor.yml @@ -37,38 +37,38 @@ environment: # Test oldest and newest supported Pythons, and a subset in between. # Skipping 3.7 and 3.9 at this time - WINPYTHON: "Python312" - - WINPYTHON: "Python310" - - WINPYTHON: "Python38" - - WINPYTHON: "Python36" # remove sets of build jobs based on criteria below # to fine tune the number and platforms tested matrix: exclude: - # test python 3.6 on Visual Studio 2017 image + # XXX test python 3.6 on Visual Studio 2017 image + # test python 3.8 on Visual Studio 2017 image - image: Visual Studio 2017 WINPYTHON: "Python312" - image: Visual Studio 2017 WINPYTHON: "Python310" - image: Visual Studio 2017 - WINPYTHON: "Python38" + WINPYTHON: "Python36" - # test python 3.8 on Visual Studio 2019 image + # test python 3.10 on Visual Studio 2019 image - image: Visual Studio 2019 WINPYTHON: "Python312" - image: Visual Studio 2019 - WINPYTHON: "Python310" + WINPYTHON: "Python38" - image: Visual Studio 2019 WINPYTHON: "Python36" - # test python 3.10 and 3.11 on Visual Studio 2022 image + # test python 3.12 on Visual Studio 2022 image - image: Visual Studio 2022 - WINPYTHON: "Python36" + WINPYTHON: "Python310" - image: Visual Studio 2022 WINPYTHON: "Python38" + - image: Visual Studio 2022 + WINPYTHON: "Python36" # Remove some binaries we don't want to be found # Note this is no longer needed, git-windows bin/ is quite minimal now. From d3419391452be551dc87fc0b29218b05bc529a9e Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 10 May 2024 11:55:48 -0600 Subject: [PATCH 079/386] Standardize license header on in-use doc files [skip appveyor] There are no code changes. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ RELEASE.txt | 1 + SCons/Action.xml | 5 +-- SCons/Defaults.xml | 5 +-- SCons/Environment.xml | 5 +-- SCons/Platform/Platform.xml | 5 +-- SCons/Platform/posix.xml | 5 +-- SCons/Platform/sunos.xml | 5 +-- SCons/Platform/win32.xml | 5 +-- SCons/Scanner/Scanner.xml | 5 +-- SCons/Script/Main.xml | 5 +-- SCons/Script/SConscript.xml | 5 +-- SCons/Subst.xml | 5 +-- SCons/Tool/386asm.xml | 5 +-- SCons/Tool/DCommon.xml | 4 ++- SCons/Tool/Tool.xml | 5 +-- SCons/Tool/aixc++.xml | 5 +-- SCons/Tool/aixcc.xml | 5 +-- SCons/Tool/aixf77.xml | 5 +-- SCons/Tool/aixlink.xml | 5 +-- SCons/Tool/applelink.xml | 5 +-- SCons/Tool/ar.xml | 5 +-- SCons/Tool/as.xml | 5 +-- SCons/Tool/bcc32.xml | 5 +-- SCons/Tool/c++.xml | 5 +-- SCons/Tool/cc.xml | 5 +-- SCons/Tool/clang.xml | 27 ++-------------- SCons/Tool/clangxx.xml | 5 +-- SCons/Tool/compilation_db.xml | 6 ++-- SCons/Tool/cvf.xml | 5 +-- SCons/Tool/cyglink.xml | 5 +-- SCons/Tool/default.xml | 4 ++- SCons/Tool/dmd.xml | 5 +-- SCons/Tool/docbook/docbook.xml | 9 +++--- SCons/Tool/dvi.xml | 5 +-- SCons/Tool/dvipdf.xml | 5 +-- SCons/Tool/dvips.xml | 5 +-- SCons/Tool/f03.xml | 5 +-- SCons/Tool/f08.xml | 5 +-- SCons/Tool/f77.xml | 5 +-- SCons/Tool/f90.xml | 5 +-- SCons/Tool/f95.xml | 5 +-- SCons/Tool/fortran.xml | 5 +-- SCons/Tool/g++.xml | 5 +-- SCons/Tool/g77.xml | 5 +-- SCons/Tool/gas.xml | 5 +-- SCons/Tool/gdc.xml | 5 +-- SCons/Tool/gettext.xml | 17 +++++----- SCons/Tool/gfortran.xml | 5 +-- SCons/Tool/gnulink.xml | 5 +-- SCons/Tool/gs.xml | 5 +-- SCons/Tool/hpc++.xml | 5 +-- SCons/Tool/hpcc.xml | 5 +-- SCons/Tool/hplink.xml | 5 +-- SCons/Tool/icc.xml | 5 +-- SCons/Tool/icl.xml | 5 +-- SCons/Tool/ifl.xml | 5 +-- SCons/Tool/ifort.xml | 5 +-- SCons/Tool/ilink.xml | 5 +-- SCons/Tool/ilink32.xml | 5 +-- SCons/Tool/install.xml | 5 +-- SCons/Tool/intelc.xml | 5 +-- SCons/Tool/jar.xml | 5 ++- SCons/Tool/javac.xml | 9 +++--- SCons/Tool/javah.xml | 5 ++- SCons/Tool/latex.xml | 5 +-- SCons/Tool/ldc.xml | 5 +-- SCons/Tool/lex.xml | 27 ++-------------- SCons/Tool/link.xml | 11 ++++--- SCons/Tool/linkloc.xml | 5 +-- SCons/Tool/m4.xml | 5 +-- SCons/Tool/masm.xml | 5 +-- SCons/Tool/midl.xml | 5 +-- SCons/Tool/mingw.xml | 5 +-- SCons/Tool/msgfmt.xml | 9 +++--- SCons/Tool/msginit.xml | 13 ++++---- SCons/Tool/msgmerge.xml | 13 ++++---- SCons/Tool/mslib.xml | 5 +-- SCons/Tool/mslink.xml | 5 +-- SCons/Tool/mssdk.xml | 7 ++-- SCons/Tool/msvc.xml | 2 +- SCons/Tool/msvs.xml | 4 +-- SCons/Tool/mwcc.xml | 5 +-- SCons/Tool/mwld.xml | 5 +-- SCons/Tool/nasm.xml | 5 +-- SCons/Tool/ninja/ninja.xml | 31 +++--------------- SCons/Tool/packaging/packaging.xml | 7 ++-- SCons/Tool/pdf.xml | 5 +-- SCons/Tool/pdflatex.xml | 5 +-- SCons/Tool/pdftex.xml | 5 +-- SCons/Tool/python.xml | 5 +-- SCons/Tool/qt.xml | 5 +-- SCons/Tool/qt3.xml | 5 +-- SCons/Tool/rmic.xml | 5 ++- SCons/Tool/rpcgen.xml | 5 +-- SCons/Tool/sgiar.xml | 5 +-- SCons/Tool/sgic++.xml | 5 +-- SCons/Tool/sgicc.xml | 5 +-- SCons/Tool/sgilink.xml | 5 +-- SCons/Tool/sunar.xml | 5 +-- SCons/Tool/sunc++.xml | 5 +-- SCons/Tool/suncc.xml | 5 +-- SCons/Tool/sunf77.xml | 5 +-- SCons/Tool/sunf90.xml | 5 +-- SCons/Tool/sunf95.xml | 5 +-- SCons/Tool/sunlink.xml | 5 +-- SCons/Tool/swig.xml | 5 +-- SCons/Tool/tar.xml | 5 +-- SCons/Tool/tex.xml | 5 +-- SCons/Tool/textfile.xml | 5 +-- SCons/Tool/tlib.xml | 5 +-- SCons/Tool/xgettext.xml | 25 ++++++++------- SCons/Tool/yacc.xml | 27 ++-------------- SCons/Tool/zip.xml | 5 +-- bin/SConsDoc.py | 51 ++++++++++-------------------- doc/man/scons.xml | 27 +++------------- doc/scons.mod | 41 ++++++++---------------- doc/user/actions.xml | 3 ++ doc/user/add-method.xml | 3 ++ doc/user/alias.xml | 3 ++ doc/user/ant.xml | 3 ++ doc/user/build-install.xml | 3 ++ doc/user/builders-built-in.xml | 3 ++ doc/user/builders-commands.xml | 3 ++ doc/user/builders-writing.xml | 3 ++ doc/user/builders.xml | 3 ++ doc/user/caching.xml | 3 ++ doc/user/command-line.xml | 3 ++ doc/user/depends.xml | 3 ++ doc/user/environments.xml | 3 ++ doc/user/errors.xml | 3 ++ doc/user/example.xml | 3 ++ doc/user/external.xml | 3 ++ doc/user/factories.xml | 3 ++ doc/user/file-removal.xml | 3 ++ doc/user/functions.xml | 3 ++ doc/user/gettext.xml | 3 ++ doc/user/hierarchy.xml | 3 ++ doc/user/install.xml | 3 ++ doc/user/java.xml | 3 ++ doc/user/less-simple.xml | 3 ++ doc/user/libraries.xml | 3 ++ doc/user/main.xml | 3 ++ doc/user/make.xml | 3 ++ doc/user/mergeflags.xml | 3 ++ doc/user/misc.xml | 3 ++ doc/user/nodes.xml | 3 ++ doc/user/output.xml | 3 ++ doc/user/parse_flags_arg.xml | 3 ++ doc/user/parseconfig.xml | 3 ++ doc/user/parseflags.xml | 3 ++ doc/user/preface.xml | 3 ++ doc/user/python.xml | 3 ++ doc/user/repositories.xml | 3 ++ doc/user/run.xml | 3 ++ doc/user/scanners.xml | 3 ++ doc/user/sconf.xml | 3 ++ doc/user/separate.xml | 3 ++ doc/user/sideeffect.xml | 3 ++ doc/user/simple.xml | 3 ++ doc/user/tasks.xml | 3 ++ doc/user/tools.xml | 3 ++ doc/user/troubleshoot.xml | 3 ++ doc/user/variables.xml | 3 ++ doc/user/variants.xml | 4 ++- 165 files changed, 547 insertions(+), 437 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e2c11c5f62..012a5969f7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -111,6 +111,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER allow a developer to supply a custom validator, which previously could be inhibited by the converter failing before the validator is reached. + - Regularized header (copyright, licens) at top of documentation files + using SPDX. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index d6fa72926a..389a1f6f6d 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -99,6 +99,7 @@ DOCUMENTATION before contents of package submodules. - Updated manpage description of Command "builder" and function. - Updated the notes about reproducible builds with SCons and the example. +- Regularized header (copyright, licens) at top of documentation files using SPDX. diff --git a/SCons/Action.xml b/SCons/Action.xml index becd30f792..c71c305eab 100644 --- a/SCons/Action.xml +++ b/SCons/Action.xml @@ -1,9 +1,10 @@ -This tool tries to make working with Docbook in SCons a little easier. +This tool tries to make working with Docbook in &SCons; a little easier. It provides several toolchains for creating different output formats, like HTML or PDF. Contained in the package is a distribution of the Docbook XSL stylesheets as of version 1.76.1. diff --git a/SCons/Tool/dvi.xml b/SCons/Tool/dvi.xml index ee67e1474e..a631c2abad 100644 --- a/SCons/Tool/dvi.xml +++ b/SCons/Tool/dvi.xml @@ -1,9 +1,10 @@ - &t-link-xgettext; - to extract internationalized messages from source code to + &t-link-xgettext; - to extract internationalized messages from source code to POT file(s), @@ -59,7 +60,7 @@ so you're encouraged to see their individual documentation. Each of the above tools provides its own builder(s) which may be used to perform particular activities related to software internationalization. You -may be however interested in top-level +may be however interested in top-level &b-link-Translate; builder. @@ -100,7 +101,7 @@ a SCons script when invoking &b-Translate; # SConscript in 'po/' directory env = Environment( tools = ["default", "gettext"] ) env['POAUTOINIT'] = 1 -env.Translate(['en','pl'], ['../a.cpp','../b.cpp']) +env.Translate(['en','pl'], ['../a.cpp','../b.cpp']) @@ -111,7 +112,7 @@ If you wish, you may also stick to conventional style known from # LINGUAS -en pl +en pl #end @@ -127,7 +128,7 @@ b.cpp env = Environment( tools = ["default", "gettext"] ) env['POAUTOINIT'] = 1 env['XGETTEXTPATH'] = ['../'] -env.Translate(LINGUAS_FILE = 1, XGETTEXTFROM = 'POTFILES.in') +env.Translate(LINGUAS_FILE = 1, XGETTEXTFROM = 'POTFILES.in') @@ -154,7 +155,7 @@ Let's prepare a development tree as below project/ + SConstruct - + build/ + + build/ + src/ + po/ + SConscript diff --git a/SCons/Tool/gfortran.xml b/SCons/Tool/gfortran.xml index 95458fc46a..fc18327820 100644 --- a/SCons/Tool/gfortran.xml +++ b/SCons/Tool/gfortran.xml @@ -1,9 +1,10 @@ : for POSIX systems or - ; for Windows), + ; for Windows), it will be split on the separator into a list of individual paths for dependency scanning purposes. It will not be modified for JDK command-line usage, @@ -278,7 +277,7 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES") &SCons; always - supplies a + supplies a when invoking the Java compiler &javac;, regardless of the setting of &cv-link-JAVASOURCEPATH;, as it passes the path(s) to the source(s) supplied diff --git a/SCons/Tool/javah.xml b/SCons/Tool/javah.xml index 16e969a5f6..7021753c71 100644 --- a/SCons/Tool/javah.xml +++ b/SCons/Tool/javah.xml @@ -1,11 +1,10 @@ '.dll'. -Used to override &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; when +Used to override &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; when creating versioned import library for a shared library/loadable module. If not defined, then &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; is used to determine whether to disable symlink generation or not. @@ -410,7 +411,7 @@ The variable is used, for example, by &t-link-gnulink; linker tool. -This will construct the SONAME using on the base library name +This will construct the SONAME using on the base library name (test in the example below) and use specified SOVERSION to create SONAME. @@ -419,7 +420,7 @@ env.SharedLibrary('test', 'test.c', SHLIBVERSION='0.1.2', SOVERSION='2') The variable is used, for example, by &t-link-gnulink; linker tool. -In the example above SONAME would be libtest.so.2 +In the example above SONAME would be libtest.so.2 which would be a symlink and point to libtest.so.0.1.2 diff --git a/SCons/Tool/linkloc.xml b/SCons/Tool/linkloc.xml index 2b5ba6a5d1..86d80d73cd 100644 --- a/SCons/Tool/linkloc.xml +++ b/SCons/Tool/linkloc.xml @@ -1,9 +1,10 @@ LINGUAS file: Example 4. -Compile files for languages defined in LINGUAS file +Compile files for languages defined in LINGUAS file (another version): @@ -126,7 +127,7 @@ See &t-link-msgfmt; tool and &b-link-MOFiles; builder. -String to display when msgfmt(1) is invoked +String to display when msgfmt(1) is invoked (default: '', which means ``print &cv-link-MSGFMTCOM;''). See &t-link-msgfmt; tool and &b-link-MOFiles; builder. diff --git a/SCons/Tool/msginit.xml b/SCons/Tool/msginit.xml index 70178518b6..667ed54b61 100644 --- a/SCons/Tool/msginit.xml +++ b/SCons/Tool/msginit.xml @@ -1,9 +1,10 @@ en.po and pl.po from # ... - env.POInit(['en', 'pl']) # messages.pot --> [en.po, pl.po] + env.POInit(['en', 'pl']) # messages.pot --> [en.po, pl.po] @@ -92,7 +93,7 @@ Initialize en.po and pl.po from # ... - env.POInit(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.po] + env.POInit(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.po] @@ -103,7 +104,7 @@ variable: # ... - env.POInit(['en', 'pl'], POTDOMAIN='foo') # foo.pot --> [en.po, pl.po] + env.POInit(['en', 'pl'], POTDOMAIN='foo') # foo.pot --> [en.po, pl.po] @@ -193,7 +194,7 @@ See &t-link-msginit; tool and &b-link-POInit; builder. -String to display when msginit(1) is invoked +String to display when msginit(1) is invoked (default: '', which means ``print &cv-link-MSGINITCOM;''). See &t-link-msginit; tool and &b-link-POInit; builder. diff --git a/SCons/Tool/msgmerge.xml b/SCons/Tool/msgmerge.xml index 266ccc7049..1f0437cf0f 100644 --- a/SCons/Tool/msgmerge.xml +++ b/SCons/Tool/msgmerge.xml @@ -1,9 +1,10 @@ This scons tool is a part of scons &t-link-gettext; toolset. It provides scons interface to msgmerge(1) command, which merges two -Uniform style .po files together. +Uniform style .po files together. @@ -62,9 +63,9 @@ does. Target nodes defined through &b-POUpdate; are not built by default (they're Ignored from '.' node). Instead, -they are added automatically to special Alias +they are added automatically to special Alias ('po-update' by default). The alias name may be changed -through the &cv-link-POUPDATE_ALIAS; construction variable. You can easily +through the &cv-link-POUPDATE_ALIAS; construction variable. You can easily update PO files in your project by scons po-update. @@ -128,7 +129,7 @@ from messages.pot template: # produce 'en.po', 'pl.po' + files defined in 'LINGUAS': - env.POUpdate(['en', 'pl' ], LINGUAS_FILE = 1) + env.POUpdate(['en', 'pl' ], LINGUAS_FILE = 1) diff --git a/SCons/Tool/mslib.xml b/SCons/Tool/mslib.xml index a79ec4869e..fdd9ea77c9 100644 --- a/SCons/Tool/mslib.xml +++ b/SCons/Tool/mslib.xml @@ -1,9 +1,10 @@ 6.0A, 6.0, 2003R2 -and +and 2003R1. diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index a0f657368c..9f433a2453 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -1,7 +1,7 @@ Determines the type of format ninja should expect when parsing header include depfiles. Can be , , or . - The option corresponds to format, and + The option corresponds to format, and or correspond to . diff --git a/SCons/Tool/packaging/packaging.xml b/SCons/Tool/packaging/packaging.xml index b248880e33..62ba558a5d 100644 --- a/SCons/Tool/packaging/packaging.xml +++ b/SCons/Tool/packaging/packaging.xml @@ -1,9 +1,10 @@ xgettext(1) program, which extracts internationalized messages from source code. The tool provides &b-POTUpdate; builder to make PO -Template files. +Template files. @@ -67,7 +68,7 @@ special alias (pot-update by default, see &cv-link-POTUPDATE_ALIAS;) so you can update/create them easily with scons pot-update. The file is not written until there is no real change in internationalized messages (or in comments that enter -POT file). +POT file). @@ -90,7 +91,7 @@ Let's create po/ directory and place following env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(['foo'], ['../a.cpp', '../b.cpp']) env.POTUpdate(['bar'], ['../c.cpp', '../d.cpp']) - + Then invoke scons few times: @@ -111,7 +112,7 @@ case default target messages.pot will be used. The default target may also be overridden by setting &cv-link-POTDOMAIN; construction variable or providing it as an override to &b-POTUpdate; builder: - + # SConstruct script env = Environment( tools = ['default', 'xgettext'] ) env['POTDOMAIN'] = "foo" @@ -124,21 +125,21 @@ variable or providing it as an override to &b-POTUpdate; builder: The sources may be specified within separate file, for example POTFILES.in: - + # POTFILES.in in 'po/' subdirectory ../a.cpp ../b.cpp # end of file - + The name of the file (POTFILES.in) containing the list of sources is provided via &cv-link-XGETTEXTFROM;: - + # SConstruct file in 'po/' subdirectory env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in') - + Example 4. @@ -209,10 +210,10 @@ message "Hello from ../a.cpp". When you reverse order in # SConstruct file in '0/1/po/' subdirectory env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../../', '../']) - + then the messages.pot will contain -msgid "Hello from ../../a.cpp" line and not +msgid "Hello from ../../a.cpp" line and not msgid "Hello from ../a.cpp". diff --git a/SCons/Tool/yacc.xml b/SCons/Tool/yacc.xml index 729c408286..89f45ba4e4 100644 --- a/SCons/Tool/yacc.xml +++ b/SCons/Tool/yacc.xml @@ -1,31 +1,10 @@ + - + Action"> Builder"> Builders"> @@ -171,9 +154,11 @@ Add"> diff --git a/doc/user/actions.xml b/doc/user/actions.xml index d357eb6190..ed2eeb7df6 100644 --- a/doc/user/actions.xml +++ b/doc/user/actions.xml @@ -3,6 +3,9 @@ Date: Fri, 28 Jun 2024 07:27:38 -0600 Subject: [PATCH 080/386] Tweak the text on API Docs intro page [skip appveyor] Signed-off-by: Mats Wichmann --- doc/sphinx/index.rst | 46 +++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/doc/sphinx/index.rst b/doc/sphinx/index.rst index a21b9db4b7..1bf81d9eda 100644 --- a/doc/sphinx/index.rst +++ b/doc/sphinx/index.rst @@ -7,21 +7,37 @@ SCons API Documentation ======================= .. Attention:: - This is the **internal** API Documentation for SCons. - The documentation is automatically generated for each release - from the source code using the - `Sphinx `_ documentation generator. - Missing information is due to shortcomings in the docstrings in the code, - which admittedly could use a lot more work (contributions welcomed!). - - The target audience is both developers working on SCons itself, - and those writing external Tools, Builders, etc. and other - related functionality, who need to reach beyond the Public API. - Note that what is Public API is not clearly deliniated in the API Docs. - The interfaces available for use in SCons configuration scripts - ("SConscript files"), which have a consistency guarantee, - are those documented in the `SCons Reference Manual - `_. + This is the **internal** API Documentation for SCons (aka + "everything"). It is generated automatically from code docstrings using + the `Sphinx `_ documentation generator. + + Any missing/incomplete information is due to shortcomings in the + docstrings in the code. To not be too flippant about it, filling + in all the docstrings has not always been a priority across the + two-plus decades SCons has been in existence (contributions on this + front are welcomed). Additionally, for SCons classes which inherit + from Python standard library classes (such as ``UserList``, + ``UserDict``, ``UserString``), the generated pages will show methods + that are inherited, sometimes with no information at all, sometimes + with a signature/description that seems mangled: Python upstream has + similar limitations as to the quality of dosctrings vs the current + standards Sphinx expects. Inherited interfaces can be identified by the + lack of a ```[source]``` button to the right of the method signature. + + If you are looking for the Public API - the interfaces that have + long-term consistency guarantees, which you can reliably use when + writing a build system for a project - see the `SCons Reference Manual + `_. Note that + what is Public API and what is not is not clearly delineated in these + API Docs. + + The target audience is both developers contributing to SCons itself, + and those writing external Tools, Builders, and other related + functionality for their project, who may need to reach beyond the + Public API to accomplish their tasks. Reaching into internals is fine, + but comes with the usual risks of "things here could change, it's up + to you to keep your code working". + .. toctree:: :maxdepth: 2 From 88f7f9ab27d2d953597eab9cc467f580dfa5f846 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 28 Jun 2024 07:33:34 -0600 Subject: [PATCH 081/386] bench: fix bas isinstance sequence in one test Signed-off-by: Mats Wichmann --- bench/is_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/is_types.py b/bench/is_types.py index 257fa915c4..ecc0edbdb0 100644 --- a/bench/is_types.py +++ b/bench/is_types.py @@ -53,7 +53,7 @@ def cache_type_e_is_Dict(e): def cache_type_e_is_List(e): t = type(e) - return t is list or isinstance(e, UserList or isinstance(e, deque)) + return t is list or isinstance(e, (UserList, deque)) def cache_type_e_is_String(e): t = type(e) From ed81110dd6552839f49e64386a9822413b48de63 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 28 Jun 2024 07:59:45 -0600 Subject: [PATCH 082/386] Fix markup error in apidocs intro [skip appveyor] Signed-off-by: Mats Wichmann --- doc/sphinx/index.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/sphinx/index.rst b/doc/sphinx/index.rst index 1bf81d9eda..1b810eb8ce 100644 --- a/doc/sphinx/index.rst +++ b/doc/sphinx/index.rst @@ -21,8 +21,9 @@ SCons API Documentation that are inherited, sometimes with no information at all, sometimes with a signature/description that seems mangled: Python upstream has similar limitations as to the quality of dosctrings vs the current - standards Sphinx expects. Inherited interfaces can be identified by the - lack of a ```[source]``` button to the right of the method signature. + standards Sphinx expects. Inherited interfaces from outside SCons + code can be identified by the lack of a ``[source]`` button to the + right of the method signature. If you are looking for the Public API - the interfaces that have long-term consistency guarantees, which you can reliably use when From 6baf4d424499b275e14a8b4cda1b1b0844b10a77 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 29 Jun 2024 09:29:08 -0600 Subject: [PATCH 083/386] Minor cleanups in Variables Continuing the maintenance pass on Variables, these are some minor tweaks for a few checker niggles and fixing up a couple of docstrings. There is no functional change. Signed-off-by: Mats Wichmann --- SCons/Variables/BoolVariable.py | 4 +-- SCons/Variables/EnumVariable.py | 5 ---- SCons/Variables/EnumVariableTests.py | 34 ++++++++++++------------- SCons/Variables/ListVariable.py | 2 +- SCons/Variables/ListVariableTests.py | 7 +++-- SCons/Variables/PackageVariableTests.py | 2 +- SCons/Variables/PathVariable.py | 2 +- SCons/Variables/PathVariableTests.py | 4 +-- SCons/Variables/VariablesTests.py | 7 ++--- SCons/Variables/__init__.py | 10 ++++---- pyproject.toml | 5 +++- test/Variables/EnumVariable.py | 3 ++- test/Variables/ListVariable.py | 7 +++-- test/Variables/PathVariable.py | 6 ++--- test/Variables/Variables.py | 16 ++++++------ test/Variables/chdir.py | 4 +-- test/Variables/help.py | 2 +- test/Variables/import.py | 4 +-- 18 files changed, 60 insertions(+), 64 deletions(-) diff --git a/SCons/Variables/BoolVariable.py b/SCons/Variables/BoolVariable.py index 4d998e4d9e..e1fe62b905 100644 --- a/SCons/Variables/BoolVariable.py +++ b/SCons/Variables/BoolVariable.py @@ -38,7 +38,7 @@ __all__ = ['BoolVariable',] -TRUE_STRINGS = ('y', 'yes', 'true', 't', '1', 'on' , 'all') +TRUE_STRINGS = ('y', 'yes', 'true', 't', '1', 'on', 'all') FALSE_STRINGS = ('n', 'no', 'false', 'f', '0', 'off', 'none') @@ -66,7 +66,7 @@ def _text2bool(val: str) -> bool: def _validator(key, val, env) -> None: """Validate that the value of *key* in *env* is a boolean. - Parmaeter *val* is not used in the check. + Parameter *val* is not used in the check. Usable as a validator function for SCons Variables. diff --git a/SCons/Variables/EnumVariable.py b/SCons/Variables/EnumVariable.py index d13e9a9fdb..3698e470dc 100644 --- a/SCons/Variables/EnumVariable.py +++ b/SCons/Variables/EnumVariable.py @@ -91,11 +91,6 @@ def EnumVariable( map: optional dictionary which may be used for converting the input value into canonical values (e.g. for aliases). ignorecase: defines the behavior of the validator and converter. - validator: callback function to test whether the value is in the - list of allowed values. - converter: callback function to convert input values according to - the given *map*-dictionary. Unmapped input values are returned - unchanged. Returns: A tuple including an appropriate converter and validator. diff --git a/SCons/Variables/EnumVariableTests.py b/SCons/Variables/EnumVariableTests.py index c4f32782d6..03848f2bef 100644 --- a/SCons/Variables/EnumVariableTests.py +++ b/SCons/Variables/EnumVariableTests.py @@ -97,9 +97,9 @@ def test_converter(self) -> None: 'c' : 'three'}, ignorecase=2)) - o0 = opts.options[0] - o1 = opts.options[1] - o2 = opts.options[2] + opt0 = opts.options[0] + opt1 = opts.options[1] + opt2 = opts.options[2] table = { 'one' : ['one', 'one', 'one'], @@ -119,13 +119,13 @@ def test_converter(self) -> None: 'C' : ['C', 'three', 'three'], } - for k, l in table.items(): - x = o0.converter(k) - assert x == l[0], f"o0 got {x}, expected {l[0]}" - x = o1.converter(k) - assert x == l[1], f"o1 got {x}, expected {l[1]}" - x = o2.converter(k) - assert x == l[2], f"o2 got {x}, expected {l[2]}" + for k, expected in table.items(): + x = opt0.converter(k) + assert x == expected[0], f"opt0 got {x}, expected {expected[0]}" + x = opt1.converter(k) + assert x == expected[1], f"opt1 got {x}, expected {expected[1]}" + x = opt2.converter(k) + assert x == expected[2], f"opt2 got {x}, expected {expected[2]}" def test_validator(self) -> None: """Test the EnumVariable validator""" @@ -149,9 +149,9 @@ def test_validator(self) -> None: 'c' : 'three'}, ignorecase=2)) - o0 = opts.options[0] - o1 = opts.options[1] - o2 = opts.options[2] + opt0 = opts.options[0] + opt1 = opts.options[1] + opt2 = opts.options[2] def valid(o, v) -> None: o.validator('X', v, {}) @@ -181,10 +181,10 @@ def invalid(o, v) -> None: 'no_v' : [invalid, invalid, invalid], } - for v, l in table.items(): - l[0](o0, v) - l[1](o1, v) - l[2](o2, v) + for v, expected in table.items(): + expected[0](opt0, v) + expected[1](opt1, v) + expected[2](opt2, v) if __name__ == "__main__": diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index bbecbaf948..f795307424 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -140,7 +140,7 @@ def _validator(key, val, env) -> None: so we need to fish the allowed elements list out of the environment to complete the validation. - Note that since 18b45e456, whether or not ``subst`` has been + Note that since 18b45e456, whether ``subst`` has been called is conditional on the value of the *subst* argument to :meth:`~SCons.Variables.Variables.Add`, so we have to account for possible different types of *val*. diff --git a/SCons/Variables/ListVariableTests.py b/SCons/Variables/ListVariableTests.py index 62ce879b75..9424509d14 100644 --- a/SCons/Variables/ListVariableTests.py +++ b/SCons/Variables/ListVariableTests.py @@ -112,7 +112,7 @@ def test_converter(self) -> None: assert str(x) == 'no_match', x # ... and fail to validate with self.assertRaises(SCons.Errors.UserError): - z = o.validator('test', 'no_match', {"test": x}) + o.validator('test', 'no_match', {"test": x}) def test_copy(self) -> None: """Test copying a ListVariable like an Environment would""" @@ -121,9 +121,8 @@ def test_copy(self) -> None: ['one', 'two', 'three'])) o = opts.options[0] - - l = o.converter('all') - n = l.__class__(copy.copy(l)) + res = o.converter('all') + _ = res.__class__(copy.copy(res)) if __name__ == "__main__": unittest.main() diff --git a/SCons/Variables/PackageVariableTests.py b/SCons/Variables/PackageVariableTests.py index ed8ec30db4..0d8aa6bc81 100644 --- a/SCons/Variables/PackageVariableTests.py +++ b/SCons/Variables/PackageVariableTests.py @@ -101,7 +101,7 @@ def test_validator(self) -> None: o.validator('T', '/path', env) o.validator('X', exists, env) - with self.assertRaises(SCons.Errors.UserError) as cm: + with self.assertRaises(SCons.Errors.UserError): o.validator('X', does_not_exist, env) diff --git a/SCons/Variables/PathVariable.py b/SCons/Variables/PathVariable.py index 4a827c5e12..d5988ac47d 100644 --- a/SCons/Variables/PathVariable.py +++ b/SCons/Variables/PathVariable.py @@ -161,7 +161,7 @@ def __call__( helpmsg = f'{help} ( /path/to/{key[0]} )' else: helpmsg = f'{help} ( /path/to/{key} )' - return (key, helpmsg, default, validator, None) + return key, helpmsg, default, validator, None PathVariable = _PathVariableClass() diff --git a/SCons/Variables/PathVariableTests.py b/SCons/Variables/PathVariableTests.py index efc75f1878..7fa8da18da 100644 --- a/SCons/Variables/PathVariableTests.py +++ b/SCons/Variables/PathVariableTests.py @@ -196,7 +196,7 @@ class ValidatorError(Exception): pass def my_validator(key, val, env): - raise ValidatorError(f"my_validator() got called for {key!r}, {val}!") + raise ValidatorError(f"my_validator() got called for {key!r}, {val!r}!") opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test2', @@ -207,7 +207,7 @@ def my_validator(key, val, env): with self.assertRaises(ValidatorError) as cm: o.validator('Y', 'value', {}) e = cm.exception - self.assertEqual(str(e), f"my_validator() got called for 'Y', value!") + self.assertEqual(str(e), "my_validator() got called for 'Y', 'value'!") if __name__ == "__main__": diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index 7c6eaab171..2c9fe580eb 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -206,7 +206,6 @@ def conv_subst(value) -> None: lambda x: int(x) + 12) env = Environment() - exc_caught = None with self.assertRaises(AssertionError): opts.Update(env) @@ -375,8 +374,10 @@ def test_Save(self) -> None: opts = SCons.Variables.Variables() def bool_converter(val): - if val in [1, 'y']: val = 1 - if val in [0, 'n']: val = 0 + if val in [1, 'y']: + val = 1 + if val in [0, 'n']: + val = 0 return val # test saving out empty file diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 1337d14f6e..d9df2a26c7 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -34,11 +34,11 @@ # Note: imports are for the benefit of SCons.Main (and tests); since they # are not used here, the "as Foo" form is for checkers. -from .BoolVariable import BoolVariable as BoolVariable -from .EnumVariable import EnumVariable as EnumVariable -from .ListVariable import ListVariable as ListVariable -from .PackageVariable import PackageVariable as PackageVariable -from .PathVariable import PathVariable as PathVariable +from .BoolVariable import BoolVariable +from .EnumVariable import EnumVariable +from .ListVariable import ListVariable +from .PackageVariable import PackageVariable +from .PathVariable import PathVariable __all__ = [ "Variable", diff --git a/pyproject.toml b/pyproject.toml index 0cdbc7b600..60bc9e607d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,10 @@ quote-style = "preserve" # Equivalent to black's "skip-string-normalization" [tool.ruff.lint.per-file-ignores] "SCons/Util/__init__.py" = [ - "F401", # Module imported but unused + "F401", # Module imported but unused +] +"SCons/Variables/__init__.py" = [ + "F401", # Symbol imported but unused ] [tool.mypy] diff --git a/test/Variables/EnumVariable.py b/test/Variables/EnumVariable.py index 066df741be..a81e8060be 100644 --- a/test/Variables/EnumVariable.py +++ b/test/Variables/EnumVariable.py @@ -67,7 +67,8 @@ def check(expect): Default(env.Alias('dummy', None)) """) -test.run(); check(['no', 'gtk', 'xaver']) +test.run() +check(['no', 'gtk', 'xaver']) test.run(arguments='debug=yes guilib=Motif some=xAVER') check(['yes', 'Motif', 'xaver']) diff --git a/test/Variables/ListVariable.py b/test/Variables/ListVariable.py index 52f1bc56ef..a322f9b8a0 100644 --- a/test/Variables/ListVariable.py +++ b/test/Variables/ListVariable.py @@ -35,11 +35,10 @@ SConstruct_path = test.workpath('SConstruct') -def check(expect): +def check(expected): result = test.stdout().split('\n') - r = result[1:len(expect)+1] - assert r == expect, (r, expect) - + r = result[1:len(expected)+1] + assert r == expected, (r, expected) test.write(SConstruct_path, """\ diff --git a/test/Variables/PathVariable.py b/test/Variables/PathVariable.py index 61b21b3573..effbd49dc2 100644 --- a/test/Variables/PathVariable.py +++ b/test/Variables/PathVariable.py @@ -40,11 +40,9 @@ def check(expect): result = test.stdout().split('\n') assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect) -#### test PathVariable #### -test.subdir('lib', 'qt', ['qt', 'lib'], 'nolib' ) +test.subdir('lib', 'qt', ['qt', 'lib'], 'nolib') workpath = test.workpath() -libpath = os.path.join(workpath, 'lib') test.write(SConstruct_path, """\ from SCons.Variables.PathVariable import PathVariable as PV @@ -67,7 +65,7 @@ def check(expect): print(env.subst('$qt_libraries')) Default(env.Alias('dummy', None)) -""" % (workpath, os.path.join('$qtdir', 'lib') )) +""" % (workpath, os.path.join('$qtdir', 'lib'))) qtpath = workpath libpath = os.path.join(qtpath, 'lib') diff --git a/test/Variables/Variables.py b/test/Variables/Variables.py index d585b57a42..e8a1873705 100644 --- a/test/Variables/Variables.py +++ b/test/Variables/Variables.py @@ -165,7 +165,7 @@ def check(expect): test.run(arguments='DEBUG_BUILD=1') check(['1', '1', cc, (ccflags + ' -O -g').strip(), 'v', 'v']) -test.run(arguments='-h', stdout = """\ +test.run(arguments='-h', stdout="""\ scons: Reading SConscript files ... 1 0 @@ -241,7 +241,7 @@ def checkSave(file, expected): gdict = {} ldict = {} with open(file, 'r') as f: - contents = f.read() + contents = f.read() exec(contents, gdict, ldict) assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict) @@ -297,18 +297,18 @@ def checkSave(file, expected): # First check for empty output file when nothing is passed on command line test.run() -check(['0','1']) +check(['0', '1']) checkSave('variables.saved', {}) # Now specify one option the same as default and make sure it doesn't write out test.run(arguments='DEBUG_BUILD=1') -check(['0','1']) +check(['0', '1']) checkSave('variables.saved', {}) # Now specify same option non-default and make sure only it is written out test.run(arguments='DEBUG_BUILD=0 LISTOPTION_TEST=a,b') -check(['0','0']) -checkSave('variables.saved',{'DEBUG_BUILD':0, 'LISTOPTION_TEST':'a,b'}) +check(['0', '0']) +checkSave('variables.saved', {'DEBUG_BUILD': 0, 'LISTOPTION_TEST': 'a,b'}) test.write('SConstruct', """ opts = Variables('custom.py') @@ -342,7 +342,7 @@ def compare(a, b): ) """) -test.run(arguments='-h', stdout = """\ +test.run(arguments='-h', stdout="""\ scons: Reading SConscript files ... scons: done reading SConscript files. Variables settable in custom.py or on the command line: @@ -364,7 +364,7 @@ def compare(a, b): actual: None Use scons -H for help about SCons built-in command-line options. -"""%cc) +""" % cc) test.write('SConstruct', """ import SCons.Variables diff --git a/test/Variables/chdir.py b/test/Variables/chdir.py index ed7cf2be3e..04e10dea54 100644 --- a/test/Variables/chdir.py +++ b/test/Variables/chdir.py @@ -24,7 +24,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -Verify that we can chdir() to the directory in which an Variables +Verify that we can chdir() to the directory in which a Variables file lives by using the __name__ value. """ @@ -67,7 +67,7 @@ VARIABLE = 'opts2.cfg value' """ -test.run(arguments = '-q -Q .', stdout=expect) +test.run(arguments='-q -Q .', stdout=expect) test.pass_test() diff --git a/test/Variables/help.py b/test/Variables/help.py index 84a405a9d3..e0addb19d2 100644 --- a/test/Variables/help.py +++ b/test/Variables/help.py @@ -37,7 +37,7 @@ test = TestSCons.TestSCons() workpath = test.workpath() -qtpath = os.path.join(workpath, 'qt') +qtpath = os.path.join(workpath, 'qt') libpath = os.path.join(qtpath, 'lib') libdirvar = os.path.join('$qtdir', 'lib') diff --git a/test/Variables/import.py b/test/Variables/import.py index 833b299c9f..8e0cd2595c 100644 --- a/test/Variables/import.py +++ b/test/Variables/import.py @@ -24,7 +24,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -Verify that an Variables file in a different directory can import +Verify that a Variables file in a different directory can import a module in that directory. """ @@ -63,7 +63,7 @@ expect = "VARIABLE = bin/local_options.py\n" -test.run(arguments = '-q -Q .', stdout = expect) +test.run(arguments='-q -Q .', stdout=expect) test.pass_test() From f40aad1b94fd06936116875903a3862f01095785 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 1 Jul 2024 13:01:58 -0600 Subject: [PATCH 084/386] Update $VSWHERE description. [skip appveyor] Requested wordsmithing on the VSWHERE construction variable. Per request, dropped msvc.py from the github/win32 skip list. Signed-off-by: Mats Wichmann --- SCons/Tool/msvc.xml | 81 ++++++++++-------- doc/generated/builders.gen | 40 ++++----- doc/generated/functions.gen | 72 +++++++++++----- doc/generated/tools.gen | 10 +-- doc/generated/variables.gen | 158 +++++++++++++++++++++++------------- windows_ci_skip.txt | 1 - 6 files changed, 226 insertions(+), 136 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 1609bd7885..596ae995c4 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -417,7 +417,7 @@ msvc &f-link-Tool; (e.g., &t-link-msvc;) or an msvc-dependent &f-link-Tool; (e.g the &cv-MSVC_VERSION; is resolved when the &consenv; is created. In this case, passing &cv-MSVC_VERSION; as an argument in the &f-link-Environment; call is the effective solution. -Otherwise, &cv-MSVC_VERSION; must be set before the first msvc &f-link-Tool; or +Otherwise, &cv-MSVC_VERSION; must be set before the first msvc &f-link-Tool; or msvc-dependent &f-link-Tool; is loaded into the environment. See the manpage section "Construction Environments" for an example. @@ -607,7 +607,7 @@ Visual Studio "Express" or "Express for Desktop" Visual Studio editions, which had feature limitations compared to the full editions. It is only necessary to specify the Exp - suffix to select the express edition when both express and + suffix to select the express edition when both express and non-express editions of the same product are installed simulaneously. The Exp suffix is unnecessary, but accepted, when only the express edition is installed. @@ -860,20 +860,28 @@ The burden is on the user to ensure the requisite UWP libraries are installed. -Specify the location of vswhere.exe. +Specify the location of vswhere.exe. - The vswhere.exe executable is distributed with Microsoft Visual Studio and Build - Tools since the 2017 edition, but is also available as a standalone installation. - It provides full information about installations of 2017 and later editions. - With the argument, vswhere.exe can detect installations of the 2010 through 2015 - editions with limited data returned. + The vswhere.exe executable is distributed with + Microsoft Visual Studio and Build Tools since the 2017 edition, + but is also available as a standalone installation. + It allows queries to obtain detailed information about + installations of 2017 and later editions; + with the argument, + it can return limited information for + installations of the 2010 through 2015 editions. + &SCons; makes use of this information to help determine + the state of compiler support. - If &cv-VSWHERE; is set to a vswhere.exe location, &SCons; will use that location. - When &cv-VSWHERE; is undefined, &SCons; will look in the following locations and set &cv-VSWHERE; to the path - of the first vswhere.exe located: + You can set the &cv-VSWHERE; variable to the path to a specific + vswhere.exe binary, + and &SCons; will use that. + If undefined (the default), &SCons; will search for one, + looking in the following locations in order, + using the first found, and updating &cv-VSWHERE; with the location. @@ -885,31 +893,38 @@ Specify the location of vswhere.exe. %SCOOP%\shims - - Note that &cv-VSWHERE; must be set prior to the initial &MSVC; compiler discovery. - For example, &cv-VSWHERE; must be set at the same time or before the first msvc &f-link-Tool; - (e.g., &t-link-msvc;) or msvc-dependent &f-link-Tool; (e.g., &t-link-midl;) is initialized. - - - Either set it as follows: - + + + In order to take effect, &cv-VSWHERE; must be set before + the initial &MSVC; compiler discovery takes place. + Discovery happens, at the latest, during the first call to the + &f-link-Environment; function, unless a tools + list is specified which excludes the entire MSVC toolchain + (that is, does not include "defaults" + or any of the specific tools), + in which case it happens when one of the tools is invoked manually. + The following two examples illustrate this: + + + +# VSWHERE set as Environment is created env = Environment(VSWHERE='c:/my/path/to/vswhere') - - -Or, if your &consenv; is created specifying: (a) an empty tools list, or (b) -a list of tools which omits all of default, msvc (e.g., &t-link-msvc;), and -msvc-dependent tools (e.g., &t-link-midl;); and before &f-link-env-Tool; -is called to initialize any of those tools: - - env = Environment(tools=[]) - env['VSWHERE'] = r'c:/my/vswhere/install/location/vswhere.exe' - env.Tool('msvc') - env.Tool('mslink') - env.Tool('msvs') - - +# Initialization deferred with empty tools, triggered manually +env = Environment(tools=[]) +env['VSWHERE'] = r'c:/my/vswhere/install/location/vswhere.exe' +env.Tool('msvc') +env.Tool('mslink') +env.Tool('msvs') + + + + The tool modules that trigger detection are + &t-link-msvc;, &t-link-mslink;, &t-link-masm;, &t-link-midl; + and &t-link-msvs;. + + diff --git a/doc/generated/builders.gen b/doc/generated/builders.gen index 4febbcfe77..d0ee3dde19 100644 --- a/doc/generated/builders.gen +++ b/doc/generated/builders.gen @@ -823,7 +823,7 @@ languages defined in LINGUAS file: Example 4. -Compile files for languages defined in LINGUAS file +Compile files for languages defined in LINGUAS file (another version): @@ -1650,7 +1650,7 @@ Initialize en.po and pl.po from # ... - env.POInit(['en', 'pl']) # messages.pot --> [en.po, pl.po] + env.POInit(['en', 'pl']) # messages.pot --> [en.po, pl.po] @@ -1660,7 +1660,7 @@ Initialize en.po and pl.po from # ... - env.POInit(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.po] + env.POInit(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.po] @@ -1671,7 +1671,7 @@ variable: # ... - env.POInit(['en', 'pl'], POTDOMAIN='foo') # foo.pot --> [en.po, pl.po] + env.POInit(['en', 'pl'], POTDOMAIN='foo') # foo.pot --> [en.po, pl.po] @@ -1756,7 +1756,7 @@ special alias (pot-update by default, see &cv-link-POTUPDATE_ALIAS;) so you can update/create them easily with scons pot-update. The file is not written until there is no real change in internationalized messages (or in comments that enter -POT file). +POT file). @@ -1779,7 +1779,7 @@ Let's create po/ directory and place following env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(['foo'], ['../a.cpp', '../b.cpp']) env.POTUpdate(['bar'], ['../c.cpp', '../d.cpp']) - + Then invoke scons few times: @@ -1800,7 +1800,7 @@ case default target messages.pot will be used. The default target may also be overridden by setting &cv-link-POTDOMAIN; construction variable or providing it as an override to &b-POTUpdate; builder: - + # SConstruct script env = Environment( tools = ['default', 'xgettext'] ) env['POTDOMAIN'] = "foo" @@ -1813,21 +1813,21 @@ variable or providing it as an override to &b-POTUpdate; builder: The sources may be specified within separate file, for example POTFILES.in: - + # POTFILES.in in 'po/' subdirectory ../a.cpp ../b.cpp # end of file - + The name of the file (POTFILES.in) containing the list of sources is provided via &cv-link-XGETTEXTFROM;: - + # SConstruct file in 'po/' subdirectory env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in') - + Example 4. @@ -1898,10 +1898,10 @@ message "Hello from ../a.cpp". When you reverse order in # SConstruct file in '0/1/po/' subdirectory env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../../', '../']) - + then the messages.pot will contain -msgid "Hello from ../../a.cpp" line and not +msgid "Hello from ../../a.cpp" line and not msgid "Hello from ../a.cpp". @@ -1923,9 +1923,9 @@ does. Target nodes defined through &b-POUpdate; are not built by default (they're Ignored from '.' node). Instead, -they are added automatically to special Alias +they are added automatically to special Alias ('po-update' by default). The alias name may be changed -through the &cv-link-POUPDATE_ALIAS; construction variable. You can easily +through the &cv-link-POUPDATE_ALIAS; construction variable. You can easily update PO files in your project by scons po-update. @@ -1989,7 +1989,7 @@ from messages.pot template: # produce 'en.po', 'pl.po' + files defined in 'LINGUAS': - env.POUpdate(['en', 'pl' ], LINGUAS_FILE = 1) + env.POUpdate(['en', 'pl' ], LINGUAS_FILE = 1) @@ -2724,7 +2724,7 @@ a SCons script when invoking &b-Translate; # SConscript in 'po/' directory env = Environment( tools = ["default", "gettext"] ) env['POAUTOINIT'] = 1 -env.Translate(['en','pl'], ['../a.cpp','../b.cpp']) +env.Translate(['en','pl'], ['../a.cpp','../b.cpp']) @@ -2735,7 +2735,7 @@ If you wish, you may also stick to conventional style known from # LINGUAS -en pl +en pl #end @@ -2751,7 +2751,7 @@ b.cpp env = Environment( tools = ["default", "gettext"] ) env['POAUTOINIT'] = 1 env['XGETTEXTPATH'] = ['../'] -env.Translate(LINGUAS_FILE = 1, XGETTEXTFROM = 'POTFILES.in') +env.Translate(LINGUAS_FILE = 1, XGETTEXTFROM = 'POTFILES.in') @@ -2778,7 +2778,7 @@ Let's prepare a development tree as below project/ + SConstruct - + build/ + + build/ + src/ + po/ + SConscript diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index 4a5a39119f..c7617c1011 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -149,21 +149,25 @@ the option value may be accessed using &f-link-GetOption; or &f-link-env-GetOption;. -&f-link-SetOption; is not currently supported for -options added with &f-AddOption;. - +override a value set in an SConscript file. + + + +Changed in 4.8.0: added the +settable keyword argument +to enable an added option to be settable via &SetOption;. @@ -981,11 +985,12 @@ Clean(docdir, os.path.join(docdir, projectname)) env.Clone([key=val, ...]) -Returns a separate copy of a construction environment. -If there are any keyword arguments specified, -they are added to the returned copy, +Returns an independent copy of a &consenv;. +If there are any unrecognized keyword arguments specified, +they are added as &consvars; in the copy, overwriting any existing values -for the keywords. +for those keywords. +See the manpage section "Construction Environments" for more details. @@ -998,8 +1003,9 @@ env3 = env.Clone(CCFLAGS='-g') -Additionally, a list of tools and a toolpath may be specified, as in -the &f-link-Environment; constructor: +A list of tools +and a toolpath may be specified, +as in the &f-link-Environment; constructor: @@ -1012,7 +1018,7 @@ env4 = env.Clone(tools=['msvc', MyTool]) The parse_flags -keyword argument is also recognized to allow merging command-line +keyword argument is also recognized, to allow merging command-line style arguments into the appropriate construction variables (see &f-link-env-MergeFlags;). @@ -1021,6 +1027,17 @@ variables (see &f-link-env-MergeFlags;). # create an environment for compiling programs that use wxWidgets wx_env = env.Clone(parse_flags='!wx-config --cflags --cxxflags') + + +The variables +keyword argument is also recognized, to allow (re)initializing +&consvars; from a Variables object. + + + +Changed in version 4.8.0: +the variables parameter was added. + @@ -1190,6 +1207,7 @@ Allows setting options for SCons debug options. Currently the only supported val DebugOptions(json='#/build/output/scons_stats.json') +New in version 4.6.0. @@ -1762,7 +1780,7 @@ EnsureSConsVersion(0,96,90) Environment([key=value, ...]) env.Environment([key=value, ...]) -Return a new construction environment +Return a new &consenv; initialized with the specified key=value pairs. @@ -1772,7 +1790,8 @@ The keyword arguments toolpath, tools and variables -are also specially recognized. +are specially recognized and do not lead to +&consvar; creation. See the manpage section "Construction Environments" for more details. @@ -2324,9 +2343,10 @@ file is found. env.GetOption(name) Query the value of settable options which may have been set -on the command line, or by using the &f-link-SetOption; function. +on the command line, via option defaults, +or by using the &f-link-SetOption; function. The value of the option is returned in a type matching how the -option was declared - see the documentation for the +option was declared - see the documentation of the corresponding command line option for information about each specific option. @@ -4191,6 +4211,16 @@ are not settable using &f-SetOption; since those files must be read in order to find the &f-SetOption; call in the first place. + +For project-specific options (sometimes called +local options) +added via an &f-link-AddOption; call, +&f-SetOption; is available only after the +&f-AddOption; call has completed successfully, +and only if that call included the +settable=True argument. + + The settable variables with their associated command-line options are: diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen index d6f2eb2fbc..b2b15831bc 100644 --- a/doc/generated/tools.gen +++ b/doc/generated/tools.gen @@ -237,7 +237,7 @@ Sets construction variables for D language compiler DMD. docbook - This tool tries to make working with Docbook in SCons a little easier. + This tool tries to make working with Docbook in &SCons; a little easier. It provides several toolchains for creating different output formats, like HTML or PDF. Contained in the package is a distribution of the Docbook XSL stylesheets as of version 1.76.1. @@ -473,7 +473,7 @@ following tools: - &t-link-xgettext; - to extract internationalized messages from source code to + &t-link-xgettext; - to extract internationalized messages from source code to POT file(s), @@ -498,7 +498,7 @@ so you're encouraged to see their individual documentation. Each of the above tools provides its own builder(s) which may be used to perform particular activities related to software internationalization. You -may be however interested in top-level +may be however interested in top-level &b-link-Translate; builder. @@ -740,7 +740,7 @@ user's environment (or options). This scons tool is a part of scons &t-link-gettext; toolset. It provides scons interface to msgmerge(1) command, which merges two -Uniform style .po files together. +Uniform style .po files together. Sets: &cv-link-MSGMERGE;, &cv-link-MSGMERGECOM;, &cv-link-MSGMERGECOMSTR;, &cv-link-MSGMERGEFLAGS;, &cv-link-POSUFFIX;, &cv-link-POTSUFFIX;, &cv-link-POUPDATE_ALIAS;.Uses: &cv-link-LINGUAS_FILE;, &cv-link-POAUTOINIT;, &cv-link-POTDOMAIN;. @@ -1097,7 +1097,7 @@ This scons tool is a part of scons &t-link-gettext; toolset. It provides scons interface to xgettext(1) program, which extracts internationalized messages from source code. The tool provides &b-POTUpdate; builder to make PO -Template files. +Template files. Sets: &cv-link-POTSUFFIX;, &cv-link-POTUPDATE_ALIAS;, &cv-link-XGETTEXTCOM;, &cv-link-XGETTEXTCOMSTR;, &cv-link-XGETTEXTFLAGS;, &cv-link-XGETTEXTFROM;, &cv-link-XGETTEXTFROMPREFIX;, &cv-link-XGETTEXTFROMSUFFIX;, &cv-link-XGETTEXTPATH;, &cv-link-XGETTEXTPATHPREFIX;, &cv-link-XGETTEXTPATHSUFFIX;, &cv-link-_XGETTEXTDOMAIN;, &cv-link-_XGETTEXTFROMFLAGS;, &cv-link-_XGETTEXTPATHFLAGS;.Uses: &cv-link-POTDOMAIN;. diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index fad7d5d4ae..048fade4d0 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -3283,7 +3283,7 @@ The default list is: IMPLIBNOVERSIONSYMLINKS -Used to override &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; when +Used to override &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; when creating versioned import library for a shared library/loadable module. If not defined, then &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; is used to determine whether to disable symlink generation or not. @@ -3694,7 +3694,7 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES") If &cv-JAVACLASSPATH; is a single string containing search path separator characters (: for POSIX systems or - ; for Windows), + ; for Windows), it will be split on the separator into a list of individual paths for dependency scanning purposes. It will not be modified for JDK command-line usage, @@ -3705,7 +3705,7 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES") &SCons; always - supplies a + supplies a when invoking the Java compiler &javac;, regardless of the setting of &cv-link-JAVASOURCEPATH;, as it passes the path(s) to the source(s) supplied @@ -4830,7 +4830,7 @@ See &t-link-msgfmt; tool and &b-link-MOFiles; builder. MSGFMTCOMSTR -String to display when msgfmt(1) is invoked +String to display when msgfmt(1) is invoked (default: '', which means ``print &cv-link-MSGFMTCOM;''). See &t-link-msgfmt; tool and &b-link-MOFiles; builder. @@ -4872,7 +4872,7 @@ See &t-link-msginit; tool and &b-link-POInit; builder. MSGINITCOMSTR -String to display when msginit(1) is invoked +String to display when msginit(1) is invoked (default: '', which means ``print &cv-link-MSGINITCOM;''). See &t-link-msginit; tool and &b-link-POInit; builder. @@ -4968,7 +4968,7 @@ Supported versions include 6.0A, 6.0, 2003R2 -and +and 2003R1. @@ -5980,26 +5980,30 @@ a warning is issued showing the versions actually discovered, and the build will eventually fail indicating a missing compiler binary. If &cv-MSVC_VERSION; is not set, &SCons; will (by default) select the latest version of &MSVC; installed on your system. + + + The valid values for &cv-MSVC_VERSION; represent major versions of the compiler, except that versions ending in Exp -refer to "Express" or "Express for Desktop" Visual Studio editions, -which require distinct entries because they use a different -filesystem layout and have feature limitations compared to -the full version. +refer to "Express" or "Express for Desktop" Visual Studio editions. Values that do not look like a valid compiler version string are not supported. -To have the desired effect, &cv-MSVC_VERSION; must be set by the -time compiler discovery takes place. -If the default tools list -or an explicit tools list including &t-link-msvc; is used, -discovery takes place as the &consenv; is created, -so passing it as an argument in the the &f-link-Environment; call +To have the desired effect, &cv-MSVC_VERSION; must be set before an msvc &f-link-Tool; +(e.g., &t-link-msvc;) or an msvc-dependent &f-link-Tool; (e.g., &t-link-midl;) is loaded +into the &consenv;. + + + +If the default tools list or an explicit tools list is used that includes an +msvc &f-link-Tool; (e.g., &t-link-msvc;) or an msvc-dependent &f-link-Tool; (e.g., &t-link-midl;); +the &cv-MSVC_VERSION; is resolved when the &consenv; is created. +In this case, passing &cv-MSVC_VERSION; as an argument in the &f-link-Environment; call is the effective solution. -Otherwise, &cv-MSVC_VERSION; must be set before the first msvc tool is -loaded into the environment. +Otherwise, &cv-MSVC_VERSION; must be set before the first msvc &f-link-Tool; or +msvc-dependent &f-link-Tool; is loaded into the environment. See the manpage section "Construction Environments" for an example. @@ -6008,12 +6012,6 @@ The following table shows the correspondence of &cv-MSVC_VERSION; values to various version indicators ('x' is used as a placeholder for a single digit that can vary). -Note that it is not necessary to install Visual Studio -to build with &SCons; (for example, you can install only -Build Tools), but if Visual Studio is installed, -additional builders such as &b-link-MSVSSolution; and -&b-link-MSVSProject; become available and will -correspond to the indicated versions. @@ -6063,10 +6061,10 @@ Visual Studio "14.1Exp" - 14.1 - 1910 + 14.1 or 14.1x + 191x Visual Studio 2017 Express - 15.0 + 15.x "14.0" @@ -6177,6 +6175,31 @@ Visual Studio + + + + + It is not necessary to install a Visual Studio IDE + to build with &SCons; (for example, you can install only + Build Tools), but when a Visual Studio IDE is installed, + additional builders such as &b-link-MSVSSolution; and + &b-link-MSVSProject; become available and correspond to + the specified versions. + + + + Versions ending in Exp refer to historical + "Express" or "Express for Desktop" Visual Studio editions, + which had feature limitations compared to the full editions. + It is only necessary to specify the Exp + suffix to select the express edition when both express and + non-express editions of the same product are installed + simulaneously. The Exp suffix is unnecessary, + but accepted, when only the express edition is installed. + + + + The compilation environment can be further or more precisely specified through the use of several other &consvars;: see the descriptions of @@ -6651,7 +6674,7 @@ scons NINJA_CMD_ARGS="-v -j 3" Determines the type of format ninja should expect when parsing header include depfiles. Can be , , or . - The option corresponds to format, and + The option corresponds to format, and or correspond to . @@ -9149,7 +9172,7 @@ for more information). SOVERSION -This will construct the SONAME using on the base library name +This will construct the SONAME using on the base library name (test in the example below) and use specified SOVERSION to create SONAME. @@ -9158,7 +9181,7 @@ env.SharedLibrary('test', 'test.c', SHLIBVERSION='0.1.2', SOVERSION='2') The variable is used, for example, by &t-link-gnulink; linker tool. -In the example above SONAME would be libtest.so.2 +In the example above SONAME would be libtest.so.2 which would be a symlink and point to libtest.so.0.1.2 @@ -9864,48 +9887,71 @@ The version of the project, specified as a string. VSWHERE -Specify the location of vswhere.exe. +Specify the location of vswhere.exe. - The vswhere.exe executable is distributed with Microsoft Visual Studio and Build - Tools since the 2017 edition, but is also available standalone. - It provides full information about installations of 2017 and later editions. - With the argument, vswhere.exe can detect installations of the 2010 through 2015 - editions with limited data returned. -If VSWHERE is set, &SCons; will use that location. + The vswhere.exe executable is distributed with + Microsoft Visual Studio and Build Tools since the 2017 edition, + but is also available as a standalone installation. + It allows queries to obtain detailed information about + installations of 2017 and later editions; + with the argument, + it can return limited information for + installations of the 2010 through 2015 editions. + &SCons; makes use of this information to help determine + the state of compiler support. - Otherwise &SCons; will look in the following locations and set VSWHERE to the path of the first vswhere.exe -located. + You can set the &cv-VSWHERE; variable to the path to a specific + vswhere.exe binary, + and &SCons; will use that. + If undefined (the default), &SCons; will search for one, + looking in the following locations in order, + using the first found, and updating &cv-VSWHERE; with the location. %ProgramFiles(x86)%\Microsoft Visual Studio\Installer %ProgramFiles%\Microsoft Visual Studio\Installer %ChocolateyInstall%\bin +%LOCALAPPDATA%\Microsoft\WinGet\Links +~\scoop\shims +%SCOOP%\shims - - Note that VSWHERE must be set at the same time or prior to any of &t-link-msvc;, &t-link-msvs; , and/or &t-link-mslink; &f-link-Tool; being initialized. - Either set it as follows - + + + In order to take effect, &cv-VSWHERE; must be set before + the initial &MSVC; compiler discovery takes place. + Discovery happens, at the latest, during the first call to the + &f-link-Environment; function, unless a tools + list is specified which excludes the entire MSVC toolchain + (that is, does not include "defaults" + or any of the specific tools), + in which case it happens when one of the tools is invoked manually. + The following two examples illustrate this: + + + +# VSWHERE set as Environment is created env = Environment(VSWHERE='c:/my/path/to/vswhere') - - -or if your &consenv; is created specifying an empty tools list -(or a list of tools which omits all of default, msvs, msvc, and mslink), -and also before &f-link-env-Tool; is called to ininitialize any of those tools: - - env = Environment(tools=[]) - env['VSWHERE'] = r'c:/my/vswhere/install/location/vswhere.exe' - env.Tool('msvc') - env.Tool('mslink') - env.Tool('msvs') - - +# Initialization deferred with empty tools, triggered manually +env = Environment(tools=[]) +env['VSWHERE'] = r'c:/my/vswhere/install/location/vswhere.exe' +env.Tool('msvc') +env.Tool('mslink') +env.Tool('msvs') + + + + The tool modules that trigger detection are + &t-link-msvc;, &t-link-mslink;, &t-link-masm;, &t-link-midl; + and &t-link-msvs;. + + diff --git a/windows_ci_skip.txt b/windows_ci_skip.txt index a26444d877..d6a8e270af 100644 --- a/windows_ci_skip.txt +++ b/windows_ci_skip.txt @@ -26,7 +26,6 @@ test/Interactive/shell.py test/Interactive/tree.py test/Interactive/unknown-command.py test/Interactive/variant_dir.py -test/MSVC/msvc.py test/packaging/msi/explicit-target.py test/packaging/msi/file-placement.py test/packaging/msi/package.py From a16a07160ce97aa83d0c902d44ff9c9b30a0a17d Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 2 Jul 2024 06:33:45 -0600 Subject: [PATCH 085/386] vswhere: remove mention of legacy option [skip appveyor] Signed-off-by: Mats Wichmann --- SCons/Tool/msvc.xml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 596ae995c4..88bbd794ed 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -868,12 +868,9 @@ Specify the location of vswhere.exe. Microsoft Visual Studio and Build Tools since the 2017 edition, but is also available as a standalone installation. It allows queries to obtain detailed information about - installations of 2017 and later editions; - with the argument, - it can return limited information for - installations of the 2010 through 2015 editions. - &SCons; makes use of this information to help determine - the state of compiler support. + installations of 2017 and later editions. + &SCons; makes use of this information to determine + the state of compiler support for those editions. You can set the &cv-VSWHERE; variable to the path to a specific From 4d7b5ab20b4ae8714fdf24552359368d03861688 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 3 Jul 2024 08:47:16 -0600 Subject: [PATCH 086/386] Further tweak $VSWHERE, also $MSVC_VERSION [skip appveyor] Apply the same note to $MSVC_VERSION about discovery, for consistency. Signed-off-by: Mats Wichmann --- SCons/Tool/msvc.xml | 70 +++++++++++++++++--------------- doc/generated/variables.gen | 79 ++++++++++++++++++++----------------- 2 files changed, 81 insertions(+), 68 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 88bbd794ed..4c8bb7677b 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -394,9 +394,38 @@ If the specified version is unavailable and/or unknown to &SCons;, a warning is issued showing the versions actually discovered, and the build will eventually fail indicating a missing compiler binary. If &cv-MSVC_VERSION; is not set, &SCons; will (by default) select the -latest version of &MSVC; installed on your system. +latest version of &MSVC; installed on your system +(excluding any preview versions). + + + In order to take effect, &cv-MSVC_VERSION; must be set before + the initial &MSVC; compiler discovery takes place. + Discovery happens, at the latest, during the first call to the + &f-link-Environment; function, unless a tools + list is specified which excludes the entire &MSVC; toolchain - + that is, omits "defaults" + and any specific tool module that refers to parts of the toolchain + (&t-link-msvc;, &t-link-mslink;, &t-link-masm;, &t-link-midl; + and &t-link-msvs;). In this case, detection is deferred until + any one of those tool modules is invoked manually. + The following two examples illustrate this: + + + +# MSVC_VERSION set as Environment is created +env = Environment(MSVC_VERSION='14.2') + +# Initialization deferred with empty tools, triggered manually +env = Environment(tools=[]) +env['MSVC_VERSION'] = '14.2 +env.Tool('msvc') +env.Tool('mslink') +env.Tool('msvs') + + + The valid values for &cv-MSVC_VERSION; represent major versions of the compiler, except that versions ending in Exp @@ -405,23 +434,6 @@ Values that do not look like a valid compiler version string are not supported. - -To have the desired effect, &cv-MSVC_VERSION; must be set before an msvc &f-link-Tool; -(e.g., &t-link-msvc;) or an msvc-dependent &f-link-Tool; (e.g., &t-link-midl;) is loaded -into the &consenv;. - - - -If the default tools list or an explicit tools list is used that includes an -msvc &f-link-Tool; (e.g., &t-link-msvc;) or an msvc-dependent &f-link-Tool; (e.g., &t-link-midl;); -the &cv-MSVC_VERSION; is resolved when the &consenv; is created. -In this case, passing &cv-MSVC_VERSION; as an argument in the &f-link-Environment; call -is the effective solution. -Otherwise, &cv-MSVC_VERSION; must be set before the first msvc &f-link-Tool; or -msvc-dependent &f-link-Tool; is loaded into the environment. -See the manpage section "Construction Environments" for an example. - - The following table shows the correspondence of &cv-MSVC_VERSION; values to various version indicators @@ -873,10 +885,10 @@ Specify the location of vswhere.exe. the state of compiler support for those editions. - You can set the &cv-VSWHERE; variable to the path to a specific + Satting the &cv-VSWHERE; variable to the path to a specific vswhere.exe binary, - and &SCons; will use that. - If undefined (the default), &SCons; will search for one, + causes &SCons; to use that binary. + If not set (the default), &SCons; will search for one, looking in the following locations in order, using the first found, and updating &cv-VSWHERE; with the location. @@ -897,10 +909,12 @@ Specify the location of vswhere.exe. the initial &MSVC; compiler discovery takes place. Discovery happens, at the latest, during the first call to the &f-link-Environment; function, unless a tools - list is specified which excludes the entire MSVC toolchain - (that is, does not include "defaults" - or any of the specific tools), - in which case it happens when one of the tools is invoked manually. + list is specified which excludes the entire &MSVC; toolchain - + that is, omits "defaults" + and any specific tool module that refers to parts of the toolchain + (&t-link-msvc;, &t-link-mslink;, &t-link-masm;, &t-link-midl; + and &t-link-msvs;). In this case, detection is deferred until + any one of those tool modules is invoked manually. The following two examples illustrate this: @@ -915,12 +929,6 @@ env.Tool('msvc') env.Tool('mslink') env.Tool('msvs') - - - The tool modules that trigger detection are - &t-link-msvc;, &t-link-mslink;, &t-link-masm;, &t-link-midl; - and &t-link-msvs;. - diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 048fade4d0..bdf56c47e3 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -5979,9 +5979,38 @@ If the specified version is unavailable and/or unknown to &SCons;, a warning is issued showing the versions actually discovered, and the build will eventually fail indicating a missing compiler binary. If &cv-MSVC_VERSION; is not set, &SCons; will (by default) select the -latest version of &MSVC; installed on your system. +latest version of &MSVC; installed on your system +(excluding any preview versions).
+ + + In order to take effect, &cv-MSVC_VERSION; must be set before + the initial &MSVC; compiler discovery takes place. + Discovery happens, at the latest, during the first call to the + &f-link-Environment; function, unless a tools + list is specified which excludes the entire &MSVC; toolchain - + that is, omits "defaults" + and any specific tool module that refers to parts of the toolchain + (&t-link-msvc;, &t-link-mslink;, &t-link-masm;, &t-link-midl; + and &t-link-msvs;). In this case, detection is deferred until + any one of those tool modules is invoked manually. + The following two examples illustrate this: + + + +# MSVC_VERSION set as Environment is created +env = Environment(MSVC_VERSION='14.2') + +# Initialization deferred with empty tools, triggered manually +env = Environment(tools=[]) +env['MSVC_VERSION'] = '14.2 +env.Tool('msvc') +env.Tool('mslink') +env.Tool('msvs') + + + The valid values for &cv-MSVC_VERSION; represent major versions of the compiler, except that versions ending in Exp @@ -5990,23 +6019,6 @@ Values that do not look like a valid compiler version string are not supported. - -To have the desired effect, &cv-MSVC_VERSION; must be set before an msvc &f-link-Tool; -(e.g., &t-link-msvc;) or an msvc-dependent &f-link-Tool; (e.g., &t-link-midl;) is loaded -into the &consenv;. - - - -If the default tools list or an explicit tools list is used that includes an -msvc &f-link-Tool; (e.g., &t-link-msvc;) or an msvc-dependent &f-link-Tool; (e.g., &t-link-midl;); -the &cv-MSVC_VERSION; is resolved when the &consenv; is created. -In this case, passing &cv-MSVC_VERSION; as an argument in the &f-link-Environment; call -is the effective solution. -Otherwise, &cv-MSVC_VERSION; must be set before the first msvc &f-link-Tool; or -msvc-dependent &f-link-Tool; is loaded into the environment. -See the manpage section "Construction Environments" for an example. - - The following table shows the correspondence of &cv-MSVC_VERSION; values to various version indicators @@ -9895,18 +9907,15 @@ Specify the location of vswhere.exe. Microsoft Visual Studio and Build Tools since the 2017 edition, but is also available as a standalone installation. It allows queries to obtain detailed information about - installations of 2017 and later editions; - with the argument, - it can return limited information for - installations of the 2010 through 2015 editions. - &SCons; makes use of this information to help determine - the state of compiler support. + installations of 2017 and later editions. + &SCons; makes use of this information to determine + the state of compiler support for those editions. - You can set the &cv-VSWHERE; variable to the path to a specific + Satting the &cv-VSWHERE; variable to the path to a specific vswhere.exe binary, - and &SCons; will use that. - If undefined (the default), &SCons; will search for one, + causes &SCons; to use that binary. + If not set (the default), &SCons; will search for one, looking in the following locations in order, using the first found, and updating &cv-VSWHERE; with the location. @@ -9927,10 +9936,12 @@ Specify the location of vswhere.exe. the initial &MSVC; compiler discovery takes place. Discovery happens, at the latest, during the first call to the &f-link-Environment; function, unless a tools - list is specified which excludes the entire MSVC toolchain - (that is, does not include "defaults" - or any of the specific tools), - in which case it happens when one of the tools is invoked manually. + list is specified which excludes the entire &MSVC; toolchain - + that is, omits "defaults" + and any specific tool module that refers to parts of the toolchain + (&t-link-msvc;, &t-link-mslink;, &t-link-masm;, &t-link-midl; + and &t-link-msvs;). In this case, detection is deferred until + any one of those tool modules is invoked manually. The following two examples illustrate this: @@ -9945,12 +9956,6 @@ env.Tool('msvc') env.Tool('mslink') env.Tool('msvs') - - - The tool modules that trigger detection are - &t-link-msvc;, &t-link-mslink;, &t-link-masm;, &t-link-midl; - and &t-link-msvs;. - From 547140fc70d3560e1e676c59415b1ad7dd5e2115 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 3 Jul 2024 10:51:30 -0600 Subject: [PATCH 087/386] Additinal tweaks for $VSWHERE [skip appveyor] Signed-off-by: Mats Wichmann --- SCons/Tool/msvc.xml | 29 +++++++++++++++-------------- doc/generated/variables.gen | 29 +++++++++++++++-------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 4c8bb7677b..97edb2ced0 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -885,22 +885,23 @@ Specify the location of vswhere.exe. the state of compiler support for those editions. - Satting the &cv-VSWHERE; variable to the path to a specific - vswhere.exe binary, + Setting the &cv-VSWHERE; variable to the path to a specific + vswhere.exe binary causes &SCons; to use that binary. - If not set (the default), &SCons; will search for one, + If not set, &SCons; will search for one, looking in the following locations in order, - using the first found, and updating &cv-VSWHERE; with the location. - - - -%ProgramFiles(x86)%\Microsoft Visual Studio\Installer -%ProgramFiles%\Microsoft Visual Studio\Installer -%ChocolateyInstall%\bin -%LOCALAPPDATA%\Microsoft\WinGet\Links -~\scoop\shims -%SCOOP%\shims - + using the first found + (&cv-VSWHERE; is updated with the location): + + + +%ProgramFiles(x86)%\Microsoft Visual Studio\Installer +%ProgramFiles%\Microsoft Visual Studio\Installer +%ChocolateyInstall%\bin +%LOCALAPPDATA%\Microsoft\WinGet\Links +%USERPROFILE%\scoop\shims +%SCOOP%\shims + diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index bdf56c47e3..68ac6db12a 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -9912,22 +9912,23 @@ Specify the location of vswhere.exe. the state of compiler support for those editions. - Satting the &cv-VSWHERE; variable to the path to a specific - vswhere.exe binary, + Setting the &cv-VSWHERE; variable to the path to a specific + vswhere.exe binary causes &SCons; to use that binary. - If not set (the default), &SCons; will search for one, + If not set, &SCons; will search for one, looking in the following locations in order, - using the first found, and updating &cv-VSWHERE; with the location. - - - -%ProgramFiles(x86)%\Microsoft Visual Studio\Installer -%ProgramFiles%\Microsoft Visual Studio\Installer -%ChocolateyInstall%\bin -%LOCALAPPDATA%\Microsoft\WinGet\Links -~\scoop\shims -%SCOOP%\shims - + using the first found + (&cv-VSWHERE; is updated with the location): + + + +%ProgramFiles(x86)%\Microsoft Visual Studio\Installer +%ProgramFiles%\Microsoft Visual Studio\Installer +%ChocolateyInstall%\bin +%LOCALAPPDATA%\Microsoft\WinGet\Links +%USERPROFILE%\scoop\shims +%SCOOP%\shims + From 7ff17a5b2be25ebddafcf2d4adf20ffae5ba5767 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 4 May 2024 11:41:54 -0600 Subject: [PATCH 088/386] Update manpage intro [skip appveyor] Just assorted wording tweaks, only to DESCRIPTION section. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 1 + doc/man/scons.xml | 178 ++++++++++++++++++++++++++-------------------- 3 files changed, 103 insertions(+), 77 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9d69e707ad..1502f9976b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -175,6 +175,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER is reached. - Regularized header (copyright, licens) at top of documentation files using SPDX. + - Updated introductory section of manual page. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index aa17f64839..961f52d1a9 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -154,6 +154,7 @@ DOCUMENTATION - Updated manpage description of Command "builder" and function. - Updated the notes about reproducible builds with SCons and the example. - Regularized header (copyright, licens) at top of documentation files using SPDX. +- Updated introductory section of manual page. diff --git a/doc/man/scons.xml b/doc/man/scons.xml index cc81f6487e..fdfb043472 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -74,7 +74,7 @@ This file is processed by the bin/SConsDoc.py module. DESCRIPTION -&scons; +&SCons; is an extensible open source build system that orchestrates the construction of software (and other tangible products such as documentation files) by determining which @@ -105,23 +105,26 @@ The CPython project retired 3.6 in Sept 2021: . -You set up an &SCons; -build system by writing a script + +You set up an &SCons; build by writing a script that describes things to build (targets), and, if necessary, the rules to build those files (actions). &SCons; comes with a collection of Builder methods -which apply premade actions for building many common software components +which supply premade Actions for building many common software components such as executable programs, object files and libraries, so that for many software projects, only the targets and input files (sources) need be specified in a call to a builder. -&SCons; thus can operate at a level of abstraction above that of pure filenames. -For example if you specify a library target named "foo", + + + +&SCons; operates at a level of abstraction above that of pure filenames. +For example if you specify a shared library target named "foo", &SCons; keeps track of the actual operating system dependent filename -(such as libfoo.so on a GNU/Linux system), -and how to refer to that library in later construction steps -that want to use it, so you don't have to specify that precise -information yourself. +(such as libfoo.so on a GNU/Linux system +and foo.dll on Windows)), +and gives you a handle to refer to that target in other steps, +so you don't have to use system-specific strings yourself. &SCons; can also scan automatically for dependency information, such as header files included by source code files (for example, #include @@ -136,23 +139,29 @@ to support additional input file types. including a cryptographic hash of the contents of source files, is cached for later reuse. By default this hash (the &contentsig;) -is used to determine if a file has changed since the last build, -although this can be controlled by selecting an appropriate +is used to decide if a file has changed since the last build, +although other algorithms can be used by selecting an appropriate &f-link-Decider; function. Implicit dependency files are also part of out-of-date computation. The scanned implicit dependency information can optionally be cached and used to speed up future builds. A hash of each executed build action (the &buildsig;) -is cached, so that changes to build instructions (changing flags, etc.) -or to the build tools themselves (new version) +is also cached, so that changes to build instructions (changing flags, etc.) +or to the build tools themselves (e.g. a compiler upgrade) can also trigger a rebuild. -&SCons; supports the concept of separated source and build -directories through the definition of +&SCons; supports separated source and build +directories (also called "out-of-tree builds") +through the definition of variant directories -(see the &f-link-VariantDir; function). +Using a separated build directory helps keep +the source directory clean of artifacts when doing searches, +allows setting up differing builds ("variants") without conflicts, +and allows resetting the build by just removing the build directory +(note that SCons does have a "clean" mode as well). +See the &f-link-VariantDir; description for more details. @@ -177,9 +186,10 @@ specified with the option, which leaves the current directory as the project top directory. + The build configuration may be split into multiple files: -the &SConstruct; file may specify additional +the &SConstruct; file can specify additional configuration files by calling the &f-link-SConscript; function, and any file thus invoked may @@ -196,23 +206,24 @@ configuration files for a project (including the &SConstruct; file), regardless of the actual file names or number of such files. A hierarchical build is not recursive - all of -the SConscript files are processed in a single pass, -although each is processed in a separate context so -as not to interfere with one another. &SCons; provides -mechanisms for information to be shared between -SConscript files when needed. +the SConscript files are processed in a single pass +so that &scons; has a picture of the complete +dependency tree at all times. +Each SConscript file is processed in a separate context +so settings made in one script do not leak into another; +information can however be shared explicitly between scripts. Before reading the &SConscript; files, &scons; -looks for a directory named -site_scons -in various system directories and in the -project top directory, or, if specified, -the directory from the +looks for a site directory - +a directory named site_scons +is searched for in various system directories and in the +project top directory, or if the -option instead, and prepends the ones it -finds to the &Python; module search path (sys.path), +option is given, checks only for that directory. +Found site directories are prepended +to the &Python; module search path (sys.path), thus allowing modules in such directories to be imported in the normal &Python; way in &SConscript; files. For each found site directory, @@ -231,28 +242,31 @@ controlling the site directories. &SConscript; files are written in the -&Python; programming language, -although it is normally not necessary to be a &Python; -programmer to use &scons; effectively. -&SConscript; files are invoked in a context that makes -the facilities described in this manual page available -in their local namespace without any special steps. +&Python; programming language. +For many tasks, the simple syntax can be understood from examples, +so it is normally not necessary to be a &Python; +programmer to use &SCons; effectively. +&SConscript; files are executed in a context that makes +the facilities described in this manual page directly +available (that is, no need to import). Standard &Python; scripting capabilities -such as flow control, data manipulation, and imported &Python; libraries -are available to use to handle complicated build situations. +such as flow control, data manipulation, and imported &Python; modules +are available to use in more complicated build configurations. Other &Python; files can be made a part of the build system, but they do not automatically have the &SCons; context and need to import it if they need access (described later). -&scons; -reads and executes all of the included &SConscript; files +&SCons; reads and executes all of the included &SConscript; files before it begins building any targets. -To make this clear, -&scons; -prints the following messages about what it is doing: +Progress messages show this behavior +(the state change lines - those +beginning with the scons: tag - +may be suppressed using the + option): + $ scons foo.out @@ -264,12 +278,6 @@ scons: done building targets. $ -The status messages -(lines beginning with the scons: tag) -may be suppressed using the - -option. - To assure reproducible builds, &SCons; @@ -277,30 +285,41 @@ uses a restricted execution environment for running external commands used to build targets, rather then propagating the full environment in effect at the time &scons; was called. -This helps avoid problems like picking up accidental settings, +This helps avoid problems like picking up accidental +or malicious settings, temporary debug values that are no longer needed, or one developer having different settings than another -(or than the CI/CD pipeline). -Environment variables that are needed for proper -operation of such commands need to be set explicitly, -which can be done either by assigning the desired values, -or by picking values individually out of environment variables -using the &Python; os.environ dictionary. -The execution environment for a given &consenv; is -contained in its &cv-link-ENV; &consvar;. -A few environment variables are picked up automatically - -see ). - - - -In particular, if the compiler or other commands -that you want to use to build your target files -are not in standard system locations, -&scons; -will not find them unless -you explicitly include the locations into the -PATH element of the -execution environment. +(or than the CI pipeline). +Environment variables needed for the proper +operation of such commands must be set in the +execution environment explicitly, +either by assigning the desired values, +or by picking those values individually or collectively +out of environment variables exposed by the &Python; +os.environ dictionary +(as external program inputs they should be validate +before use). +The execution environment for a given &consenv; +is its &cv-link-ENV; value. +A small number of environment variables are picked up automatically +by &scons; itself (see ). + + + +In particular, if a compiler or other external command +needed to build a target file +is not in &scons;' idea of a standard system location, +it will not be found at runtime unless +you explicitly add the location into the +execution environment's PATH element. +This is a particular consideration on Windows platforms, +where it is common for a command to install into an app-specific +location and depend on setting +PATH in order for them to be found, +which does not automatically work for &SCons;. + + + One example approach is to extract the entire PATH environment variable and set that into the @@ -323,13 +342,13 @@ import os env = Environment( ENV={ 'PATH': os.environ['PATH'], - 'ANDROID_HOME': os.environ['ANDROID_HOME'], - 'ANDROID_NDK_HOME': os.environ['ANDROID_NDK_HOME'], + 'MODULEPATH': os.environ['MODULEPATH'], + 'PKG_CONFIG_PATH': os.environ['PKG_CONFIG_PATH'], } ) -Or you may explicitly propagate the invoking user's +Or you can explicitly propagate the invoking user's complete external environment: @@ -435,6 +454,7 @@ and the PharLap ETS compiler. On Windows system which identify as cygwin (that is, if &scons; is invoked from a cygwin shell), the order changes to prefer the GCC toolchain over the MSVC tools. + On OS/2 systems, &scons; searches in order for the @@ -447,13 +467,17 @@ searches for the native compiler tools (MIPSpro, Visual Age, aCC, and Forte tools respectively) and the GCC tool chain. On all other platforms, -including POSIX (Linux and UNIX) platforms, +including POSIX (Linux and UNIX) and macOS platforms, &scons; searches in order for the GCC tool chain, and the Intel compiler tools. -These default values may be overridden -by appropriate setting of &consvars;. +The defaul tool selection can be pre-empted +through the use of the tools +argument to &consenv; creation methods, +explcitly calling the &f-link-Tool; loader, +the through the setting of various setting of &consvars;. + Target Selection From 6f9a9d2118e995cd6d05fd9f99b26713a82e277f Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 4 Jul 2024 10:07:39 -0600 Subject: [PATCH 089/386] manpage intro: a few more tweaks [skip appveyor] Signed-off-by: Mats Wichmann --- doc/man/scons.xml | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/doc/man/scons.xml b/doc/man/scons.xml index fdfb043472..d2dbc38106 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -122,7 +122,7 @@ need be specified in a call to a builder. For example if you specify a shared library target named "foo", &SCons; keeps track of the actual operating system dependent filename (such as libfoo.so on a GNU/Linux system -and foo.dll on Windows)), +and foo.dll on Windows), and gives you a handle to refer to that target in other steps, so you don't have to use system-specific strings yourself. &SCons; can also scan automatically for dependency information, @@ -166,22 +166,23 @@ See the &f-link-VariantDir; description for more details. When invoked, &scons; -looks for a file named -&SConstruct; -(other names are also accepted, -see ) -in the current directory and reads the -build configuration from that file; -that directory is considered the project top directory. +looks for a file describing the build configuration +in the current directory and reads that in. +The file is by default named &SConstruct;, +although some variants of that, +or a developer-chosen name, are also accepted +(see ). +If found, the currrent directory +is set as the project top directory. Certain command-line options specify alternate -places to look for the &SConstruct; +places to look for &SConstruct; (see , , and ), -which will set the project top directory to the place found. -A path to the main build configuration file can also be +which will set the project top directory to the path found. +A path to the build configuration can also be specified with the option, which leaves the current directory as the project top directory. @@ -208,13 +209,13 @@ regardless of the actual file names or number of such files. A hierarchical build is not recursive - all of the SConscript files are processed in a single pass so that &scons; has a picture of the complete -dependency tree at all times. +dependency tree when it begins considering what needs building. Each SConscript file is processed in a separate context so settings made in one script do not leak into another; information can however be shared explicitly between scripts. -Before reading the &SConscript; files, +Before reading the SConscript files, &scons; looks for a site directory - a directory named site_scons @@ -288,7 +289,7 @@ in effect at the time &scons; was called. This helps avoid problems like picking up accidental or malicious settings, temporary debug values that are no longer needed, -or one developer having different settings than another +or a developer having different settings than another (or than the CI pipeline). Environment variables needed for the proper operation of such commands must be set in the From b2f2be195fc1671a024a5d8c12f1d9d26cf4cf8c Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Fri, 5 Jul 2024 11:06:05 -0500 Subject: [PATCH 090/386] Add `SCsub` to known SConscript names --- CHANGES.txt | 2 ++ RELEASE.txt | 2 ++ SCons/Script/Main.py | 4 ++++ doc/man/scons.xml | 5 +++++ 4 files changed, 13 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 9d69e707ad..bfbe9fe520 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -76,6 +76,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Migrate setup.cfg logic to pyproject.toml; remove setup.cfg. - Update .gitattributes to match .editorconfig; enforce eol settings. - Replace black/flake8 with ruff for more efficient formatting & linting. + - When debugging (--debug=pdb), the filename SCsub is now recognized when + manipulating breakpoints. From Raymond Li: - Fix issue #3935: OSErrors are now no longer hidden during execution of diff --git a/RELEASE.txt b/RELEASE.txt index aa17f64839..e019f78f48 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -74,6 +74,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY allow a developer to supply a custom validator, which previously could be inhibited by the converter failing before the validator is reached. +- When debugging (--debug=pdb), the filename SCsub is now recognized when + manipulating breakpoints. FIXES ----- diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index a5f8f42c2d..4588345a9a 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -86,7 +86,11 @@ "Sconstruct", "sconstruct", "SConscript", + "Sconscript", "sconscript", + "SCsub", # Uncommon alternative to SConscript + "Scsub", + "scsub", ] # Global variables diff --git a/doc/man/scons.xml b/doc/man/scons.xml index cc81f6487e..64204feaba 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -1023,6 +1023,11 @@ The names &SConstruct; and &SConscript; are now recognized without requiring .py suffix. + +Changed in version 4.7.1: +The name SCsub is now recognized +without requiring .py suffix. + From 9fc18579b415b39cfb69e8af80551f9a5f83d2f0 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 25 Jun 2024 08:08:56 -0600 Subject: [PATCH 091/386] Some minor test tweaking Don't """code block""" % locals() if there's not actually a substitution in the code block. While there, fix any old-style file headers, and add a DefaultEnvironment call if not present. Signed-off-by: Mats Wichmann --- bin/scons-time.py | 1 + test/CacheDir/value_dependencies/SConstruct | 4 +- test/Configure/custom-tests.py | 4 +- test/Errors/preparation.py | 10 ++--- test/GetBuildFailures/option-k.py | 11 +++-- test/Java/JARCHDIR.py | 3 +- test/Java/JARFLAGS.py | 4 +- test/Java/JAVACFLAGS.py | 2 +- test/Java/JAVACLASSPATH.py | 2 +- test/Java/JAVAH.py | 5 +-- test/Java/multi-step.py | 5 ++- test/Java/no-JARCHDIR.py | 7 +-- test/Java/swig-dependencies.py | 15 ++++--- test/LEX/no_lex.py | 2 +- test/MSVC/MSVC_USE_SCRIPT.py | 3 +- test/MSVC/MSVC_USE_SETTINGS.py | 7 +-- test/MSVC/PCH-source.py | 2 +- test/MSVC/TARGET_ARCH.py | 8 ++-- test/MSVC/batch-longlines.py | 2 +- test/MinGW/MinGWSharedLibrary.py | 2 +- test/MinGW/WINDOWS_INSERT_DEF.py | 2 +- test/Removed/Old/warn-missing-sconscript.py | 2 +- test/TEX/LATEX2.py | 4 +- test/TEX/LATEXCOMSTR.py | 12 ++--- test/TEX/PDFLATEXCOMSTR.py | 12 ++--- test/TEX/PDFTEXCOMSTR.py | 12 ++--- test/TEX/TEXCOMSTR.py | 22 ++++----- test/TEX/configure.py | 15 ++++--- test/TEX/dryrun.py | 13 +++--- test/TEX/rename_result.py | 11 ++--- test/TEX/usepackage.py | 11 ++--- test/Variables/import.py | 4 +- test/YACC/live-check-output-cleaned.py | 2 +- test/ZIP/ZIP.py | 3 +- test/ZIP/ZIPROOT.py | 3 +- test/ZIP/ZIP_OVERRIDE_TIMESTAMP.py | 5 ++- test/gettext/POTUpdate/UserExamples.py | 19 +++++--- test/implicit-cache/DualTargets.py | 15 +++---- test/no-global-dependencies.py | 37 ++++++++-------- test/sconsign/script/SConsignFile.py | 49 +++++++++++---------- testing/framework/TestCommonTests.py | 12 +++-- testing/framework/TestSCons.py | 4 +- 42 files changed, 196 insertions(+), 172 deletions(-) diff --git a/bin/scons-time.py b/bin/scons-time.py index c8121fb6f2..f8858f772f 100644 --- a/bin/scons-time.py +++ b/bin/scons-time.py @@ -278,6 +278,7 @@ class SConsTimer: name = 'scons-time' name_spaces = ' ' * len(name) + @staticmethod def makedict(**kw): return kw diff --git a/test/CacheDir/value_dependencies/SConstruct b/test/CacheDir/value_dependencies/SConstruct index 9e971c64d3..3923228bf1 100644 --- a/test/CacheDir/value_dependencies/SConstruct +++ b/test/CacheDir/value_dependencies/SConstruct @@ -7,8 +7,8 @@ import SCons.Node CacheDir('cache') def b(target, source, env): - with open(target[0].abspath, 'w') as f: - pass + with open(target[0].abspath, 'w') as f: + pass def scan(node, env, path): """Have the node depend on a directory, which depends on a Value node.""" diff --git a/test/Configure/custom-tests.py b/test/Configure/custom-tests.py index 3f5bcb6db3..9a6d0f61ab 100644 --- a/test/Configure/custom-tests.py +++ b/test/Configure/custom-tests.py @@ -55,7 +55,7 @@ """) test.write('SConstruct', """\ -DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) def CheckCustom(test): test.Message( 'Executing MyTest ... ' ) retCompileOK = test.TryCompile( '%(compileOK)s', '.c' ) @@ -164,7 +164,7 @@ def CheckEmptyDict(test): conf.CheckDict() conf.CheckEmptyDict() env = conf.Finish() -""" % locals()) +""") test.run() diff --git a/test/Errors/preparation.py b/test/Errors/preparation.py index 914827a713..51ca2de9b2 100644 --- a/test/Errors/preparation.py +++ b/test/Errors/preparation.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__" """ A currently disabled test that used to verify that we print a useful @@ -49,6 +48,7 @@ test.subdir('install', 'work') test.write(['work', 'SConstruct'], """\ +_ = DefaultEnvironment(tools=[]) file_out = Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE')) Alias("install", file_out) @@ -56,7 +56,7 @@ # IOError or OSError when we try to open it to read its signature. import os os.mkdir('file.in') -""" % locals()) +""") if sys.platform == 'win32': error_message = "Permission denied" diff --git a/test/GetBuildFailures/option-k.py b/test/GetBuildFailures/option-k.py index 039ad50a91..50ad302980 100644 --- a/test/GetBuildFailures/option-k.py +++ b/test/GetBuildFailures/option-k.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,14 +22,11 @@ # 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 that a failed build action with -k works as expected. """ -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - import TestSCons _python_ = TestSCons._python_ @@ -85,7 +84,7 @@ def print_build_failures(): scons: done building targets (errors occurred during build). f4 failed: Error 1 f5 failed: Error 1 -""" % locals() +""" expect_stderr = """\ scons: *** [f4] Error 1 @@ -99,7 +98,7 @@ def print_build_failures(): test.must_match(test.workpath('f3'), 'f3.in\n') test.must_not_exist(test.workpath('f4')) test.must_not_exist(test.workpath('f5')) -test.must_match(test.workpath('f6'), 'f6.in\n') +test.must_match(test.workpath('f6'), 'f6.in\n') test.pass_test() diff --git a/test/Java/JARCHDIR.py b/test/Java/JARCHDIR.py index c24717231d..c84509bd5d 100644 --- a/test/Java/JARCHDIR.py +++ b/test/Java/JARCHDIR.py @@ -32,7 +32,6 @@ ${TARGET} or ${SOURCE} work. """ - import TestSCons test = TestSCons.TestSCons() @@ -60,7 +59,7 @@ source_env.Jar('out/s.jar', 'in/s.class') Default(bin, jar, inner) -""" % locals()) +""") test.subdir('in') diff --git a/test/Java/JARFLAGS.py b/test/Java/JARFLAGS.py index 36af5929ec..957464d98d 100644 --- a/test/Java/JARFLAGS.py +++ b/test/Java/JARFLAGS.py @@ -39,7 +39,7 @@ env['JARFLAGS'] = 'cvf' class_files = env.Java(target='classes', source='src') env.Jar(target='test.jar', source=class_files) -""" % locals()) +""") test.write(['src', 'Example1.java'], """\ package src; @@ -60,7 +60,7 @@ jar cvf test.jar -C classes src.Example1\\.class .* adding: src.Example1\\.class.* -""" % locals()) +""") test.run(arguments = '.', diff --git a/test/Java/JAVACFLAGS.py b/test/Java/JAVACFLAGS.py index 3a555a3a1a..51bf3f68dd 100644 --- a/test/Java/JAVACFLAGS.py +++ b/test/Java/JAVACFLAGS.py @@ -37,7 +37,7 @@ DefaultEnvironment(tools=[]) env = Environment(tools=['javac'], JAVACFLAGS='-O') env.Java(target='classes', source='src') -""" % locals()) +""") test.write(['src', 'Example1.java'], """\ package src; diff --git a/test/Java/JAVACLASSPATH.py b/test/Java/JAVACLASSPATH.py index 38f01cc03b..48034dd096 100644 --- a/test/Java/JAVACLASSPATH.py +++ b/test/Java/JAVACLASSPATH.py @@ -59,7 +59,7 @@ j1 = env.Java(target='class1', source='com.1/Example1.java') j2 = env.Java(target='class2', source='com.2/Example2.java') env.JavaH(target='outdir', source=[j1, j2], JAVACLASSPATH='class2') -""" % locals()) +""") test.subdir('com.1', 'com.2') diff --git a/test/Java/JAVAH.py b/test/Java/JAVAH.py index 27e98216f2..1ad3933c05 100644 --- a/test/Java/JAVAH.py +++ b/test/Java/JAVAH.py @@ -39,7 +39,7 @@ # Skip this test as SCons doesn't (currently) predict the generated # inner/anonymous class generated .class files generated by gcj -# and so will always fail +# and so will always fail if test.javac_is_gcj: test.skip_test('Test not valid for gcj (gnu java); skipping test(s).\n') @@ -242,8 +242,7 @@ class Private { test.must_match( 'wrapper.out', - "wrapper_with_args.py javah -d outdir2 -classpath class2 com.sub.bar.Example4 com.other.Example5 com.sub.bar.Example6\n" - % locals(), + "wrapper_with_args.py javah -d outdir2 -classpath class2 com.sub.bar.Example4 com.other.Example5 com.sub.bar.Example6\n", mode='r', ) diff --git a/test/Java/multi-step.py b/test/Java/multi-step.py index ab627cb46f..05ef90d40e 100644 --- a/test/Java/multi-step.py +++ b/test/Java/multi-step.py @@ -49,7 +49,7 @@ # Skip this test as SCons doesn't (currently) predict the generated # inner/anonymous class generated .class files generated by gcj -# and so will always fail +# and so will always fail if test.javac_is_gcj: test.skip_test('Test not valid for gcj (gnu java); skipping test(s).\n') @@ -96,6 +96,7 @@ else: tools = ['default', 'javac', 'javah', 'swig'] +_ = DefaultEnvironment(tools=[]) env = Environment(tools=tools, CPPPATH=["$JAVAINCLUDES"]) Export('env') # env.PrependENVPath('PATH',os.environ.get('PATH',[])) @@ -125,7 +126,7 @@ 'buildout/javah/SConscript', ] ) -""" % locals()) +""") test.write(['src', 'HelloApplet', 'Hello.html'], """\ diff --git a/test/Java/no-JARCHDIR.py b/test/Java/no-JARCHDIR.py index d3392518c8..0798b090ad 100644 --- a/test/Java/no-JARCHDIR.py +++ b/test/Java/no-JARCHDIR.py @@ -51,10 +51,11 @@ """) test.write('SConstruct', """\ -env = Environment(tools = ['javac', 'jar']) +_ = DefaultEnvironment(tools=[]) +env = Environment(tools=['javac', 'jar']) jar = env.Jar('x.jar', env.Java(target = 'classes', source = 'src')) -""" % locals()) +""") test.run(arguments = '.') test.run(program = where_jar, arguments = 'tf x.jar') @@ -73,7 +74,7 @@ JARCHDIR = None) jar = env.Jar('x.jar', env.Java(target = 'classes', source = 'src')) -""" % locals()) +""") test.run(arguments = '.') diff --git a/test/Java/swig-dependencies.py b/test/Java/swig-dependencies.py index 48010ad8f0..3fa563dd44 100644 --- a/test/Java/swig-dependencies.py +++ b/test/Java/swig-dependencies.py @@ -47,6 +47,7 @@ test.write(['SConstruct'], """\ import os +_ = DefaultEnvironment(tools=[]) env = Environment(ENV=os.environ) if env['PLATFORM'] != 'win32': env.Append(CPPFLAGS=' -g -Wall') @@ -56,7 +57,7 @@ SConscript('#foo/SConscript') SConscript('#java/SConscript') -""" % locals()) +""") test.write(['foo', 'SConscript'], """\ Import('env') @@ -68,7 +69,7 @@ #include "foo.h" int fooAdd(int a, int b) { - return a + b; + return a + b; } """) @@ -115,11 +116,11 @@ swigflags = '-c++ -java -Wall -Ifoo -DTEST_$PLATFORM' Java_foo_interface = env.SharedLibrary( - 'Java_foo_interface', - 'Java_foo_interface.i', - LIBS = libadd, - LIBPATH = libpath, - SWIGFLAGS = swigflags, + 'Java_foo_interface', + 'Java_foo_interface.i', + LIBS = libadd, + LIBPATH = libpath, + SWIGFLAGS = swigflags, SWIGOUTDIR = Dir('build'), SWIGCXXFILESUFFIX = "_wrap.cpp") diff --git a/test/LEX/no_lex.py b/test/LEX/no_lex.py index e53b02e8e9..969dd3840f 100644 --- a/test/LEX/no_lex.py +++ b/test/LEX/no_lex.py @@ -46,7 +46,7 @@ def Detect(self, progs): DefaultEnvironment(tools=[]) foo = TestEnvironment(tools=['default', 'lex']) print(foo.Dictionary('LEX')) -""" % locals()) +""") test.run(arguments='-Q -s', stdout='None\n') diff --git a/test/MSVC/MSVC_USE_SCRIPT.py b/test/MSVC/MSVC_USE_SCRIPT.py index 67eddfc1e9..d6c683ffe5 100644 --- a/test/MSVC/MSVC_USE_SCRIPT.py +++ b/test/MSVC/MSVC_USE_SCRIPT.py @@ -35,8 +35,9 @@ test.skip_if_not_msvc() test.write('SConstruct', """ +_ = DefaultEnvironment(tools=[]) env = Environment(tools=['msvc'], MSVC_USE_SCRIPT='nosuchscriptexists') -""" % locals()) +""") test.run(arguments = ".", status=2, stderr=None) test.must_contain_all(test.stderr(), "Script specified by MSVC_USE_SCRIPT not found") diff --git a/test/MSVC/MSVC_USE_SETTINGS.py b/test/MSVC/MSVC_USE_SETTINGS.py index fd6f85ceb5..a473862fa3 100644 --- a/test/MSVC/MSVC_USE_SETTINGS.py +++ b/test/MSVC/MSVC_USE_SETTINGS.py @@ -35,6 +35,7 @@ test.skip_if_not_msvc() test.write('SConstruct', """ +_ = DefaultEnvironment(tools=[]) e1 = Environment() cl1 = e1.WhereIs('cl.exe') @@ -46,21 +47,21 @@ if cl1 == cl3: print("CL.EXE PATHS MATCH") -""" % locals()) +""") test.run(arguments=".", status=0, stderr=None) test.must_contain_all(test.stdout(), "CL.EXE PATHS MATCH") test.write('SConstruct', """ env = Environment(MSVC_USE_SETTINGS={}) -""" % locals()) +""") test.run(arguments="--warn=visual-c-missing .", status=0, stderr=None) test.must_contain_all(test.stderr(), "Could not find requested MSVC compiler 'cl'") test.write('SConstruct', """ env = Environment(MSVC_USE_SETTINGS='dict or None') -""" % locals()) +""") test.run(arguments=".", status=2, stderr=None) test.must_contain_all(test.stderr(), "MSVCUseSettingsError: MSVC_USE_SETTINGS type error") diff --git a/test/MSVC/PCH-source.py b/test/MSVC/PCH-source.py index 84a39bd78b..99683171b7 100644 --- a/test/MSVC/PCH-source.py +++ b/test/MSVC/PCH-source.py @@ -41,7 +41,7 @@ env['PCH'] = env.PCH('Source1.cpp')[0] env['PCHSTOP'] = 'Header1.hpp' env.Program('foo', ['foo.cpp', 'Source2.cpp', 'Source1.cpp']) -""" % locals()) +""") test.write('Header1.hpp', r""" """) diff --git a/test/MSVC/TARGET_ARCH.py b/test/MSVC/TARGET_ARCH.py index d8a2f933e7..f093a425d8 100644 --- a/test/MSVC/TARGET_ARCH.py +++ b/test/MSVC/TARGET_ARCH.py @@ -38,7 +38,7 @@ DefaultEnvironment(tools=[]) env_64 = Environment(tools=['default', 'msvc'], TARGET_ARCH='amd64') env_32 = Environment(tools=['default', 'msvc'], TARGET_ARCH='x86') -""" % locals()) +""") test.run(arguments=".") @@ -47,7 +47,7 @@ test.write('SConstruct', """ DefaultEnvironment(tools=[]) env_xx = Environment(tools=['default', 'msvc'], TARGET_ARCH='nosucharch') -""" % locals()) +""") test.run(arguments=".", status=2, stderr=None) test.must_contain_any_line(test.stderr(), "Unrecognized target architecture") @@ -58,7 +58,7 @@ env = Environment(tools=['default', 'msvc'], TARGET_ARCH='arm', MSVC_VERSION='11.0') if env.Detect('cl'): env.Command('checkarm', [], 'cl') -""" % locals()) +""") test.run(arguments=".", stderr=None) if test.stderr().strip() != "" and "ARM" not in test.stderr(): test.fail_test() @@ -68,7 +68,7 @@ env = Environment(tools=['default', 'msvc'], TARGET_ARCH='arm64', MSVC_VERSION='11.0') if env.Detect('cl'): env.Command('checkarm64', [], 'cl') -""" % locals()) +""") test.run(arguments=".", stderr=None) if test.stderr().strip() != "" and "ARM64" not in test.stderr(): test.fail_test() diff --git a/test/MSVC/batch-longlines.py b/test/MSVC/batch-longlines.py index addf001806..0e78fd5ae6 100644 --- a/test/MSVC/batch-longlines.py +++ b/test/MSVC/batch-longlines.py @@ -45,7 +45,7 @@ env = Environment(tools=['msvc', 'mslink'], MSVC_BATCH=ARGUMENTS.get('MSVC_BATCH')) env.SharedLibrary('mylib', Glob('source*.cxx')) -""" % locals()) +""") test.run(arguments='MSVC_BATCH=1 .') test.must_exist('mylib.dll') diff --git a/test/MinGW/MinGWSharedLibrary.py b/test/MinGW/MinGWSharedLibrary.py index 9fac820d9e..9dbac50576 100644 --- a/test/MinGW/MinGWSharedLibrary.py +++ b/test/MinGW/MinGWSharedLibrary.py @@ -63,7 +63,7 @@ # Now verify versioned shared library doesn't fail env.SharedLibrary('foobar_ver', foobar_obj, SHLIBVERSION='2.4') -""" % locals()) +""") test.run(arguments = ".") diff --git a/test/MinGW/WINDOWS_INSERT_DEF.py b/test/MinGW/WINDOWS_INSERT_DEF.py index 5119c97518..27e075f393 100644 --- a/test/MinGW/WINDOWS_INSERT_DEF.py +++ b/test/MinGW/WINDOWS_INSERT_DEF.py @@ -55,7 +55,7 @@ DefaultEnvironment(tools=[]) env = Environment(TOOLS=['mingw']) hello_dll = env.SharedLibrary(WINDOWS_INSERT_DEF=0, target='hello', source='hello.c') -""" % locals()) +""") test.run(arguments = ".") diff --git a/test/Removed/Old/warn-missing-sconscript.py b/test/Removed/Old/warn-missing-sconscript.py index 7859b6423e..2c733aae7d 100644 --- a/test/Removed/Old/warn-missing-sconscript.py +++ b/test/Removed/Old/warn-missing-sconscript.py @@ -59,7 +59,7 @@ def build(target, source, env): # this is the old message: #expect = r""" #scons: warning: Ignoring missing SConscript 'no_such_file' -"" + TestSCons.file_expr +#"""" + TestSCons.file_expr test.run(arguments='--warn=missing-sconscript .', stderr=expect) test.run(arguments='--warn=no-missing-sconscript .', stderr="") diff --git a/test/TEX/LATEX2.py b/test/TEX/LATEX2.py index 6dc5050d93..a7215e906b 100644 --- a/test/TEX/LATEX2.py +++ b/test/TEX/LATEX2.py @@ -47,10 +47,12 @@ test.write('SConstruct', """ import os + +_ = DefaultEnvironment(tools=[]) foo = Environment() foo['TEXINPUTS'] = ['subdir',os.environ.get('TEXINPUTS', '')] foo.PDF(source = ['foo.ltx','bar.latex','makeindex.tex','latexi.tex']) -""" % locals()) +""") latex = r""" \documentclass{letter} diff --git a/test/TEX/LATEXCOMSTR.py b/test/TEX/LATEXCOMSTR.py index 41c5dc72c6..de63ebfa33 100644 --- a/test/TEX/LATEXCOMSTR.py +++ b/test/TEX/LATEXCOMSTR.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,11 +22,8 @@ # 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__" - -r""" +""" Test that the $LATEXCOMSTR construction variable allows you to configure the C compilation output. """ @@ -38,6 +37,7 @@ test.file_fixture('mycompile.py') test.write('SConstruct', """ +_ = DefaultEnvironment(tools=[]) env = Environment(TOOLS = ['latex'], LATEXCOM = r'%(_python_)s mycompile.py latex $TARGET $SOURCE', LATEXCOMSTR = 'Building $TARGET from $SOURCE') @@ -51,7 +51,7 @@ test.run(stdout = test.wrap_stdout("""\ Building test1.dvi from test1.latex -""" % locals())) +""")) test.must_match('test1.dvi', "test1.latex\n") diff --git a/test/TEX/PDFLATEXCOMSTR.py b/test/TEX/PDFLATEXCOMSTR.py index d695bdec10..f0e9467d5b 100644 --- a/test/TEX/PDFLATEXCOMSTR.py +++ b/test/TEX/PDFLATEXCOMSTR.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,11 +22,8 @@ # 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__" - -r""" +""" Test that the $PDFLATEXCOMSTR construction variable allows you to configure the C compilation output. """ @@ -38,6 +37,7 @@ test.file_fixture('mycompile.py') test.write('SConstruct', """ +_ = DefaultEnvironment(tools=[]) env = Environment(TOOLS = ['pdflatex'], PDFLATEXCOM = r'%(_python_)s mycompile.py latex $TARGET $SOURCE', PDFLATEXCOMSTR = 'Building $TARGET from $SOURCE') @@ -51,7 +51,7 @@ test.run(stdout = test.wrap_stdout("""\ Building test1.pdf from test1.latex -""" % locals())) +""")) test.must_match('test1.pdf', "test1.latex\n") diff --git a/test/TEX/PDFTEXCOMSTR.py b/test/TEX/PDFTEXCOMSTR.py index 50edd28a07..30f3539264 100644 --- a/test/TEX/PDFTEXCOMSTR.py +++ b/test/TEX/PDFTEXCOMSTR.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,11 +22,8 @@ # 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__" - -r""" +""" Test that the $PDFTEXCOMSTR construction variable allows you to configure the C compilation output. """ @@ -38,6 +37,7 @@ test.file_fixture('mycompile.py') test.write('SConstruct', """ +_ = DefaultEnvironment(tools=[]) env = Environment(TOOLS = ['pdftex'], PDFTEXCOM = r'%(_python_)s mycompile.py tex $TARGET $SOURCE', PDFTEXCOMSTR = 'Building $TARGET from $SOURCE') @@ -51,7 +51,7 @@ test.run(stdout = test.wrap_stdout("""\ Building test1.pdf from test1.tex -""" % locals())) +""")) test.must_match('test1.pdf', "test1.tex\n") diff --git a/test/TEX/TEXCOMSTR.py b/test/TEX/TEXCOMSTR.py index 9dbba133ab..c41de6d191 100644 --- a/test/TEX/TEXCOMSTR.py +++ b/test/TEX/TEXCOMSTR.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,11 +22,8 @@ # 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__" - -r""" +""" Test that the $TEXCOMSTR construction variable allows you to configure the C compilation output. """ @@ -38,9 +37,12 @@ test.file_fixture('mycompile.py') test.write('SConstruct', """ -env = Environment(TOOLS = ['tex'], - TEXCOM = r'%(_python_)s mycompile.py tex $TARGET $SOURCE', - TEXCOMSTR = 'Building $TARGET from $SOURCE') +_ = DefaultEnvironment(tools=[]) +env = Environment( + TOOLS=['tex'], + TEXCOM=r'%(_python_)s mycompile.py tex $TARGET $SOURCE', + TEXCOMSTR='Building $TARGET from $SOURCE', +) env.DVI('test1') """ % locals()) @@ -49,9 +51,9 @@ /*tex*/ """) -test.run(stdout = test.wrap_stdout("""\ +test.run(stdout=test.wrap_stdout("""\ Building test1.dvi from test1.tex -""" % locals())) +""")) test.must_match('test1.dvi', "test1.tex\n") diff --git a/test/TEX/configure.py b/test/TEX/configure.py index 9fb4b3e8a5..31bec050a8 100644 --- a/test/TEX/configure.py +++ b/test/TEX/configure.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,11 +22,8 @@ # 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__" - -r""" +""" Verify execution of custom test case. The old code base would not be able to fail the test """ @@ -50,6 +49,8 @@ # misspell package name to ensure failure test.write('SConstruct', r""" +import os + lmodern_test_text = r''' \documentclass{article} \usepackage{lmodernD} @@ -68,13 +69,13 @@ def CheckLModern(context): context.Result(is_ok) return is_ok -import os +_ = DefaultEnvironment(tools=[]) env = Environment() env['TEXINPUTS'] = '.' conf = Configure( env, custom_tests={'CheckLModern' : CheckLModern} ) conf.CheckLModern() env = conf.Finish() -""" % locals()) +""") test.run() diff --git a/test/TEX/dryrun.py b/test/TEX/dryrun.py index 90357fcc66..4dc12e4e35 100644 --- a/test/TEX/dryrun.py +++ b/test/TEX/dryrun.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,11 +22,8 @@ # 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__" -r""" +""" Validate that we can set the LATEX string to our own utility, that the produced .dvi, .aux and .log files get removed by the -c option, and that we can use this to wrap calls to the real latex utility. @@ -43,9 +42,11 @@ test.write('SConstruct', """ import os + +_ = DefaultEnvironment(tools=[]) foo = Environment() foo.DVI(target = 'foo.dvi', source = 'foo.ltx') -""" % locals()) +""") test.write('foo.ltx', r""" \documentclass{letter} diff --git a/test/TEX/rename_result.py b/test/TEX/rename_result.py index f67e5692e3..5d9f1b0368 100644 --- a/test/TEX/rename_result.py +++ b/test/TEX/rename_result.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__" r""" Validate that we can rename the output from latex to the @@ -42,11 +41,13 @@ test.write('SConstruct', """ import os + +_ = DefaultEnvironment(tools=[]) foo = Environment() foo['TEXINPUTS'] = [ 'subdir', os.environ.get('TEXINPUTS', '') ] foo.DVI(target = 'foobar.dvi', source = 'foo.ltx') foo.PDF(target = 'bar.xyz', source = 'bar.ltx') -""" % locals()) +""") test.write('foo.ltx', r""" \documentclass{letter} diff --git a/test/TEX/usepackage.py b/test/TEX/usepackage.py index 66510c2e35..34d22b35f9 100644 --- a/test/TEX/usepackage.py +++ b/test/TEX/usepackage.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__" r""" Validate that we can set the LATEX string to our own utility, that @@ -43,10 +42,12 @@ test.write('SConstruct', """ import os + +_ = DefaultEnvironment(tools=[]) foo = Environment() foo['TEXINPUTS'] = [ 'subdir', os.environ.get('TEXINPUTS', '') ] foo.DVI(target = 'foo.dvi', source = 'foo.ltx') -""" % locals()) +""") test.write('foo.ltx', r""" \documentclass{letter} diff --git a/test/Variables/import.py b/test/Variables/import.py index 8e0cd2595c..85c75fd39c 100644 --- a/test/Variables/import.py +++ b/test/Variables/import.py @@ -37,6 +37,7 @@ test.subdir('bin', 'subdir') test.write('SConstruct', """\ +_ = DefaultEnvironment(tools=[]) opts = Variables('../bin/opts.cfg', ARGUMENTS) opts.Add('VARIABLE') Export("opts") @@ -45,7 +46,6 @@ SConscript_contents = """\ Import("opts") -_ = DefaultEnvironment(tools=[]) env = Environment(tools=[]) opts.Update(env) print("VARIABLE = %s"%env.get('VARIABLE')) @@ -53,7 +53,7 @@ test.write(['bin', 'opts.cfg'], """\ from local_options import VARIABLE -""" % locals()) +""") test.write(['bin', 'local_options.py'], """\ VARIABLE = 'bin/local_options.py' diff --git a/test/YACC/live-check-output-cleaned.py b/test/YACC/live-check-output-cleaned.py index aef47deb28..b4914a892e 100644 --- a/test/YACC/live-check-output-cleaned.py +++ b/test/YACC/live-check-output-cleaned.py @@ -42,7 +42,7 @@ DefaultEnvironment(tools=[]) foo = Environment(YACCFLAGS='-v -d', tools = ['default', 'yacc']) foo.CFile(source = 'foo.y') -""" % locals()) +""") yacc = r""" %%{ diff --git a/test/ZIP/ZIP.py b/test/ZIP/ZIP.py index b79173c4a4..09115fea13 100644 --- a/test/ZIP/ZIP.py +++ b/test/ZIP/ZIP.py @@ -35,12 +35,13 @@ test.subdir('sub1') test.write('SConstruct', """ +_ = DefaultEnvironment(tools=[]) env = Environment(tools = ['zip']) env.Zip(target = 'aaa.zip', source = ['file1', 'file2']) env.Zip(target = 'aaa.zip', source = 'file3') env.Zip(target = 'bbb', source = 'sub1') env.Zip(target = 'bbb', source = 'file4') -""" % locals()) +""") test.write('file1', "file1\n") test.write('file2', "file2\n") diff --git a/test/ZIP/ZIPROOT.py b/test/ZIP/ZIPROOT.py index 10e01d950f..42579c2ac2 100644 --- a/test/ZIP/ZIPROOT.py +++ b/test/ZIP/ZIPROOT.py @@ -37,10 +37,11 @@ test.subdir(['sub1', 'sub2']) test.write('SConstruct', """ +_ = DefaultEnvironment(tools=[]) env = Environment(tools = ['zip']) env.Zip(target = 'aaa.zip', source = ['sub1/file1'], ZIPROOT='sub1') env.Zip(target = 'bbb.zip', source = ['sub1/file2', 'sub1/sub2/file2'], ZIPROOT='sub1') -""" % locals()) +""") test.write(['sub1', 'file1'], "file1\n") test.write(['sub1', 'file2'], "file2a\n") diff --git a/test/ZIP/ZIP_OVERRIDE_TIMESTAMP.py b/test/ZIP/ZIP_OVERRIDE_TIMESTAMP.py index 89049445dd..1c9a2c0426 100644 --- a/test/ZIP/ZIP_OVERRIDE_TIMESTAMP.py +++ b/test/ZIP/ZIP_OVERRIDE_TIMESTAMP.py @@ -39,14 +39,15 @@ def zipfile_get_file_datetime(zipfilename, fname): for info in zf.infolist(): if info.filename == fname: return info.date_time - + raise Exception("Unable to find %s" % fname) test.write('SConstruct', """ +_ = DefaultEnvironment(tools=[]) env = Environment(tools = ['zip']) env.Zip(target = 'aaa.zip', source = ['file1'], ZIP_OVERRIDE_TIMESTAMP=(1983,3,11,1,2,2)) -""" % locals()) +""") test.write(['file1'], "file1\n") diff --git a/test/gettext/POTUpdate/UserExamples.py b/test/gettext/POTUpdate/UserExamples.py index 06203fafa7..a3075ebca1 100644 --- a/test/gettext/POTUpdate/UserExamples.py +++ b/test/gettext/POTUpdate/UserExamples.py @@ -1,6 +1,8 @@ -2#!/usr/bin/env python +#!/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__" """ End-to-end tests for POTUpdate. Assure, that the examples from user's @@ -44,6 +43,7 @@ test.subdir(['ex1', 'po']) test.write( ['ex1', 'po', 'SConstruct' ], """ +_ = DefaultEnvironment(tools=[]) env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(['foo'], ['../a.cpp', '../b.cpp']) env.POTUpdate(['bar'], ['../c.cpp', '../d.cpp']) @@ -88,6 +88,7 @@ test.subdir(['ex2']) test.write( ['ex2', 'SConstruct'], """ +_ = DefaultEnvironment(tools=[]) env = Environment( tools = ['default', 'xgettext'] ) env['POTDOMAIN'] = "foo" env.POTUpdate(source = ["a.cpp", "b.cpp"]) # Creates foo.pot ... @@ -121,6 +122,7 @@ test.write( ['ex3', 'po', 'SConstruct'], """ # SConstruct file in 'po/' subdirectory +_ = DefaultEnvironment(tools=[]) env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in') """) @@ -147,6 +149,7 @@ test.write( ['ex4', 'po', 'SConstruct'], """ # SConstruct file in 'po/' subdirectory +_ = DefaultEnvironment(tools=[]) env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH='../') """) @@ -176,6 +179,7 @@ test.write( ['ex5', '0', '1', 'po', 'SConstruct'], """ # SConstruct file in '0/1/po/' subdirectory +_ = DefaultEnvironment(tools=[]) env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../', '../../']) """) @@ -191,14 +195,15 @@ # scons 'pot-update' creates messages.pot test.run(arguments = 'pot-update', chdir = path.join('ex5', '0', '1', 'po')) test.must_exist( ['ex5', '0', '1', 'po', 'messages.pot']) -test.must_contain( ['ex5', '0', '1', 'po', 'messages.pot'], +test.must_contain( ['ex5', '0', '1', 'po', 'messages.pot'], 'Hello from ../a.cpp', mode='r' ) -test.must_not_contain( ['ex5', '0', '1', 'po', 'messages.pot'], +test.must_not_contain( ['ex5', '0', '1', 'po', 'messages.pot'], 'Hello from ../../a.cpp', mode='r' ) test.write(['ex5', '0', '1', 'po', 'SConstruct'], """ # SConstruct file in '0/1/po/' subdirectory +_ = DefaultEnvironment(tools=[]) env = Environment( tools = ['default', 'xgettext'] ) env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../../', '../']) """) diff --git a/test/implicit-cache/DualTargets.py b/test/implicit-cache/DualTargets.py index 8612d1a531..174d39c149 100644 --- a/test/implicit-cache/DualTargets.py +++ b/test/implicit-cache/DualTargets.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__" """ Test that --implicit-cache works correctly in conjonction with a @@ -53,9 +52,9 @@ def source_scan(node, env, path): env = Environment(tools=[]) env['BUILDERS']['DualTarget'] = Builder( action = Action( - [ - Copy( '$TARGET', '$SOURCE' ), - Copy( '${TARGET.base}.b', '$SOURCE' ), + [ + Copy( '$TARGET', '$SOURCE' ), + Copy( '${TARGET.base}.b', '$SOURCE' ), ], ), suffix = '.a', @@ -69,7 +68,7 @@ def source_scan(node, env, path): env.Command( 'x.lib', '', Touch('$TARGET') ) env.DualTarget('x.cpp') -""" % locals()) +""") test.must_not_exist('x.cpp') test.must_not_exist('x.lib') diff --git a/test/no-global-dependencies.py b/test/no-global-dependencies.py index 95761e7de3..541a645ec0 100644 --- a/test/no-global-dependencies.py +++ b/test/no-global-dependencies.py @@ -48,11 +48,12 @@ BoolVariable('duplicate', 'Duplicate sources to variant dir', True) ) +_ = DefaultEnvironment(tools=[]) env = Environment(options=opts) Export('env') SConscript(dirs='.', variant_dir='build', duplicate=env['duplicate']) -""" % locals()) +""") test.write('SConscript', """\ @@ -62,7 +63,7 @@ SConscript(dirs='dir1') SConscript(dirs='dir2') -""" % locals()) +""") test.write('dir1/SConscript', """\ Import('env') @@ -70,13 +71,13 @@ env.Command('x.cpp', '', Touch('$TARGET')) env.Object(env.File('x.cpp')) -""" % locals()) +""") test.write('dir2/SConscript', """\ Import('env') env.Object(env.File('#/build/dir1/x.cpp')) -""" % locals()) +""") test.must_not_exist('build/dir1/x.cpp') @@ -87,36 +88,36 @@ # # Build everything first. -test.run(arguments = 'duplicate=False view_all_dependencies=True .') +test.run(arguments='duplicate=False view_all_dependencies=True .') test.must_exist('build/dir1/x.cpp') test.must_not_contain_any_line(test.stdout(), ["`.' is up to date."]) # Double check that targets are not rebuilt. -test.run(arguments = 'duplicate=False view_all_dependencies=True .') +test.run(arguments='duplicate=False view_all_dependencies=True .') test.must_exist('build/dir1/x.cpp') test.must_contain_all_lines(test.stdout(), ["`.' is up to date."]) # Clean-up only the object file -test.run(arguments = 'duplicate=False view_all_dependencies=False -c .') +test.run(arguments='duplicate=False view_all_dependencies=False -c .') test.must_exist('build/dir1/x.cpp') # Rebuild the only object file without seeing all the dependencies. -test.run(arguments = 'duplicate=False view_all_dependencies=False .') +test.run(arguments='duplicate=False view_all_dependencies=False .') test.must_exist('build/dir1/x.cpp') test.must_not_contain_any_line(test.stdout(), ["`.' is up to date."]) # Double check that targets are not rebuilt without and with all the # dependencies. -test.run(arguments = 'duplicate=False view_all_dependencies=False .') +test.run(arguments='duplicate=False view_all_dependencies=False .') test.must_exist('build/dir1/x.cpp') test.must_contain_all_lines(test.stdout(), ["`.' is up to date."]) -test.run(arguments = 'duplicate=False view_all_dependencies=True .') +test.run(arguments='duplicate=False view_all_dependencies=True .') test.must_exist('build/dir1/x.cpp') test.must_contain_all_lines(test.stdout(), ["`.' is up to date."]) # Clean-up everything. -test.run(arguments = 'duplicate=False view_all_dependencies=True -c .') +test.run(arguments='duplicate=False view_all_dependencies=True -c .') test.must_not_exist('build/dir1/x.cpp') @@ -131,36 +132,36 @@ # # # Build everything first. -# test.run(arguments = 'duplicate=True view_all_dependencies=True .') +# test.run(arguments='duplicate=True view_all_dependencies=True .') # test.must_exist('build/dir1/x.cpp') # test.must_not_contain_any_line(test.stdout(), ["`.' is up to date."]) # # Double check that targets are not rebuilt. -# test.run(arguments = 'duplicate=True view_all_dependencies=True .') +# test.run(arguments='duplicate=True view_all_dependencies=True .') # test.must_exist('build/dir1/x.cpp') # test.must_contain_all_lines(test.stdout(), ["`.' is up to date."]) # # Clean-up only the object file -# test.run(arguments = 'duplicate=True view_all_dependencies=False -c .') +# test.run(arguments='duplicate=True view_all_dependencies=False -c .') # test.must_exist('build/dir1/x.cpp') # # Rebuild the only object file without seeing all the dependencies. -# test.run(arguments = 'duplicate=True view_all_dependencies=False .') +# test.run(arguments='duplicate=True view_all_dependencies=False .') # test.must_exist('build/dir1/x.cpp') # test.must_not_contain_any_line(test.stdout(), ["`.' is up to date."]) # # Double check that targets are not rebuilt without and with all the # # dependencies. -# test.run(arguments = 'duplicate=True view_all_dependencies=False .') +# test.run(arguments='duplicate=True view_all_dependencies=False .') # test.must_exist('build/dir1/x.cpp') # test.must_contain_all_lines(test.stdout(), ["`.' is up to date."]) -# test.run(arguments = 'duplicate=True view_all_dependencies=True .') +# test.run(arguments='duplicate=True view_all_dependencies=True .') # test.must_exist('build/dir1/x.cpp') # test.must_contain_all_lines(test.stdout(), ["`.' is up to date."]) # # Clean-up everything. -# test.run(arguments = 'duplicate=True view_all_dependencies=True -c .') +# test.run(arguments='duplicate=True view_all_dependencies=True -c .') # test.must_not_exist('build/dir1/x.cpp') diff --git a/test/sconsign/script/SConsignFile.py b/test/sconsign/script/SConsignFile.py index d19cfab290..680eae5451 100644 --- a/test/sconsign/script/SConsignFile.py +++ b/test/sconsign/script/SConsignFile.py @@ -111,6 +111,7 @@ def process(infp, outfp): ['SConstruct'], f"""\ SConsignFile() +_ = DefaultEnvironment(tools=[]) env1 = Environment( PROGSUFFIX='.exe', OBJSUFFIX='.obj', @@ -155,14 +156,14 @@ def process(infp, outfp): #define STRING2 "inc2.h" """) -test.run(arguments = '--implicit-cache .') +test.run(arguments='--implicit-cache .') sig_re = r'[0-9a-fA-F]{32,64}' database_name = test.get_sconsignname() -test.run_sconsign(arguments = database_name, - stdout = r"""=== .: +test.run_sconsign(arguments=database_name, + stdout=r"""=== .: SConstruct: None \d+ \d+ fake_cc\.py: %(sig_re)s \d+ \d+ fake_link\.py: %(sig_re)s \d+ \d+ @@ -192,8 +193,8 @@ def process(infp, outfp): inc2.h: %(sig_re)s \d+ \d+ """ % locals()) -test.run_sconsign(arguments = "--raw " + database_name, - stdout = r"""=== .: +test.run_sconsign(arguments="--raw " + database_name, + stdout=r"""=== .: SConstruct: {'csig': None, 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} @@ -320,10 +321,10 @@ def process(infp, outfp): size: \d+ """ % locals() -test.run_sconsign(arguments = "-v " + database_name, stdout=expect) +test.run_sconsign(arguments="-v " + database_name, stdout=expect) -test.run_sconsign(arguments = "-c -v " + database_name, - stdout = r"""=== .: +test.run_sconsign(arguments="-c -v " + database_name, + stdout=r"""=== .: SConstruct: csig: None fake_cc\.py: @@ -350,8 +351,8 @@ def process(infp, outfp): csig: %(sig_re)s """ % locals()) -test.run_sconsign(arguments = "-s -v " + database_name, - stdout = r"""=== .: +test.run_sconsign(arguments="-s -v " + database_name, + stdout=r"""=== .: SConstruct: size: \d+ fake_cc\.py: @@ -376,10 +377,10 @@ def process(infp, outfp): size: \d+ inc2.h: size: \d+ -""" % locals()) +""") -test.run_sconsign(arguments = "-t -v " + database_name, - stdout = r"""=== .: +test.run_sconsign(arguments="-t -v " + database_name, + stdout=r"""=== .: SConstruct: timestamp: \d+ fake_cc\.py: @@ -404,10 +405,10 @@ def process(infp, outfp): timestamp: \d+ inc2.h: timestamp: \d+ -""" % locals()) +""") -test.run_sconsign(arguments = "-e hello.obj " + database_name, - stdout = r"""=== .: +test.run_sconsign(arguments="-e hello.obj " + database_name, + stdout=r"""=== .: === sub1: hello.obj: %(sig_re)s \d+ \d+ %(sub1_hello_c)s: %(sig_re)s \d+ \d+ @@ -421,11 +422,11 @@ def process(infp, outfp): fake_cc\.py: %(sig_re)s \d+ \d+ %(sig_re)s \[.*\] """ % locals(), - stderr = r"""sconsign: no entry `hello\.obj' in `\.' -""" % locals()) + stderr=r"""sconsign: no entry `hello\.obj' in `\.' +""") -test.run_sconsign(arguments = "-e hello.obj -e hello.exe -e hello.obj " + database_name, - stdout = r"""=== .: +test.run_sconsign(arguments="-e hello.obj -e hello.exe -e hello.obj " + database_name, + stdout=r"""=== .: === sub1: hello.obj: %(sig_re)s \d+ \d+ %(sub1_hello_c)s: %(sig_re)s \d+ \d+ @@ -457,13 +458,13 @@ def process(infp, outfp): fake_cc\.py: %(sig_re)s \d+ \d+ %(sig_re)s \[.*\] """ % locals(), - stderr = r"""sconsign: no entry `hello\.obj' in `\.' + stderr=r"""sconsign: no entry `hello\.obj' in `\.' sconsign: no entry `hello\.exe' in `\.' sconsign: no entry `hello\.obj' in `\.' -""" % locals()) +""") -#test.run_sconsign(arguments = "-i -v " + database_name, -# stdout = r"""=== sub1: +#test.run_sconsign(arguments="-i -v " + database_name, +# stdout=r"""=== sub1: #hello.exe: # implicit: # hello.obj: %(sig_re)s diff --git a/testing/framework/TestCommonTests.py b/testing/framework/TestCommonTests.py index 31e13e9b51..014a018766 100644 --- a/testing/framework/TestCommonTests.py +++ b/testing/framework/TestCommonTests.py @@ -762,7 +762,7 @@ def test_failure(self) -> None: 'www' 'zzz' Extra output =================================================================== - """ % globals()) + """) run_env.run(program=sys.executable, stdin=script) stdout = run_env.stdout() @@ -848,7 +848,7 @@ def test_title(self) -> None: 'www' 'zzz' Extra STDOUT =================================================================== - """ % globals()) + """) run_env.run(program=sys.executable, stdin=script) stdout = run_env.stdout() @@ -2234,10 +2234,14 @@ def test_options_plus_arguments(self) -> None: self.run_execution_test(script, "", "") def test_signal_handling(self) -> None: - """Test run(): signal handling""" + """Test run(): signal handling. + Only strange platforms unlikely to support SCons like the + webassembly ones don't support kill(), but keep the test + in place anyway. + """ try: - os.kill + _ = os.kill except AttributeError: sys.stderr.write('can not test, no os.kill ... ') return diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 2b40a361de..e0b45377b1 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -426,12 +426,12 @@ def wrap_stdout(self, build_str: str="", read_str: str="", error: int=0, cleanin Makes a complete message to match against. Args: - read_str: the message for the execution part of the output. + build_str: the message for the execution part of the output. If non-empty, needs to be newline-terminated. read_str: the message for the reading-sconscript part of the output. If non-empty, needs to be newline-terminated. error: if true, expect a fail message rather than a done message. - cleaning (int): index into type messages, if 0 selects + cleaning: index into type messages, if 0 selects build messages, if 1 selects clean messages. """ cap, lc = [('Build', 'build'), From 93a7d1e3755da1f08431889fcdd8d004bf07b518 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 6 Jul 2024 08:22:20 -0600 Subject: [PATCH 092/386] Fix scons-time fix Apparently, the weird structor of the SConsTimer class is such that the previous fix for the "makedict" method fails on older Pythons. Just moved the "static method" outside the class to be a top-level function, that seems to work better. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + bin/scons-time.py | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7b5cf5ba51..b89cebf974 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -178,6 +178,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Regularized header (copyright, licens) at top of documentation files using SPDX. - Updated introductory section of manual page. + - Minor cleanups in tests - drop unused "% locals()" stanzas. RELEASE 4.7.0 - Sun, 17 Mar 2024 17:22:20 -0700 diff --git a/bin/scons-time.py b/bin/scons-time.py index f8858f772f..d81d4c4e00 100644 --- a/bin/scons-time.py +++ b/bin/scons-time.py @@ -260,6 +260,8 @@ def redirect_to_file(command, log): def tee_to_file(command, log): return '%s 2>&1 | tee %s' % (command, log) +def makedict(**kw): + return kw class SConsTimer: """ @@ -278,10 +280,6 @@ class SConsTimer: name = 'scons-time' name_spaces = ' ' * len(name) - @staticmethod - def makedict(**kw): - return kw - default_settings = makedict( chdir=None, config_file=None, From 48930096d6ebc1bf36bbecc75c6b376b47a49f49 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 7 Jul 2024 16:53:24 -0600 Subject: [PATCH 093/386] Bump SCons "added" version to 4.8 [skip appveyor] Two additions in the cycle since 4.7.0 had documentation annotations that they were added in 4.7.1. Update to 4.8.0. One of those changes didn't have an annotation in the code. Signed-off-by: Mats Wichmann --- SCons/Script/Main.py | 4 ++++ SCons/Script/SConscript.xml | 2 +- doc/man/scons.xml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 4588345a9a..123281c677 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -1423,6 +1423,10 @@ def lookupmodule(self, filename: str) -> Optional[str]: sconscript files that don't have the suffix. .. versionadded:: 4.6.0 + + .. versionchanged:: 4.8.0 + The additional name ``SCsub`` (with spelling variants) + is also recognized - Godot uses this name. """ if os.path.isabs(filename) and os.path.exists(filename): return filename diff --git a/SCons/Script/SConscript.xml b/SCons/Script/SConscript.xml index 95bb5387d5..b64d9c3061 100644 --- a/SCons/Script/SConscript.xml +++ b/SCons/Script/SConscript.xml @@ -145,7 +145,7 @@ EnsureSConsVersion(0,96,90) Returns the current SCons version in the form of a Tuple[int, int, int], representing the major, minor, and revision values respectively. -Added in 4.7.1. +Added in 4.8.0.
diff --git a/doc/man/scons.xml b/doc/man/scons.xml index f266c59837..ea518c84c5 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -1049,7 +1049,7 @@ recognized without requiring .py suffix.
-Changed in version 4.7.1: +Changed in version 4.8.0: The name SCsub is now recognized without requiring .py suffix. From da8aeadee03a20bf64034e78add223742959493a Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 7 Jul 2024 16:48:37 -0700 Subject: [PATCH 094/386] Updates for SCons 4.8.0 release --- SCons/Script/Main.py | 2 +- .../examples/caching_ex-random_1.xml | 4 +- .../examples/commandline_BoolVariable_5.xml | 4 +- .../examples/commandline_EnumVariable_2.xml | 2 +- .../examples/commandline_EnumVariable_4.xml | 6 +-- .../examples/commandline_ListVariable_3.xml | 3 +- .../examples/commandline_ListVariable_4.xml | 3 +- .../examples/commandline_PathVariable_2.xml | 2 +- .../examples/troubleshoot_Dump_1.xml | 2 +- .../examples/troubleshoot_Dump_2.xml | 3 +- .../examples/troubleshoot_stacktrace_2.xml | 4 +- .../troubleshoot_taskmastertrace_1.xml | 50 +++++++++---------- doc/generated/functions.gen | 2 +- 13 files changed, 42 insertions(+), 45 deletions(-) diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 123281c677..6b2418436a 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -69,7 +69,7 @@ minimum_python_version = (3, 6, 0) deprecated_python_version = (3, 7, 0) # the first non-deprecated version -# ordered list of SConsctruct names to look for if there is no -f flag +# ordered list of SConstruct names to look for if there is no -f flag KNOWN_SCONSTRUCT_NAMES = [ 'SConstruct', 'Sconstruct', diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml index 00041ee128..70f14a02c4 100644 --- a/doc/generated/examples/caching_ex-random_1.xml +++ b/doc/generated/examples/caching_ex-random_1.xml @@ -1,8 +1,8 @@ % scons -Q -cc -o f3.o -c f3.c -cc -o f5.o -c f5.c cc -o f4.o -c f4.c +cc -o f5.o -c f5.c cc -o f1.o -c f1.c cc -o f2.o -c f2.c +cc -o f3.o -c f3.c cc -o prog f1.o f2.o f3.o f4.o f5.o diff --git a/doc/generated/examples/commandline_BoolVariable_5.xml b/doc/generated/examples/commandline_BoolVariable_5.xml index 3cd75e9aca..f5bb77834c 100644 --- a/doc/generated/examples/commandline_BoolVariable_5.xml +++ b/doc/generated/examples/commandline_BoolVariable_5.xml @@ -1,6 +1,6 @@ % scons -Q RELEASE=bad_value foo.o -scons: *** Error converting option: RELEASE -Invalid value for boolean option: bad_value +scons: *** Error converting option: 'RELEASE' +Invalid value for boolean variable: 'bad_value' File "/home/my/project/SConstruct", line 3, in <module> diff --git a/doc/generated/examples/commandline_EnumVariable_2.xml b/doc/generated/examples/commandline_EnumVariable_2.xml index 8856d29fe2..b5b99caae5 100644 --- a/doc/generated/examples/commandline_EnumVariable_2.xml +++ b/doc/generated/examples/commandline_EnumVariable_2.xml @@ -1,5 +1,5 @@ % scons -Q COLOR=magenta foo.o -scons: *** Invalid value for option COLOR: magenta. Valid values are: ('red', 'green', 'blue') +scons: *** Invalid value for enum variable 'COLOR': 'magenta'. Valid values are: ('red', 'green', 'blue') File "/home/my/project/SConstruct", line 10, in <module> diff --git a/doc/generated/examples/commandline_EnumVariable_4.xml b/doc/generated/examples/commandline_EnumVariable_4.xml index 3e73c5b23a..0714d4bc7e 100644 --- a/doc/generated/examples/commandline_EnumVariable_4.xml +++ b/doc/generated/examples/commandline_EnumVariable_4.xml @@ -1,13 +1,13 @@ % scons -Q COLOR=Red foo.o -scons: *** Invalid value for option COLOR: Red. Valid values are: ('red', 'green', 'blue') +scons: *** Invalid value for enum variable 'COLOR': 'Red'. Valid values are: ('red', 'green', 'blue') File "/home/my/project/SConstruct", line 10, in <module> % scons -Q COLOR=BLUE foo.o -scons: *** Invalid value for option COLOR: BLUE. Valid values are: ('red', 'green', 'blue') +scons: *** Invalid value for enum variable 'COLOR': 'BLUE'. Valid values are: ('red', 'green', 'blue') File "/home/my/project/SConstruct", line 10, in <module> % scons -Q COLOR=nAvY foo.o -scons: *** Invalid value for option COLOR: nAvY. Valid values are: ('red', 'green', 'blue') +scons: *** Invalid value for enum variable 'COLOR': 'nAvY'. Valid values are: ('red', 'green', 'blue') File "/home/my/project/SConstruct", line 10, in <module> diff --git a/doc/generated/examples/commandline_ListVariable_3.xml b/doc/generated/examples/commandline_ListVariable_3.xml index 11936ab0fb..70047f21c3 100644 --- a/doc/generated/examples/commandline_ListVariable_3.xml +++ b/doc/generated/examples/commandline_ListVariable_3.xml @@ -1,6 +1,5 @@ % scons -Q COLORS=magenta foo.o -scons: *** Error converting option: COLORS -Invalid value(s) for option: magenta +scons: *** Invalid value(s) for variable 'COLORS': 'magenta'. Valid values are: blue,green,red,all,none File "/home/my/project/SConstruct", line 7, in <module> diff --git a/doc/generated/examples/commandline_ListVariable_4.xml b/doc/generated/examples/commandline_ListVariable_4.xml index d464c48396..b87dd79571 100644 --- a/doc/generated/examples/commandline_ListVariable_4.xml +++ b/doc/generated/examples/commandline_ListVariable_4.xml @@ -1,6 +1,5 @@ % scons -Q foo.o -scons: *** Error converting option: COLORS -Invalid value(s) for option: 0 +scons: *** Invalid value(s) for variable 'COLORS': '0'. Valid values are: blue,green,red,all,none File "/home/my/project/SConstruct", line 7, in <module> diff --git a/doc/generated/examples/commandline_PathVariable_2.xml b/doc/generated/examples/commandline_PathVariable_2.xml index 156702af16..bd85d4cfb7 100644 --- a/doc/generated/examples/commandline_PathVariable_2.xml +++ b/doc/generated/examples/commandline_PathVariable_2.xml @@ -1,5 +1,5 @@ % scons -Q CONFIG=/does/not/exist foo.o -scons: *** Path for option CONFIG does not exist: /does/not/exist +scons: *** Path for variable 'CONFIG' does not exist: /does/not/exist File "/home/my/project/SConstruct", line 7, in <module> diff --git a/doc/generated/examples/troubleshoot_Dump_1.xml b/doc/generated/examples/troubleshoot_Dump_1.xml index 54d1288d2d..b03d9327c5 100644 --- a/doc/generated/examples/troubleshoot_Dump_1.xml +++ b/doc/generated/examples/troubleshoot_Dump_1.xml @@ -62,7 +62,7 @@ scons: Reading SConscript files ... 'TEMPFILEARGESCFUNC': <function quote_spaces at 0x700000>, 'TEMPFILEARGJOIN': ' ', 'TEMPFILEPREFIX': '@', - 'TOOLS': ['install', 'install'], + 'TOOLS': ['install'], '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, ' 'TARGET, SOURCE)}', '_CPPINCFLAGS': '${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, ' diff --git a/doc/generated/examples/troubleshoot_Dump_2.xml b/doc/generated/examples/troubleshoot_Dump_2.xml index a29772a2b8..30c24d833f 100644 --- a/doc/generated/examples/troubleshoot_Dump_2.xml +++ b/doc/generated/examples/troubleshoot_Dump_2.xml @@ -106,8 +106,7 @@ scons: Reading SConscript files ... 'TEMPFILEARGESCFUNC': <function quote_spaces at 0x700000>, 'TEMPFILEARGJOIN': '\n', 'TEMPFILEPREFIX': '@', - 'TOOLS': ['msvc', 'install', 'install'], - 'VSWHERE': None, + 'TOOLS': ['msvc', 'install'], '_CCCOMCOM': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS', '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, ' 'TARGET, SOURCE)}', diff --git a/doc/generated/examples/troubleshoot_stacktrace_2.xml b/doc/generated/examples/troubleshoot_stacktrace_2.xml index 0250025757..4db0f9d8d8 100644 --- a/doc/generated/examples/troubleshoot_stacktrace_2.xml +++ b/doc/generated/examples/troubleshoot_stacktrace_2.xml @@ -1,9 +1,9 @@ % scons -Q --debug=stacktrace scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'. scons: internal stack trace: - File "SCons/Taskmaster/Job.py", line 671, in _work + File "SCons/Taskmaster/Job.py", line 670, in _work task.prepare() - File "SCons/Script/Main.py", line 201, in prepare + File "SCons/Script/Main.py", line 208, in prepare return SCons.Taskmaster.OutOfDateTask.prepare(self) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "SCons/Taskmaster/__init__.py", line 195, in prepare diff --git a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml index 70f3fbb9f1..e25d3223d6 100644 --- a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml +++ b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml @@ -1,8 +1,8 @@ % scons -Q --taskmastertrace=- prog -Job.NewParallel._work(): [Thread:8149967104] Gained exclusive access -Job.NewParallel._work(): [Thread:8149967104] Starting search -Job.NewParallel._work(): [Thread:8149967104] Found 0 completed tasks to process -Job.NewParallel._work(): [Thread:8149967104] Searching for new tasks +Job.NewParallel._work(): [Thread:8645271808] Gained exclusive access +Job.NewParallel._work(): [Thread:8645271808] Starting search +Job.NewParallel._work(): [Thread:8645271808] Found 0 completed tasks to process +Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'prog'> and its children: @@ -18,12 +18,12 @@ Taskmaster: Evaluating <pending 0 'prog.c'> Task.make_ready_current(): node <pending 0 'prog.c'> Task.prepare(): node <up_to_date 0 'prog.c'> -Job.NewParallel._work(): [Thread:8149967104] Found internal task +Job.NewParallel._work(): [Thread:8645271808] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'prog.c'> Task.postprocess(): node <up_to_date 0 'prog.c'> Task.postprocess(): removing <up_to_date 0 'prog.c'> Task.postprocess(): adjusted parent ref count <pending 1 'prog.o'> -Job.NewParallel._work(): [Thread:8149967104] Searching for new tasks +Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'inc.h'> and its children: @@ -31,12 +31,12 @@ Taskmaster: Evaluating <pending 0 'inc.h'> Task.make_ready_current(): node <pending 0 'inc.h'> Task.prepare(): node <up_to_date 0 'inc.h'> -Job.NewParallel._work(): [Thread:8149967104] Found internal task +Job.NewParallel._work(): [Thread:8645271808] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'inc.h'> Task.postprocess(): node <up_to_date 0 'inc.h'> Task.postprocess(): removing <up_to_date 0 'inc.h'> Task.postprocess(): adjusted parent ref count <pending 0 'prog.o'> -Job.NewParallel._work(): [Thread:8149967104] Searching for new tasks +Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog.o'> and its children: @@ -46,19 +46,19 @@ Taskmaster: Evaluating <pending 0 'prog.o'> Task.make_ready_current(): node <pending 0 'prog.o'> Task.prepare(): node <executing 0 'prog.o'> -Job.NewParallel._work(): [Thread:8149967104] Found task requiring execution -Job.NewParallel._work(): [Thread:8149967104] Executing task +Job.NewParallel._work(): [Thread:8645271808] Found task requiring execution +Job.NewParallel._work(): [Thread:8645271808] Executing task Task.execute(): node <executing 0 'prog.o'> cc -o prog.o -c -I. prog.c -Job.NewParallel._work(): [Thread:8149967104] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8149967104] Gained exclusive access -Job.NewParallel._work(): [Thread:8149967104] Starting search -Job.NewParallel._work(): [Thread:8149967104] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8645271808] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8645271808] Gained exclusive access +Job.NewParallel._work(): [Thread:8645271808] Starting search +Job.NewParallel._work(): [Thread:8645271808] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog.o'> Task.postprocess(): node <executed 0 'prog.o'> Task.postprocess(): removing <executed 0 'prog.o'> Task.postprocess(): adjusted parent ref count <pending 0 'prog'> -Job.NewParallel._work(): [Thread:8149967104] Searching for new tasks +Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog'> and its children: @@ -67,21 +67,21 @@ Taskmaster: Evaluating <pending 0 'prog'> Task.make_ready_current(): node <pending 0 'prog'> Task.prepare(): node <executing 0 'prog'> -Job.NewParallel._work(): [Thread:8149967104] Found task requiring execution -Job.NewParallel._work(): [Thread:8149967104] Executing task +Job.NewParallel._work(): [Thread:8645271808] Found task requiring execution +Job.NewParallel._work(): [Thread:8645271808] Executing task Task.execute(): node <executing 0 'prog'> cc -o prog prog.o -Job.NewParallel._work(): [Thread:8149967104] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8149967104] Gained exclusive access -Job.NewParallel._work(): [Thread:8149967104] Starting search -Job.NewParallel._work(): [Thread:8149967104] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8645271808] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8645271808] Gained exclusive access +Job.NewParallel._work(): [Thread:8645271808] Starting search +Job.NewParallel._work(): [Thread:8645271808] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog'> Task.postprocess(): node <executed 0 'prog'> -Job.NewParallel._work(): [Thread:8149967104] Searching for new tasks +Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. -Job.NewParallel._work(): [Thread:8149967104] Found no task requiring execution, and have no jobs: marking complete -Job.NewParallel._work(): [Thread:8149967104] Gained exclusive access -Job.NewParallel._work(): [Thread:8149967104] Completion detected, breaking from main loop +Job.NewParallel._work(): [Thread:8645271808] Found no task requiring execution, and have no jobs: marking complete +Job.NewParallel._work(): [Thread:8645271808] Gained exclusive access +Job.NewParallel._work(): [Thread:8645271808] Completion detected, breaking from main loop diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index c7617c1011..ace8fc6a8d 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -2590,7 +2590,7 @@ processed prior to the &f-GetOption; call in the &SConscript; files. Returns the current SCons version in the form of a Tuple[int, int, int], representing the major, minor, and revision values respectively. -Added in 4.7.1. +Added in 4.8.0. From 7c688f694c644b61342670ce92977bf4a396c0d4 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 7 Jul 2024 16:51:32 -0700 Subject: [PATCH 095/386] Updates for SCons 4.8.0 release --- CHANGES.txt | 2 +- RELEASE.txt | 17 +++++++---------- ReleaseConfig | 6 +++--- SConstruct | 2 +- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b89cebf974..e43679ff32 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,7 +10,7 @@ NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. NOTE: Python 3.6 support is deprecated and will be dropped in a future reease. python.org no longer supports 3.6 or 3.7, and will drop 3.8 in Oct. 2024. -RELEASE VERSION/DATE TO BE FILLED IN LATER +RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 From Joseph Brill: - For msvc version specifications without an 'Exp' suffix, an express installation diff --git a/RELEASE.txt b/RELEASE.txt index 30632039b6..43db03183c 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,12 +1,4 @@ -If you are reading this in the git repository, the contents -refer to *unreleased* changes since the last SCons release. -Past official release announcements appear at: - - https://scons.org/tag/releases.html - -================================================================== - -A new SCons release, 4.7.1, is now available on the SCons download page: +A new SCons release, 4.8.0, is now available on the SCons download page: https://scons.org/pages/download.html @@ -189,4 +181,9 @@ Thanks to the following contributors listed below for their contributions to thi ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.0.1..HEAD + git shortlog --no-merges -ns 4.7.0..HEAD + 70 Mats Wichmann + 35 Joseph Brill + 26 William Deegan + 7 Raymond Li + 7 Thaddeus Crews diff --git a/ReleaseConfig b/ReleaseConfig index 52fb6aaf7e..da600d9581 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -31,14 +31,14 @@ # If the release type is not 'final', the patchlevel is set to the # release date. This value is mandatory and must be present in this file. #version_tuple = (2, 2, 0, 'final', 0) -version_tuple = (4, 6, 2, 'a', 0) +version_tuple = (4, 8, 0) # Python versions prior to unsupported_python_version cause a fatal error # when that version is used. Python versions prior to deprecate_python_version # cause a warning to be issued (assuming it's not disabled). These values are # mandatory and must be present in the configuration file. unsupported_python_version = (3, 6, 0) -deprecated_python_version = (3, 6, 0) +deprecated_python_version = (3, 7, 0) # If release_date is (yyyy, mm, dd, hh, mm, ss), that is used as the release # date and time. If release_date is (yyyy, mm, dd), it is used for the @@ -50,7 +50,7 @@ deprecated_python_version = (3, 6, 0) #month_year = 'December 2012' # If copyright years is not given, the release year is used as the end. -copyright_years = '2001 - 2023' +copyright_years = '2001 - 2024' # Local Variables: # tab-width:4 diff --git a/SConstruct b/SConstruct index 04bde62850..96f47d381c 100644 --- a/SConstruct +++ b/SConstruct @@ -36,7 +36,7 @@ copyright_years = strftime('2001 - %Y') # This gets inserted into the man pages to reflect the month of release. month_year = strftime('%B %Y') project = 'scons' -default_version = '4.7.1' +default_version = '4.8.0' copyright = f"Copyright (c) {copyright_years} The SCons Foundation" # We let the presence or absence of various utilities determine whether From 8db30760aa9169c3b110ca52dc9739dcbaff8cb3 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 7 Jul 2024 16:56:50 -0700 Subject: [PATCH 096/386] 4.8.0 updates --- SCons/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SCons/__init__.py b/SCons/__init__.py index e89057fe29..a61ba2c7c7 100644 --- a/SCons/__init__.py +++ b/SCons/__init__.py @@ -1,9 +1,9 @@ -__version__="4.7.0" +__version__="4.8.0" __copyright__="Copyright (c) 2001 - 2024 The SCons Foundation" __developer__="bdbaddog" -__date__="Sun, 17 Mar 2024 17:33:54 -0700" +__date__="Sun, 07 Jul 2024 16:52:07 -0700" __buildsys__="M1Dog2021" -__revision__="265be6883fadbb5a545612265acc919595158366" -__build__="265be6883fadbb5a545612265acc919595158366" +__revision__="7c688f694c644b61342670ce92977bf4a396c0d4" +__build__="7c688f694c644b61342670ce92977bf4a396c0d4" # make sure compatibility is always in place import SCons.compat # noqa \ No newline at end of file From 3b10709c1a5486d3e018effd27d85e55807afc56 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 7 Jul 2024 17:19:09 -0700 Subject: [PATCH 097/386] Return master to development mode post release --- CHANGES.txt | 9 +- RELEASE.txt | 172 +++++---------------------------- ReleaseConfig | 2 +- SCons/Script/Main.py | 2 +- SConstruct | 2 +- doc/user/main.xml | 2 +- testing/framework/TestSCons.py | 4 +- 7 files changed, 38 insertions(+), 155 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e43679ff32..390a0d5352 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,9 +7,16 @@ NOTE: The 4.0.0 release of SCons dropped Python 2.7 support. Use 3.1.2 if Python 2.7 support is required (but note old SCons releases are unsupported). NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. -NOTE: Python 3.6 support is deprecated and will be dropped in a future reease. +NOTE: Python 3.6 support is deprecated and will be dropped in a future release. python.org no longer supports 3.6 or 3.7, and will drop 3.8 in Oct. 2024. +RELEASE VERSION/DATE TO BE FILLED IN LATER + + From John Doe: + + - Whatever John Doe did. + + RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 From Joseph Brill: diff --git a/RELEASE.txt b/RELEASE.txt index 43db03183c..1797698a25 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,189 +1,65 @@ -A new SCons release, 4.8.0, is now available on the SCons download page: +If you are reading this in the git repository, the contents +refer to *unreleased* changes since the last SCons release. +Past official release announcements appear at: + + https://scons.org/tag/releases.html + +================================================================== + +A new SCons release, 4.8.1, is now available on the SCons download page: https://scons.org/pages/download.html -Here is a summary of the changes since 4.7.0: +Here is a summary of the changes since 4.8.0: NEW FUNCTIONALITY ----------------- -- GetSConsVersion() added to retrieve the SCons version. +- List new features (presumably why a checkpoint is being released) DEPRECATED FUNCTIONALITY ------------------------ -- Mark Python 3.6 support as deprecated. Use --warn=no-python-version - to quiet the warning. +- List anything that's been deprecated since the last release CHANGED/ENHANCED EXISTING FUNCTIONALITY --------------------------------------- -- Dump() with json format selected now recognizes additional compound types - (UserDict and UserList), which improves the detail of the display. - json output is also sorted, to match the default display. -- Python 3.13 changes the behavior of isabs() on Windows. Adjust SCons - usage of this in NodeInfo classes to avoid test problems. -- Drop duplicated __getstate__ and __setstate__ methods in AliasNodeInfo, - FileNodeInfo and ValueNodeInfo classes, as they are identical to the - ones in parent NodeInfoBase and can just be inherited. -- All exceptions during the execution of an Action are now returned by value - rather than by raising an exception, for more consistent behavior. - NOTE: With this change, user created Actions should now catch and handle - expected exceptions (whereas previously many of these were silently caught - and suppressed by the SCons Action exection code). -- ParseFlags now sorts a --stdlib=libname argument into CXXFLAGS instead - of CCFLAGS; the latter variable could cause a compiler warning. -- The implementation of Variables was slightly refactored, there should - not be user-visible changes. -- MSVC: For msvc version specifications without an 'Exp' suffix, an express - installation is used when no other edition is detected for the msvc version. - This was the behavior for Visual Studio 2008 (9.0) through Visual Studio 2015 - (14.0). This behavior was extended to Visual Studio 2017 (14.1) and Visual - Studio 2008 (8.0). An express installation of the IDE binary is used when no - other IDE edition is detected. -- The vswhere executable locations for the WinGet and Scoop package managers were - added to the default vswhere executable search list after the Chocolatey - installation location. -- SCons.Environment.is_valid_construction_var() now returns a boolean to - match the convention that functions beginning with "is" have yes/no - answers (previously returned either None or an re.match object). - Now matches the annotation and docstring (which were prematurely - updated in 4.6). All SCons usage except unit test was already fully - consistent with a bool. -- The Variables object Add method now accepts a subst keyword argument - (defaults to True) which can be set to inhibit substitution prior to - calling the variable's converter and validator. -- AddOption and the internal add_local_option which AddOption calls now - recognize a "settable" keyword argument to indicate a project-added - option can also be modified using SetOption. - NOTE: If you were using ninja and using SetOption() for ninja options - in your SConscripts prior to loading the ninja tool, you will now - see an error. The fix is to move the SetOption() to after you've loaded - the ninja tool. -- ListVariable now has a separate validator, with the functionality - that was previously part of the converter. The main effect is to - allow a developer to supply a custom validator, which previously - could be inhibited by the converter failing before the validator - is reached. -- When debugging (--debug=pdb), the filename SCsub is now recognized when - manipulating breakpoints. +- List modifications to existing features, where the previous behavior + wouldn't actually be considered a bug FIXES ----- -- OSErrors are now no longer hidden during the execution of Actions. -- Improved the conversion of a "foreign" exception from an action - into BuildError by making sure our defaults get applied even in - corner cases. Fixes Issue #4530 -- MSVC: Visual Studio 2010 (10.0) could be inadvertently detected due to an - sdk-only install of Windows SDK 7.1. An sdk-only install of Visual Studio - 2010 is ignored as the msvc batch files will fail. The installed files are - intended to be used in conjunction with the SDK batch file. Similar protection - was added for Visual Studio 2008 (9.0). -- MSVC: For Visual Studio 2005 (8.0) to Visual Studio 2015 (14.0), detection of - installed files was expanded to include the primary msvc batch file, dependent - msvc batch file, and compiler executable. In certain installations, the - dependent msvc batch file may not exist while the compiler executable does exist - resulting in a build failure. -- MSVC: Visual Studio 2008 (9.0) Visual C++ For Python was not detected when - installed using the ALLUSERS command-line option: - msiexec /i VCForPython27.msi ALLUSERS=1 - When installed for all users, Visual Studio 2008 (9.0) Visual C++ For Python is - now correctly detected. -- MSVC: For Visual Studio 2008 (9.0), a full development edition (e.g., Professional) - is now selected before a Visual C++ For Python edition. Prior to this change, - Visual C++ For Python was selected before a full development edition when both - editions are installed. -- The vswhere executable is frozen upon initial detection. Specifying a different - vswhere executable via the construction variable VSWHERE after the initial - detection now results in an exception. Multiple bugs in the implementation of - specifying a vswhere executable via the construction variable VSWHERE have been - fixed. Previously, when a user specified vswhere executable detects new msvc - installations after the initial detection, the internal msvc installation cache - and the default msvc version based on the initial detection are no longer valid. - For example, when no vswhere executable is found for the initial detection - and then later an environment is constructed with a user specified vswhere - executable that detects new msvc installations. -- MSVC: Visual Studio 2022 v143 BuildTools now supports msvc toolset versions from - 14.30 to 14.4X. Fixes Issue #4543. -- The Clone() method now respects the variables argument (fixes #3590) +- List fixes of outright bugs IMPROVEMENTS ------------ -- Make the testing framework a little more resilient: the temporary - directory for tests now includes a component named "scons" which can - be given to antivirus software to exclude. -- Performance tweak: the __setitem__ method of an Environment, used for - setting construction variables, now uses the string method isidentifier - to validate the name (updated from microbenchmark results). -- MSVC: Visual Studio 2015 Express (14.0Exp) does not support the sdk version - argument. Visual Studio 2015 Express does not support the store argument for - target architectures other than x86. Script argument validation now takes into - account these restrictions. -- MSVC: Visual Studio 2015 BuildTools (14.0) does not support the sdk version - argument and does not support the store argument. Script argument validation now - takes into account these restrictions. -- MSVC: The registry detection of VS2015 (14.0), and earlier, is now cached at runtime - and is only evaluated once for each msvc version. -- MSVC: The vswhere detection of VS2017 (14.1), and later, is now cached at runtime and - is only evaluated once using a single vswhere invocation for all msvc versions. +- List improvements that wouldn't be visible to the user in the + documentation: performance improvements (describe the circumstances + under which they would be observed), or major code cleanups PACKAGING --------- -- setup.cfg logic now handled via pyproject.toml; consequently, setup.cfg - was removed. - +- List changes in the way SCons is packaged and/or released DOCUMENTATION ------------- -- Updated Value Node docs. -- Update manpage for Tools, and for the TOOL variable. -- Update manpage and user guide for Variables usage. -- Restructured API Docs build so main package contents are listed - before contents of package submodules. -- Updated manpage description of Command "builder" and function. -- Updated the notes about reproducible builds with SCons and the example. -- Regularized header (copyright, licens) at top of documentation files using SPDX. -- Updated introductory section of manual page. - - +- List any significant changes to the documentation (not individual + typo fixes, even if they're mentioned in src/CHANGES.txt to give + the contributor credit) DEVELOPMENT ----------- -- Documentation build now properly passes through skipping the PDF - (and EPUB) builds of manpage and user guide; this can also be done - manually if directly calling doc/man/SConstruct and doc/user/SConstruct - by adding SKIP_PDF=1. This should help with distro packaging of SCons, - which now does not need "fop" and other tools to be set up in order to - build pdf versions which are then ignored. -- .gitattributes has been setup to mirror .editorconfig's eol settings. - The repo-wide line-ending is now `lf`, with the exception of a few - Windows-only files using `crlf` instead. Any files not already fitting - this format have been explicitly converted. -- Repository linter/formatter changed from flake8/black to ruff, as the - latter grants an insane speed boost without compromising functionality. - Existing settings were migrated 1-to-1 where possible. -- The test runner now recognizes the unittest module's return code of 5, - which means no tests were run. SCons/Script/MainTests.py currently - has no tests, so this particular error code is expected - should not - cause runtest to give up with an "unknown error code". -- is_valid_construction_var() (not part of the public API) moved from - SCons.Environment to SCons.Util to avoid the chance of import loops. Variables - and Environment both use the routine and Environment() uses a Variables() - object so better to move to a safer location. +- List visible changes in the way SCons is developed Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.7.0..HEAD - 70 Mats Wichmann - 35 Joseph Brill - 26 William Deegan - 7 Raymond Li - 7 Thaddeus Crews + git shortlog --no-merges -ns 4.0.1..HEAD diff --git a/ReleaseConfig b/ReleaseConfig index da600d9581..d1eaa39f7c 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -31,7 +31,7 @@ # If the release type is not 'final', the patchlevel is set to the # release date. This value is mandatory and must be present in this file. #version_tuple = (2, 2, 0, 'final', 0) -version_tuple = (4, 8, 0) +version_tuple = (4, 9, 0, 'a', 0) # Python versions prior to unsupported_python_version cause a fatal error # when that version is used. Python versions prior to deprecate_python_version diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 6b2418436a..77d80cb293 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -67,7 +67,7 @@ # these define the range of versions SCons supports minimum_python_version = (3, 6, 0) -deprecated_python_version = (3, 7, 0) # the first non-deprecated version +deprecated_python_version = (3, 7, 0) # ordered list of SConstruct names to look for if there is no -f flag KNOWN_SCONSTRUCT_NAMES = [ diff --git a/SConstruct b/SConstruct index 96f47d381c..2784d2bd1a 100644 --- a/SConstruct +++ b/SConstruct @@ -36,7 +36,7 @@ copyright_years = strftime('2001 - %Y') # This gets inserted into the man pages to reflect the month of release. month_year = strftime('%B %Y') project = 'scons' -default_version = '4.8.0' +default_version = '4.8.1' copyright = f"Copyright (c) {copyright_years} The SCons Foundation" # We let the presence or absence of various utilities determine whether diff --git a/doc/user/main.xml b/doc/user/main.xml index 0cdaa703a6..ef92c6eea2 100644 --- a/doc/user/main.xml +++ b/doc/user/main.xml @@ -36,7 +36,7 @@ This file is processed by the bin/SConsDoc.py module. The SCons Development Team - Released: Mon, 17 Mar 2024 17:52:49 -0700 + Released: Mon, 07 Jul 2024 17:17:52 -0700 2004 - 2024 diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index e0b45377b1..c4ba199c96 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -55,11 +55,11 @@ # here provides some independent verification that what we packaged # conforms to what we expect. -default_version = '4.7.1ayyyymmdd' +default_version = '4.9.0ayyyymmdd' # TODO: these need to be hand-edited when there are changes python_version_unsupported = (3, 6, 0) -python_version_deprecated = (3, 7, 0) # lowest non-deprecated Python +python_version_deprecated = (3, 7, 0) python_version_supported_str = "3.7.0" # str of lowest non-deprecated Python SConsVersion = default_version From 187bf05b1631ff2e2359ce2bc5875854f9082258 Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Sun, 14 Apr 2024 13:11:47 -0500 Subject: [PATCH 098/386] Implement somewhat pythonic type hints in sctypes --- CHANGES.txt | 8 +++++--- RELEASE.txt | 6 +++++- SCons/Util/sctypes.py | 38 ++++++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 390a0d5352..dea43b8bbe 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,9 +12,11 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release. RELEASE VERSION/DATE TO BE FILLED IN LATER - From John Doe: - - - Whatever John Doe did. + From Thaddeus Crews: + - Add explicit return types to sctypes `is_*` functions. For Python <=3.9, + the return type is simply `bool`, same as before. Python 3.10 and later + will benefit from `TypeGuard`/`TypeIs`, to produce intellisense similar + to using `isinstance` directly. RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 1797698a25..5845e560cd 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -56,7 +56,11 @@ DOCUMENTATION DEVELOPMENT ----------- -- List visible changes in the way SCons is developed +- sctypes `is_*` functions given explicit return types. Python 3.13+ uses + `TypeIs` for a near-equivalent of `isinstance`. Python 3.10 through 3.12 + uses `TypeGuard`, a less accurate implementation but still provides + usable type hinting. Python 3.9 and earlier simply returns `bool`, same + as before. Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== diff --git a/SCons/Util/sctypes.py b/SCons/Util/sctypes.py index bcbefb6c80..765458e130 100644 --- a/SCons/Util/sctypes.py +++ b/SCons/Util/sctypes.py @@ -11,7 +11,8 @@ import os import pprint import re -from typing import Optional +import sys +from typing import Optional, Union from collections import UserDict, UserList, UserString, deque from collections.abc import MappingView, Iterable @@ -50,38 +51,63 @@ # Empirically, it is faster to check explicitly for str than for basestring. BaseStringTypes = str +# Later Python versions allow us to explicitly apply type hints based off the +# return value similar to isinstance(), albeit not as precise. +if sys.version_info >= (3, 13): + from typing import TypeAlias, TypeIs + + DictTypeRet: TypeAlias = TypeIs[Union[dict, UserDict]] + ListTypeRet: TypeAlias = TypeIs[Union[list, UserList, deque]] + SequenceTypeRet: TypeAlias = TypeIs[Union[list, tuple, deque, UserList, MappingView]] + TupleTypeRet: TypeAlias = TypeIs[tuple] + StringTypeRet: TypeAlias = TypeIs[Union[str, UserString]] +elif sys.version_info >= (3, 10): + from typing import TypeAlias, TypeGuard + + DictTypeRet: TypeAlias = TypeGuard[Union[dict, UserDict]] + ListTypeRet: TypeAlias = TypeGuard[Union[list, UserList, deque]] + SequenceTypeRet: TypeAlias = TypeGuard[Union[list, tuple, deque, UserList, MappingView]] + TupleTypeRet: TypeAlias = TypeGuard[tuple] + StringTypeRet: TypeAlias = TypeGuard[Union[str, UserString]] +else: + DictTypeRet = Union[bool, bool] + ListTypeRet = Union[bool, bool] + SequenceTypeRet = Union[bool, bool] + TupleTypeRet = Union[bool, bool] + StringTypeRet = Union[bool, bool] + def is_Dict( # pylint: disable=redefined-outer-name,redefined-builtin obj, isinstance=isinstance, DictTypes=DictTypes -) -> bool: +) -> DictTypeRet: """Check if object is a dict.""" return isinstance(obj, DictTypes) def is_List( # pylint: disable=redefined-outer-name,redefined-builtin obj, isinstance=isinstance, ListTypes=ListTypes -) -> bool: +) -> ListTypeRet: """Check if object is a list.""" return isinstance(obj, ListTypes) def is_Sequence( # pylint: disable=redefined-outer-name,redefined-builtin obj, isinstance=isinstance, SequenceTypes=SequenceTypes -) -> bool: +) -> SequenceTypeRet: """Check if object is a sequence.""" return isinstance(obj, SequenceTypes) def is_Tuple( # pylint: disable=redefined-builtin obj, isinstance=isinstance, tuple=tuple -) -> bool: +) -> TupleTypeRet: """Check if object is a tuple.""" return isinstance(obj, tuple) def is_String( # pylint: disable=redefined-outer-name,redefined-builtin obj, isinstance=isinstance, StringTypes=StringTypes -) -> bool: +) -> StringTypeRet: """Check if object is a string.""" return isinstance(obj, StringTypes) From cfe50b5e406226aeaff4a0027a8fdd2b9634273a Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 12 Jul 2024 08:22:20 -0600 Subject: [PATCH 099/386] Enhancement: Dump() takes multiple args now env.Dump previously either printed the whole dict of construction variables (with no args), or a single value (with one variable name argument). Now it takes a varargs specifier so you can give any number of consvar names. All returned strings are now in dict form, including the single-arg case which previously just returned the value matching the key, not a dict with a key:value pair. This is a slight ABI change, but should not affect any actual scripts since the output is intended for human consumption, not for programmatic use - env.Dictionary() can be used to fetch construction vars for programmatic use (in fact, Dump is a consumer of Dictionary's output). Signed-off-by: Mats Wichmann --- CHANGES.txt | 9 +++++++ RELEASE.txt | 15 +++++++++--- SCons/Environment.py | 38 ++++++++++++++++++----------- SCons/Environment.xml | 51 +++++++++++++++++++++++++++------------ SCons/EnvironmentTests.py | 48 ++++++++++++++++++++++++------------ 5 files changed, 112 insertions(+), 49 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index dea43b8bbe..44d8a97873 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,15 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER will benefit from `TypeGuard`/`TypeIs`, to produce intellisense similar to using `isinstance` directly. + From Mats Wichmann: + - env.Dump() now considers the "key" positional argument to be a varargs + type (zero, one or many). However called, it returns a serialized + result that looks like a dict. Previously, only one "key" was + accepted. and unlike the zero-args case, it was be serialized + to a string containing the value without the key. For example, if + "print(repr(env.Dump('CC'))" previously returned "'gcc'", it will now + return "{'CC': 'gcc'}". + RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 5845e560cd..b29ecbd1a4 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -26,8 +26,15 @@ DEPRECATED FUNCTIONALITY CHANGED/ENHANCED EXISTING FUNCTIONALITY --------------------------------------- -- List modifications to existing features, where the previous behavior - wouldn't actually be considered a bug +- env.Dump() previously accepted a single optional "key" argument. + It now accepts any number of optional "key" arguments; any supplied + keys will be serialized with their values in a Python dict style. + As a result there is a small change in behavior: if a *single* key + argument is given, where it previously would return a string containing + just the value, now it will return a string that looks like a dictionary + including the key. For example, from "'gcc'" to "{'CC': 'gcc'}". + This should not have any impact as the result of calling Dump is + intended for diagnostic output, not for use by other interfaces. FIXES ----- @@ -38,8 +45,8 @@ IMPROVEMENTS ------------ - List improvements that wouldn't be visible to the user in the - documentation: performance improvements (describe the circumstances - under which they would be observed), or major code cleanups +documentation: performance improvements (describe the circumstances +under which they would be observed), or major code cleanups PACKAGING --------- diff --git a/SCons/Environment.py b/SCons/Environment.py index 9322450627..ac752c71ad 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -1694,28 +1694,37 @@ def Dictionary(self, *args): return dlist - def Dump(self, key: Optional[str] = None, format: str = 'pretty') -> str: - """ Returns a dump of serialized construction variables. + def Dump(self, *key: str, format: str = 'pretty') -> str: + """Return string of serialized construction variables. - The display formats are intended for humaan readers when - debugging - none of the supported formats produce a result that - SCons itself can directly make use of. Objects that cannot - directly be represented get a placeholder like - ```` or ``<>``. + Produces a "pretty" output of a dictionary of selected + construction variables, or all of them. The display *format* is + selectable. The result is intended for human consumption (e.g, + to print), mainly when debugging. Objects that cannot directly be + represented get a placeholder like ```` + (pretty-print) or ``<>`` (JSON). Args: - key: if ``None``, format the whole dict of variables, - else format just the value of *key*. + key: if omitted, format the whole dict of variables, + else format *key*(s) with the corresponding values. format: specify the format to serialize to. ``"pretty"`` generates a pretty-printed string, ``"json"`` a JSON-formatted string. Raises: ValueError: *format* is not a recognized serialization format. + + .. versionchanged:: NEXT_VERSION + *key* is no longer limited to a single construction variable name. + If *key* is supplied, a formatted dictionary is generated like the + no-arg case - previously a single *key* displayed just the value. """ - if key: - cvars = self.Dictionary(key) - else: + if not key: cvars = self.Dictionary() + elif len(key) == 1: + dkey = key[0] + cvars = {dkey: self[dkey]} + else: + cvars = dict(zip(key, self.Dictionary(*key))) fmt = format.lower() @@ -1735,14 +1744,15 @@ def Dump(self, key: Optional[str] = None, format: str = 'pretty') -> str: class DumpEncoder(json.JSONEncoder): """SCons special json Dump formatter.""" + def default(self, obj): if isinstance(obj, (UserList, UserDict)): return obj.data return f'<>' return json.dumps(cvars, indent=4, cls=DumpEncoder, sort_keys=True) - else: - raise ValueError("Unsupported serialization format: %s." % fmt) + + raise ValueError("Unsupported serialization format: %s." % fmt) def FindIxes(self, paths: Sequence[str], prefix: str, suffix: str) -> Optional[str]: diff --git a/SCons/Environment.xml b/SCons/Environment.xml index f489ca17f6..ce73cad6aa 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -1692,21 +1692,24 @@ for more information. -([key], [format]) +([key, ...], [format=]) -Serializes &consvars; to a string. +Serializes &consvars; from the current &consenv; +to a string. The method supports the following formats specified by -format: +format, +which must be used a a keyword argument: + pretty -Returns a pretty printed representation of the environment (if -format -is not specified, this is the default). +Returns a pretty-printed representation of the variables +(this is the default). +The variables will be presented in &Python; dict form. @@ -1714,17 +1717,27 @@ is not specified, this is the default). json -Returns a JSON-formatted string representation of the environment. +Returns a JSON-formatted string representation of the variables. +The variables will be presented as a JSON object literal, +the JSON equivalent of a &Python; dict. -If key is -None (the default) the entire -dictionary of &consvars; is serialized. -If supplied, it is taken as the name of a &consvar; -whose value is serialized. + +If no key is supplied, +all the &consvars; are serialized. +If one or more keys are supplied, +only those keys and their values are serialized. + + + +Changed in NEXT_VERSION: +More than one key can be specified. +The returned string always looks like a dict (or JSON equivalent); +previously a single key serialized only the value, +not the key with the value. @@ -1732,16 +1745,21 @@ This SConstruct: -env=Environment() +env = Environment() print(env.Dump('CCCOM')) +print(env.Dump('CC', 'CCFLAGS', format='json')) -will print: +will print something like: -'$CC -c -o $TARGET $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES' +{'CCCOM': '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'} +{ + "CC": "gcc", + "CCFLAGS": [] +} @@ -1754,7 +1772,7 @@ print(env.Dump()) -will print: +will print something like: { 'AR': 'ar', @@ -1765,6 +1783,7 @@ will print: 'ASFLAGS': [], ... + diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index 8d90d4d171..f171ac8962 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -3176,24 +3176,42 @@ def test_NoClean(self) -> None: def test_Dump(self) -> None: """Test the Dump() method""" - env = self.TestEnvironment(FOO='foo', FOOFLAGS=CLVar('--bar --baz')) - assert env.Dump('FOO') == "'foo'", env.Dump('FOO') - assert len(env.Dump()) > 200, env.Dump() # no args version - assert env.Dump('FOO', 'json') == '"foo"' # JSON key version - expect = """[\n "--bar",\n "--baz"\n]""" - self.assertEqual(env.Dump('FOOFLAGS', 'json'), expect) - import json - env_dict = json.loads(env.Dump(format = 'json')) - assert env_dict['FOO'] == 'foo' # full JSON version + # changed in NEXT_VERSION: single arg now displays as a dict, + # not a bare value; more than one arg is allowed. + with self.subTest(): # one-arg version + self.assertEqual(env.Dump('FOO'), "{'FOO': 'foo'}") + + with self.subTest(): # multi-arg version + expect = "{'FOO': 'foo', 'FOOFLAGS': ['--bar', '--baz']}" + self.assertEqual(env.Dump('FOO', 'FOOFLAGS'), expect) + + with self.subTest(): # no-arg version + self.assertGreater(len(env.Dump()), 200) + + with self.subTest(): # one-arg JSON version, simple value + expect = '{\n "FOO": "foo"\n}' + self.assertEqual(env.Dump('FOO', format='json'), expect) + + with self.subTest(): # one-arg JSON version, list value + expect = '{\n "FOOFLAGS": [\n "--bar",\n "--baz"\n ]\n}' + self.assertEqual(env.Dump('FOOFLAGS', format='json'), expect) + + with self.subTest(): # multi--arg JSON version, list value + expect = '{\n "FOO": "foo",\n "FOOFLAGS": [\n "--bar",\n "--baz"\n ]\n}' + self.assertEqual(env.Dump('FOO', 'FOOFLAGS', format='json'), expect) + + with self.subTest(): # full JSON version + import json + env_dict = json.loads(env.Dump(format='json')) + self.assertEqual(env_dict['FOO'], 'foo') + + with self.subTest(): # unsupported format type + with self.assertRaises(ValueError) as cm: + env.Dump(format='markdown') + self.assertEqual(str(cm.exception), "Unsupported serialization format: markdown.") - try: - env.Dump(format = 'markdown') - except ValueError as e: - assert str(e) == "Unsupported serialization format: markdown." - else: - self.fail("Did not catch expected ValueError.") def test_Environment(self) -> None: """Test the Environment() method""" From 9feb2facd8666cc5ca89adba683afeffc2f60c3e Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 16 Jul 2024 07:29:41 -0600 Subject: [PATCH 100/386] Add timeout to test/ninja/default_targets.py Has timed out not completing in the CI. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 ++ test/ninja/default_targets.py | 28 ++++++++----------- .../sconstruct_default_targets | 9 +++--- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 44d8a97873..4e946ccfc0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,6 +26,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER to a string containing the value without the key. For example, if "print(repr(env.Dump('CC'))" previously returned "'gcc'", it will now return "{'CC': 'gcc'}". + - Add a timeout to test/ninja/default_targets.py - it's gotten stuck on + the GitHub Windows action and taken the run to the full six hour timeout. + Usually runs in a few second, so set the timeout to 3min (120). RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/test/ninja/default_targets.py b/test/ninja/default_targets.py index dc7c7c1be2..1b2f2b9f4b 100644 --- a/test/ninja/default_targets.py +++ b/test/ninja/default_targets.py @@ -20,7 +20,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. -# import os @@ -32,45 +31,42 @@ try: import ninja except ImportError: - test.skip_test("Could not find module in python") + test.skip_test("Could not find 'ninja'; skipping test.\n") _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = os.path.abspath( + os.path.join(ninja.__file__, os.pardir, 'data', 'bin', 'ninja' + _exe) +) test.dir_fixture('ninja-fixture') test.file_fixture('ninja_test_sconscripts/sconstruct_default_targets', 'SConstruct') +# this test has had some hangs on the GitHut Windows runner, add timeout. + # generate simple build -test.run(stdout=None) +test.run(stdout=None, timeout=120) test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja']) test.must_contain_all(test.stdout(), 'Executing:') -test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals()) +test.must_contain_all(test.stdout(), f'ninja{_exe} -f') test.must_not_exist([test.workpath('out1.txt')]) test.must_exist([test.workpath('out2.txt')]) # clean build and ninja files -test.run(arguments='-c', stdout=None) -test.must_contain_all_lines(test.stdout(), [ - 'Removed out2.txt', - 'Removed build.ninja']) +test.run(arguments='-c', stdout=None, timeout=120) +test.must_contain_all_lines(test.stdout(), ['Removed out2.txt', 'Removed build.ninja']) # only generate the ninja file -test.run(arguments='--disable-execute-ninja', stdout=None) +test.run(arguments='--disable-execute-ninja', stdout=None, timeout=120) test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja']) test.must_not_exist(test.workpath('out1.txt')) test.must_not_exist(test.workpath('out2.txt')) # run ninja independently program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin -test.run(program=program, stdout=None) +test.run(program=program, stdout=None, timeout=120) test.must_not_exist([test.workpath('out1.txt')]) test.must_exist(test.workpath('out2.txt')) diff --git a/test/ninja/ninja_test_sconscripts/sconstruct_default_targets b/test/ninja/ninja_test_sconscripts/sconstruct_default_targets index 1a92be3a5a..ebcc563ce1 100644 --- a/test/ninja/ninja_test_sconscripts/sconstruct_default_targets +++ b/test/ninja/ninja_test_sconscripts/sconstruct_default_targets @@ -4,13 +4,14 @@ import SCons -SetOption('experimental','ninja') +SetOption('experimental', 'ninja') DefaultEnvironment(tools=[]) - env = Environment(tools=[]) env.Tool('ninja') -env.Command('out1.txt', 'foo.c', 'echo test > $TARGET' ) -out2_node = env.Command('out2.txt', 'foo.c', 'echo test > $TARGET', NINJA_FORCE_SCONS_BUILD=True) +env.Command('out1.txt', 'foo.c', 'echo test > $TARGET') +out2_node = env.Command( + 'out2.txt', 'foo.c', 'echo test > $TARGET', NINJA_FORCE_SCONS_BUILD=True +) alias = env.Alias('def', out2_node) env.Default(alias) From 666f1198508d9b52e0726abca5564aaf161ce97c Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 17 Jul 2024 08:35:58 -0600 Subject: [PATCH 101/386] Restore usage of "from SCons.Variables import *` This (unadvertised) import turns out to have real-world usage, and there's no real reason to prohibit it, although we do suggest using `from SCons.Script import *` as a more complete version. SCons 4.8.0 introduced an `__all__` in the variables module; add the five variables types to this so the listed import will work as expected. Signed-off-by: Mats Wichmann --- CHANGES.txt | 6 ++++++ RELEASE.txt | 8 +++++++- SCons/Variables/__init__.py | 5 +++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4e946ccfc0..47687c8500 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -29,6 +29,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Add a timeout to test/ninja/default_targets.py - it's gotten stuck on the GitHub Windows action and taken the run to the full six hour timeout. Usually runs in a few second, so set the timeout to 3min (120). + - SCons 4.8.0 added an `__all__` specifier at the top of the Variables + module (`Variables/__init__.py`) to control what is made available in + a star import. However, there was existing usage of doing + `from SCons.Variables import *` which expected the variable *types* + to be avaiable. While we never advertised this usage, there's no + real reason it shouldn't keep working - add to `__all__`. RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index b29ecbd1a4..887ceb2eff 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -39,7 +39,13 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY FIXES ----- -- List fixes of outright bugs +- SCons 4.8.0 added an `__all__` specifier at the top of the Variables + module (`Variables/__init__.py`) to control what is made available in + a star import. However, there was existing usage of doing + `from SCons.Variables import *` which expected the variable *types* + to be avaiable. `BoolVariable`, `EnumVariable`, `ListVariable`, + `PackageVariable` and `PathVariable` are added to `__all__`, + so this form of import should now work again. IMPROVEMENTS ------------ diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index d9df2a26c7..501505ff33 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -43,6 +43,11 @@ __all__ = [ "Variable", "Variables", + "BoolVariable", + "EnumVariable", + "ListVariable", + "PackageVariable", + "PathVariable", ] class Variable: From ec3f26db2ea892005e5fe59b1ba7ee2ad2bac3c2 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 18 Jul 2024 14:53:39 -0600 Subject: [PATCH 102/386] Switch scons build to use setuptools version hook [skip appveyor] The stanza we used, still published in the Python packaging docs, is now considered the least desirable of the alternatives for keeping a "single package version", so update to a better one. Note there's what's considered a "better" one than this, but it requires Python 3.8 ("provisional" until 3.10) or a backport of importlib.metadata. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ pyproject.toml | 3 +++ setup.py | 19 +++++-------------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4e946ccfc0..171e176548 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -29,6 +29,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Add a timeout to test/ninja/default_targets.py - it's gotten stuck on the GitHub Windows action and taken the run to the full six hour timeout. Usually runs in a few second, so set the timeout to 3min (120). + - Switch SCons build to use setuptools' supported version fetcher from + the old homegrown one. RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/pyproject.toml b/pyproject.toml index 60bc9e607d..cb824b7ba9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,9 @@ zip-safe = false include-package-data = true license-files = ["LICENSE"] +[tool.setuptools.dynamic] +version = {attr = "SCons.__version__"} + [tool.setuptools.packages.find] exclude = ["template"] namespaces = false diff --git a/setup.py b/setup.py index 6d52278dce..9cb37592a5 100644 --- a/setup.py +++ b/setup.py @@ -14,24 +14,16 @@ def read(rel_path): return fp.read() -def get_version(rel_path): - for line in read(rel_path).splitlines(): - if line.startswith('__version__'): - delim = '"' if '"' in line else "'" - return line.split(delim)[1] - else: - raise RuntimeError("Unable to find version string.") - - exclude = ['*Tests'] class build_py(build_py_orig): def find_package_modules(self, package, package_dir): - """ - Custom module to find package modules. - It will strip out any modules which match the glob patters in exclude above + """Custom module to find package modules. + + Will strip out any modules which match the glob patters in + *exclude* above """ modules = super().find_package_modules(package, package_dir) return [(pkg, mod, file, ) for (pkg, mod, file, ) in modules @@ -42,5 +34,4 @@ def find_package_modules(self, package, package_dir): cmdclass={ 'build_py': build_py, }, - version=get_version('SCons/__init__.py'), -) \ No newline at end of file +) From 0d04f545b036b56acef94fe681e2f527033c1bbc Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 23 Jul 2024 18:48:23 -0700 Subject: [PATCH 103/386] Added test to check that 'from SCons.Variables import *' will import all types of SCons defined Variables --- SCons/Variables/VariablesTests.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index 2c9fe580eb..0791ea2556 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -29,6 +29,7 @@ import SCons.Subst import SCons.Warnings from SCons.Util import cmp +from SCons.Variables import * class Environment: @@ -723,6 +724,27 @@ def test_AddOptionWithAliasUpdatesUnknown(self) -> None: assert len(r) == 0, r assert env['ADDEDLATER'] == 'added', env['ADDEDLATER'] + def test_VariableTypesImportVisibility(self) -> None: + """Test that 'from SCons.Variables import *' will import all types of SCons defined Variables + """ + + try: + x = BoolVariable('test', 'test option help', False) + y = EnumVariable('test', 'test option help', 0, + ['one', 'two', 'three'], + {}) + z = ListVariable('test', 'test option help', 'all', + ['one', 'two', 'three']) + o = PackageVariable('test', 'test build variable help', '/default/path') + p = PathVariable('test', + 'test build variable help', + '/default/path') + except Exception as e: + self.fail(f"Could not import all known Variable types: {e}") + + + + if __name__ == "__main__": unittest.main() From a4dc81d1c7f561c6b97759a0d8fb8771603285a0 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 25 Jul 2024 09:53:20 -0600 Subject: [PATCH 104/386] Wordsmithing on manpage env Methods section [skip appveyor] Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 4 +- doc/man/scons.xml | 155 ++++++++++++++++++++++++---------------------- 3 files changed, 84 insertions(+), 76 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 15c48f6d61..0bbbfdf3ba 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -37,6 +37,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER real reason it shouldn't keep working - add to `__all__`. - Switch SCons build to use setuptools' supported version fetcher from the old homegrown one. + - Improve wording of manpage "Functions and Environment Methods" section. RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 887ceb2eff..534727d66c 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -62,9 +62,7 @@ PACKAGING DOCUMENTATION ------------- -- List any significant changes to the documentation (not individual - typo fixes, even if they're mentioned in src/CHANGES.txt to give - the contributor credit) +- Improve wording of manpage "Functions and Environment Methods" section. DEVELOPMENT ----------- diff --git a/doc/man/scons.xml b/doc/man/scons.xml index ea518c84c5..cea021c996 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -2519,7 +2519,7 @@ that can be used in &SConscript; files. Quick links: Construction Environments Tools Builder Methods - Methods and Functions to do Things + Functions and Environment Methods SConscript Variables Construction Variables Configure Contexts @@ -3384,89 +3384,98 @@ object. - -Methods and Functions To Do Things + +&SCons; Functions and Environment Methods -In addition to Builder methods, -&scons; -provides a number of other &consenv; methods -and global functions to -manipulate the build configuration. -Usually, a &consenv; method -and global function with the same name both exist -for convenience. -In the following list, the global function -is documented in this style: + +&SCons; provides a variety of &consenv; methods +and global functions to manipulate the build configuration. +Often, a &consenv; method and a global function with +the same name exist for convenience. +In this section, both forms are shown if the function can be called +in either way. +The documentation style for these is as follows: + -Function(arguments, [optional arguments]) +Function(arguments, [optional arguments, ...]) # Global function +env.Function(arguments, [optional arguments, ...]) # Environment method -and the &consenv; method looks like: - - -env.Function(arguments, [optional arguments]) - + +In these function signatures, +arguments in brackets ([]) are optional, +and ellipses (...) indicate possible repetition. +Positional vs. keyword arguments are usually detailed +in the following text, not in the signature itself. +The &Python; positional-only (/) +and keyword-only (*) markers are not used. + -If the function can be called both ways, -then both forms are listed. + +When the &Python; keyword=value style is shown, +it can have two meanings. +If the keyword argument is known to the function, +the value is the default for that argument if it is omitted. +If the keyword is unknown to the function, +some methods treat it as a &consvar; assignment; +otherwise an exception is raised for an unknown argument. + -The global function and same-named -&consenv; method -provide almost identical functionality, with a couple of exceptions. -First, many of the &consenv; methods affect only that -&consenv;, while the global function has a -global effect (or, alternatively, takes an additional -positional argument to specify the affected &consenv;). -Second, where appropriate, -calling the functionality through a &consenv; will -substitute &consvars; into -any supplied string arguments, while the global function, -unless it takes a &consenv; parameter, -does not have the context of a &consenv; to pick variables from, -and thus cannot perform substitutions. -For example: + +A global function and a same-named &consenv; method +have the same base functionality, +with two key differences: + - -Default('$FOO') + + + +&Consenv; methods that change the environment +act on the environment instance from which they are called, +while the corresponding global function acts on +a special “hidden” &consenv; called the Default Environment. +In some cases, the global function may take +an initial argument giving the object to operate on. + + + + +String-valued arguments +(including strings in list-valued arguments) +are subject to construction variable expansion +by the environment method form; +variable expansion is not immediately performed in the global function. +For example, Default('$MYTARGET') +adds '$MYTARGET' to the +list of default targets, +while if the value in env of +MYTARGET is 'mine', +env.Default('$MYTARGET' adds +'mine' +to the default targets. +For more details on &consvar; expansion, see the +&Consvars; section. + + + -env = Environment(FOO='foo') -env.Default('$FOO') - - -In the above example, -the call to the global &f-Default; -function will add a target named -$FOO -to the list of default targets, -while the call to the -&f-env-Default; &consenv; method -will expand the value -and add a target named -foo -to the list of default targets. -For more on &consvar; expansion, -see the -&Consvars; -section below. - - - -Global functions are automatically in scope inside -&SConscript; files. -If you have custom &Python; code that you import into an &SConscript; file, -such code will need to bring them into their own scope. -You can do that by adding the following import -to the &Python; module: + +Global functions are automatically in scope inside &SConscript; files. +If your project adds &Python; modules that you include +via the &Python; import statement +from an &SConscript; file, +such code will need to add the functions +to that module’s global scope explicitly. +You can do that by adding the following import to the &Python; module: +from SCons.Script import *. + - -from SCons.Script import * - + +&SCons; provides the following &consenv; methods and global functions. +The list can be augmented on a project basis using &f-link-AddMethod; + -&Consenv; methods -and global functions provided by -&scons; -include: From a5c85cf6cda3b01b670fb50b9478f76deebda09f Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 31 Jul 2024 09:15:27 -0600 Subject: [PATCH 105/386] Doc: use consistent repeated-args syntax [skip appveyor] For now, want to keep the "key=value, ..." form for Functions/Method signatures instead of using **kwargs, since that terminology hasn't been introduced. May switch signatures later, but if so, do them all, let's not be piecemeal. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ SCons/Defaults.xml | 72 ++++++++++++++++++++++++------------------- SCons/Environment.xml | 2 +- SCons/Script/Main.xml | 26 ++++++++++------ 4 files changed, 60 insertions(+), 42 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0bbbfdf3ba..ffebcd0cbf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -38,6 +38,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Switch SCons build to use setuptools' supported version fetcher from the old homegrown one. - Improve wording of manpage "Functions and Environment Methods" section. + Make doc function signature style more consistent - tweaks to AddOption, + DefaultEnvironment and Tool,. RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml index c896f69d6d..3e8601f518 100644 --- a/SCons/Defaults.xml +++ b/SCons/Defaults.xml @@ -729,43 +729,53 @@ searching the repositories. -([**kwargs]) +([key=value, ...]) Instantiates and returns the global &consenv; object. -This environment is used internally by SCons -when it executes many of the global functions listed in this section -(that is, those not called as methods of a specific &consenv;). -The &defenv; is a singleton: -the keyword arguments are used only on the first call; -on subsequent calls the already-constructed object is returned +The &DefEnv; is used internally by &SCons; +when executing a global function +or the global form of a Builder method +that requires access to a &consenv;. + + + +On the first call, +arguments are interpreted as for the &f-link-Environment; function. +The &DefEnv; is a singleton; +subsequent calls to &f-DefaultEnvironment; return +the already-constructed object, and any keyword arguments are silently ignored. -The &defenv; can still be modified after instantiation -in the same way as any other &consenv;. -The &defenv; is independent: -modifying it has no effect on any other &consenv; -constructed by an &f-link-Environment; or &f-link-Clone; call. - - - -It is not mandatory to call &f-DefaultEnvironment;: -the &defenv; is instantiated automatically when the -build phase begins if this function has not been called; -however calling it explicitly gives the opportunity to -affect and examine the contents of the &defenv;. -Instantiation happens even if no build instructions -appar to use it, as there are internal uses. -If there are no uses in the project &SConscript; files, -a small performance gain may be seen by calling -&f-DefaultEnvironment; with an empty tools list, -thus avoiding that part of the initialization cost. -This is mainly of interest in testing when &scons; is -launched repeatedly in a short time period: - -DefaultEnvironment(tools=[]) - + + +The &DefEnv; can be modified after instantiation, +similar to other &consenvs;, +although some &consenv; methods may be unavailable. +Modifying the &DefEnv; has no effect on any other &consenv;, +either existing or newly constructed. + + + +It is not necessary to explcitly call &f-DefaultEnvironment;. +&SCons; instantiates the &defenv; automatically when the +build phase begins, if has not already been done. +However, calling it explicitly provides the opportunity to +affect and examine its contents. +Instantiation occurs even if nothing in the build system +appars to use it, due to internal uses. + + + +If the project &SConscript; files do not use global functions or Builders, +a small performance gain may be achieved by calling +&f-DefaultEnvironment; with an empty tools list +(DefaultEnvironment(tools=[])). +This avoids the tool initialization cost for the &DefEnv;, +which is mainly of interest in the test suite +where &scons; is launched repeatedly in a short time period. + diff --git a/SCons/Environment.xml b/SCons/Environment.xml index ce73cad6aa..25f0536eed 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -3489,7 +3489,7 @@ source_nodes = env.subst('$EXPAND_TO_NODELIST', conv=lambda x: x) -(name, [toolpath, **kwargs]) +(name, [toolpath, key=value, ...]) diff --git a/SCons/Script/Main.xml b/SCons/Script/Main.xml index 7b5d0ef0be..9dd9609a10 100644 --- a/SCons/Script/Main.xml +++ b/SCons/Script/Main.xml @@ -27,18 +27,25 @@ This file is processed by the bin/SConsDoc.py module. -(arguments) +(opt_str, ..., attr=value, ...) Adds a local (project-specific) command-line option. -arguments -are the same as those supported by the add_option -method in the standard Python library module optparse, -with a few additional capabilities noted below. -See the documentation for -optparse +One or more opt_str values are +the strings representing how the option can be called, +while the keyword arguments define attributes of the option. +For the most part these are the same as for the +OptionParser.add_option +method in the standard Python library module +optparse, +but with a few additional capabilities noted below. +See the + +optparse documentation for a thorough discussion of its option-processing capabities. +All options added through &f-AddOption; are placed +in a special "Local Options" option group. @@ -49,10 +56,9 @@ method, &f-AddOption; allows setting the nargs keyword value to -a string consisting of a question mark -('?') +a string '?' (question mark) to indicate that the option argument for -that option string is optional. +that option string may be omitted. If the option string is present on the command line but has no matching option argument, the value of the From b44c536eff4ecf50c6c07142765cbcf95fc216af Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 5 Aug 2024 20:55:48 -0700 Subject: [PATCH 106/386] added release.txt blurb. Fixed spelling typo in Defaults.xml --- RELEASE.txt | 3 +++ SCons/Defaults.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/RELEASE.txt b/RELEASE.txt index 534727d66c..2efc001214 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -63,6 +63,9 @@ DOCUMENTATION ------------- - Improve wording of manpage "Functions and Environment Methods" section. +- Make doc function signature style more consistent - tweaks to AddOption, + DefaultEnvironment and Tool,. + DEVELOPMENT ----------- diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml index 3e8601f518..933fe9dc94 100644 --- a/SCons/Defaults.xml +++ b/SCons/Defaults.xml @@ -758,7 +758,7 @@ either existing or newly constructed. -It is not necessary to explcitly call &f-DefaultEnvironment;. +It is not necessary to explicitly call &f-DefaultEnvironment;. &SCons; instantiates the &defenv; automatically when the build phase begins, if has not already been done. However, calling it explicitly provides the opportunity to From 8c88c9e9fde33d67ea500258d3cb32254db3b918 Mon Sep 17 00:00:00 2001 From: siegria Date: Thu, 8 Aug 2024 15:56:28 +0200 Subject: [PATCH 107/386] Apply 'utf-8' encoding if encoding is set to None As None is not a valid encoding value, fallback to 'utf-8'. This case happen if stdout or stderr is of type io.stringIO. --- SCons/Platform/win32.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Platform/win32.py b/SCons/Platform/win32.py index 1779b03649..b33fe3dfc7 100644 --- a/SCons/Platform/win32.py +++ b/SCons/Platform/win32.py @@ -167,7 +167,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: with open(tmpFileStdoutName, "rb") as tmpFileStdout: output = tmpFileStdout.read() - stdout.write(output.decode(stdout.encoding, "replace")) + stdout.write(output.decode(stdout.encoding if stdout.encoding is not None else 'utf-8', "replace")) os.remove(tmpFileStdoutName) except OSError: pass @@ -176,7 +176,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: with open(tmpFileStderrName, "rb") as tmpFileStderr: errors = tmpFileStderr.read() - stderr.write(errors.decode(stderr.encoding, "replace")) + stderr.write(errors.decode(stderr.encoding if stderr.encoding is not None else 'utf-8', "replace")) os.remove(tmpFileStderrName) except OSError: pass From acc5799dda53287e04b13bfbfafa605d886a36d5 Mon Sep 17 00:00:00 2001 From: SIEGRIST Anthony Date: Fri, 9 Aug 2024 14:57:46 +0200 Subject: [PATCH 108/386] Add comments to CHANGES.txt and RELEASE.txt --- CHANGES.txt | 4 ++++ RELEASE.txt | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index ffebcd0cbf..bd083a022e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -41,6 +41,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Make doc function signature style more consistent - tweaks to AddOption, DefaultEnvironment and Tool,. + From Anthony Siegrist; + - On win32 platform, handle piped process output more robustly. Output encoding + fallback to UTF8 if it is defined at None by the output stream object. + RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 2efc001214..cbd4768be9 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -47,6 +47,14 @@ FIXES `PackageVariable` and `PathVariable` are added to `__all__`, so this form of import should now work again. +- On win32 platform, SCons 4.7.0 modified the determination + of the output encoding of piped processes. Instead of using the default + encoding, it relied on the encoding attribute of the output stream. + If the encoding attribute of the output stream was set to None, + it was triggering an invalid argument exeption. This was the case with + streams of type io.StringIO for example. + From now, if the encoding is set to None, UTF8 is used. + IMPROVEMENTS ------------ From 6ab561ad5cf3ab9e92af3112970718034f277c68 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 15 Aug 2024 19:19:05 -0700 Subject: [PATCH 109/386] Updates and slight refactor in fix --- CHANGES.txt | 8 +++++--- SCons/Platform/win32.py | 10 ++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bd083a022e..523066543b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER will benefit from `TypeGuard`/`TypeIs`, to produce intellisense similar to using `isinstance` directly. + From Anthony Siegrist; + - On win32 platform, handle piped process output more robustly. Output encoding + fallback to UTF8 if it is defined at None by the output stream object. + From Mats Wichmann: - env.Dump() now considers the "key" positional argument to be a varargs type (zero, one or many). However called, it returns a serialized @@ -41,9 +45,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Make doc function signature style more consistent - tweaks to AddOption, DefaultEnvironment and Tool,. - From Anthony Siegrist; - - On win32 platform, handle piped process output more robustly. Output encoding - fallback to UTF8 if it is defined at None by the output stream object. + RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/SCons/Platform/win32.py b/SCons/Platform/win32.py index b33fe3dfc7..c299c63132 100644 --- a/SCons/Platform/win32.py +++ b/SCons/Platform/win32.py @@ -148,6 +148,12 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): if not stderrRedirected: args.append("2>" + tmpFileStderrName) + # Sanitize encoding. None is not a valid encoding. + if stdout.encoding is None: + stdout.encoding = 'utf-8' + if stderr.encoding is None: + stderr.encoding = 'utf-8' + # actually do the spawn try: args = [sh, '/C', escape(' '.join(args))] @@ -167,7 +173,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: with open(tmpFileStdoutName, "rb") as tmpFileStdout: output = tmpFileStdout.read() - stdout.write(output.decode(stdout.encoding if stdout.encoding is not None else 'utf-8', "replace")) + stdout.write(output.decode(stdout.encoding, "replace")) os.remove(tmpFileStdoutName) except OSError: pass @@ -176,7 +182,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: with open(tmpFileStderrName, "rb") as tmpFileStderr: errors = tmpFileStderr.read() - stderr.write(errors.decode(stderr.encoding if stderr.encoding is not None else 'utf-8', "replace")) + stderr.write(errors.decode(stderr.encoding, "replace")) os.remove(tmpFileStderrName) except OSError: pass From 6ba71afe768844ce7f081f277129a495c1852879 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 15 Aug 2024 19:27:54 -0700 Subject: [PATCH 110/386] Switched to 'oem' encoding per jcbrill's advice --- SCons/Platform/win32.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SCons/Platform/win32.py b/SCons/Platform/win32.py index c299c63132..548f99e0fa 100644 --- a/SCons/Platform/win32.py +++ b/SCons/Platform/win32.py @@ -149,10 +149,12 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): args.append("2>" + tmpFileStderrName) # Sanitize encoding. None is not a valid encoding. + # Since we're handling a redirected shell command use + # the shells default encoding. if stdout.encoding is None: - stdout.encoding = 'utf-8' + stdout.encoding = 'oem' if stderr.encoding is None: - stderr.encoding = 'utf-8' + stderr.encoding = 'oem' # actually do the spawn try: From 320337c148b345a9a1dc9f5639bc0024958ae60f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 15 Aug 2024 20:58:35 -0700 Subject: [PATCH 111/386] Added \r\n -> \n per jcbrill feedback in PR --- SCons/Platform/win32.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Platform/win32.py b/SCons/Platform/win32.py index 548f99e0fa..9b247d3a13 100644 --- a/SCons/Platform/win32.py +++ b/SCons/Platform/win32.py @@ -175,7 +175,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: with open(tmpFileStdoutName, "rb") as tmpFileStdout: output = tmpFileStdout.read() - stdout.write(output.decode(stdout.encoding, "replace")) + stdout.write(output.decode(stdout.encoding, "replace").replace("\r\n", "\n")) os.remove(tmpFileStdoutName) except OSError: pass @@ -184,7 +184,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: with open(tmpFileStderrName, "rb") as tmpFileStderr: errors = tmpFileStderr.read() - stderr.write(errors.decode(stderr.encoding, "replace")) + stderr.write(errors.decode(stderr.encoding, "replace").replace("\r\n", "\n")) os.remove(tmpFileStderrName) except OSError: pass From 6cf21b8298d81fa34f583b60d3fd19a4201d8a04 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 14 Aug 2024 05:44:00 -0600 Subject: [PATCH 112/386] Fix ListVariable with a space-containing value Fix ListVariable handling of a quoted variable value containing spaces. As a side effect of splitting the former monolithic converter/validator for ListVariable into separate callbacks, it became possible for subst to be called twice. The ListVariable converter produces an instance of a _ListVariable container, and running subst on that result ends up losing information, so avoid doing so. While developing a test for this, it turned out the test framework also didn't handle a quoted argument containing a space, so that a test case passing arguments to scons via "run(arguments='...')" could end up with scons seeing a different (broken) command line than scons invoked with the same arguments typing to a shell prompt. A regex is now used to more accurately split the "arguments" parameter, and a unit test was added to the framework tests to validate. The framework fix had a side effect - it was possible that when run as part of the test suite, the Variables package could receive a value still wrapped in quotes, leading to string mismatches ('"with space"' is not equal to 'with space'), so ListVariable now strips wrapping quote marks. Also during testing it turned out that the earlier fix for #4241, allowing a Variable to declare the value should not be subst'd, introduced problems for two types which assumed they would always be passed a string. With subst=False, they could be passed a default value that had been specified as a bool. Fixed to not fail on that. Fixes #4585 Signed-off-by: Mats Wichmann --- CHANGES.txt | 6 +- RELEASE.txt | 2 + SCons/Variables/BoolVariable.py | 9 +- SCons/Variables/BoolVariableTests.py | 12 ++- SCons/Variables/ListVariable.py | 8 +- SCons/Variables/ListVariableTests.py | 56 +++++++++++-- SCons/Variables/PackageVariable.py | 9 +- SCons/Variables/PackageVariableTests.py | 10 +++ SCons/Variables/PathVariable.py | 10 +-- SCons/Variables/__init__.py | 13 ++- test/Variables/ListVariable.py | 104 +++++++++++++++--------- testing/framework/TestCmd.py | 5 +- testing/framework/TestCmdTests.py | 44 ++++++---- 13 files changed, 207 insertions(+), 81 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ffebcd0cbf..d4df05c786 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -22,8 +22,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - env.Dump() now considers the "key" positional argument to be a varargs type (zero, one or many). However called, it returns a serialized result that looks like a dict. Previously, only one "key" was - accepted. and unlike the zero-args case, it was be serialized - to a string containing the value without the key. For example, if + accepted, and unlike the zero-args case, it was be serialized + to a string containing the value (without the key). For example, if "print(repr(env.Dump('CC'))" previously returned "'gcc'", it will now return "{'CC': 'gcc'}". - Add a timeout to test/ninja/default_targets.py - it's gotten stuck on @@ -40,6 +40,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Improve wording of manpage "Functions and Environment Methods" section. Make doc function signature style more consistent - tweaks to AddOption, DefaultEnvironment and Tool,. + - Fix handling of ListVariable when supplying a quoted choice containing + a space character (issue #4585). RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 2efc001214..5b7f9f101f 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -46,6 +46,8 @@ FIXES to be avaiable. `BoolVariable`, `EnumVariable`, `ListVariable`, `PackageVariable` and `PathVariable` are added to `__all__`, so this form of import should now work again. +- Fix handling of ListVariable when supplying a quoted choice containing + a space character (issue #4585). IMPROVEMENTS ------------ diff --git a/SCons/Variables/BoolVariable.py b/SCons/Variables/BoolVariable.py index e1fe62b905..815a4b7865 100644 --- a/SCons/Variables/BoolVariable.py +++ b/SCons/Variables/BoolVariable.py @@ -32,7 +32,7 @@ ... """ -from typing import Tuple, Callable +from typing import Callable, Tuple, Union import SCons.Errors @@ -42,7 +42,7 @@ FALSE_STRINGS = ('n', 'no', 'false', 'f', '0', 'off', 'none') -def _text2bool(val: str) -> bool: +def _text2bool(val: Union[str, bool]) -> bool: """Convert boolean-like string to boolean. If *val* looks like it expresses a bool-like value, based on @@ -54,6 +54,9 @@ def _text2bool(val: str) -> bool: Raises: ValueError: if *val* cannot be converted to boolean. """ + if isinstance(val, bool): + # mainly for the subst=False case: default might be a bool + return val lval = val.lower() if lval in TRUE_STRINGS: return True @@ -63,7 +66,7 @@ def _text2bool(val: str) -> bool: raise ValueError(f"Invalid value for boolean variable: {val!r}") -def _validator(key, val, env) -> None: +def _validator(key: str, val, env) -> None: """Validate that the value of *key* in *env* is a boolean. Parameter *val* is not used in the check. diff --git a/SCons/Variables/BoolVariableTests.py b/SCons/Variables/BoolVariableTests.py index 4c9b23e805..6d950fe3a8 100644 --- a/SCons/Variables/BoolVariableTests.py +++ b/SCons/Variables/BoolVariableTests.py @@ -43,7 +43,6 @@ def test_converter(self) -> None: """Test the BoolVariable converter""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.BoolVariable('test', 'test option help', False)) - o = opts.options[0] true_values = [ @@ -76,6 +75,17 @@ def test_converter(self) -> None: with self.assertRaises(ValueError): o.converter('x') + # Synthesize the case where the variable is created with subst=False: + # Variables code won't subst before calling the converter, + # and we might have pulled a bool from the option default. + with self.subTest(): + x = o.converter(True) + assert x, f"converter returned False for {t!r}" + with self.subTest(): + x = o.converter(False) + assert not x, f"converter returned False for {t!r}" + + def test_validator(self) -> None: """Test the BoolVariable validator""" opts = SCons.Variables.Variables() diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index f795307424..0042fa8ff3 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -81,6 +81,7 @@ def __init__( if allowedElems is None: allowedElems = [] super().__init__([_f for _f in initlist if _f]) + # TODO: why sorted? don't we want to display in the order user gave? self.allowedElems = sorted(allowedElems) def __cmp__(self, other): @@ -118,7 +119,12 @@ def _converter(val, allowedElems, mapdict) -> _ListVariable: The arguments *allowedElems* and *mapdict* are non-standard for a :class:`Variables` converter: the lambda in the :func:`ListVariable` function arranges for us to be called correctly. + + Incoming values ``all`` and ``none`` are recognized and converted + into their expanded form. """ + val = val.replace('"', '') # trim any wrapping quotes + val = val.replace("'", '') if val == 'none': val = [] elif val == 'all': @@ -155,7 +161,7 @@ def _validator(key, val, env) -> None: allowedElems = env[key].allowedElems if isinstance(val, _ListVariable): # not substituted, use .data notAllowed = [v for v in val.data if v not in allowedElems] - else: # val will be a string + else: # presumably a string notAllowed = [v for v in val.split() if v not in allowedElems] if notAllowed: # Converter only synthesized 'all' and 'none', they are never diff --git a/SCons/Variables/ListVariableTests.py b/SCons/Variables/ListVariableTests.py index 9424509d14..ca5a7935b0 100644 --- a/SCons/Variables/ListVariableTests.py +++ b/SCons/Variables/ListVariableTests.py @@ -21,6 +21,8 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"""Test List Variables elements.""" + import copy import unittest @@ -50,18 +52,22 @@ def test_ListVariable(self) -> None: assert o.default == 'one,three' def test_converter(self) -> None: - """Test the ListVariable converter""" + """Test the ListVariable converter. + + There is now a separate validator (for a long time validation was + in the converter), but it depends on the _ListVariable instance the + converter creates, so it's easier to test them in the same function. + """ opts = SCons.Variables.Variables() opts.Add( SCons.Variables.ListVariable( 'test', 'test option help', - 'all', - ['one', 'two', 'three'], - {'ONE': 'one', 'TWO': 'two'}, + default='all', + names=['one', 'two', 'three'], + map={'ONE': 'one', 'TWO': 'two'}, ) ) - o = opts.options[0] x = o.converter('all') @@ -110,10 +116,48 @@ def test_converter(self) -> None: # invalid value should convert (no change) without error x = o.converter('no_match') assert str(x) == 'no_match', x - # ... and fail to validate + + # validator checks + + # first, the one we just set up with self.assertRaises(SCons.Errors.UserError): o.validator('test', 'no_match', {"test": x}) + # now a new option, this time with a name w/ space in it (issue #4585) + opts.Add( + SCons.Variables.ListVariable( + 'test2', + help='test2 option help', + default='two', + names=['one', 'two', 'three', 'four space'], + ) + ) + o = opts.options[1] + + def test_valid(opt, seq): + """Validate a ListVariable value. + + Call the converter manually, since we need the _ListVariable + object to pass to the validator - this would normally be done + by the Variables.Update method. + """ + x = opt.converter(seq) + self.assertIsNone(opt.validator(opt.key, x, {opt.key: x})) + + with self.subTest(): + test_valid(o, 'one') + with self.subTest(): + test_valid(o, 'one,two,three') + with self.subTest(): + test_valid(o, 'all') + with self.subTest(): + test_valid(o, 'none') + with self.subTest(): + test_valid(o, 'four space') + with self.subTest(): + test_valid(o, 'one,four space') + + def test_copy(self) -> None: """Test copying a ListVariable like an Environment would""" opts = SCons.Variables.Variables() diff --git a/SCons/Variables/PackageVariable.py b/SCons/Variables/PackageVariable.py index fc281250c7..9fa514088f 100644 --- a/SCons/Variables/PackageVariable.py +++ b/SCons/Variables/PackageVariable.py @@ -51,7 +51,7 @@ """ import os -from typing import Callable, Optional, Tuple +from typing import Callable, Optional, Tuple, Union import SCons.Errors @@ -60,13 +60,16 @@ ENABLE_STRINGS = ('1', 'yes', 'true', 'on', 'enable', 'search') DISABLE_STRINGS = ('0', 'no', 'false', 'off', 'disable') -def _converter(val): +def _converter(val: Union[str, bool]) -> Union[str, bool]: """Convert package variables. Returns True or False if one of the recognized truthy or falsy values is seen, else return the value unchanged (expected to be a path string). """ + if isinstance(val, bool): + # mainly for the subst=False case: default might be a bool + return val lval = val.lower() if lval in ENABLE_STRINGS: return True @@ -75,7 +78,7 @@ def _converter(val): return val -def _validator(key, val, env, searchfunc) -> None: +def _validator(key: str, val, env, searchfunc) -> None: """Validate package variable for valid path. Checks that if a path is given as the value, that pathname actually exists. diff --git a/SCons/Variables/PackageVariableTests.py b/SCons/Variables/PackageVariableTests.py index 0d8aa6bc81..00cf1e3aef 100644 --- a/SCons/Variables/PackageVariableTests.py +++ b/SCons/Variables/PackageVariableTests.py @@ -82,6 +82,16 @@ def test_converter(self) -> None: x = o.converter(str(False)) assert not x, "converter returned a string when given str(False)" + # Synthesize the case where the variable is created with subst=False: + # Variables code won't subst before calling the converter, + # and we might have pulled a bool from the option default. + with self.subTest(): + x = o.converter(True) + assert x, f"converter returned False for {t!r}" + with self.subTest(): + x = o.converter(False) + assert not x, f"converter returned False for {t!r}" + def test_validator(self) -> None: """Test the PackageVariable validator""" opts = SCons.Variables.Variables() diff --git a/SCons/Variables/PathVariable.py b/SCons/Variables/PathVariable.py index d5988ac47d..43904e62c7 100644 --- a/SCons/Variables/PathVariable.py +++ b/SCons/Variables/PathVariable.py @@ -93,12 +93,12 @@ class _PathVariableClass: """ @staticmethod - def PathAccept(key, val, env) -> None: + def PathAccept(key: str, val, env) -> None: """Validate path with no checking.""" return @staticmethod - def PathIsDir(key, val, env) -> None: + def PathIsDir(key: str, val, env) -> None: """Validate path is a directory.""" if os.path.isdir(val): return @@ -109,7 +109,7 @@ def PathIsDir(key, val, env) -> None: raise SCons.Errors.UserError(msg) @staticmethod - def PathIsDirCreate(key, val, env) -> None: + def PathIsDirCreate(key: str, val, env) -> None: """Validate path is a directory, creating if needed.""" if os.path.isdir(val): return @@ -123,7 +123,7 @@ def PathIsDirCreate(key, val, env) -> None: raise SCons.Errors.UserError(msg) from exc @staticmethod - def PathIsFile(key, val, env) -> None: + def PathIsFile(key: str, val, env) -> None: """Validate path is a file.""" if not os.path.isfile(val): if os.path.isdir(val): @@ -133,7 +133,7 @@ def PathIsFile(key, val, env) -> None: raise SCons.Errors.UserError(msg) @staticmethod - def PathExists(key, val, env) -> None: + def PathExists(key: str, val, env) -> None: """Validate path exists.""" if not os.path.exists(val): msg = f'Path for variable {key!r} does not exist: {val}' diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 501505ff33..28325266fb 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -61,7 +61,10 @@ def __lt__(self, other): def __str__(self) -> str: """Provide a way to "print" a Variable object.""" - return f"({self.key!r}, {self.aliases}, {self.help!r}, {self.default!r}, {self.validator}, {self.converter})" + return ( + f"({self.key!r}, {self.aliases}, {self.help!r}, {self.default!r}, " + f"validator={self.validator}, converter={self.converter})" + ) class Variables: @@ -287,7 +290,13 @@ def Update(self, env, args: Optional[dict] = None) -> None: for option in self.options: if option.validator and option.key in values: if option.do_subst: - value = env.subst('${%s}' % option.key) + val = env[option.key] + if not SCons.Util.is_String(val): + # issue #4585: a _ListVariable should not be further + # substituted, breaks on values with spaces. + value = val + else: + value = env.subst('${%s}' % option.key) else: value = env[option.key] option.validator(option.key, value, env) diff --git a/test/Variables/ListVariable.py b/test/Variables/ListVariable.py index a322f9b8a0..d6356e822b 100644 --- a/test/Variables/ListVariable.py +++ b/test/Variables/ListVariable.py @@ -37,7 +37,7 @@ def check(expected): result = test.stdout().split('\n') - r = result[1:len(expected)+1] + r = result[1 : len(expected) + 1] assert r == expected, (r, expected) @@ -45,17 +45,24 @@ def check(expected): from SCons.Variables.ListVariable import ListVariable as LV from SCons.Variables import ListVariable -list_of_libs = Split('x11 gl qt ical') +list_of_libs = Split('x11 gl qt ical') + ["with space"] optsfile = 'scons.variables' opts = Variables(optsfile, args=ARGUMENTS) opts.AddVariables( - ListVariable('shared', - 'libraries to build as shared libraries', - 'all', - names = list_of_libs, - map = {'GL':'gl', 'QT':'qt'}), - LV('listvariable', 'listvariable help', 'all', names=['l1', 'l2', 'l3']) + ListVariable( + 'shared', + 'libraries to build as shared libraries', + default='all', + names=list_of_libs, + map={'GL': 'gl', 'QT': 'qt'}, + ), + LV( + 'listvariable', + 'listvariable help', + default='all', + names=['l1', 'l2', 'l3'], + ), ) _ = DefaultEnvironment(tools=[]) # test speedup @@ -70,7 +77,7 @@ def check(expected): else: print('0') -print(" ".join(env['shared'])) +print(",".join(env['shared'])) print(env.subst('$shared')) # Test subst_path() because it's used in $CPPDEFINES expansions. @@ -79,14 +86,27 @@ def check(expected): """) test.run() -check(['all', '1', 'gl ical qt x11', 'gl ical qt x11', - "['gl ical qt x11']"]) +check( + [ + 'all', + '1', + 'gl,ical,qt,with space,x11', + 'gl ical qt with space x11', + "['gl ical qt with space x11']", + ] +) -expect = "shared = 'all'"+os.linesep+"listvariable = 'all'"+os.linesep +expect = "shared = 'all'" + os.linesep + "listvariable = 'all'" + os.linesep test.must_match(test.workpath('scons.variables'), expect) - -check(['all', '1', 'gl ical qt x11', 'gl ical qt x11', - "['gl ical qt x11']"]) +check( + [ + 'all', + '1', + 'gl,ical,qt,with space,x11', + 'gl ical qt with space x11', + "['gl ical qt with space x11']", + ] +) test.run(arguments='shared=none') check(['none', '0', '', '', "['']"]) @@ -95,74 +115,80 @@ def check(expected): check(['none', '0', '', '', "['']"]) test.run(arguments='shared=x11,ical') -check(['ical,x11', '1', 'ical x11', 'ical x11', - "['ical x11']"]) +check(['ical,x11', '1', 'ical,x11', 'ical x11', "['ical x11']"]) test.run(arguments='shared=x11,,ical,,') -check(['ical,x11', '1', 'ical x11', 'ical x11', - "['ical x11']"]) +check(['ical,x11', '1', 'ical,x11', 'ical x11', "['ical x11']"]) test.run(arguments='shared=GL') check(['gl', '0', 'gl', 'gl']) test.run(arguments='shared=QT,GL') -check(['gl,qt', '0', 'gl qt', 'gl qt', "['gl qt']"]) +check(['gl,qt', '0', 'gl,qt', 'gl qt', "['gl qt']"]) +#test.run(arguments='shared="with space"') +#check(['with space', '0', 'with space', 'with space', "['with space']"]) expect_stderr = """ -scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,x11,all,none -""" + test.python_file_line(SConstruct_path, 18) +scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,with space,x11,all,none +""" + test.python_file_line(SConstruct_path, 25) test.run(arguments='shared=foo', stderr=expect_stderr, status=2) # be paranoid in testing some more combinations expect_stderr = """ -scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,x11,all,none -""" + test.python_file_line(SConstruct_path, 18) +scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,with space,x11,all,none +""" + test.python_file_line(SConstruct_path, 25) test.run(arguments='shared=foo,ical', stderr=expect_stderr, status=2) expect_stderr = """ -scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,x11,all,none -""" + test.python_file_line(SConstruct_path, 18) +scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,with space,x11,all,none +""" + test.python_file_line(SConstruct_path, 25) test.run(arguments='shared=ical,foo', stderr=expect_stderr, status=2) expect_stderr = """ -scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,x11,all,none -""" + test.python_file_line(SConstruct_path, 18) +scons: *** Invalid value(s) for variable 'shared': 'foo'. Valid values are: gl,ical,qt,with space,x11,all,none +""" + test.python_file_line(SConstruct_path, 25) test.run(arguments='shared=ical,foo,x11', stderr=expect_stderr, status=2) expect_stderr = """ -scons: *** Invalid value(s) for variable 'shared': 'foo,bar'. Valid values are: gl,ical,qt,x11,all,none -""" + test.python_file_line(SConstruct_path, 18) +scons: *** Invalid value(s) for variable 'shared': 'foo,bar'. Valid values are: gl,ical,qt,with space,x11,all,none +""" + test.python_file_line(SConstruct_path, 25) test.run(arguments='shared=foo,x11,,,bar', stderr=expect_stderr, status=2) -test.write('SConstruct', """ +test.write('SConstruct2', """\ from SCons.Variables import ListVariable opts = Variables(args=ARGUMENTS) opts.AddVariables( - ListVariable('gpib', - 'comment', - ['ENET', 'GPIB'], - names = ['ENET', 'GPIB', 'LINUX_GPIB', 'NO_GPIB']), - ) + ListVariable( + 'gpib', + 'comment', + default=['ENET', 'GPIB'], + names=['ENET', 'GPIB', 'LINUX_GPIB', 'NO_GPIB'], + ), +) DefaultEnvironment(tools=[]) # test speedup -env = Environment(variables=opts) +env = Environment(tools=[], variables=opts) Help(opts.GenerateHelpText(env)) print(env['gpib']) Default(env.Alias('dummy', None)) """) -test.run(stdout=test.wrap_stdout(read_str="ENET,GPIB\n", build_str="""\ +test.run( + arguments="-f SConstruct2", + stdout=test.wrap_stdout(read_str="ENET,GPIB\n", + build_str="""\ scons: Nothing to be done for `dummy'. -""")) +""") +) test.pass_test() diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index fba0b755bb..5d9aed9a15 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -1178,7 +1178,10 @@ def command_args(self, program=None, interpreter=None, arguments=None): cmd.extend([f"{k}={v}" for k, v in arguments.items()]) return cmd if isinstance(arguments, str): - arguments = arguments.split() + # Split into a list for passing to SCons - don't lose + # quotes, and don't break apart quoted substring with space. + # str split() fails on the spaces, shlex.split() on the quotes. + arguments = re.findall(r"(?:\".*?\"|\S)+", arguments) cmd.extend(arguments) return cmd diff --git a/testing/framework/TestCmdTests.py b/testing/framework/TestCmdTests.py index 4340d90b89..2a6ccebaf4 100644 --- a/testing/framework/TestCmdTests.py +++ b/testing/framework/TestCmdTests.py @@ -1,8 +1,5 @@ #!/usr/bin/env python -""" -Unit tests for the TestCmd.py module. -""" - +# # Copyright 2000-2010 Steven Knight # This module is free software, and you may redistribute it and/or modify # it under the same terms as Python itself, so long as this copyright message @@ -19,6 +16,9 @@ # AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +""" +Unit tests for the TestCmd.py module. +""" import os import shutil @@ -2225,59 +2225,67 @@ def test_command_args(self) -> None: r = test.command_args('prog') expect = [run_env.workpath('prog')] - assert r == expect, (expect, r) + self.assertEqual(expect, r) r = test.command_args(test.workpath('new_prog')) expect = [test.workpath('new_prog')] - assert r == expect, (expect, r) + self.assertEqual(expect, r) r = test.command_args('prog', 'python') expect = ['python', run_env.workpath('prog')] - assert r == expect, (expect, r) + self.assertEqual(expect, r) r = test.command_args('prog', 'python', 'arg1 arg2') expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2'] - assert r == expect, (expect, r) + self.assertEqual(expect, r) + + r = test.command_args('prog', 'python', 'arg1 arg2="quoted"') + expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2="quoted"'] + with self.subTest(): + self.assertEqual(expect, r) + + r = test.command_args('prog', 'python', 'arg1 arg2="quoted with space"') + expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2="quoted with space"'] + with self.subTest(): + self.assertEqual(expect, r) test.program_set('default_prog') default_prog = run_env.workpath('default_prog') r = test.command_args() expect = [default_prog] - assert r == expect, (expect, r) + self.assertEqual(expect, r) r = test.command_args(interpreter='PYTHON') expect = ['PYTHON', default_prog] - assert r == expect, (expect, r) + self.assertEqual(expect, r) r = test.command_args(interpreter='PYTHON', arguments='arg3 arg4') expect = ['PYTHON', default_prog, 'arg3', 'arg4'] - assert r == expect, (expect, r) + self.assertEqual(expect, r) # Test arguments = dict r = test.command_args(interpreter='PYTHON', arguments={'VAR1':'1'}) expect = ['PYTHON', default_prog, 'VAR1=1'] - assert r == expect, (expect, r) - + self.assertEqual(expect, r) test.interpreter_set('default_python') r = test.command_args() expect = ['default_python', default_prog] - assert r == expect, (expect, r) + self.assertEqual(expect, r) r = test.command_args(arguments='arg5 arg6') expect = ['default_python', default_prog, 'arg5', 'arg6'] - assert r == expect, (expect, r) + self.assertEqual(expect, r) r = test.command_args('new_prog_1') expect = [run_env.workpath('new_prog_1')] - assert r == expect, (expect, r) + self.assertEqual(expect, r) r = test.command_args(program='new_prog_2') expect = [run_env.workpath('new_prog_2')] - assert r == expect, (expect, r) - + self.assertEqual(expect, r) class start_TestCase(TestCmdTestCase): From 84f4364d7c1ac283c6cb19f6b3bd53a0dd42ec2d Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 21 Jul 2024 10:36:01 -0600 Subject: [PATCH 113/386] Fix bug with unique adds and delete_existing AppendUnique and PrependUnique, when called with delete_existing true, had a small logic flaw: it might remove an existing value if the value to be added is a scalar (string, most likely) and there was a substring match. The code needs to do an equality test, not a membership test. Unit tests updated and got some reformatting, plus dropped a duplicate definition of reserved_variables. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 + RELEASE.txt | 2 + SCons/Environment.py | 42 +++++++++++------- SCons/EnvironmentTests.py | 93 ++++++++++++++++++++++++--------------- 4 files changed, 87 insertions(+), 52 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ffebcd0cbf..466e1c2db7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -40,6 +40,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Improve wording of manpage "Functions and Environment Methods" section. Make doc function signature style more consistent - tweaks to AddOption, DefaultEnvironment and Tool,. + - Fix a problem with AppendUnique and PrependUnique where a value could + be erroneously removed due to a substring match. RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 2efc001214..296755f602 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -46,6 +46,8 @@ FIXES to be avaiable. `BoolVariable`, `EnumVariable`, `ListVariable`, `PackageVariable` and `PathVariable` are added to `__all__`, so this form of import should now work again. +- Fix a problem with AppendUnique and PrependUnique where a value could + be erroneously removed due to a substring match. IMPROVEMENTS ------------ diff --git a/SCons/Environment.py b/SCons/Environment.py index ac752c71ad..6c434298b7 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -1512,11 +1512,17 @@ def AppendENVPath(self, name, newpath, envname: str='ENV', self._dict[envname][name] = nv - def AppendUnique(self, delete_existing: bool=False, **kw) -> None: - """Append values to existing construction variables - in an Environment, if they're not already there. - If delete_existing is True, removes existing values first, so - values move to end. + def AppendUnique(self, delete_existing: bool = False, **kw) -> None: + """Append values uniquely to existing construction variables. + + Similar to :meth:`Append`, but the result may not contain duplicates + of any values passed for each given key (construction variable), + so an existing list may need to be pruned first, however it may still + contain other duplicates. + + If *delete_existing* is true, removes existing values first, so values + move to the end; otherwise (the default) values are skipped if + already present. """ kw = copy_non_reserved_keywords(kw) for key, val in kw.items(): @@ -1539,12 +1545,11 @@ def AppendUnique(self, delete_existing: bool=False, **kw) -> None: val = [x for x in val if x not in dk] self._dict[key] = dk + val else: + # val is not a list, so presumably a scalar (likely str). dk = self._dict[key] if is_List(dk): - # 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)) + dk = [x for x in dk if x != val] self._dict[key] = dk + [val] else: if val not in dk: @@ -1939,11 +1944,17 @@ def PrependENVPath(self, name, newpath, envname: str='ENV', self._dict[envname][name] = nv - def PrependUnique(self, delete_existing: bool=False, **kw) -> None: - """Prepend values to existing construction variables - in an Environment, if they're not already there. - If delete_existing is True, removes existing values first, so - values move to front. + def PrependUnique(self, delete_existing: bool = False, **kw) -> None: + """Prepend values uniquely to existing construction variables. + + Similar to :meth:`Prepend`, but the result may not contain duplicates + of any values passed for each given key (construction variable), + so an existing list may need to be pruned first, however it may still + contain other duplicates. + + If *delete_existing* is true, removes existing values first, so values + move to the front; otherwise (the default) values are skipped if + already present. """ kw = copy_non_reserved_keywords(kw) for key, val in kw.items(): @@ -1966,12 +1977,11 @@ def PrependUnique(self, delete_existing: bool=False, **kw) -> None: val = [x for x in val if x not in dk] self._dict[key] = val + dk else: + # val is not a list, so presumably a scalar (likely str). dk = self._dict[key] if is_List(dk): - # By elimination, val is not a list. Since dk is a - # list, wrap val in a list first. if delete_existing: - dk = [x for x in dk if x not in val] + dk = [x for x in dk if x != val] self._dict[key] = [val] + dk else: if val not in dk: diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index f171ac8962..a3a86e7c7e 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -1193,18 +1193,6 @@ def test_ENV(self) -> None: def test_ReservedVariables(self) -> None: """Test warning generation when reserved variable names are set""" - - reserved_variables = [ - 'CHANGED_SOURCES', - 'CHANGED_TARGETS', - 'SOURCE', - 'SOURCES', - 'TARGET', - 'TARGETS', - 'UNCHANGED_SOURCES', - 'UNCHANGED_TARGETS', - ] - warning = SCons.Warnings.ReservedVariableWarning SCons.Warnings.enableWarningClass(warning) old = SCons.Warnings.warningAsException(1) @@ -1759,19 +1747,26 @@ def test_AppendENVPath(self) -> None: ENV={'PATH': r'C:\dir\num\one;C:\dir\num\two'}, MYENV={'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'}, ) + # have to include the pathsep here so that the test will work on UNIX too. env1.AppendENVPath('PATH', r'C:\dir\num\two', sep=';') env1.AppendENVPath('PATH', r'C:\dir\num\three', sep=';') - env1.AppendENVPath('MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';') assert ( env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three' ), env1['ENV']['PATH'] + # add nonexisting - at end env1.AppendENVPath('MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';') + assert ( + env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\two;C:\mydir\num\three' + ), env1['MYENV']['MYPATH'] + + # add existing with delete_existing true - moves to the end env1.AppendENVPath( - 'MYPATH', r'C:\mydir\num\one', 'MYENV', sep=';', delete_existing=1 + 'MYPATH', r'C:\mydir\num\one', 'MYENV', sep=';', delete_existing=True ) - # this should do nothing since delete_existing is 0 + # this should do nothing since delete_existing is false (the default) + env1.AppendENVPath('MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';') assert ( env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one' ), env1['MYENV']['MYPATH'] @@ -1783,6 +1778,7 @@ def test_AppendENVPath(self) -> None: env1.AppendENVPath('PATH', env1.fs.Dir('sub2'), sep=';') assert env1['ENV']['PATH'] == p + ';sub1;sub2', env1['ENV']['PATH'] + def test_AppendUnique(self) -> None: """Test appending to unique values to construction variables @@ -1832,34 +1828,46 @@ def test_AppendUnique(self) -> None: assert env['CCC1'] == 'c1', env['CCC1'] assert env['CCC2'] == ['c2'], env['CCC2'] assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1'] - assert env['LL1'] == [env.Literal('a literal'), env.Literal('b literal')], env['LL1'] - assert env['LL2'] == [env.Literal('c literal'), env.Literal('b literal'), env.Literal('a literal')], [str(x) for x in env['LL2']] + assert env['LL1'] == [env.Literal('a literal'), \ + env.Literal('b literal')], env['LL1'] + assert env['LL2'] == [ + env.Literal('c literal'), + env.Literal('b literal'), + env.Literal('a literal'), + ], [str(x) for x in env['LL2']] + + env.AppendUnique(DDD1='b', delete_existing=True) + assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # b moves to end - env.AppendUnique(DDD1 = 'b', delete_existing=1) - assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # b moves to end - env.AppendUnique(DDD1 = ['a','b'], delete_existing=1) - assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end - env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1) - assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last + env.AppendUnique(DDD1=['a', 'b'], delete_existing=True) + assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end + + env.AppendUnique(DDD1=['e', 'f', 'e'], delete_existing=True) + assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last + + # issue regression: substrings should not be deleted + env.AppendUnique(BBB4='b4.newer', delete_existing=True) + assert env['BBB4'] == ['b4', 'b4.new', 'b4.newer'], env['BBB4'] env['CLVar'] = CLVar([]) - env.AppendUnique(CLVar = 'bar') + env.AppendUnique(CLVar='bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar'], result env['CLVar'] = CLVar(['abc']) - env.AppendUnique(CLVar = 'bar') + env.AppendUnique(CLVar='bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['abc', 'bar'], result env['CLVar'] = CLVar(['bar']) - env.AppendUnique(CLVar = 'bar') + env.AppendUnique(CLVar='bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar'], result + def test_Clone(self) -> None: """Test construction environment cloning. @@ -2501,6 +2509,7 @@ def test_PrependENVPath(self) -> None: ENV={'PATH': r'C:\dir\num\one;C:\dir\num\two'}, MYENV={'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'}, ) + # have to include the pathsep here so that the test will work on UNIX too. env1.PrependENVPath('PATH', r'C:\dir\num\two', sep=';') env1.PrependENVPath('PATH', r'C:\dir\num\three', sep=';') @@ -2508,11 +2517,18 @@ def test_PrependENVPath(self) -> None: env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one' ), env1['ENV']['PATH'] + + # add nonexisting - at front env1.PrependENVPath('MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';') + assert ( + env1['MYENV']['MYPATH'] == r'C:\mydir\num\three;C:\mydir\num\one;C:\mydir\num\two' + ), env1['MYENV']['MYPATH'] + + # add existing - moves to the front env1.PrependENVPath('MYPATH', r'C:\mydir\num\one', 'MYENV', sep=';') - # this should do nothing since delete_existing is 0 + # this should do nothing since delete_existing is false env1.PrependENVPath( - 'MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';', delete_existing=0 + 'MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';', delete_existing=False ) assert ( env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two' @@ -2525,6 +2541,7 @@ def test_PrependENVPath(self) -> None: env1.PrependENVPath('PATH', env1.fs.Dir('sub2'), sep=';') assert env1['ENV']['PATH'] == 'sub2;sub1;' + p, env1['ENV']['PATH'] + def test_PrependUnique(self) -> None: """Test prepending unique values to construction variables @@ -2570,32 +2587,36 @@ def test_PrependUnique(self) -> None: assert env['CCC2'] == ['c2'], env['CCC2'] assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1'] - env.PrependUnique(DDD1 = 'b', delete_existing=1) - assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front - env.PrependUnique(DDD1 = ['a','c'], delete_existing=1) - assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front - env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1) + env.PrependUnique(DDD1='b', delete_existing=True) + assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front + env.PrependUnique(DDD1=['a', 'c'], delete_existing=True) + assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front + env.PrependUnique(DDD1=['d', 'e', 'd'], delete_existing=True) assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1'] + # issue regression: substrings should not be deleted + env.PrependUnique(BBB4='b4.newer', delete_existing=True) + assert env['BBB4'] == ['b4.newer', 'b4.new', 'b4'], env['BBB4'] env['CLVar'] = CLVar([]) - env.PrependUnique(CLVar = 'bar') + env.PrependUnique(CLVar='bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar'], result env['CLVar'] = CLVar(['abc']) - env.PrependUnique(CLVar = 'bar') + env.PrependUnique(CLVar='bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar', 'abc'], result env['CLVar'] = CLVar(['bar']) - env.PrependUnique(CLVar = 'bar') + env.PrependUnique(CLVar='bar') result = env['CLVar'] assert isinstance(result, CLVar), repr(result) assert result == ['bar'], result + def test_Replace(self) -> None: """Test replacing construction variables in an Environment From aca5728fd680cca0182b5873b6d34fcd9f6f2821 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 27 Aug 2024 10:21:54 -0600 Subject: [PATCH 114/386] Variables testing: confirm space-containing values The previous commit introduced a change to how the framework handled arguments, which necessitated some changes in the variables code. It got too complicated, too many places would need too much logic. Just accept that the test.run(arguments="...") will never be quite like the same arguments on the CLI, and just use lists to avoid things being broken on embedded spaces - those won't be split. Many tests arleady do this, so it's nothing new. Added a comment in TestCmd to make it more clear. Signed-off-by: Mats Wichmann --- CHANGES.txt | 6 +++--- SCons/Variables/EnumVariableTests.py | 18 +++++++++++++++++- SCons/Variables/ListVariable.py | 2 -- test/Variables/EnumVariable.py | 9 ++++++--- test/Variables/PackageVariable.py | 5 +++++ test/Variables/PathVariable.py | 20 ++++++++++++++++---- testing/framework/TestCmd.py | 12 ++++++------ testing/framework/TestCmdTests.py | 8 ++++---- testing/framework/test-framework.rst | 12 ++++++++++++ 9 files changed, 69 insertions(+), 23 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d4df05c786..cb7d672871 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -21,9 +21,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Mats Wichmann: - env.Dump() now considers the "key" positional argument to be a varargs type (zero, one or many). However called, it returns a serialized - result that looks like a dict. Previously, only one "key" was - accepted, and unlike the zero-args case, it was be serialized - to a string containing the value (without the key). For example, if + result that looks like a dict. Previously, only a single "key" was + accepted, and unlike the zero-args case, it was serialized to a + string containing just the value (without the key). For example, if "print(repr(env.Dump('CC'))" previously returned "'gcc'", it will now return "{'CC': 'gcc'}". - Add a timeout to test/ninja/default_targets.py - it's gotten stuck on diff --git a/SCons/Variables/EnumVariableTests.py b/SCons/Variables/EnumVariableTests.py index 03848f2bef..41cb396768 100644 --- a/SCons/Variables/EnumVariableTests.py +++ b/SCons/Variables/EnumVariableTests.py @@ -159,7 +159,7 @@ def valid(o, v) -> None: def invalid(o, v) -> None: with self.assertRaises( SCons.Errors.UserError, - msg=f"did not catch expected UserError for o = {o.key}, v = {v}", + msg=f"did not catch expected UserError for o = {o.key!r}, v = {v!r}", ): o.validator('X', v, {}) table = { @@ -186,6 +186,22 @@ def invalid(o, v) -> None: expected[1](opt1, v) expected[2](opt2, v) + # make sure there are no problems with space-containing entries + opts = SCons.Variables.Variables() + opts.Add( + SCons.Variables.EnumVariable( + 'test0', + help='test option help', + default='nospace', + allowed_values=['nospace', 'with space'], + map={}, + ignorecase=0, + ) + ) + opt = opts.options[0] + valid(opt, 'nospace') + valid(opt, 'with space') + if __name__ == "__main__": unittest.main() diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index 0042fa8ff3..7a0ce49c9a 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -123,8 +123,6 @@ def _converter(val, allowedElems, mapdict) -> _ListVariable: Incoming values ``all`` and ``none`` are recognized and converted into their expanded form. """ - val = val.replace('"', '') # trim any wrapping quotes - val = val.replace("'", '') if val == 'none': val = [] elif val == 'all': diff --git a/test/Variables/EnumVariable.py b/test/Variables/EnumVariable.py index a81e8060be..48f24081c9 100644 --- a/test/Variables/EnumVariable.py +++ b/test/Variables/EnumVariable.py @@ -52,8 +52,8 @@ def check(expect): allowed_values=('motif', 'gtk', 'kde'), map={}, ignorecase=1), # case insensitive EV('some', 'some option', 'xaver', - allowed_values=('xaver', 'eins'), - map={}, ignorecase=2), # make lowercase + allowed_values=('xaver', 'eins', 'zwei wörter'), + map={}, ignorecase=2), # case lowering ) _ = DefaultEnvironment(tools=[]) @@ -89,11 +89,14 @@ def check(expect): test.run(arguments='guilib=IrGeNdwas', stderr=expect_stderr, status=2) expect_stderr = """ -scons: *** Invalid value for enum variable 'some': 'irgendwas'. Valid values are: ('xaver', 'eins') +scons: *** Invalid value for enum variable 'some': 'irgendwas'. Valid values are: ('xaver', 'eins', 'zwei wörter') """ + test.python_file_line(SConstruct_path, 20) test.run(arguments='some=IrGeNdwas', stderr=expect_stderr, status=2) +test.run(arguments=['some=zwei Wörter']) +check(['no', 'gtk', 'zwei wörter']) # case-lowering converter + test.pass_test() # Local Variables: diff --git a/test/Variables/PackageVariable.py b/test/Variables/PackageVariable.py index 64e0fa878c..e87164cab5 100644 --- a/test/Variables/PackageVariable.py +++ b/test/Variables/PackageVariable.py @@ -70,6 +70,11 @@ def check(expect): test.run(arguments=['x11=%s' % test.workpath()]) check([test.workpath()]) +space_subdir = test.workpath('space subdir') +test.subdir(space_subdir) +test.run(arguments=[f'x11={space_subdir}']) +check([space_subdir]) + expect_stderr = """ scons: *** Path does not exist for variable 'x11': '/non/existing/path/' """ + test.python_file_line(SConstruct_path, 13) diff --git a/test/Variables/PathVariable.py b/test/Variables/PathVariable.py index effbd49dc2..af9efd3281 100644 --- a/test/Variables/PathVariable.py +++ b/test/Variables/PathVariable.py @@ -106,12 +106,19 @@ def check(expect): default_file = test.workpath('default_file') default_subdir = test.workpath('default_subdir') + existing_subdir = test.workpath('existing_subdir') test.subdir(existing_subdir) existing_file = test.workpath('existing_file') test.write(existing_file, "existing_file\n") +space_subdir = test.workpath('space subdir') +test.subdir(space_subdir) + +space_file = test.workpath('space file') +test.write(space_file, "space_file\n") + non_existing_subdir = test.workpath('non_existing_subdir') non_existing_file = test.workpath('non_existing_file') @@ -135,17 +142,22 @@ def check(expect): test.run(arguments=['X=%s' % existing_file]) check([existing_file]) -test.run(arguments=['X=%s' % non_existing_file]) -check([non_existing_file]) - test.run(arguments=['X=%s' % existing_subdir]) check([existing_subdir]) +test.run(arguments=['X=%s' % space_file]) +check([space_file]) + +test.run(arguments=['X=%s' % space_subdir]) +check([space_subdir]) + test.run(arguments=['X=%s' % non_existing_subdir]) check([non_existing_subdir]) +test.must_not_exist(non_existing_subdir) +test.run(arguments=['X=%s' % non_existing_file]) +check([non_existing_file]) test.must_not_exist(non_existing_file) -test.must_not_exist(non_existing_subdir) test.write(SConstruct_path, """\ opts = Variables(args=ARGUMENTS) diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 5d9aed9a15..616297ad45 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -1023,7 +1023,7 @@ def __init__( interpreter=None, workdir=None, subdir=None, - verbose=None, + verbose: int = -1, match=None, match_stdout=None, match_stderr=None, @@ -1039,7 +1039,7 @@ def __init__( self.description_set(description) self.program_set(program) self.interpreter_set(interpreter) - if verbose is None: + if verbose == -1: try: verbose = max(0, int(os.environ.get('TESTCMD_VERBOSE', 0))) except ValueError: @@ -1178,10 +1178,10 @@ def command_args(self, program=None, interpreter=None, arguments=None): cmd.extend([f"{k}={v}" for k, v in arguments.items()]) return cmd if isinstance(arguments, str): - # Split into a list for passing to SCons - don't lose - # quotes, and don't break apart quoted substring with space. - # str split() fails on the spaces, shlex.split() on the quotes. - arguments = re.findall(r"(?:\".*?\"|\S)+", arguments) + # Split into a list for passing to SCons. This *will* + # break if the string has embedded spaces as part of a substing - + # use a # list to pass those to avoid the problem. + arguments = arguments.split() cmd.extend(arguments) return cmd diff --git a/testing/framework/TestCmdTests.py b/testing/framework/TestCmdTests.py index 2a6ccebaf4..a2d941d611 100644 --- a/testing/framework/TestCmdTests.py +++ b/testing/framework/TestCmdTests.py @@ -2239,13 +2239,13 @@ def test_command_args(self) -> None: expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2'] self.assertEqual(expect, r) - r = test.command_args('prog', 'python', 'arg1 arg2="quoted"') - expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2="quoted"'] + r = test.command_args('prog', 'python', 'arg1 arg2=value') + expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2=value'] with self.subTest(): self.assertEqual(expect, r) - r = test.command_args('prog', 'python', 'arg1 arg2="quoted with space"') - expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2="quoted with space"'] + r = test.command_args('prog', 'python', ['arg1', 'arg2=with space']) + expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2=with space'] with self.subTest(): self.assertEqual(expect, r) diff --git a/testing/framework/test-framework.rst b/testing/framework/test-framework.rst index 39ec6e3752..1a07923e91 100644 --- a/testing/framework/test-framework.rst +++ b/testing/framework/test-framework.rst @@ -621,6 +621,18 @@ or:: test.must_match(..., match=TestSCons.match_re, ...) +Often you want to supply arguments to SCons when it is invoked +to run a test, which you can do using an *arguments* parameter:: + + test.run(arguments="-O -v FOO=BAR") + +One caveat here: the way the parameter is processed is unavoidably +different from typing on the command line - if you need it not to +be split on spaces, pre-split it yourself, and pass the list, like:: + + test.run(arguments=["-f", "SConstruct2", "FOO=Two Words"]) + + Avoiding tests based on tool existence ====================================== From b0342f52159e19f9580b304cd28dfa8053a48975 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 1 Sep 2024 19:58:11 -0700 Subject: [PATCH 115/386] Changed default encoding for pspawned processes on win32 to now be oem. Updated CHANGES/RELEASE --- CHANGES.txt | 3 ++- RELEASE.txt | 5 +++-- SCons/Platform/win32.py | 12 ++---------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 523066543b..b93c3ede6d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -20,7 +20,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Anthony Siegrist; - On win32 platform, handle piped process output more robustly. Output encoding - fallback to UTF8 if it is defined at None by the output stream object. + now uses 'oem' which should be the systems default encoding for the shell where + the process is being spawned. From Mats Wichmann: - env.Dump() now considers the "key" positional argument to be a varargs diff --git a/RELEASE.txt b/RELEASE.txt index cbd4768be9..dce76e02e6 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -51,9 +51,10 @@ FIXES of the output encoding of piped processes. Instead of using the default encoding, it relied on the encoding attribute of the output stream. If the encoding attribute of the output stream was set to None, - it was triggering an invalid argument exeption. This was the case with + it was triggering an invalid argument exception. This was the case with streams of type io.StringIO for example. - From now, if the encoding is set to None, UTF8 is used. + This has been changed to always use the `oem` encoding which should be the + encoding in the shell where the command was spawned. IMPROVEMENTS ------------ diff --git a/SCons/Platform/win32.py b/SCons/Platform/win32.py index 9b247d3a13..f1659f5594 100644 --- a/SCons/Platform/win32.py +++ b/SCons/Platform/win32.py @@ -148,14 +148,6 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): if not stderrRedirected: args.append("2>" + tmpFileStderrName) - # Sanitize encoding. None is not a valid encoding. - # Since we're handling a redirected shell command use - # the shells default encoding. - if stdout.encoding is None: - stdout.encoding = 'oem' - if stderr.encoding is None: - stderr.encoding = 'oem' - # actually do the spawn try: args = [sh, '/C', escape(' '.join(args))] @@ -175,7 +167,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: with open(tmpFileStdoutName, "rb") as tmpFileStdout: output = tmpFileStdout.read() - stdout.write(output.decode(stdout.encoding, "replace").replace("\r\n", "\n")) + stdout.write(output.decode('oem', "replace").replace("\r\n", "\n")) os.remove(tmpFileStdoutName) except OSError: pass @@ -184,7 +176,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): try: with open(tmpFileStderrName, "rb") as tmpFileStderr: errors = tmpFileStderr.read() - stderr.write(errors.decode(stderr.encoding, "replace").replace("\r\n", "\n")) + stderr.write(errors.decode('oem', "replace").replace("\r\n", "\n")) os.remove(tmpFileStderrName) except OSError: pass From 08661ed4c552323ef3a7f0ff1af38868cbabb05e Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 3 Sep 2024 17:43:13 -0700 Subject: [PATCH 116/386] Regenerated docs for 4.8.1 release. --- .../examples/caching_ex-random_1.xml | 2 +- .../examples/troubleshoot_Dump_ENV_1.xml | 2 +- .../examples/troubleshoot_Dump_ENV_2.xml | 6 +- .../troubleshoot_taskmastertrace_1.xml | 50 +++--- doc/generated/functions.gen | 153 +++++++++++------- 5 files changed, 124 insertions(+), 89 deletions(-) diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml index 70f14a02c4..0f0cfe1415 100644 --- a/doc/generated/examples/caching_ex-random_1.xml +++ b/doc/generated/examples/caching_ex-random_1.xml @@ -1,7 +1,7 @@ % scons -Q +cc -o f1.o -c f1.c cc -o f4.o -c f4.c cc -o f5.o -c f5.c -cc -o f1.o -c f1.c cc -o f2.o -c f2.c cc -o f3.o -c f3.c cc -o prog f1.o f2.o f3.o f4.o f5.o diff --git a/doc/generated/examples/troubleshoot_Dump_ENV_1.xml b/doc/generated/examples/troubleshoot_Dump_ENV_1.xml index 1d009115a5..92334e7ddd 100644 --- a/doc/generated/examples/troubleshoot_Dump_ENV_1.xml +++ b/doc/generated/examples/troubleshoot_Dump_ENV_1.xml @@ -1,6 +1,6 @@ % scons scons: Reading SConscript files ... -{'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin:/snap/bin'} +{'ENV': {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin:/snap/bin'}} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. diff --git a/doc/generated/examples/troubleshoot_Dump_ENV_2.xml b/doc/generated/examples/troubleshoot_Dump_ENV_2.xml index 0879e27e88..f602dedec0 100644 --- a/doc/generated/examples/troubleshoot_Dump_ENV_2.xml +++ b/doc/generated/examples/troubleshoot_Dump_ENV_2.xml @@ -1,8 +1,8 @@ C:\>scons scons: Reading SConscript files ... -{ 'PATH': 'C:\\WINDOWS\\System32:/usr/bin', - 'PATHEXT': '.COM;.EXE;.BAT;.CMD', - 'SystemRoot': 'C:\\WINDOWS'} +{ 'ENV': { 'PATH': 'C:\\WINDOWS\\System32:/usr/bin', + 'PATHEXT': '.COM;.EXE;.BAT;.CMD', + 'SystemRoot': 'C:\\WINDOWS'}} scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. diff --git a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml index e25d3223d6..5d10fea3a1 100644 --- a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml +++ b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml @@ -1,8 +1,8 @@ % scons -Q --taskmastertrace=- prog -Job.NewParallel._work(): [Thread:8645271808] Gained exclusive access -Job.NewParallel._work(): [Thread:8645271808] Starting search -Job.NewParallel._work(): [Thread:8645271808] Found 0 completed tasks to process -Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks +Job.NewParallel._work(): [Thread:8682049344] Gained exclusive access +Job.NewParallel._work(): [Thread:8682049344] Starting search +Job.NewParallel._work(): [Thread:8682049344] Found 0 completed tasks to process +Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'prog'> and its children: @@ -18,12 +18,12 @@ Taskmaster: Evaluating <pending 0 'prog.c'> Task.make_ready_current(): node <pending 0 'prog.c'> Task.prepare(): node <up_to_date 0 'prog.c'> -Job.NewParallel._work(): [Thread:8645271808] Found internal task +Job.NewParallel._work(): [Thread:8682049344] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'prog.c'> Task.postprocess(): node <up_to_date 0 'prog.c'> Task.postprocess(): removing <up_to_date 0 'prog.c'> Task.postprocess(): adjusted parent ref count <pending 1 'prog.o'> -Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks +Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'inc.h'> and its children: @@ -31,12 +31,12 @@ Taskmaster: Evaluating <pending 0 'inc.h'> Task.make_ready_current(): node <pending 0 'inc.h'> Task.prepare(): node <up_to_date 0 'inc.h'> -Job.NewParallel._work(): [Thread:8645271808] Found internal task +Job.NewParallel._work(): [Thread:8682049344] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'inc.h'> Task.postprocess(): node <up_to_date 0 'inc.h'> Task.postprocess(): removing <up_to_date 0 'inc.h'> Task.postprocess(): adjusted parent ref count <pending 0 'prog.o'> -Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks +Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog.o'> and its children: @@ -46,19 +46,19 @@ Taskmaster: Evaluating <pending 0 'prog.o'> Task.make_ready_current(): node <pending 0 'prog.o'> Task.prepare(): node <executing 0 'prog.o'> -Job.NewParallel._work(): [Thread:8645271808] Found task requiring execution -Job.NewParallel._work(): [Thread:8645271808] Executing task +Job.NewParallel._work(): [Thread:8682049344] Found task requiring execution +Job.NewParallel._work(): [Thread:8682049344] Executing task Task.execute(): node <executing 0 'prog.o'> cc -o prog.o -c -I. prog.c -Job.NewParallel._work(): [Thread:8645271808] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8645271808] Gained exclusive access -Job.NewParallel._work(): [Thread:8645271808] Starting search -Job.NewParallel._work(): [Thread:8645271808] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8682049344] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8682049344] Gained exclusive access +Job.NewParallel._work(): [Thread:8682049344] Starting search +Job.NewParallel._work(): [Thread:8682049344] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog.o'> Task.postprocess(): node <executed 0 'prog.o'> Task.postprocess(): removing <executed 0 'prog.o'> Task.postprocess(): adjusted parent ref count <pending 0 'prog'> -Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks +Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog'> and its children: @@ -67,21 +67,21 @@ Taskmaster: Evaluating <pending 0 'prog'> Task.make_ready_current(): node <pending 0 'prog'> Task.prepare(): node <executing 0 'prog'> -Job.NewParallel._work(): [Thread:8645271808] Found task requiring execution -Job.NewParallel._work(): [Thread:8645271808] Executing task +Job.NewParallel._work(): [Thread:8682049344] Found task requiring execution +Job.NewParallel._work(): [Thread:8682049344] Executing task Task.execute(): node <executing 0 'prog'> cc -o prog prog.o -Job.NewParallel._work(): [Thread:8645271808] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8645271808] Gained exclusive access -Job.NewParallel._work(): [Thread:8645271808] Starting search -Job.NewParallel._work(): [Thread:8645271808] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8682049344] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8682049344] Gained exclusive access +Job.NewParallel._work(): [Thread:8682049344] Starting search +Job.NewParallel._work(): [Thread:8682049344] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog'> Task.postprocess(): node <executed 0 'prog'> -Job.NewParallel._work(): [Thread:8645271808] Searching for new tasks +Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. -Job.NewParallel._work(): [Thread:8645271808] Found no task requiring execution, and have no jobs: marking complete -Job.NewParallel._work(): [Thread:8645271808] Gained exclusive access -Job.NewParallel._work(): [Thread:8645271808] Completion detected, breaking from main loop +Job.NewParallel._work(): [Thread:8682049344] Found no task requiring execution, and have no jobs: marking complete +Job.NewParallel._work(): [Thread:8682049344] Gained exclusive access +Job.NewParallel._work(): [Thread:8682049344] Completion detected, breaking from main loop diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index ace8fc6a8d..ff10f9df0d 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -84,16 +84,23 @@ env.other_method_name('another arg') - AddOption(arguments) + AddOption(opt_str, ..., attr=value, ...) Adds a local (project-specific) command-line option. -arguments -are the same as those supported by the add_option -method in the standard Python library module optparse, -with a few additional capabilities noted below. -See the documentation for -optparse +One or more opt_str values are +the strings representing how the option can be called, +while the keyword arguments define attributes of the option. +For the most part these are the same as for the +OptionParser.add_option +method in the standard Python library module +optparse, +but with a few additional capabilities noted below. +See the + +optparse documentation for a thorough discussion of its option-processing capabities. +All options added through &f-AddOption; are placed +in a special "Local Options" option group. @@ -104,10 +111,9 @@ method, &f-AddOption; allows setting the nargs keyword value to -a string consisting of a question mark -('?') +a string '?' (question mark) to indicate that the option argument for -that option string is optional. +that option string may be omitted. If the option string is present on the command line but has no matching option argument, the value of the @@ -1498,41 +1504,51 @@ env.Default(hello) - DefaultEnvironment([**kwargs]) + DefaultEnvironment([key=value, ...]) Instantiates and returns the global &consenv; object. -This environment is used internally by SCons -when it executes many of the global functions listed in this section -(that is, those not called as methods of a specific &consenv;). -The &defenv; is a singleton: -the keyword arguments are used only on the first call; -on subsequent calls the already-constructed object is returned +The &DefEnv; is used internally by &SCons; +when executing a global function +or the global form of a Builder method +that requires access to a &consenv;. + + + +On the first call, +arguments are interpreted as for the &f-link-Environment; function. +The &DefEnv; is a singleton; +subsequent calls to &f-DefaultEnvironment; return +the already-constructed object, and any keyword arguments are silently ignored. -The &defenv; can still be modified after instantiation -in the same way as any other &consenv;. -The &defenv; is independent: -modifying it has no effect on any other &consenv; -constructed by an &f-link-Environment; or &f-link-Clone; call. - - - -It is not mandatory to call &f-DefaultEnvironment;: -the &defenv; is instantiated automatically when the -build phase begins if this function has not been called; -however calling it explicitly gives the opportunity to -affect and examine the contents of the &defenv;. -Instantiation happens even if no build instructions -appar to use it, as there are internal uses. -If there are no uses in the project &SConscript; files, -a small performance gain may be seen by calling -&f-DefaultEnvironment; with an empty tools list, -thus avoiding that part of the initialization cost. -This is mainly of interest in testing when &scons; is -launched repeatedly in a short time period: - -DefaultEnvironment(tools=[]) - + + +The &DefEnv; can be modified after instantiation, +similar to other &consenvs;, +although some &consenv; methods may be unavailable. +Modifying the &DefEnv; has no effect on any other &consenv;, +either existing or newly constructed. + + + +It is not necessary to explicitly call &f-DefaultEnvironment;. +&SCons; instantiates the &defenv; automatically when the +build phase begins, if has not already been done. +However, calling it explicitly provides the opportunity to +affect and examine its contents. +Instantiation occurs even if nothing in the build system +appars to use it, due to internal uses. + + + +If the project &SConscript; files do not use global functions or Builders, +a small performance gain may be achieved by calling +&f-DefaultEnvironment; with an empty tools list +(DefaultEnvironment(tools=[])). +This avoids the tool initialization cost for the &DefEnv;, +which is mainly of interest in the test suite +where &scons; is launched repeatedly in a short time period. + @@ -1658,19 +1674,22 @@ for more information. - env.Dump([key], [format]) + env.Dump([key, ...], [format=]) -Serializes &consvars; to a string. +Serializes &consvars; from the current &consenv; +to a string. The method supports the following formats specified by -format: +format, +which must be used a a keyword argument: + pretty -Returns a pretty printed representation of the environment (if -format -is not specified, this is the default). +Returns a pretty-printed representation of the variables +(this is the default). +The variables will be presented in &Python; dict form. @@ -1678,17 +1697,27 @@ is not specified, this is the default). json -Returns a JSON-formatted string representation of the environment. +Returns a JSON-formatted string representation of the variables. +The variables will be presented as a JSON object literal, +the JSON equivalent of a &Python; dict. -If key is -None (the default) the entire -dictionary of &consvars; is serialized. -If supplied, it is taken as the name of a &consvar; -whose value is serialized. + +If no key is supplied, +all the &consvars; are serialized. +If one or more keys are supplied, +only those keys and their values are serialized. + + + +Changed in NEXT_VERSION: +More than one key can be specified. +The returned string always looks like a dict (or JSON equivalent); +previously a single key serialized only the value, +not the key with the value. @@ -1696,16 +1725,21 @@ This SConstruct: -env=Environment() +env = Environment() print(env.Dump('CCCOM')) +print(env.Dump('CC', 'CCFLAGS', format='json')) -will print: +will print something like: -'$CC -c -o $TARGET $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES' +{'CCCOM': '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'} +{ + "CC": "gcc", + "CCFLAGS": [] +} @@ -1718,7 +1752,7 @@ print(env.Dump()) -will print: +will print something like: { 'AR': 'ar', @@ -1729,6 +1763,7 @@ will print: 'ASFLAGS': [], ... + @@ -4620,8 +4655,8 @@ Tag('file2.txt', DOC) - Tool(name, [toolpath, **kwargs]) - env.Tool(name, [toolpath, **kwargs]) + Tool(name, [toolpath, key=value, ...]) + env.Tool(name, [toolpath, key=value, ...]) Locates the tool specification module name and returns a callable tool object for that tool. From ff783e717edcd057df23f3d64d02961c64e5c898 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 3 Sep 2024 17:50:02 -0700 Subject: [PATCH 117/386] Updates for 4.8.1 release --- CHANGES.txt | 4 +--- RELEASE.txt | 35 +++++++---------------------------- ReleaseConfig | 2 +- SCons/__init__.py | 8 ++++---- 4 files changed, 13 insertions(+), 36 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5d4b8c8151..ad013672f1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,7 +10,7 @@ NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. NOTE: Python 3.6 support is deprecated and will be dropped in a future release. python.org no longer supports 3.6 or 3.7, and will drop 3.8 in Oct. 2024. -RELEASE VERSION/DATE TO BE FILLED IN LATER +RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 From Thaddeus Crews: - Add explicit return types to sctypes `is_*` functions. For Python <=3.9, @@ -51,8 +51,6 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER a space character (issue #4585). - - RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 From Joseph Brill: diff --git a/RELEASE.txt b/RELEASE.txt index 21c02d172d..9f78f4451f 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,10 +1,3 @@ -If you are reading this in the git repository, the contents -refer to *unreleased* changes since the last SCons release. -Past official release announcements appear at: - - https://scons.org/tag/releases.html - -================================================================== A new SCons release, 4.8.1, is now available on the SCons download page: @@ -13,15 +6,6 @@ A new SCons release, 4.8.1, is now available on the SCons download page: Here is a summary of the changes since 4.8.0: -NEW FUNCTIONALITY ------------------ - -- List new features (presumably why a checkpoint is being released) - -DEPRECATED FUNCTIONALITY ------------------------- - -- List anything that's been deprecated since the last release CHANGED/ENHANCED EXISTING FUNCTIONALITY --------------------------------------- @@ -60,17 +44,6 @@ FIXES This has been changed to always use the `oem` encoding which should be the encoding in the shell where the command was spawned. -IMPROVEMENTS ------------- - -- List improvements that wouldn't be visible to the user in the -documentation: performance improvements (describe the circumstances -under which they would be observed), or major code cleanups - -PACKAGING ---------- - -- List changes in the way SCons is packaged and/or released DOCUMENTATION ------------- @@ -93,4 +66,10 @@ Thanks to the following contributors listed below for their contributions to thi ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.0.1..HEAD + git shortlog --no-merges -ns 4.8.0..HEAD + + 9 Mats Wichmann + 8 William Deegan + 1 SIEGRIST Anthony + 1 Thaddeus Crews + 1 siegria diff --git a/ReleaseConfig b/ReleaseConfig index d1eaa39f7c..06e34bf7ae 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -31,7 +31,7 @@ # If the release type is not 'final', the patchlevel is set to the # release date. This value is mandatory and must be present in this file. #version_tuple = (2, 2, 0, 'final', 0) -version_tuple = (4, 9, 0, 'a', 0) +version_tuple = (4, 8.1) # Python versions prior to unsupported_python_version cause a fatal error # when that version is used. Python versions prior to deprecate_python_version diff --git a/SCons/__init__.py b/SCons/__init__.py index a61ba2c7c7..bbf9eebe6c 100644 --- a/SCons/__init__.py +++ b/SCons/__init__.py @@ -1,9 +1,9 @@ -__version__="4.8.0" +__version__="4.8.1" __copyright__="Copyright (c) 2001 - 2024 The SCons Foundation" __developer__="bdbaddog" -__date__="Sun, 07 Jul 2024 16:52:07 -0700" +__date__="Tue, 03 Sep 2024 17:46:32 -0700" __buildsys__="M1Dog2021" -__revision__="7c688f694c644b61342670ce92977bf4a396c0d4" -__build__="7c688f694c644b61342670ce92977bf4a396c0d4" +__revision__="08661ed4c552323ef3a7f0ff1af38868cbabb05e" +__build__="08661ed4c552323ef3a7f0ff1af38868cbabb05e" # make sure compatibility is always in place import SCons.compat # noqa \ No newline at end of file From 63da30d0e0cf69e114419432b158e127a4e6f1dc Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 3 Sep 2024 18:14:43 -0700 Subject: [PATCH 118/386] put master branch back in develop mode --- CHANGES.txt | 7 +++ RELEASE.txt | 84 +++++++++++++++------------------- ReleaseConfig | 2 +- SConstruct | 2 +- doc/user/main.xml | 2 +- testing/framework/TestSCons.py | 2 +- 6 files changed, 48 insertions(+), 51 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ad013672f1..7ee45aa2ac 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,13 @@ NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. NOTE: Python 3.6 support is deprecated and will be dropped in a future release. python.org no longer supports 3.6 or 3.7, and will drop 3.8 in Oct. 2024. +RELEASE VERSION/DATE TO BE FILLED IN LATER + + From John Doe: + + - Whatever John Doe did. + + RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 From Thaddeus Crews: diff --git a/RELEASE.txt b/RELEASE.txt index 9f78f4451f..439c8630d8 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,75 +1,65 @@ +If you are reading this in the git repository, the contents +refer to *unreleased* changes since the last SCons release. +Past official release announcements appear at: -A new SCons release, 4.8.1, is now available on the SCons download page: + https://scons.org/tag/releases.html + +================================================================== + +A new SCons release, 4.4.1, is now available on the SCons download page: https://scons.org/pages/download.html -Here is a summary of the changes since 4.8.0: +Here is a summary of the changes since 4.4.0: + +NEW FUNCTIONALITY +----------------- + +- List new features (presumably why a checkpoint is being released) + +DEPRECATED FUNCTIONALITY +------------------------ +- List anything that's been deprecated since the last release CHANGED/ENHANCED EXISTING FUNCTIONALITY --------------------------------------- -- env.Dump() previously accepted a single optional "key" argument. - It now accepts any number of optional "key" arguments; any supplied - keys will be serialized with their values in a Python dict style. - As a result there is a small change in behavior: if a *single* key - argument is given, where it previously would return a string containing - just the value, now it will return a string that looks like a dictionary - including the key. For example, from "'gcc'" to "{'CC': 'gcc'}". - This should not have any impact as the result of calling Dump is - intended for diagnostic output, not for use by other interfaces. +- List modifications to existing features, where the previous behavior + wouldn't actually be considered a bug FIXES ----- -- SCons 4.8.0 added an `__all__` specifier at the top of the Variables - module (`Variables/__init__.py`) to control what is made available in - a star import. However, there was existing usage of doing - `from SCons.Variables import *` which expected the variable *types* - to be avaiable. `BoolVariable`, `EnumVariable`, `ListVariable`, - `PackageVariable` and `PathVariable` are added to `__all__`, - so this form of import should now work again. -- Fix a problem with AppendUnique and PrependUnique where a value could - be erroneously removed due to a substring match. -- Fix handling of ListVariable when supplying a quoted choice containing - a space character (issue #4585). - -- On win32 platform, SCons 4.7.0 modified the determination - of the output encoding of piped processes. Instead of using the default - encoding, it relied on the encoding attribute of the output stream. - If the encoding attribute of the output stream was set to None, - it was triggering an invalid argument exception. This was the case with - streams of type io.StringIO for example. - This has been changed to always use the `oem` encoding which should be the - encoding in the shell where the command was spawned. +- List fixes of outright bugs +IMPROVEMENTS +------------ + +- List improvements that wouldn't be visible to the user in the + documentation: performance improvements (describe the circumstances + under which they would be observed), or major code cleanups + +PACKAGING +--------- + +- List changes in the way SCons is packaged and/or released DOCUMENTATION ------------- -- Improve wording of manpage "Functions and Environment Methods" section. -- Make doc function signature style more consistent - tweaks to AddOption, - DefaultEnvironment and Tool,. - +- List any significant changes to the documentation (not individual + typo fixes, even if they're mentioned in src/CHANGES.txt to give + the contributor credit) DEVELOPMENT ----------- -- sctypes `is_*` functions given explicit return types. Python 3.13+ uses - `TypeIs` for a near-equivalent of `isinstance`. Python 3.10 through 3.12 - uses `TypeGuard`, a less accurate implementation but still provides - usable type hinting. Python 3.9 and earlier simply returns `bool`, same - as before. +- List visible changes in the way SCons is developed Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.8.0..HEAD - - 9 Mats Wichmann - 8 William Deegan - 1 SIEGRIST Anthony - 1 Thaddeus Crews - 1 siegria + git shortlog --no-merges -ns 4.0.1..HEAD diff --git a/ReleaseConfig b/ReleaseConfig index 06e34bf7ae..f124879a04 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -31,7 +31,7 @@ # If the release type is not 'final', the patchlevel is set to the # release date. This value is mandatory and must be present in this file. #version_tuple = (2, 2, 0, 'final', 0) -version_tuple = (4, 8.1) +version_tuple = (4, 8, 2, 'a', 0) # Python versions prior to unsupported_python_version cause a fatal error # when that version is used. Python versions prior to deprecate_python_version diff --git a/SConstruct b/SConstruct index 2784d2bd1a..7a03f435e6 100644 --- a/SConstruct +++ b/SConstruct @@ -36,7 +36,7 @@ copyright_years = strftime('2001 - %Y') # This gets inserted into the man pages to reflect the month of release. month_year = strftime('%B %Y') project = 'scons' -default_version = '4.8.1' +default_version = '4.8.2' copyright = f"Copyright (c) {copyright_years} The SCons Foundation" # We let the presence or absence of various utilities determine whether diff --git a/doc/user/main.xml b/doc/user/main.xml index ef92c6eea2..b0e54a7fe7 100644 --- a/doc/user/main.xml +++ b/doc/user/main.xml @@ -36,7 +36,7 @@ This file is processed by the bin/SConsDoc.py module. The SCons Development Team - Released: Mon, 07 Jul 2024 17:17:52 -0700 + Released: Mon, 03 Sep 2024 18:13:57 -0700 2004 - 2024 diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index c4ba199c96..7360466d0f 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -55,7 +55,7 @@ # here provides some independent verification that what we packaged # conforms to what we expect. -default_version = '4.9.0ayyyymmdd' +default_version = '4.8.2ayyyymmdd' # TODO: these need to be hand-edited when there are changes python_version_unsupported = (3, 6, 0) From b2a103bff8787f9de51af975eae5e57347cdac80 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 3 Sep 2024 18:21:00 -0700 Subject: [PATCH 119/386] updated copyright on README --- README-SF.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-SF.rst b/README-SF.rst index 854524afbc..bbf2da3700 100755 --- a/README-SF.rst +++ b/README-SF.rst @@ -614,5 +614,5 @@ many contributors, including but not at all limited to: \... and many others. -Copyright (c) 2001 - 2023 The SCons Foundation +Copyright (c) 2001 - 2024 The SCons Foundation From afd59b1f66ffeb53d86a361d2622a1fe50cfbdc9 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 31 Aug 2024 08:24:44 -0600 Subject: [PATCH 120/386] PackageVariable now returns the default on "true" In all doc versions until 4.8.0, PackageVariable had wording like: "The option will support the values yes, true, on, enable or search, in which case the specified default will be used", but the code didn't actually do that, it just returned True. With this change it now returns the default value, with a slight tweak - if the default is one of the spelled out enabling strigs, it returns the boolean True instead. The indication that the default is produced if a truthy string is given is restored to the manpage (it was never dropped from the User Guide). Signed-off-by: Mats Wichmann --- CHANGES.txt | 8 ++- RELEASE.txt | 5 +- SCons/Variables/ListVariable.py | 4 +- SCons/Variables/PackageVariable.py | 50 +++++++------ doc/man/scons.xml | 111 ++++++++++++++--------------- test/Variables/PackageVariable.py | 40 +++++++++-- 6 files changed, 127 insertions(+), 91 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7ee45aa2ac..fad40c6f1b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,9 +12,11 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release. RELEASE VERSION/DATE TO BE FILLED IN LATER - From John Doe: - - - Whatever John Doe did. + From Mats Wichmann: + - PackageVariable now does what the documentation always said it does + if the variable is used on the command line with one of the enabling + string as the value: the variable's default value is produced (previously + it always produced True in this case). RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 439c8630d8..196103cfbc 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -32,7 +32,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY FIXES ----- -- List fixes of outright bugs +- PackageVariable now does what the documentation always said it does + if the variable is used on the command line with one of the enabling + string as the value: the variable's default value is produced (previously + it always produced True in this case). IMPROVEMENTS ------------ diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index 7a0ce49c9a..2c79ee7f15 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -54,6 +54,7 @@ # since elements can occur twice. import collections +import functools from typing import Callable, List, Optional, Tuple, Union import SCons.Util @@ -223,7 +224,8 @@ def ListVariable( default = ','.join(default) help = '\n '.join( (help, '(all|none|comma-separated list of names)', names_str)) - return key, help, default, validator, lambda val: _converter(val, names, map) + converter = functools.partial(_converter, allowedElems=names, mapdict=map) + return key, help, default, validator, converter # Local Variables: # tab-width:4 diff --git a/SCons/Variables/PackageVariable.py b/SCons/Variables/PackageVariable.py index 9fa514088f..7271cfbf8f 100644 --- a/SCons/Variables/PackageVariable.py +++ b/SCons/Variables/PackageVariable.py @@ -51,6 +51,7 @@ """ import os +import functools from typing import Callable, Optional, Tuple, Union import SCons.Errors @@ -60,21 +61,33 @@ ENABLE_STRINGS = ('1', 'yes', 'true', 'on', 'enable', 'search') DISABLE_STRINGS = ('0', 'no', 'false', 'off', 'disable') -def _converter(val: Union[str, bool]) -> Union[str, bool]: - """Convert package variables. +def _converter(val: Union[str, bool], default: str) -> Union[str, bool]: + """Convert a package variable. - Returns True or False if one of the recognized truthy or falsy - values is seen, else return the value unchanged (expected to - be a path string). + Returns *val* if it looks like a path string, and ``False`` if it + is a disabling string. If *val* is an enabling string, returns + *default* unless *default* is an enabling or disabling string, + in which case ignore *default* and return ``True``. + + .. versionchanged: NEXT_RELEASE + Now returns the default in case of a truthy value, matching what the + public documentation always claimed, except if the default looks + like one of the true/false strings. """ if isinstance(val, bool): - # mainly for the subst=False case: default might be a bool - return val - lval = val.lower() - if lval in ENABLE_STRINGS: - return True + # check for non-subst case, so we don't lower() a bool. + lval = str(val).lower() + else: + lval = val.lower() if lval in DISABLE_STRINGS: return False + if lval in ENABLE_STRINGS: + # Can't return the default if it is one of the enable/disable strings; + # test code expects a bool. + if default in ENABLE_STRINGS: + return True + else: + return default return val @@ -83,8 +96,8 @@ def _validator(key: str, val, env, searchfunc) -> None: Checks that if a path is given as the value, that pathname actually exists. """ - # NOTE: searchfunc is currently undocumented and unsupported if env[key] is True: + # NOTE: searchfunc is not in the documentation. if searchfunc: env[key] = searchfunc(key, val) # TODO: need to check path, or be sure searchfunc raises. @@ -103,21 +116,16 @@ def PackageVariable( a tuple with the correct converter and validator appended. The result is usable as input to :meth:`~SCons.Variables.Variables.Add`. - A 'package list' variable may either be a truthy string from + A 'package list' variable may be specified as a truthy string from :const:`ENABLE_STRINGS`, a falsy string from - :const:`DISABLE_STRINGS`, or a pathname string. + :const:`DISABLE_STRINGS`, or as a pathname string. This information is appended to *help* using only one string each for truthy/falsy. """ - # NB: searchfunc is currently undocumented and unsupported help = '\n '.join((help, f'( yes | no | /path/to/{key} )')) - return ( - key, - help, - default, - lambda k, v, e: _validator(k, v, e, searchfunc), - _converter, - ) + validator = functools.partial(_validator, searchfunc=searchfunc) + converter = functools.partial(_converter, default=default) + return key, help, default, validator, converter # Local Variables: # tab-width:4 diff --git a/doc/man/scons.xml b/doc/man/scons.xml index cea021c996..ca1937260a 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -5135,22 +5135,21 @@ vars.FormatVariableHelpText = my_format -To make it more convenient to describe custom variables, -&SCons; provides some pre-defined variable types, +&SCons; provides five pre-defined variable types, acessible through factory functions that generate -a tuple appropriate for directly passing to -the &Add; or &AddVariables; method: +a tuple appropriate for directly passing to the +Add +AddVariables +methods. + BoolVariable(key, help, default) -Set up a Boolean variable. -The variable will use -the specified name -key, -have a default value of +Set up a Boolean variable named key. +The variable will have a default value of default, and help will form the descriptive part of the help text. @@ -5181,12 +5180,10 @@ as false. EnumVariable(key, help, default, allowed_values, [map, ignorecase]) -Set up a variable +Set up a variable named key whose value may only be from a specified list ("enumeration") of values. -The variable will have the name -key, -have a default value of +The variable will have a default value of default and help will form the descriptive part of the help text. @@ -5226,12 +5223,10 @@ converted to lower case. ListVariable(key, help, default, names, [map, validator]) -Set up a variable +Set up a variable named key whose value may be one or more from a specified list of values. -The variable will have the name -key, -have a default value of +The variable will have a default value of default, and help will form the descriptive part of the help text. @@ -5240,11 +5235,10 @@ Any value that is not in all or none will raise an error. -More than one value may be specified, -separated by commas. +Use a comma separator to specify multiple values. default may be specified -either as a string of comma-separated value, -or as a list of values. +either as a string of comma-separated values, +or as a &Python; list of values. The optional @@ -5273,24 +5267,21 @@ The default is to use an internal validator routine. PackageVariable(key, help, default) -Set up a variable for a package, -where if the variable is specified, -the &consvar; named by key -will end with a value of True, -False, or a user-specified value. -For example, -a package could be a third-party software component, -the build could use the information to -exclude the package, include the package in the standard way, -or include the package using a specified -directory path to find the package files. +Set up a variable named key +to help control a build component, +such as a software package. +The variable can be specified to disable, enable, +or enable with a custom path. +The resulting &consvar; will have a value of +True, False, or a path string. +Interpretation of this value is up to the consumer, +but a path string must refer to an existing filesystem entry +or the PackageVariable validator +will raise an exception. + -The variable will have a default value -default, -and help -will form the descriptive part of the help text. -The variable supports (case-insensitive) truthy values +Any of the (case-insensitive) strings 1, yes, true, @@ -5298,8 +5289,9 @@ The variable supports (case-insensitive) truthy values enable and search +can be used to indicate the package is "enabled", -and the (case-insensitive) falsy values +and the (case-insensitive) strings 0, no, false, @@ -5308,14 +5300,17 @@ and disable to indicate the package is "disabled". + -The value -of the variable may also be set to an -arbitrary string, -which is taken to be the path name to the package -that is being enabled. -The validator will raise an exception -if this path does not exist in the filesystem. +The default parameter +can be either a path string or one of the enabling or disabling strings. +default is produced if the variable is not specified, +or if it is specified with one of the enabling strings, +except that if default is one +of the enabling strings, the boolean literal True +is produced instead of the string. +The help parameter +specifies the descriptive part of the help text. @@ -5324,22 +5319,18 @@ if this path does not exist in the filesystem. PathVariable(key, help, default, [validator]) -Set up a variable -whose value is expected to be a path name. -The &consvar; named by key -will have have a default value of +Set up a variable named key to hold a path string. +The variable will have have a default value of default, -and help -will form the descriptive part of the help text. +and the help parameter +will be used as the descriptive part of the help text. -An optional -validator argument -may be specified. -The validator will be called to -verify that the specified path -is acceptable. +The optional +validator parameter +describes a callback function which will be called to +verify that the specified path is acceptable. SCons supplies the following ready-made validators: @@ -5406,8 +5397,10 @@ if the specified value is not acceptable. These functions make it convenient to create a number of variables with consistent behavior -in a single call to the &AddVariables; -method: +in a single call to the +AddVariables +method: + vars.AddVariables( diff --git a/test/Variables/PackageVariable.py b/test/Variables/PackageVariable.py index e87164cab5..1e32eeecf5 100644 --- a/test/Variables/PackageVariable.py +++ b/test/Variables/PackageVariable.py @@ -27,6 +27,8 @@ Test the PackageVariable canned Variable type. """ +import os +from typing import List import TestSCons @@ -34,8 +36,9 @@ SConstruct_path = test.workpath('SConstruct') -def check(expect): +def check(expect: List[str]) -> None: result = test.stdout().split('\n') + # skip first line and any lines beyond the length of expect assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect) test.write(SConstruct_path, """\ @@ -44,11 +47,9 @@ def check(expect): opts = Variables(args=ARGUMENTS) opts.AddVariables( - PackageVariable('x11', - 'use X11 installed here (yes = search some places', - 'yes'), + PackageVariable('x11', 'use X11 installed here (yes = search some places', 'yes'), PV('package', 'help for package', 'yes'), - ) +) _ = DefaultEnvironment(tools=[]) env = Environment(variables=opts, tools=[]) @@ -77,10 +78,37 @@ def check(expect): expect_stderr = """ scons: *** Path does not exist for variable 'x11': '/non/existing/path/' -""" + test.python_file_line(SConstruct_path, 13) +""" + test.python_file_line(SConstruct_path, 11) test.run(arguments='x11=/non/existing/path/', stderr=expect_stderr, status=2) +# test that an enabling value produces the default value +# as long as that's a path string +tinycbor_path = test.workpath('path', 'to', 'tinycbor') +test.subdir(tinycbor_path) +SConstruct_pathstr = test.workpath('SConstruct.path') +test.write(SConstruct_pathstr, f"""\ +from SCons.Variables import PackageVariable + +vars = Variables(args=ARGUMENTS) +vars.Add( + PackageVariable( + 'tinycbor', + help="use 'tinycbor' at ", + default='{tinycbor_path}' + ) +) + +_ = DefaultEnvironment(tools=[]) +env = Environment(variables=vars, tools=[]) + +print(env['tinycbor']) +Default(env.Alias('dummy', None)) +""") + +test.run(arguments=['-f', 'SConstruct.path', 'tinycbor=yes']) +check([tinycbor_path]) + test.pass_test() # Local Variables: From 3b4f74a387d8e99d1263367fa0b77309a6c967e8 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 1 Sep 2024 10:21:01 -0600 Subject: [PATCH 121/386] New PackageVariable test - enter path as rawstring Else it fails on Windows. Signed-off-by: Mats Wichmann --- test/Variables/PackageVariable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Variables/PackageVariable.py b/test/Variables/PackageVariable.py index 1e32eeecf5..bc447dd58e 100644 --- a/test/Variables/PackageVariable.py +++ b/test/Variables/PackageVariable.py @@ -95,7 +95,7 @@ def check(expect: List[str]) -> None: PackageVariable( 'tinycbor', help="use 'tinycbor' at ", - default='{tinycbor_path}' + default=r'{tinycbor_path}' ) ) From 706f0aff7972d4c055eaabcf2e084c7036a70d5d Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 17 Mar 2024 06:25:49 -0600 Subject: [PATCH 122/386] Don't let deletions leak from OverrideEnvironment Previously, deleting from an OverrideEnvironment removed not just the override item but also the same item in the subject (base) environment. This prevents supplying the value from the subject, as that would be contrary to the expectation after "I deleted this variable". However, this lets the override modify its subject, a form of leakage we don't want. Now a deleted item has its key recorded to prevent refilling later. Direct assignment will still set the item back in the override. rpm packaging tool: call Override factory method rather than directly instantiating OverrideEnvironment ("best practices") Signed-off-by: Mats Wichmann --- CHANGES.txt | 13 +- RELEASE.txt | 5 + SCons/Environment.py | 231 ++++++++++++++++++++++--------- SCons/Environment.xml | 13 ++ SCons/EnvironmentTests.py | 184 +++++++++++++++++++----- SCons/Tool/packaging/__init__.py | 17 +-- SCons/Tool/packaging/rpm.py | 3 +- 7 files changed, 352 insertions(+), 114 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7ee45aa2ac..c76912ca77 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,9 +12,16 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release. RELEASE VERSION/DATE TO BE FILLED IN LATER - From John Doe: - - - Whatever John Doe did. + From Mats Wichmann: + - Override envirionments, created when giving construction environment + keyword arguments to Builder calls (or manually, through the undocumented + Override method), were modified not to "leak" on item deletion. The item + will now not be deleted from the base environment. Methods of regular + environments modified to (mostly) not directly access the _dict attribute + which stores the construction variables - in case they're called via + proxying from an OverrideEnv, which does not have such an attribute + (though for completeness, it now pretends to). Let the dictionary-like + methods handle access/update instead. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 439c8630d8..ce51f3a7c8 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -29,6 +29,11 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - List modifications to existing features, where the previous behavior wouldn't actually be considered a bug +- Override envirionments, created when giving construction environment + keyword arguments to Builder calls (or manually, through the + undocumented Override method), were modified not to "leak" on item deletion. + The item will now not be deleted from the base environment. + FIXES ----- diff --git a/SCons/Environment.py b/SCons/Environment.py index 6c434298b7..77966f1cb1 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -534,6 +534,11 @@ class SubstitutionEnvironment: Environment.Base to create their own flavors of construction environment, we'll save that for a future refactoring when this class actually becomes useful.) + + Special note: methods here and in actual child classes might be called + via proxy from an :class:`OverrideEnvironment`, which isn't in the + Python inheritance chain. Take care that methods called with a *self* + that's really an ``OverrideEnvironment`` don't make bad assumptions. """ def __init__(self, **kw) -> None: @@ -567,6 +572,20 @@ def _init_special(self) -> None: self._special_set_keys = list(self._special_set.keys()) def __eq__(self, other): + """Compare two environments. + + This is used by checks in Builder to determine if duplicate + targets have environments that would cause the same result. + The more reliable way (respecting the admonition to avoid poking + at :attr:`_dict` directly) would be to use ``Dictionary`` so this + is sure to work even if one or both are are instances of + :class:`OverrideEnvironment`. However an actual + ``SubstitutionEnvironment`` doesn't have a ``Dictionary`` method + That causes problems for unit tests written to excercise + ``SubsitutionEnvironment`` directly, although nobody else seems + to ever instantiate one. We count on :class:`OverrideEnvironment` + to fake the :attr:`_dict` to make things work. + """ return self._dict == other._dict def __delitem__(self, key) -> None: @@ -811,16 +830,30 @@ def RemoveMethod(self, function) -> None: self.added_methods = [dm for dm in self.added_methods if dm.method is not function] def Override(self, overrides): - """ - Produce a modified environment whose variables are overridden by - the overrides dictionaries. "overrides" is a dictionary that - will override the variables of this environment. + """Create an override environment from the current environment. - This function is much more efficient than Clone() or creating - a new Environment because it doesn't copy the construction + Produces a modified environment where the current variables are + overridden by any same-named variables from the *overrides* dict. + + An override is much more efficient than doing :meth:`~Base.Clone` + or creating a new Environment because it doesn't copy the construction environment dictionary, it just wraps the underlying construction environment, and doesn't even create a wrapper object if there are no overrides. + + Using this method is preferred over directly instantiating an + :class:`OverrideEnvirionment` because extra checks are performed, + substitution takes place, and there is special handling for a + *parse_flags* keyword argument. + + This method is not currently exposed as part of the public API, + but is invoked internally when things like builder calls have + keyword arguments, which are then passed as *overrides* here. + Some tools also call this explicitly. + + Returns: + A proxy environment of type :class:`OverrideEnvironment`. + or the current environment if *overrides* is empty. """ if not overrides: return self o = copy_non_reserved_keywords(overrides) @@ -946,7 +979,7 @@ def append_define(name, mapping=mapping) -> None: else: mapping[append_next_arg_to].append(arg) append_next_arg_to = None - elif not arg[0] in ['-', '+']: + elif arg[0] not in ['-', '+']: mapping['LIBS'].append(self.fs.File(arg)) elif arg == '-dylib_file': mapping['LINKFLAGS'].append(arg) @@ -1414,7 +1447,6 @@ def Append(self, **kw) -> None: The variable is created if it is not already present. """ - kw = copy_non_reserved_keywords(kw) for key, val in kw.items(): if key == 'CPPDEFINES': @@ -1676,26 +1708,25 @@ def Detect(self, progs): return None - def Dictionary(self, *args): - r"""Return construction variables from an environment. + def Dictionary(self, *args: str): + """Return construction variables from an environment. Args: - \*args (optional): variable names to look up + args (optional): variable names to look up Returns: - If `args` omitted, the dictionary of all construction variables. + If *args* omitted, the dictionary of all construction variables. If one arg, the corresponding value is returned. If more than one arg, a list of values is returned. Raises: - KeyError: if any of `args` is not in the construction environment. - + KeyError: if any of *args* is not in the construction environment. """ if not args: return self._dict dlist = [self._dict[x] for x in args] if len(dlist) == 1: - dlist = dlist[0] + return dlist[0] return dlist @@ -1857,7 +1888,6 @@ def Prepend(self, **kw) -> None: The variable is created if it is not already present. """ - kw = copy_non_reserved_keywords(kw) for key, val in kw.items(): if key == 'CPPDEFINES': @@ -2537,45 +2567,75 @@ def FindInstalledFiles(self): class OverrideEnvironment(Base): - """A proxy that overrides variables in a wrapped construction - environment by returning values from an overrides dictionary in - preference to values from the underlying subject environment. - - This is a lightweight (I hope) proxy that passes through most use of - attributes to the underlying Environment.Base class, but has just - enough additional methods defined to act like a real construction - environment with overridden values. It can wrap either a Base - construction environment, or another OverrideEnvironment, which - can in turn nest arbitrary OverrideEnvironments... - - Note that we do *not* call the underlying base class - (SubsitutionEnvironment) initialization, because we get most of those - from proxying the attributes of the subject construction environment. - But because we subclass SubstitutionEnvironment, this class also - has inherited arg2nodes() and subst*() methods; those methods can't - be proxied because they need *this* object's methods to fetch the - values from the overrides dictionary. + """A proxy that implements override environments. + + Returns attributes/methods and construction variables from the + base environment *subject*, except that same-named construction + variables from *overrides* are returned on read access; assignment + to a construction variable creates an override entry - *subject* is + not modified. This is a much lighter weight approach for limited-use + setups than cloning an environment, for example to handle a builder + call with keyword arguments that make a temporary change to the + current environment:: + + env.Program(target="foo", source=sources, DEBUG=True) + + While the majority of methods are proxied from the underlying environment + class, enough plumbing is defined in this class for it to behave + like an ordinary Environment without the caller needing to know it is + "special" in some way. We don't call the initializer of the class + we're proxying, rather depend on it already being properly set up. + + Deletion is handled specially, if a variable was explicitly deleted, + it should no longer appear to be in the env, but we also don't want to + modify the subject environment. + + :class:`OverrideEnvironment` can nest arbitratily, *subject* + can be an existing instance. Although instances can be + instantiated directly, the expected use is to call the + :meth:`~SubstitutionEnvironment.Override` method as a factory. + + Note Python does not give us a way to assure the subject environment + is not modified. Assigning to a variable creates a new entry in + the override, but moditying a variable will first fetch the one + from the subject, and if mutable, it will just be modified in place. + For example: ``over_env.Append(CPPDEFINES="-O")``, where ``CPPDEFINES`` + is an existing list or :class:`~SCons.Util.CLVar`, will successfully + append to ``CPPDEFINES`` in the subject env. To avoid such leakage, + clients such as Scanners, Emitters and Action functions called by a + Builder using override syntax must take care if modifying an env + (which is not advised anyway) in case they were passed an + ``OverrideEnvironment``. """ - def __init__(self, subject, overrides=None) -> None: + def __init__(self, subject, overrides: Optional[dict] = None) -> None: if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.OverrideEnvironment') + overrides = {} if overrides is None else overrides + # set these directly via __dict__ to avoid trapping by __setattr__ self.__dict__['__subject'] = subject - if overrides is None: - self.__dict__['overrides'] = {} - else: - self.__dict__['overrides'] = overrides + self.__dict__['overrides'] = overrides + self.__dict__['__deleted'] = [] # Methods that make this class act like a proxy. + def __getattr__(self, name): + # Proxied environment methods don't know they could be called with + # us as 'self' and may access the _data consvar dict directly. + # And they shouldn't *have* to know, so we need to pretend to have one, + # and not serve up the one from the subject, or it will miss the + # overridden values (and possibly modify the base). Use ourselves + # and hope the dict-like methods below are sufficient. + if name == '_dict': + return self + attr = getattr(self.__dict__['__subject'], name) - # Here we check if attr is one of the Wrapper classes. For - # example when a pseudo-builder is being called from an - # OverrideEnvironment. - # - # These wrappers when they're constructed capture the - # Environment they are being constructed with and so will not - # have access to overrided values. So we rebuild them with the - # OverrideEnvironment so they have access to overrided values. + + # Check first if attr is one of the Wrapper classes, for example + # when a pseudo-builder is being called from an OverrideEnvironment. + # These wrappers, when they're constructed, capture the Environment + # they are being constructed with and so will not have access to + # overridden values. So we rebuild them with the OverrideEnvironment + # so they have access to overridden values. if isinstance(attr, MethodWrapper): return attr.clone(self) else: @@ -2585,13 +2645,21 @@ def __setattr__(self, name, value) -> None: setattr(self.__dict__['__subject'], name, value) # Methods that make this class act like a dictionary. + def __getitem__(self, key): + """Return the visible value of *key*. + + Backfills from the subject env if *key* doesn't have an entry in + the override, and is not explicity deleted. + """ try: return self.__dict__['overrides'][key] except KeyError: + if key in self.__dict__['__deleted']: + raise return self.__dict__['__subject'].__getitem__(key) - def __setitem__(self, key, value): + def __setitem__(self, key, value) -> None: # This doesn't have the same performance equation as a "real" # environment: in an override you're basically just writing # new stuff; it's not a common case to be changing values already @@ -2599,37 +2667,72 @@ def __setitem__(self, key, value): if not key.isidentifier(): raise UserError(f"Illegal construction variable {key!r}") self.__dict__['overrides'][key] = value + if key in self.__dict__['__deleted']: + # it's no longer "deleted" if we set it + self.__dict__['__deleted'].remove(key) - def __delitem__(self, key): + def __delitem__(self, key) -> None: + """Delete *key* from override. + + Makes *key* not visible in the override. Previously implemented + by deleting from ``overrides`` and from ``__subject``, which + keeps :meth:`__getitem__` from filling it back in next time. + However, that approach was a form of leak, as the subject + environment was modified. So instead we log that it's deleted + and use that to make decisions elsewhere. + """ try: del self.__dict__['overrides'][key] except KeyError: - deleted = 0 + deleted = False else: - deleted = 1 - try: - result = self.__dict__['__subject'].__delitem__(key) - except KeyError: - if not deleted: - raise - result = None - return result + deleted = True + if not deleted and key not in self.__dict__['__subject']: + raise KeyError(key) + self.__dict__['__deleted'].append(key) def get(self, key, default=None): - """Emulates the get() method of dictionaries.""" + """Emulates the ``get`` method of dictionaries. + + Backfills from the subject environment if *key* is not in the override + and not deleted. + """ try: return self.__dict__['overrides'][key] except KeyError: + if key in self.__dict__['__deleted']: + return default return self.__dict__['__subject'].get(key, default) def __contains__(self, key) -> bool: + """Emulates the ``contains`` method of dictionaries. + + Backfills from the subject environment if *key* is not in the override + and not deleted. + """ if key in self.__dict__['overrides']: return True + if key in self.__dict__['__deleted']: + return False return key in self.__dict__['__subject'] def Dictionary(self, *args): + """Return construction variables from an environment. + + Returns all the visible variables, or just those in *args* if + specified. Obtains a Dictionary view of the subject env, then layers + the overrrides on top, after which any any deleted items are removed. + + Returns: + Like :meth:`Base.Dictionary`, returns a dict if *args* is + omitted; a single value if there is one arg; else a list of values. + + Raises: + KeyError: if any of *args* is not visible in the construction environment. + """ d = self.__dict__['__subject'].Dictionary().copy() d.update(self.__dict__['overrides']) + d = {k: v for k, v in d.items() if k not in self.__dict__['__deleted']} if not args: return d dlist = [d[x] for x in args] @@ -2638,19 +2741,19 @@ def Dictionary(self, *args): return dlist def items(self): - """Emulates the items() method of dictionaries.""" + """Emulates the ``items`` method of dictionaries.""" return self.Dictionary().items() def keys(self): - """Emulates the keys() method of dictionaries.""" + """Emulates the ``keys`` method of dictionaries.""" return self.Dictionary().keys() def values(self): - """Emulates the values() method of dictionaries.""" + """Emulates the ``values`` method of dictionaries.""" return self.Dictionary().values() def setdefault(self, key, default=None): - """Emulates the setdefault() method of dictionaries.""" + """Emulates the ``setdefault`` method of dictionaries.""" try: return self.__getitem__(key) except KeyError: @@ -2658,6 +2761,7 @@ def setdefault(self, key, default=None): return default # Overridden private construction environment methods. + def _update(self, other) -> None: self.__dict__['overrides'].update(other) @@ -2680,6 +2784,7 @@ def lvars(self): return lvars # Overridden public construction environment methods. + def Replace(self, **kw) -> None: kw = copy_non_reserved_keywords(kw) self.__dict__['overrides'].update(semi_deepcopy(kw)) diff --git a/SCons/Environment.xml b/SCons/Environment.xml index 25f0536eed..97d034364e 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -1647,6 +1647,19 @@ Example: cvars = env.Dictionary() cc_values = env.Dictionary('CC', 'CCFLAGS', 'CCCOM') + + +The object returned by &f-link-env-Dictionary; should be treated +as a read-only view into the &consvars;. +Some &consvars; have special internal handling, +and making changes through the &f-env-Dictionary; object can bypass +that handling and cause data inconsistencies. +The primary use of &f-env-Dictionary; is for diagnostic purposes - +it is used widely by test cases specifically because +it bypasses the special handling so that behavior +can be verified. + + diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index a3a86e7c7e..6a69e3cd1e 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -180,16 +180,19 @@ def test___cmp__(self) -> None: env3 = SubstitutionEnvironment(XXX = 'xxx') env4 = SubstitutionEnvironment(XXX = 'x', YYY = 'x') - assert env1 == env2 - assert env1 != env3 - assert env1 != env4 + with self.subTest(): + self.assertEqual(env1, env2) + with self.subTest(): + self.assertNotEqual(env1, env3) + with self.subTest(): + self.assertNotEqual(env1, env4) def test___delitem__(self) -> None: """Test deleting a variable from a SubstitutionEnvironment.""" env1 = SubstitutionEnvironment(XXX = 'x', YYY = 'y') env2 = SubstitutionEnvironment(XXX = 'x') del env1['YYY'] - assert env1 == env2 + self.assertEqual(env1, env2) def test___getitem__(self) -> None: """Test fetching a variable from a SubstitutionEnvironment.""" @@ -201,7 +204,7 @@ def test___setitem__(self) -> None: env1 = SubstitutionEnvironment(XXX = 'x') env2 = SubstitutionEnvironment(XXX = 'x', YYY = 'y') env1['YYY'] = 'y' - assert env1 == env2 + self.assertEqual(env1, env2) def test_get(self) -> None: """Test the SubstitutionEnvironment get() method.""" @@ -3786,13 +3789,17 @@ class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture): def setUp(self) -> None: env = Environment() env._dict = {'XXX' : 'x', 'YYY' : 'y'} + def verify_value(env, key, value, *args, **kwargs) -> None: """Verifies that key is value on the env this is called with.""" - assert env[key] == value + self.assertEqual(env[key], value) + env.AddMethod(verify_value) + # env2 does not overrride 'YYY' to test passthrough env2 = OverrideEnvironment(env, {'XXX' : 'x2'}) + # env3 overrides both, plus sets a new var 'ZZZ' env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}) - self.envs = [ env, env2, env3 ] + self.envs = [env, env2, env3] def checkpath(self, node, expect): return str(node) == os.path.normpath(expect) @@ -3800,31 +3807,79 @@ def checkpath(self, node, expect): def test___init__(self) -> None: """Test OverrideEnvironment initialization""" env, env2, env3 = self.envs - assert env['XXX'] == 'x', env['XXX'] - assert env2['XXX'] == 'x2', env2['XXX'] - assert env3['XXX'] == 'x3', env3['XXX'] - assert env['YYY'] == 'y', env['YYY'] - assert env2['YYY'] == 'y', env2['YYY'] - assert env3['YYY'] == 'y3', env3['YYY'] + + with self.subTest(): + self.assertEqual(env['XXX'], 'x') + with self.subTest(): + self.assertEqual(env2['XXX'], 'x2') + with self.subTest(): + self.assertEqual(env3['XXX'], 'x3') + with self.subTest(): + self.assertEqual(env['YYY'], 'y') + with self.subTest(): + self.assertEqual(env2['YYY'], 'y') + with self.subTest(): + self.assertEqual(env3['YYY'], 'y3') + with self.subTest(): + self.assertNotIn('ZZZ', env) + with self.subTest(): + self.assertNotIn('ZZZ', env2) + with self.subTest(): + self.assertEqual(env3['ZZZ'], 'z3') + + def test___setitem__(self) -> None: + """Test setting a variable does not leak through.""" + env, env2, env3 = self.envs + env3['QQQ'] = 'q' + with self.subTest(): + self.assertEqual(env3['QQQ'], 'q') + with self.subTest(): + self.assertNotIn('QQQ', env2) + with self.subTest(): + self.assertNotIn('QQQ', env) def test___delitem__(self) -> None: """Test deleting variables from an OverrideEnvironment""" env, env2, env3 = self.envs + # changed in NEXT_VERSION: delete does not cascade to underlying envs + # XXX is in all three, del from env3 should affect only it del env3['XXX'] - assert 'XXX' not in env, "env has XXX?" - assert 'XXX' not in env2, "env2 has XXX?" - assert 'XXX' not in env3, "env3 has XXX?" + with self.subTest(): + self.assertIn('XXX', env) + with self.subTest(): + self.assertIn('XXX', env2) + with self.subTest(): + self.assertNotIn('XXX', env3) + # YYY unique in env and env3, shadowed in env2: env2 should still work. del env3['YYY'] - assert 'YYY' not in env, "env has YYY?" - assert 'YYY' not in env2, "env2 has YYY?" - assert 'YYY' not in env3, "env3 has YYY?" + with self.subTest(): + self.assertIn('YYY', env) + with self.subTest(): + self.assertEqual(env2['YYY'], 'y') + with self.subTest(): + self.assertIn('YYY', env2) + with self.subTest(): + self.assertNotIn('YYY', env3) + # ZZZ is only in env3, none should have it del env3['ZZZ'] - assert 'ZZZ' not in env, "env has ZZZ?" - assert 'ZZZ' not in env2, "env2 has ZZZ?" - assert 'ZZZ' not in env3, "env3 has ZZZ?" + with self.subTest(): + self.assertNotIn('ZZZ', env) + with self.subTest(): + self.assertNotIn('ZZZ', env2) + with self.subTest(): + self.assertNotIn('ZZZ', env3) + + # make sure we can write back after deletion + env3['XXX'] = 'x4' + with self.subTest(): + self.assertEqual(env3['XXX'], 'x4') + with self.subTest(): + self.assertEqual(env2['XXX'], 'x2') + with self.subTest(): + self.assertEqual(env['XXX'], 'x') def test_get(self) -> None: """Test the OverrideEnvironment get() method""" @@ -3858,21 +3913,28 @@ def test_Dictionary(self) -> None: # nothing overrriden items = env.Dictionary() assert items == {'XXX' : 'x', 'YYY' : 'y'}, items + # env2 overrides XXX, YYY unchanged items = env2.Dictionary() assert items == {'XXX' : 'x2', 'YYY' : 'y'}, items + # env3 overrides XXX, YYY, adds ZZZ items = env3.Dictionary() assert items == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, items + # test one-arg and multi-arg Dictionary assert env3.Dictionary('XXX') == 'x3', env3.Dictionary('XXX') xxx, yyy = env2.Dictionary('XXX', 'YYY') assert xxx == 'x2', xxx assert yyy == 'y', yyy + + # test deletion in top override del env3['XXX'] - assert 'XXX' not in env3.Dictionary() - assert 'XXX' not in env2.Dictionary() - assert 'XXX' not in env.Dictionary() + self.assertRaises(KeyError, env3.Dictionary, 'XXX') + # changed in NEXT_VERSION: *not* deleted from underlying envs + assert 'XXX' in env2.Dictionary() + assert 'XXX' in env.Dictionary() + def test_items(self) -> None: """Test the OverrideEnvironment items() method""" @@ -3937,15 +3999,9 @@ def test_lvars(self) -> None: def test_Replace(self) -> None: """Test the OverrideEnvironment Replace() method""" env, env2, env3 = self.envs - assert env['XXX'] == 'x', env['XXX'] - assert env2['XXX'] == 'x2', env2['XXX'] - assert env3['XXX'] == 'x3', env3['XXX'] - assert env['YYY'] == 'y', env['YYY'] - assert env2['YYY'] == 'y', env2['YYY'] - assert env3['YYY'] == 'y3', env3['YYY'] - - env.Replace(YYY = 'y4') + # initial state already proven by test___init__ + env.Replace(YYY='y4') assert env['XXX'] == 'x', env['XXX'] assert env2['XXX'] == 'x2', env2['XXX'] assert env3['XXX'] == 'x3', env3['XXX'] @@ -3953,8 +4009,68 @@ def test_Replace(self) -> None: assert env2['YYY'] == 'y4', env2['YYY'] assert env3['YYY'] == 'y3', env3['YYY'] + + def test_Override_Leakage(self) -> None: + """Test OverrideEnvironment modifying a variable for leakage.""" + env, env2, env3 = self.envs + # initial state already proven by test___init__ + + # string appending should be additive - only in the override + env.Append(WWW='w') + self.assertEqual(env2['WWW'], 'w') + with self.subTest(): + env2.Append(WWW='w2') + self.assertEqual(env2['WWW'], 'ww2') + # did it leak? + self.assertEqual(env['WWW'], 'w', "leak error") + + # append to a string already in the override + self.assertEqual(env['XXX'], 'x') + self.assertEqual(env2['XXX'], 'x2') + with self.subTest(): + env2.Append(XXX='x4') + self.assertEqual(env2['XXX'], 'x2x4') + # did it leak? + self.assertEqual(env['XXX'], 'x', "leak error") + + # add a new mutable key to base env, but copy it before modifying + # This isn't a terribly interesting test, just shows that if you + # "behave carefully", things don't leak. + env.Append(QQQ=deque(['q1', 'q2', 'q3'])) + self.assertEqual(env2['QQQ'], deque(['q1', 'q2', 'q3'])) + with self.subTest(): + env2['QQQ'] = env2['QQQ'].copy() + env2.Append(QQQ='q4') + self.assertEqual(env2['QQQ'], deque(['q1', 'q2', 'q3', 'q4'])) + # did it leak? + self.assertNotIn('q4', env['QQQ'], "leak error") + + + @unittest.expectedFailure + def test_Override_Mutable_Leakage(self) -> None: + """Test OverrideEnvironment modifying a mutable variable for leakage. + + This is factored out from test_Override_Leakage as currently + there is no way to prevent the leakage when updating a mutable + element such as a list - thus it's marked as an xfail. This + gives us something to come back to if we ever invent some sort + of isolation via object copying, etc. + """ + env, env2, env3 = self.envs + # initial state already proven by test___init__ + + # add a new key to base env, with a mutable value + env.Append(QQQ=deque(['q1', 'q2', 'q3'])) + self.assertEqual(env2['QQQ'], deque(['q1', 'q2', 'q3'])) + with self.subTest(): + env2.Append(QQQ='q4') + self.assertEqual(env2['QQQ'], deque(['q1', 'q2', 'q3', 'q4'])) + # did it leak? + self.assertNotIn('q4', env['QQQ'], "leak error") + + # Tests a number of Base methods through an OverrideEnvironment to - # make sure they handle overridden constructionv variables properly. + # make sure they handle overridden construction variables properly. # # The following Base methods also call self.subst(), and so could # theoretically be subject to problems with evaluating overridden diff --git a/SCons/Tool/packaging/__init__.py b/SCons/Tool/packaging/__init__.py index ea2f429a10..0bf08fa6ad 100644 --- a/SCons/Tool/packaging/__init__.py +++ b/SCons/Tool/packaging/__init__.py @@ -86,8 +86,7 @@ def Tag(env, target, source, *more_tags, **kw_tags): t.Tag(k, v) def Package(env, target=None, source=None, **kw): - """ Entry point for the package tool. - """ + """Entry point for the package tool.""" # check if we need to find the source files ourselves if not source: source = env.FindInstalledFiles() @@ -96,17 +95,11 @@ def Package(env, target=None, source=None, **kw): raise UserError("No source for Package() given") # decide which types of packages shall be built. Can be defined through - # four mechanisms: command line argument, keyword argument, - # environment argument and default selection (zip or tar.gz) in that - # order. - try: - kw['PACKAGETYPE'] = env['PACKAGETYPE'] - except KeyError: - pass - - if not kw.get('PACKAGETYPE'): + # four mechanisms: command line argument, keyword argument, environment + # argument and default selection (zip or tar.gz) in that order. + kw.setdefault('PACKAGETYPE', env.get('PACKAGETYPE')) + if kw['PACKAGETYPE'] is None: kw['PACKAGETYPE'] = GetOption('package_type') - if kw['PACKAGETYPE'] is None: if 'Tar' in env['BUILDERS']: kw['PACKAGETYPE'] = 'targz' diff --git a/SCons/Tool/packaging/rpm.py b/SCons/Tool/packaging/rpm.py index 03633db269..a51e8679e6 100644 --- a/SCons/Tool/packaging/rpm.py +++ b/SCons/Tool/packaging/rpm.py @@ -25,7 +25,6 @@ import SCons.Builder import SCons.Tool.rpmutils -from SCons.Environment import OverrideEnvironment from SCons.Tool.packaging import stripinstallbuilder, src_targz from SCons.Errors import UserError @@ -67,7 +66,7 @@ def package(env, target, source, PACKAGEROOT, NAME, VERSION, kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') # mangle the source and target list for the rpmbuild - env = OverrideEnvironment(env, kw) + env = env.Override(kw) target, source = stripinstallbuilder(target, source, env) target, source = addspecfile(target, source, env) target, source = collectintargz(target, source, env) From 00c12f9af36c2bf200d68d252f5aa719ec51bf80 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 8 Aug 2024 06:58:51 -0600 Subject: [PATCH 123/386] Tweak SConsOptions docstrings Indicate more clearly (for the API docs) that something is an SCons addition/modification, since that doc shows members inherited from the optparse class - this was always easier to tell in the code than in the doc. A few other minor rearrangements and comments and a few type annotations. Does not change any behavior. Signed-off-by: Mats Wichmann --- SCons/Script/SConsOptions.py | 120 +++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 47 deletions(-) diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index d4cc992682..202640a934 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -27,6 +27,7 @@ import shutil import sys import textwrap +from typing import Optional import SCons.Node.FS import SCons.Platform.virtualenv @@ -73,7 +74,7 @@ class SConsValues(optparse.Values): 1. set on the command line. 2. set in an SConscript file via :func:`~SCons.Script.Main.SetOption`. 3. the default setting (from the the ``op.add_option()`` - calls in the :func:`Parser` function, below). + calls in the :func:`Parser` function. The command line always overrides a value set in a SConscript file, which in turn always overrides default settings. Because we want @@ -144,16 +145,14 @@ def __getattr__(self, attr): ] def set_option(self, name: str, value) -> None: - """Sets an option *name* from an SConscript file. + """Set an option value from a :func:`~SCons.Script.Main.SetOption` call. - Vvalidation steps for known (that is, defined in SCons itself) - options are in-line here. Validation should be along the same - lines as for options processed from the command line - - it's kind of a pain to have to duplicate. Project-defined options - can specify callbacks for the command-line version, but will have - no inbuilt validation here. It's up to the build system maintainer - to make sure :func:`~SCons.Script.Main.SetOption` is being used - correctly, we can't really do any better here. + Validation steps for settable options (those defined in SCons + itself) are in-line here. Duplicates the logic for the matching + command-line options in :func:`Parse` - these need to be kept + in sync. Cannot provide validation for options added via + :func:`~SCons.Script.Main.AddOption` since we don't know about those + ahead of time - it is up to the developer to figure that out. Raises: UserError: the option is not settable. @@ -233,13 +232,32 @@ def set_option(self, name: str, value) -> None: class SConsOption(optparse.Option): - def convert_value(self, opt, value): + """SCons added option. + + Changes :attr:`CHECK_METHODS` and :attr:`CONST_ACTIONS` settings from + :class:`optparse.Option` base class to tune for our usage. + + New function :meth:`_check_nargs_optional` implements the ``nargs=?`` + syntax from :mod:`argparse`, and is added to the ``CHECK_METHODS`` list. + Overridden :meth:`convert_value` supports this usage. + """ + # can uncomment to have a place to trap SConsOption creation for debugging: + # def __init__(self, *args, **kwargs): + # super().__init__(*args, **kwargs) + + def convert_value(self, opt: str, value): + """SCons override: recognize nargs="?".""" if value is not None: if self.nargs in (1, '?'): return self.check_value(opt, value) return tuple([self.check_value(opt, v) for v in value]) def process(self, opt, value, values, parser): + """Process a value. + + Direct copy of optparse version including the comments - + we don't change anything so this could just be dropped. + """ # First, convert the value(s) to the right type. Howl if any # value(s) are bogus. value = self.convert_value(opt, value) @@ -250,7 +268,8 @@ def process(self, opt, value, values, parser): return self.take_action( self.action, self.dest, opt, value, values, parser) - def _check_nargs_optional(self): + def _check_nargs_optional(self) -> None: + """SCons added: deal with optional option-arguments.""" if self.nargs == '?' and self._short_opts: fmt = "option %s: nargs='?' is incompatible with short options" raise SCons.Errors.UserError(fmt % self._short_opts[0]) @@ -258,7 +277,7 @@ def _check_nargs_optional(self): CHECK_METHODS = optparse.Option.CHECK_METHODS if CHECK_METHODS is None: CHECK_METHODS = [] - CHECK_METHODS = CHECK_METHODS + [_check_nargs_optional] + CHECK_METHODS = CHECK_METHODS + [_check_nargs_optional] # added for SCons CONST_ACTIONS = optparse.Option.CONST_ACTIONS + optparse.Option.TYPED_ACTIONS @@ -270,8 +289,8 @@ class SConsOptionGroup(optparse.OptionGroup): lined up with the normal "SCons Options". """ - def format_help(self, formatter): - """ Format an option group's help text. + def format_help(self, formatter) -> str: + """SCons-specific formatting of an option group's help text. The title is dedented so it's flush with the "SCons Options" title we print at the top. @@ -285,14 +304,15 @@ def format_help(self, formatter): class SConsBadOptionError(optparse.BadOptionError): - """Exception used to indicate that invalid command line options were specified - - :ivar str opt_str: The offending option specified on command line which is not recognized - :ivar OptionParser parser: The active argument parser + """Raised if an invalid option value is encountered on the command line. + Attributes: + opt_str: The unrecognized command-line option. + parser: The active argument parser. """ + # TODO why is 'parser' needed? Not called in current code base. - def __init__(self, opt_str, parser=None) -> None: + def __init__(self, opt_str: str, parser: Optional["SConsOptionParser"] = None) -> None: self.opt_str = opt_str self.parser = parser @@ -304,8 +324,8 @@ class SConsOptionParser(optparse.OptionParser): preserve_unknown_options = False raise_exception_on_error = False - def error(self, msg): - """Overridden OptionValueError exception handler.""" + def error(self, msg: str) -> None: + """SCons-specific handling of option errors.""" if self.raise_exception_on_error: raise SConsBadOptionError(msg, self) else: @@ -313,8 +333,8 @@ def error(self, msg): sys.stderr.write("SCons Error: %s\n" % msg) sys.exit(2) - def _process_long_opt(self, rargs, values): - """ SCons-specific processing of long options. + def _process_long_opt(self, rargs, values) -> None: + """SCons-specific processing of long options. This is copied directly from the normal ``optparse._process_long_opt()`` method, except that, if configured @@ -341,9 +361,9 @@ def _process_long_opt(self, rargs, values): % (opt, self._match_long_opt(opt)) ) except optparse.BadOptionError: + # SCons addition: if requested, add unknown options to + # the "leftover arguments" list for later processing. if self.preserve_unknown_options: - # SCons-specific: if requested, add unknown options to - # the "leftover arguments" list for later processing. self.largs.append(arg) if had_explicit_value: # The unknown option will be re-processed later, @@ -355,6 +375,7 @@ def _process_long_opt(self, rargs, values): option = self._long_opt[opt] if option.takes_value(): nargs = option.nargs + # SCons addition: recognize '?' for nargs if nargs == '?': if had_explicit_value: value = rargs.pop(0) @@ -362,6 +383,7 @@ def _process_long_opt(self, rargs, values): value = option.const elif len(rargs) < nargs: if nargs == 1: + # SCons addition: nicer msg if option had choices if not option.choices: self.error(_("%s option requires an argument") % opt) else: @@ -399,10 +421,8 @@ def reparse_local_options(self) -> None: allow exact matches for long-opts only (no partial argument names!). Otherwise there could be problems in :meth:`add_local_option` below. When called from there, we try to reparse the - command-line arguments that - - 1. haven't been processed so far (`self.largs`), but - 2. are possibly not added to the list of options yet. + command-line arguments that haven't been processed so far + (``self.largs``), but are possibly not added to the options list yet. So, when we only have a value for ``--myargument`` so far, a command-line argument of ``--myarg=test`` would set it, @@ -450,21 +470,22 @@ def reparse_local_options(self) -> None: self.largs = self.largs + largs_restore def add_local_option(self, *args, **kw) -> SConsOption: - """ Adds a local option to the parser. + """Add a local option to the parser. - This is initiated by an :func:`~SCons.Script.Main.AddOption` call to - add a user-defined command-line option. Add the option to a separate - option group for the local options, creating the group if necessary. + This is the implementation of :func:`~SCons.Script.Main.AddOption`, + to add a project-defined command-line option. Local options + are added to a separate option group, which is created if necessary. The keyword argument *settable* is recognized specially (and removed from *kw*). If true, the option is marked as modifiable; by default "local" (project-added) options are not eligible for - for :func:`~SCons.Script.Main.SetOption` calls. + :func:`~SCons.Script.Main.SetOption` calls. .. versionchanged:: 4.8.0 Added special handling of *settable*. """ + group: SConsOptionGroup try: group = self.local_option_group except AttributeError: @@ -473,6 +494,7 @@ def add_local_option(self, *args, **kw) -> SConsOption: self.local_option_group = group settable = kw.pop('settable') + # this gives us an SConsOption due to the setting of self.option_class result = group.add_option(*args, **kw) if result: # The option was added successfully. We now have to add the @@ -483,6 +505,7 @@ def add_local_option(self, *args, **kw) -> SConsOption: # any value overridden on the command line is immediately # available if the user turns around and does a GetOption() # right away. + # TODO: what if dest is None? setattr(self.values.__defaults__, result.dest, result.default) self.reparse_local_options() if settable: @@ -491,7 +514,7 @@ def add_local_option(self, *args, **kw) -> SConsOption: return result def format_local_option_help(self, formatter=None, file=None): - """Return the help for the project-level ("local") options. + """Return the help for the project-level ("local") SCons options. .. versionadded:: 4.6.0 """ @@ -514,7 +537,7 @@ def format_local_option_help(self, formatter=None, file=None): return local_help def print_local_option_help(self, file=None): - """Print help for just project-defined options. + """Print help for just local SCons options. Writes to *file* (default stdout). @@ -527,11 +550,11 @@ def print_local_option_help(self, file=None): class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): def format_usage(self, usage) -> str: - """ Formats the usage message. """ + """Format the usage message for SCons.""" return "usage: %s\n" % usage def format_heading(self, heading): - """ Translates heading to "SCons Options" + """Translate heading to "SCons Options" Heading of "Options" changed to "SCons Options." Unfortunately, we have to do this here, because those titles @@ -542,11 +565,10 @@ def format_heading(self, heading): return super().format_heading(heading) def format_option(self, option): - """ Customized option formatter. + """SCons-specific option formatter. - A copy of the normal ``optparse.IndentedHelpFormatter.format_option()`` - method. This has been snarfed so we can modify text wrapping to - our liking: + A copy of the :meth:`optparse.IndentedHelpFormatter.format_option` + method. Overridden so we can modify text wrapping to our liking: * add our own regular expression that doesn't break on hyphens (so things like ``--no-print-directory`` don't get broken). @@ -556,21 +578,25 @@ def format_option(self, option): The help for each option consists of two parts: - * the opt strings and metavars e.g. ("-x", or - "-fFILENAME, --file=FILENAME") + * the opt strings and metavars e.g. (``-x``, or + ``-fFILENAME, --file=FILENAME``) * the user-supplied help string e.g. - ("turn on expert mode", "read data from FILENAME") + (``turn on expert mode``, ``read data from FILENAME``) If possible, we write both of these on the same line:: -x turn on expert mode - But if the opt string list is too long, we put the help + If the opt string list is too long, we put the help string on a second line, indented to the same column it would start in if it fit on the first line:: -fFILENAME, --file=FILENAME read data from FILENAME + + Help strings are wrapped for terminal width and do not preserve + any hand-made formatting that may have been used in the ``AddOption`` + call, so don't attempt prettying up a list of choices (for example). """ result = [] opts = self.option_strings[option] From 569f24ae804fd13f56a9f44e8343d0cf219fb1f1 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 9 Sep 2024 11:52:57 -0600 Subject: [PATCH 124/386] Some tweaks to testing framework. Most interesting is an "api change" - the test methods test.must_exist() and test.must_exist_one_of() now take an optional 'message' keyword argument which is passed on to fail_test() if the test fails. The regex used to test an exception is now working for Python 3.13, and enabled conditionally - the "enhanced error reporting" changed, in a way that made it easy to reuse the existing regex (if somebody wants to take a shot at unifying them, more power!). Also one unexpected issue was found - one of the check routines does "output = os.newline.join(output)", but there is no os.newline. Could use os.linesep, but just changed it to the Python newline character. Some annotations added, and some cleanup done on possibly unsafe uses - mainly that self.stderr() and selt.stdout() *can* return None, but several places in the code just did string operations on the return unconditionally. There's already precendent- other places did do a check before using, so just extended the concept to possibly vulnerable palces. Signed-off-by: Mats Wichmann --- CHANGES.txt | 10 +- testing/framework/TestCmd.py | 107 ++++++----- testing/framework/TestCmdTests.py | 3 + testing/framework/TestCommon.py | 260 ++++++++++++++++----------- testing/framework/TestCommonTests.py | 104 +++++++---- testing/framework/TestSCons.py | 69 ++++--- 6 files changed, 344 insertions(+), 209 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7ee45aa2ac..487c45819d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,9 +12,13 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release. RELEASE VERSION/DATE TO BE FILLED IN LATER - From John Doe: - - - Whatever John Doe did. + From Mats Wichmann: + - Minor updates to test framework. The functional change is that + test.must_exist() and test.must_exist_one_of() now take an optional + 'message' keyword argument which is passed on to fail_test() if + the test fails. The rest is cleanup and type annotations. Be more + careful that the returns from stderr() and stdout(), which *can* + return None, are not used without checking. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 616297ad45..cada7be602 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -1,3 +1,22 @@ +# Copyright 2000-2024 Steven Knight +# +# This module is free software, and you may redistribute it and/or modify +# it under the same terms as Python itself, so long as this copyright message +# and disclaimer are retained in their original form. +# +# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# +# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +# +# Python License: https://docs.python.org/3/license.html#psf-license + """ A testing framework for commands and scripts. @@ -276,22 +295,6 @@ TestCmd.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') """ -# Copyright 2000-2010 Steven Knight -# This module is free software, and you may redistribute it and/or modify -# it under the same terms as Python itself, so long as this copyright message -# and disclaimer are retained in their original form. -# -# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -# -# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - __author__ = "Steven Knight " __revision__ = "TestCmd.py 1.3.D001 2010/06/03 12:58:27 knight" __version__ = "1.3" @@ -320,7 +323,7 @@ from collections import UserList, UserString from pathlib import Path from subprocess import PIPE, STDOUT -from typing import Optional +from typing import Callable, Dict, Optional, Union IS_WINDOWS = sys.platform == 'win32' IS_MACOS = sys.platform == 'darwin' @@ -427,7 +430,13 @@ def clean_up_ninja_daemon(self, result_type) -> None: shutil.rmtree(daemon_dir) -def fail_test(self=None, condition: bool=True, function=None, skip: int=0, message=None) -> None: +def fail_test( + self=None, + condition: bool = True, + function: Optional[Callable] = None, + skip: int = 0, + message: str = "", +) -> None: """Causes a test to exit with a fail. Reports that the test FAILED and exits with a status of 1, unless @@ -1047,7 +1056,7 @@ def __init__( self.verbose_set(verbose) self.combine = combine self.universal_newlines = universal_newlines - self.process = None + self.process: Optional[Popen] = None # Two layers of timeout: one at the test class instance level, # one set on an individual start() call (usually via a run() call) self.timeout = timeout @@ -1055,29 +1064,25 @@ def __init__( self.set_match_function(match, match_stdout, match_stderr) self.set_diff_function(diff, diff_stdout, diff_stderr) self._dirlist = [] - self._preserve = {'pass_test': 0, 'fail_test': 0, 'no_result': 0} + self._preserve: Dict[str, Union[str, bool]] = { + 'pass_test': False, + 'fail_test': False, + 'no_result': False, + } preserve_value = os.environ.get('PRESERVE', False) if preserve_value not in [0, '0', 'False']: self._preserve['pass_test'] = os.environ['PRESERVE'] self._preserve['fail_test'] = os.environ['PRESERVE'] self._preserve['no_result'] = os.environ['PRESERVE'] else: - try: - self._preserve['pass_test'] = os.environ['PRESERVE_PASS'] - except KeyError: - pass - try: - self._preserve['fail_test'] = os.environ['PRESERVE_FAIL'] - except KeyError: - pass - try: - self._preserve['no_result'] = os.environ['PRESERVE_NO_RESULT'] - except KeyError: - pass + self._preserve['pass_test'] = os.environ.get('PRESERVE_PASS', False) + self._preserve['fail_test'] = os.environ.get('PRESERVE_FAIL', False) + self._preserve['no_result'] = os.environ.get('PRESERVE_NO_RESULT', False) self._stdout = [] self._stderr = [] - self.status = None + self.status: Optional[int] = None self.condition = 'no_result' + self.workdir: Optional[str] self.workdir_set(workdir) self.subdir(subdir) @@ -1144,8 +1149,8 @@ def cleanup(self, condition=None) -> None: list = self._dirlist[:] list.reverse() for dir in list: - self.writable(dir, 1) - shutil.rmtree(dir, ignore_errors=1) + self.writable(dir, True) + shutil.rmtree(dir, ignore_errors=True) self._dirlist = [] global _Cleanup @@ -1242,16 +1247,24 @@ def diff_stdout(self, a, b, *args, **kw): unified_diff = staticmethod(difflib.unified_diff) - def fail_test(self, condition: bool=True, function=None, skip: int=0, message=None) -> None: + def fail_test( + self, + condition: bool = True, + function: Optional[Callable] = None, + skip: int = 0, + message: str = "", + )-> None: """Cause the test to fail.""" if not condition: return self.condition = 'fail_test' - fail_test(self=self, - condition=condition, - function=function, - skip=skip, - message=message) + fail_test( + self=self, + condition=condition, + function=function, + skip=skip, + message=message + ) def interpreter_set(self, interpreter) -> None: """Set the program to be used to interpret the program @@ -1338,7 +1351,7 @@ def preserve(self, *conditions) -> None: if not conditions: conditions = ('pass_test', 'fail_test', 'no_result') for cond in conditions: - self._preserve[cond] = 1 + self._preserve[cond] = True def program_set(self, program) -> None: """Sets the executable program or script to be tested.""" @@ -1701,12 +1714,12 @@ def run(self, program=None, if self.verbose >= 2: write = sys.stdout.write write('============ STATUS: %d\n' % self.status) - out = self.stdout() + out = self.stdout() or "" if out or self.verbose >= 3: write(f'============ BEGIN STDOUT (len={len(out)}):\n') write(out) write('============ END STDOUT\n') - err = self.stderr() + err = self.stderr() or "" if err or self.verbose >= 3: write(f'============ BEGIN STDERR (len={len(err)})\n') write(err) @@ -1985,7 +1998,7 @@ def do_chmod(fname) -> None: # in the tree bottom-up, lest disabling read permission from # the top down get in the way of being able to get at lower # parts of the tree. - for dirpath, dirnames, filenames in os.walk(top, topdown=0): + for dirpath, dirnames, filenames in os.walk(top, topdown=False): for name in dirnames + filenames: do_chmod(os.path.join(dirpath, name)) do_chmod(top) @@ -2036,7 +2049,7 @@ def do_chmod(fname) -> None: do_chmod(top) else: do_chmod(top) - for dirpath, dirnames, filenames in os.walk(top, topdown=0): + for dirpath, dirnames, filenames in os.walk(top, topdown=False): for name in dirnames + filenames: do_chmod(os.path.join(dirpath, name)) @@ -2090,7 +2103,7 @@ def do_chmod(fname) -> None: # in the tree bottom-up, lest disabling execute permission from # the top down get in the way of being able to get at lower # parts of the tree. - for dirpath, dirnames, filenames in os.walk(top, topdown=0): + for dirpath, dirnames, filenames in os.walk(top, topdown=False): for name in dirnames + filenames: do_chmod(os.path.join(dirpath, name)) do_chmod(top) diff --git a/testing/framework/TestCmdTests.py b/testing/framework/TestCmdTests.py index a2d941d611..61c0c5da97 100644 --- a/testing/framework/TestCmdTests.py +++ b/testing/framework/TestCmdTests.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # # Copyright 2000-2010 Steven Knight +# # This module is free software, and you may redistribute it and/or modify # it under the same terms as Python itself, so long as this copyright message # and disclaimer are retained in their original form. @@ -15,6 +16,8 @@ # PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, # AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +# +# Python License: https://docs.python.org/3/license.html#psf-license """ Unit tests for the TestCmd.py module. diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index 993dc02ed7..f5bb084e68 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -1,3 +1,22 @@ +# Copyright 2000-2010 Steven Knight +# +# This module is free software, and you may redistribute it and/or modify +# it under the same terms as Python itself, so long as this copyright message +# and disclaimer are retained in their original form. +# +# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# +# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +# +# Python License: https://docs.python.org/3/license.html#psf-license + """ A testing framework for commands and scripts with commonly useful error handling @@ -39,26 +58,37 @@ test.must_contain_all_lines(output, lines, ['title', find]) + test.must_contain_single_instance_of(output, lines, ['title']) + test.must_contain_any_line(output, lines, ['title', find]) test.must_contain_exactly_lines(output, lines, ['title', find]) - test.must_exist('file1', ['file2', ...]) + test.must_exist('file1', ['file2', ...], ['message']) + + test.must_exist_one_of(files, ['message']) test.must_match('file', "expected contents\n") + test.must_match_file(file, golden_file, ['message', 'newline']) + test.must_not_be_writable('file1', ['file2', ...]) test.must_not_contain('file', 'banned text\n') test.must_not_contain_any_line(output, lines, ['title', find]) + test.must_not_contain_lines(lines, output, ['title', find]): + test.must_not_exist('file1', ['file2', ...]) + test.must_not_exist_any_of(files) + test.must_not_be_empty('file') test.run( options="options to be prepended to arguments", + arguments="string or list of arguments", stdout="expected standard output from the program", stderr="expected error output from the program", status=expected_status, @@ -80,22 +110,6 @@ """ -# Copyright 2000-2010 Steven Knight -# This module is free software, and you may redistribute it and/or modify -# it under the same terms as Python itself, so long as this copyright message -# and disclaimer are retained in their original form. -# -# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -# -# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - __author__ = "Steven Knight " __revision__ = "TestCommon.py 1.3.D001 2010/06/03 12:58:27 knight" __version__ = "1.3" @@ -107,6 +121,7 @@ import sysconfig from collections import UserList +from typing import Callable, List, Optional, Union from TestCmd import * from TestCmd import __all__ @@ -211,15 +226,14 @@ def separate_files(flist): missing.append(f) return existing, missing -def contains(seq, subseq, find) -> bool: - # Returns True or False. +def contains(seq, subseq, find: Optional[Callable] = None) -> bool: if find is None: return subseq in seq else: f = find(seq, subseq) return f not in (None, -1) and f is not False -def find_index(seq, subseq, find): +def find_index(seq, subseq, find: Optional[Callable] = None) -> Optional[int]: # Returns either an index of the subseq within the seq, or None. # Accepts a function find(seq, subseq), which returns an integer on success # and either: None, False, or -1, on failure. @@ -264,8 +278,14 @@ def __init__(self, **kw) -> None: super().__init__(**kw) os.chdir(self.workdir) - def options_arguments(self, options, arguments): + def options_arguments( + self, + options: Union[str, List[str]], + arguments: Union[str, List[str]], + ): """Merges the "options" keyword argument with the arguments.""" + # TODO: this *doesn't* split unless both are non-empty strings. + # Did we want to always split strings? if options: if arguments is None: return options @@ -282,14 +302,15 @@ def options_arguments(self, options, arguments): return arguments def must_be_writable(self, *files) -> None: - """Ensures that the specified file(s) exist and are writable. + """Ensure that the specified file(s) exist and are writable. + An individual file can be specified as a list of directory names, in which case the pathname will be constructed by concatenating them. Exits FAILED if any of the files does not exist or is not writable. """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - existing, missing = separate_files(files) + flist = [is_List(x) and os.path.join(*x) or x for x in files] + existing, missing = separate_files(flist) unwritable = [x for x in existing if not is_writable(x)] if missing: print("Missing files: `%s'" % "', `".join(missing)) @@ -297,17 +318,23 @@ def must_be_writable(self, *files) -> None: print("Unwritable files: `%s'" % "', `".join(unwritable)) self.fail_test(missing + unwritable) - def must_contain(self, file, required, mode: str='rb', find=None) -> None: + def must_contain( + self, + file: str, + required: str, + mode: str = 'rb', + find: Optional[Callable] = None, + ) -> None: """Ensures specified file contains the required text. Args: - file (string): name of file to search in. + file: name of file to search in. required (string): text to search for. For the default find function, type must match the return type from reading the file; current implementation will convert. - mode (string): file open mode. - find (func): optional custom search routine. Must take the - form "find(output, line)" non-negative integer on success + mode: file open mode. + find: optional custom search routine. Must take the + form ``find(output, line)``, returning non-negative integer on success and None, False, or -1, on failure. Calling test exits FAILED if search result is false @@ -326,7 +353,7 @@ def must_contain(self, file, required, mode: str='rb', find=None) -> None: print(file_contents) self.fail_test() - def must_contain_all(self, output, input, title=None, find=None) -> None: + def must_contain_all(self, output, input, title: str = "", find: Optional[Callable] = None)-> None: """Ensures that the specified output string (first argument) contains all of the specified input as a block (second argument). @@ -338,10 +365,10 @@ def must_contain_all(self, output, input, title=None, find=None) -> None: for lines in the output. """ if is_List(output): - output = os.newline.join(output) + output = '\n'.join(output) if not contains(output, input, find): - if title is None: + if not title: title = 'output' print(f'Missing expected input from {title}:') print(input) @@ -349,7 +376,7 @@ def must_contain_all(self, output, input, title=None, find=None) -> None: print(output) self.fail_test() - def must_contain_all_lines(self, output, lines, title=None, find=None) -> None: + def must_contain_all_lines(self, output, lines, title: str = "", find: Optional[Callable] = None) -> None: """Ensures that the specified output string (first argument) contains all of the specified lines (second argument). @@ -365,7 +392,7 @@ def must_contain_all_lines(self, output, lines, title=None, find=None) -> None: missing = [line for line in lines if not contains(output, line, find)] if missing: - if title is None: + if not title: title = 'output' sys.stdout.write(f"Missing expected lines from {title}:\n") for line in missing: @@ -374,13 +401,12 @@ def must_contain_all_lines(self, output, lines, title=None, find=None) -> None: sys.stdout.write(output) self.fail_test() - def must_contain_single_instance_of(self, output, lines, title=None) -> None: + def must_contain_single_instance_of(self, output, lines, title: str = "") -> None: """Ensures that the specified output string (first argument) contains one instance of the specified lines (second argument). An optional third argument can be used to describe the type of output being searched, and only shows up in failure output. - """ if is_List(output): output = '\n'.join(output) @@ -392,7 +418,7 @@ def must_contain_single_instance_of(self, output, lines, title=None) -> None: counts[line] = count if counts: - if title is None: + if not title: title = 'output' sys.stdout.write(f"Unexpected number of lines from {title}:\n") for line in counts: @@ -401,7 +427,7 @@ def must_contain_single_instance_of(self, output, lines, title=None) -> None: sys.stdout.write(output) self.fail_test() - def must_contain_any_line(self, output, lines, title=None, find=None) -> None: + def must_contain_any_line(self, output, lines, title: str = "", find: Optional[Callable] = None) -> None: """Ensures that the specified output string (first argument) contains at least one of the specified lines (second argument). @@ -416,7 +442,7 @@ def must_contain_any_line(self, output, lines, title=None, find=None) -> None: if contains(output, line, find): return - if title is None: + if not title: title = 'output' sys.stdout.write(f"Missing any expected line from {title}:\n") for line in lines: @@ -425,7 +451,7 @@ def must_contain_any_line(self, output, lines, title=None, find=None) -> None: sys.stdout.write(output) self.fail_test() - def must_contain_exactly_lines(self, output, expect, title=None, find=None) -> None: + def must_contain_exactly_lines(self, output, expect, title: str = "", find: Optional[Callable] = None) -> None: """Ensures that the specified output string (first argument) contains all of the lines in the expected string (second argument) with none left over. @@ -458,7 +484,7 @@ def must_contain_exactly_lines(self, output, expect, title=None, find=None) -> N # all lines were matched return - if title is None: + if not title: title = 'output' if missing: sys.stdout.write(f"Missing expected lines from {title}:\n") @@ -473,23 +499,23 @@ def must_contain_exactly_lines(self, output, expect, title=None, find=None) -> N sys.stdout.flush() self.fail_test() - def must_contain_lines(self, lines, output, title=None, find = None): + def must_contain_lines(self, lines, output, title: str = "", find: Optional[Callable] = None) -> None: # Deprecated; retain for backwards compatibility. - return self.must_contain_all_lines(output, lines, title, find) + self.must_contain_all_lines(output, lines, title, find) - def must_exist(self, *files) -> None: + def must_exist(self, *files, message: str = "") -> None: """Ensures that the specified file(s) must exist. An individual file be specified as a list of directory names, in which case the pathname will be constructed by concatenating them. Exits FAILED if any of the files does not exist. """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - missing = [x for x in files if not os.path.exists(x) and not os.path.islink(x) ] + flist = [is_List(x) and os.path.join(*x) or x for x in files] + missing = [x for x in flist if not os.path.exists(x) and not os.path.islink(x)] if missing: print("Missing files: `%s'" % "', `".join(missing)) - self.fail_test(missing) + self.fail_test(bool(missing), message=message) - def must_exist_one_of(self, files) -> None: + def must_exist_one_of(self, files, message: str = "") -> None: """Ensures that at least one of the specified file(s) exists. The filenames can be given as a list, where each entry may be a single path string, or a tuple of folder names and the final @@ -507,9 +533,17 @@ def must_exist_one_of(self, files) -> None: return missing.append(xpath) print("Missing one of: `%s'" % "', `".join(missing)) - self.fail_test(missing) - - def must_match(self, file, expect, mode: str = 'rb', match=None, message=None, newline=None): + self.fail_test(bool(missing), message=message) + + def must_match( + self, + file, + expect, + mode: str = 'rb', + match: Optional[Callable] = None, + message: str = "", + newline=None, + ): """Matches the contents of the specified file (first argument) against the expected contents (second argument). The expected contents are a list of lines or a string which will be split @@ -519,7 +553,10 @@ def must_match(self, file, expect, mode: str = 'rb', match=None, message=None, n if not match: match = self.match try: - self.fail_test(not match(to_str(file_contents), to_str(expect)), message=message) + self.fail_test( + not match(to_str(file_contents), to_str(expect)), + message=message, + ) except KeyboardInterrupt: raise except: @@ -527,7 +564,15 @@ def must_match(self, file, expect, mode: str = 'rb', match=None, message=None, n self.diff(expect, file_contents, 'contents ') raise - def must_match_file(self, file, golden_file, mode: str='rb', match=None, message=None, newline=None): + def must_match_file( + self, + file, + golden_file, + mode: str = 'rb', + match: Optional[Callable] = None, + message: str = "", + newline=None, + ) -> None: """Matches the contents of the specified file (first argument) against the expected contents (second argument). The expected contents are a list of lines or a string which will be split @@ -540,7 +585,10 @@ def must_match_file(self, file, golden_file, mode: str='rb', match=None, message match = self.match try: - self.fail_test(not match(to_str(file_contents), to_str(golden_file_contents)), message=message) + self.fail_test( + not match(to_str(file_contents), to_str(golden_file_contents)), + message=message, + ) except KeyboardInterrupt: raise except: @@ -561,7 +609,7 @@ def must_not_contain(self, file, banned, mode: str = 'rb', find = None) -> None: print(file_contents) self.fail_test() - def must_not_contain_any_line(self, output, lines, title=None, find=None) -> None: + def must_not_contain_any_line(self, output, lines, title: str = "", find: Optional[Callable] = None) -> None: """Ensures that the specified output string (first argument) does not contain any of the specified lines (second argument). @@ -578,7 +626,7 @@ def must_not_contain_any_line(self, output, lines, title=None, find=None) -> Non unexpected.append(line) if unexpected: - if title is None: + if not title: title = 'output' sys.stdout.write(f"Unexpected lines in {title}:\n") for line in unexpected: @@ -587,8 +635,8 @@ def must_not_contain_any_line(self, output, lines, title=None, find=None) -> Non sys.stdout.write(output) self.fail_test() - def must_not_contain_lines(self, lines, output, title=None, find=None): - return self.must_not_contain_any_line(output, lines, title, find) + def must_not_contain_lines(self, lines, output, title: str = "", find: Optional[Callable] = None) -> None: + self.must_not_contain_any_line(output, lines, title, find) def must_not_exist(self, *files) -> None: """Ensures that the specified file(s) must not exist. @@ -596,11 +644,11 @@ def must_not_exist(self, *files) -> None: which case the pathname will be constructed by concatenating them. Exits FAILED if any of the files exists. """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - existing = [x for x in files if os.path.exists(x) or os.path.islink(x)] + flist = [is_List(x) and os.path.join(*x) or x for x in files] + existing = [x for x in flist if os.path.exists(x) or os.path.islink(x)] if existing: print("Unexpected files exist: `%s'" % "', `".join(existing)) - self.fail_test(existing) + self.fail_test(bool(existing)) def must_not_exist_any_of(self, files) -> None: """Ensures that none of the specified file(s) exists. @@ -620,7 +668,7 @@ def must_not_exist_any_of(self, files) -> None: existing.append(xpath) if existing: print("Unexpected files exist: `%s'" % "', `".join(existing)) - self.fail_test(existing) + self.fail_test(bool(existing)) def must_not_be_empty(self, file) -> None: """Ensures that the specified file exists, and that it is not empty. @@ -646,8 +694,8 @@ def must_not_be_writable(self, *files) -> None: them. Exits FAILED if any of the files does not exist or is writable. """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - existing, missing = separate_files(files) + flist = [is_List(x) and os.path.join(*x) or x for x in files] + existing, missing = separate_files(flist) writable = [file for file in existing if is_writable(file)] if missing: print("Missing files: `%s'" % "', `".join(missing)) @@ -716,50 +764,63 @@ def start(self, program = None, sys.stderr.write(f'Exception trying to execute: {cmd_args}\n') raise e - def finish(self, popen, stdout = None, stderr: str = '', status: int = 0, **kw) -> None: - """ - Finishes and waits for the process being run under control of - the specified popen argument. Additional arguments are similar - to those of the run() method: - stdout The expected standard output from - the command. A value of None means - don't test standard output. + def finish( + self, + popen, + stdout: Optional[str] = None, + stderr: Optional[str] = '', + status: Optional[int] = 0, + **kw, + ) -> None: + """Finish and wait for the process being run. - stderr The expected error output from - the command. A value of None means - don't test error output. + The *popen* argument describes a ``Popen`` object controlling + the process. - status The expected exit status from the - command. A value of None means don't - test exit status. + The default behavior is to expect a successful exit, to not test + standard output, and to expect error output to be empty. + + Only arguments extending :meth:`TestCmd.finish` are shown. + + Args: + stdout: The expected standard output from the command. + A value of ``None`` means don't test standard output. + stderr: The expected error output from the command. + A value of ``None`` means don't test error output. + status: The expected exit status from the command. + A value of ``None`` means don't test exit status. """ super().finish(popen, **kw) match = kw.get('match', self.match) - self._complete(self.stdout(), stdout, - self.stderr(), stderr, status, match) - - def run(self, options = None, arguments = None, - stdout = None, stderr: str = '', status: int = 0, **kw) -> None: + self._complete(self.stdout(), stdout, self.stderr(), stderr, status, match) + + + def run( + self, + options=None, + arguments=None, + stdout: Optional[str] = None, + stderr: Optional[str] = '', + status: Optional[int] = 0, + **kw, + ) -> None: """Runs the program under test, checking that the test succeeded. - The parameters are the same as the base TestCmd.run() method, - with the addition of: + The default behavior is to expect a successful exit, not test + standard output, and expects error output to be empty. - options Extra options that get prepended to the beginning - of the arguments. + Only arguments extending :meth:`TestCmd.run` are shown. - stdout The expected standard output from - the command. A value of None means - don't test standard output. - - stderr The expected error output from - the command. A value of None means - don't test error output. - - status The expected exit status from the - command. A value of None means don't - test exit status. + Args: + options: Extra options that get prepended to the beginning + of the arguments. + stdout: The expected standard output from the command. + A value of ``None`` means don't test standard output. + stderr: The expected error output from the command. + A value of ``None`` means don't test error output. + status: The expected exit status from the command. + A value of ``None`` means don't test exit status. By default, this expects a successful exit (status = 0), does not test standard output (stdout = None), and expects that error @@ -767,8 +828,7 @@ def run(self, options = None, arguments = None, """ kw['arguments'] = self.options_arguments(options, arguments) try: - match = kw['match'] - del kw['match'] + match = kw.pop('match') except KeyError: match = self.match super().run(**kw) diff --git a/testing/framework/TestCommonTests.py b/testing/framework/TestCommonTests.py index 014a018766..3f7d3fd2e4 100644 --- a/testing/framework/TestCommonTests.py +++ b/testing/framework/TestCommonTests.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -""" -Unit tests for the TestCommon.py module. -""" - +# # Copyright 2000-2010 Steven Knight +# # This module is free software, and you may redistribute it and/or modify # it under the same terms as Python itself, so long as this copyright message # and disclaimer are retained in their original form. @@ -18,6 +16,12 @@ # PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, # AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +# +# Python License: https://docs.python.org/3/license.html#psf-license + +""" +Unit tests for the TestCommon.py module. +""" import os import re @@ -466,6 +470,7 @@ def test_find(self) -> None: def re_search(output, line): return re.compile(line, re.S).search(output) + test.must_contain_all_lines(output, lines, find=re_search) test.pass_test() @@ -964,6 +969,22 @@ def test_failure(self) -> None: stderr = run_env.stderr() assert stderr.find("FAILED") != -1, stderr + def test_failure_message(self) -> None: + """Test must_exist(): failure with extra message""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_exist('file1', message="Extra Info") + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Missing files: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("Extra Info") != -1, stderr + def test_file_specified_as_list(self) -> None: """Test must_exist(): file specified as list""" run_env = self.run_env @@ -1034,6 +1055,22 @@ def test_failure(self) -> None: stderr = run_env.stderr() assert stderr.find("FAILED") != -1, stderr + def test_failure_message(self) -> None: + """Test must_exist_one_of(): failure with extra message""" + run_env = self.run_env + + script = lstrip("""\ + from TestCommon import TestCommon + tc = TestCommon(workdir='') + tc.must_exist_one_of(['file1'], message="Extra Info") + tc.pass_test() + """) + run_env.run(program=sys.executable, stdin=script) + stdout = run_env.stdout() + assert stdout == "Missing one of: `file1'\n", stdout + stderr = run_env.stderr() + assert stderr.find("Extra Info") != -1, stderr + def test_files_specified_as_list(self) -> None: """Test must_exist_one_of(): files specified as list""" run_env = self.run_env @@ -1077,8 +1114,7 @@ def test_file_given_as_list(self) -> None: tc = TestCommon(workdir='') tc.subdir('sub') tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_exist_one_of(['file2', - ['sub', 'file1']]) + tc.must_exist_one_of(['file2', ['sub', 'file1']]) tc.pass_test() """) run_env.run(program=sys.executable, stdin=script) @@ -1096,8 +1132,7 @@ def test_file_given_as_sequence(self) -> None: tc = TestCommon(workdir='') tc.subdir('sub') tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_exist_one_of(['file2', - ('sub', 'file1')]) + tc.must_exist_one_of(['file2', ('sub', 'file1')]) tc.pass_test() """) run_env.run(program=sys.executable, stdin=script) @@ -1943,49 +1978,44 @@ def raise_exception(*args, **kw): fr"""Exception trying to execute: \[{re.escape(repr(sys.executable))}, '[^']*pass'\] Traceback \(most recent call last\): File "", line \d+, in (\?|) - File "[^"]+TestCommon.py", line \d+, in run - super\(\).run\(\*\*kw\) - File "[^"]+TestCmd.py", line \d+, in run - p = self.start\(program=program, -(?:\s*\^*\s)? File \"[^\"]+TestCommon.py\", line \d+, in start + File "[^"]+TestCommon\.py", line \d+, in run + super\(\)\.run\(\*\*kw\) + File "[^"]+TestCmd\.py", line \d+, in run + p = self\.start\(program=program, +(?:\s*\^*\s)? File \"[^\"]+TestCommon\.py\", line \d+, in start raise e - File "[^"]+TestCommon.py", line \d+, in start - return super\(\).start\(program, interpreter, arguments, + File "[^"]+TestCommon\.py", line \d+, in start + return super\(\)\.start\(program, interpreter, arguments, (?:\s*\^*\s)? File \"\", line \d+, in raise_exception TypeError: forced TypeError """) - expect_stderr = re.compile(expect_stderr, re.M) - # TODO: Python 3.13+ expanded error msgs again. This doesn't work yet. + # Python 3.13+ expanded error msgs again, not in a way we can easily + # accomodate with the other regex. expect_enhanced_stderr = lstrip( fr"""Exception trying to execute: \[{re.escape(repr(sys.executable))}, '[^']*pass'\] -Traceback (most recent call last): +Traceback \(most recent call last\): File "", line \d+, in (\?|) - File "[^"]+TestCommon.py", line \d+, in run - super\(\).run\(\*\*kw\) - ~~~~~~~~~~~^^^^^^ - File "[^"]+TestCmd.py", line \d+, in run - p = self.start\(program=program, - ~~~~~~~~~~^^^^^^^^^^^^^^^^^ + File "[^"]+TestCommon\.py", line \d+, in run + super\(\)\.run\(\*\*kw\) +(?:\s*[~\^]*\s*)?File "[^"]+TestCmd\.py", line \d+, in run + p = self\.start\(program=program, interpreter=interpreter, - ^^^^^^^^^^^^^^^^^^^^^^^^ - ...<2 lines>... + \.\.\.<2 lines>\.\.\. timeout=timeout, - ^^^^^^^^^^^^^^^^ stdin=stdin\) - ^^^^^^^^^^^^ -(?:\s*\^*\s)? File \"[^\"]+TestCommon.py\", line \d+, in start +(?:\s*[~\^]*\s*)?File \"[^\"]+TestCommon\.py\", line \d+, in start raise e - File "[^"]+TestCommon.py", line \d+, in start - return super\(\).start\(program, interpreter, arguments, - ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - universal_newlines, \*\*kw\) - ^^^^^^^^^^^^^^^^^^^^^^^^^ -(?:\s*\^*\s)? File \"\", line \d+, in raise_exception + File "[^"]+TestCommon\.py", line \d+, in start + return super\(\)\.start\(program, interpreter, arguments, +(?:\s*[~\^]*\s*)?universal_newlines, \*\*kw\) +(?:\s*[~\^]*\s*)?File \"\", line \d+, in raise_exception TypeError: forced TypeError """) - expect_enhanced_stderr = re.compile(expect_enhanced_stderr, re.M) - + if sys.version_info[:2] > (3, 12): + expect_stderr = re.compile(expect_enhanced_stderr, re.M) + else: + expect_stderr = re.compile(expect_stderr, re.M) self.run_execution_test(script, expect_stdout, expect_stderr) def test_ignore_stderr(self) -> None: diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 7360466d0f..243be753a4 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -42,6 +42,7 @@ import subprocess as sp import zipfile from collections import namedtuple +from typing import Optional, Tuple from TestCommon import * from TestCommon import __all__, _python_ @@ -573,8 +574,9 @@ def err_out(): self.pass_test() else: # test failed; have to do this by hand... + stdout = self.stdout() or "" print(self.banner('STDOUT ')) - print(self.stdout()) + print(stdout) print(self.diff(warning, stderr, 'STDERR ')) self.fail_test() @@ -728,8 +730,9 @@ def paths(self, patterns): result.extend(sorted(glob.glob(p))) return result - def get_sconsignname(self): + def get_sconsignname(self) -> str: """Get the scons database name used, and return both the prefix and full filename. + if the user left the options defaulted AND the default algorithm set by SCons is md5, then set the database name to be the special default name @@ -739,6 +742,11 @@ def get_sconsignname(self): Returns: a pair containing: the current dbname, the dbname.dblite filename + + TODO: docstring is not truthful about returning "both" - but which to fix? + Say it returns just basename, or return both? + TODO: has no way to account for an ``SConsignFile()`` call which might assign + a different dbname. Document that it's only useful for hash testing? """ hash_format = get_hash_format() current_hash_algorithm = get_current_hash_algorithm_used() @@ -749,11 +757,17 @@ def get_sconsignname(self): return database_prefix - def unlink_sconsignfile(self, name: str='.sconsign.dblite') -> None: + def unlink_sconsignfile(self, name: str = '.sconsign.dblite') -> None: """Delete the sconsign file. + Provides a hook to do special things for the sconsign DB, + although currently it just calls unlink. + Args: name: expected name of sconsign file + + TODO: deal with suffix if :meth:`getsconsignname` does not provide it. + How do we know, since multiple formats are allowed? """ return self.unlink(name) @@ -851,7 +865,7 @@ def java_where_includes(self, version=None): result.append(os.path.join(d, 'linux')) return result - def java_where_java_home(self, version=None) -> str: + def java_where_java_home(self, version=None) -> Optional[str]: """ Find path to what would be JAVA_HOME. SCons does not read JAVA_HOME from the environment, so deduce it. @@ -905,6 +919,7 @@ def java_where_java_home(self, version=None) -> str: "Could not run Java: unable to detect valid JAVA_HOME, skipping test.\n", from_fw=True, ) + return None def java_mac_check(self, where_java_bin, java_bin_name) -> None: """Extra check for Java on MacOS. @@ -965,7 +980,7 @@ def java_where_java(self, version=None) -> str: return where_java - def java_where_javac(self, version=None) -> str: + def java_where_javac(self, version=None) -> Tuple[str, str]: """ Find java compiler. Args: @@ -989,21 +1004,25 @@ def java_where_javac(self, version=None) -> str: stderr=None, status=None) # Note recent versions output version info to stdout instead of stderr + stdout = self.stdout() or "" + stderr = self.stderr() or "" if version: verf = f'javac {version}' - if self.stderr().find(verf) == -1 and self.stdout().find(verf) == -1: + if stderr.find(verf) == -1 and stdout.find(verf) == -1: fmt = "Could not find javac for Java version %s, skipping test(s).\n" self.skip_test(fmt % version, from_fw=True) else: version_re = r'javac (\d*\.*\d)' - m = re.search(version_re, self.stderr()) + m = re.search(version_re, stderr) if not m: - m = re.search(version_re, self.stdout()) + m = re.search(version_re, stdout) if m: version = m.group(1) self.javac_is_gcj = False - elif self.stderr().find('gcj') != -1: + return where_javac, version + + if stderr.find('gcj') != -1: version = '1.2' self.javac_is_gcj = True else: @@ -1167,7 +1186,7 @@ def Qt_dummy_installation(self, dir: str='qt') -> None: def Qt_create_SConstruct(self, place, qt_tool: str='qt3') -> None: if isinstance(place, list): - place = test.workpath(*place) + place = self.workpath(*place) var_prefix=qt_tool.upper() self.write(place, f"""\ @@ -1366,10 +1385,11 @@ def checkConfigureLogAndStdout(self, checks, if doCheckStdout: exp_stdout = self.wrap_stdout(".*", rdstr) - if not self.match_re_dotall(self.stdout(), exp_stdout): + stdout = self.stdout() or "" + if not self.match_re_dotall(stdout, exp_stdout): print("Unexpected stdout: ") print("-----------------------------------------------------") - print(repr(self.stdout())) + print(repr(stdout)) print("-----------------------------------------------------") print(repr(exp_stdout)) print("-----------------------------------------------------") @@ -1522,10 +1542,11 @@ def checkLogAndStdout(self, checks, results, cached, if doCheckStdout: exp_stdout = self.wrap_stdout(".*", rdstr) - if not self.match_re_dotall(self.stdout(), exp_stdout): + stdout = self.stdout() or "" + if not self.match_re_dotall(stdout, exp_stdout): print("Unexpected stdout: ") print("----Actual-------------------------------------------") - print(repr(self.stdout())) + print(repr(stdout)) print("----Expected-----------------------------------------") print(repr(exp_stdout)) print("-----------------------------------------------------") @@ -1610,7 +1631,8 @@ def venv_path(): else: print("False") """) - incpath, libpath, libname, python_h = self.stdout().strip().split('\n') + stdout = self.stdout() or "" + incpath, libpath, libname, python_h = stdout.strip().split('\n') if python_h == "False" and python_h_required: self.skip_test('Can not find required "Python.h", skipping test.\n', from_fw=True) @@ -1737,7 +1759,7 @@ def __init__(self, *args, **kw) -> None: directory containing the executing script to the temporary working directory. """ - self.variables = kw.get('variables') + self.variables: dict = kw.get('variables') default_calibrate_variables = [] if self.variables is not None: for variable, value in self.variables.items(): @@ -1872,8 +1894,9 @@ def startup(self, *args, **kw) -> None: # the full and null builds. kw['status'] = None self.run(*args, **kw) - sys.stdout.write(self.stdout()) - stats = self.collect_stats(self.stdout()) + stdout = self.stdout() or "" + sys.stdout.write(stdout) + stats = self.collect_stats(stdout) # Delete the time-commands, since no commands are ever # executed on the help run and it is (or should be) always 0.0. del stats['time-commands'] @@ -1885,8 +1908,9 @@ def full(self, *args, **kw) -> None: """ self.add_timing_options(kw) self.run(*args, **kw) - sys.stdout.write(self.stdout()) - stats = self.collect_stats(self.stdout()) + stdout = self.stdout() or "" + sys.stdout.write(stdout) + stats = self.collect_stats(stdout) self.report_traces('full', stats) self.trace('full-memory', 'initial', **stats['memory-initial']) self.trace('full-memory', 'prebuild', **stats['memory-prebuild']) @@ -1917,8 +1941,9 @@ def null(self, *args, **kw) -> None: # SConscript:/private/var/folders/ng/48pttrpj239fw5rmm3x65pxr0000gn/T/testcmd.12081.pk1bv5i5/SConstruct took 533.646 ms read_str = 'SConscript:.*\n' self.up_to_date(arguments='.', read_str=read_str, **kw) - sys.stdout.write(self.stdout()) - stats = self.collect_stats(self.stdout()) + stdout = self.stdout() or "" + sys.stdout.write(stdout) + stats = self.collect_stats(stdout) # time-commands should always be 0.0 on a null build, because # no commands should be executed. Remove it from the stats # so we don't trace it, but only if it *is* 0 so that we'll From 50bee2e7edcfd46dc21f28b131c6f1065ac44dcc Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 11 Sep 2024 12:12:14 -0600 Subject: [PATCH 125/386] Fix some AddOption issues The optparse add_option method supports an additional calling style that is not directly described in SCons docs, but is included by reference ("see the optparse documentation for details"): it takes a single arg consisting of a premade option object. Because the optparse code detects that case based on seeing zero kwargs, and we always add at least one (default=) that would fail for AddOption. Fix for consistency, but don't advertise it further: not addewd to manpage synoposis/description. Signed-off-by: Mats Wichmann --- CHANGES.txt | 8 ++++++++ RELEASE.txt | 5 +++++ SCons/Script/Main.py | 36 ++++++++++++++++++++++++------------ SCons/Script/SConsOptions.py | 20 +++++++++++++------- test/AddOption/basic.py | 9 +++++++++ 5 files changed, 59 insertions(+), 19 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1ac704339e..4ecb32c7fd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -23,6 +23,14 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER the test fails. The rest is cleanup and type annotations. Be more careful that the returns from stderr() and stdout(), which *can* return None, are not used without checking. + - The optparse add_option method supports an additional calling style + that is not directly described in SCons docs, but is included + by reference ("see the optparse documentation for details"): + a single arg consisting of a premade option object. Because optparse + detects that case based on seeing zero kwargs and we always + added at least one (default=) that would fail for AddOption. Fix + for consistency, but don't advertise it further - not addewd to + manpage synoposis/description. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 196103cfbc..05bdf7b813 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -44,6 +44,11 @@ IMPROVEMENTS documentation: performance improvements (describe the circumstances under which they would be observed), or major code cleanups +- For consistency with the optparse "add_option" method, AddOption accepts + an SConsOption object as a single argument (this failed previouly). + Calling AddOption with the full set of arguments (option names and + attributes) to set up the option is still the recommended approach. + PACKAGING --------- diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 77d80cb293..04b420a3bf 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -34,6 +34,7 @@ import SCons.compat import importlib.util +import optparse import os import re import sys @@ -59,8 +60,7 @@ import SCons.Util import SCons.Warnings import SCons.Script.Interactive -if TYPE_CHECKING: - from SCons.Script import SConsOption +from .SConsOptions import SConsOption from SCons.Util.stats import count_stats, memory_stats, time_stats, ENABLE_JSON, write_scons_stats_file, JSON_OUTPUT_FILE from SCons import __version__ as SConsVersion @@ -508,29 +508,41 @@ def __getattr__(self, attr): # TODO: to quiet checkers, FakeOptionParser should also define # raise_exception_on_error, preserve_unknown_options, largs and parse_args - def add_local_option(self, *args, **kw) -> "SConsOption": + def add_local_option(self, *args, **kw) -> SConsOption: pass OptionsParser = FakeOptionParser() -def AddOption(*args, settable: bool = False, **kw) -> "SConsOption": +def AddOption(*args, **kw) -> SConsOption: """Add a local option to the option parser - Public API. - If the *settable* parameter is true, the option will be included in the - list of settable options; all other keyword arguments are passed on to - :meth:`~SCons.Script.SConsOptions.SConsOptionParser.add_local_option`. + If the SCons-specific *settable* kwarg is true (default ``False``), + the option will allow calling :func:``SetOption`. .. versionchanged:: 4.8.0 The *settable* parameter added to allow including the new option - to the table of options eligible to use :func:`SetOption`. - + in the table of options eligible to use :func:`SetOption`. """ + settable = kw.get('settable', False) + if len(args) == 1 and isinstance(args[0], SConsOption): + # If they passed an SConsOption object, ignore kw - the underlying + # add_option method relies on seeing zero kwargs to recognize this. + # Since we don't support an omitted default, overrwrite optparse's + # marker to get the same effect as setting it in kw otherwise. + optobj = args[0] + if optobj.default is optparse.NO_DEFAULT: + optobj.default = None + # make sure settable attribute exists; positive setting wins + attr_settable = getattr(optobj, "settable") + if attr_settable is None or settable > attr_settable: + optobj.settable = settable + return OptionsParser.add_local_option(*args) + if 'default' not in kw: kw['default'] = None - kw['settable'] = settable - result = OptionsParser.add_local_option(*args, **kw) - return result + kw['settable'] = settable # just to make sure it gets set + return OptionsParser.add_local_option(*args, **kw) def GetOption(name: str): """Get the value from an option - Public API.""" diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index 202640a934..c9c7aa0542 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -240,6 +240,11 @@ class SConsOption(optparse.Option): New function :meth:`_check_nargs_optional` implements the ``nargs=?`` syntax from :mod:`argparse`, and is added to the ``CHECK_METHODS`` list. Overridden :meth:`convert_value` supports this usage. + + .. versionchanged:: NEXT_RELEASE + The *settable* attribute is added to ``ATTRS``, allowing it to be + set in the option. A parameter to mark the option settable was added + in 4.8.0, but was not initially made part of the option object itself. """ # can uncomment to have a place to trap SConsOption creation for debugging: # def __init__(self, *args, **kwargs): @@ -274,10 +279,11 @@ def _check_nargs_optional(self) -> None: fmt = "option %s: nargs='?' is incompatible with short options" raise SCons.Errors.UserError(fmt % self._short_opts[0]) + ATTRS = optparse.Option.ATTRS + ['settable'] # added for SCons CHECK_METHODS = optparse.Option.CHECK_METHODS if CHECK_METHODS is None: CHECK_METHODS = [] - CHECK_METHODS = CHECK_METHODS + [_check_nargs_optional] # added for SCons + CHECK_METHODS += [_check_nargs_optional] # added for SCons CONST_ACTIONS = optparse.Option.CONST_ACTIONS + optparse.Option.TYPED_ACTIONS @@ -481,19 +487,19 @@ def add_local_option(self, *args, **kw) -> SConsOption: by default "local" (project-added) options are not eligible for :func:`~SCons.Script.Main.SetOption` calls. - .. versionchanged:: 4.8.0 - Added special handling of *settable*. - + .. versionchanged:: NEXT_VERSION + If the option's *settable* attribute is true, it is added to + the :attr:`SConsValues.settable` list. *settable* handling was + added in 4.8.0, but was not made an option attribute at the time. """ group: SConsOptionGroup try: group = self.local_option_group except AttributeError: group = SConsOptionGroup(self, 'Local Options') - group = self.add_option_group(group) + self.add_option_group(group) self.local_option_group = group - settable = kw.pop('settable') # this gives us an SConsOption due to the setting of self.option_class result = group.add_option(*args, **kw) if result: @@ -508,7 +514,7 @@ def add_local_option(self, *args, **kw) -> SConsOption: # TODO: what if dest is None? setattr(self.values.__defaults__, result.dest, result.default) self.reparse_local_options() - if settable: + if result.settable: SConsValues.settable.append(result.dest) return result diff --git a/test/AddOption/basic.py b/test/AddOption/basic.py index 9fc3c4d9f5..83297a8277 100644 --- a/test/AddOption/basic.py +++ b/test/AddOption/basic.py @@ -33,6 +33,8 @@ test = TestSCons.TestSCons() test.write('SConstruct', """\ +from SCons.Script.SConsOptions import SConsOption + DefaultEnvironment(tools=[]) env = Environment(tools=[]) AddOption( @@ -55,6 +57,9 @@ action="store_true", help="try SetOption of 'prefix' to '/opt/share'" ) +z_opt = SConsOption("--zcount", type="int", nargs=1, settable=True) +AddOption(z_opt) + f = GetOption('force') if f: f = "True" @@ -63,6 +68,8 @@ if GetOption('set'): SetOption('prefix', '/opt/share') print(GetOption('prefix')) +if GetOption('zcount'): + print(GetOption('zcount')) """) test.run('-Q -q .', stdout="None\nNone\n") @@ -73,6 +80,8 @@ test.run('-Q -q . --set', stdout="None\nNone\n/opt/share\n") # but the "command line wins" rule is not violated test.run('-Q -q . --set --prefix=/home/foo', stdout="None\n/home/foo\n/home/foo\n") +# also try in case we pass a premade option object to AddOption +test.run('-Q -q . --zcount=22', stdout="None\nNone\n22\n") test.pass_test() From 9653670a0d2dcdfcc7008c158850db1f85185c0a Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 14 Sep 2024 07:03:10 -0600 Subject: [PATCH 126/386] Update some doc for OverrideEnvironment Signed-off-by: Mats Wichmann --- CHANGES.txt | 15 ++++++--------- SCons/Environment.py | 12 ++++++------ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index dc466fd3af..2592db81cc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -13,15 +13,6 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release. RELEASE VERSION/DATE TO BE FILLED IN LATER From Mats Wichmann: - - Override envirionments, created when giving construction environment - keyword arguments to Builder calls (or manually, through the undocumented - Override method), were modified not to "leak" on item deletion. The item - will now not be deleted from the base environment. Methods of regular - environments modified to (mostly) not directly access the _dict attribute - which stores the construction variables - in case they're called via - proxying from an OverrideEnv, which does not have such an attribute - (though for completeness, it now pretends to). Let the dictionary-like - methods handle access/update instead. - PackageVariable now does what the documentation always said it does if the variable is used on the command line with one of the enabling string as the value: the variable's default value is produced (previously @@ -32,6 +23,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER the test fails. The rest is cleanup and type annotations. Be more careful that the returns from stderr() and stdout(), which *can* return None, are not used without checking. + - Override envirionments, created when giving construction environment + keyword arguments to Builder calls (or manually, through the undocumented + Override method), were modified not to "leak" on item deletion. The item + will now not be deleted from the base environment. Override Environments + now also pretend to have a _dict attribute so that regular environment + methods don't have a problem if passed an OE instance. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/SCons/Environment.py b/SCons/Environment.py index 77966f1cb1..72572db94a 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -2619,12 +2619,12 @@ def __init__(self, subject, overrides: Optional[dict] = None) -> None: # Methods that make this class act like a proxy. def __getattr__(self, name): - # Proxied environment methods don't know they could be called with - # us as 'self' and may access the _data consvar dict directly. - # And they shouldn't *have* to know, so we need to pretend to have one, - # and not serve up the one from the subject, or it will miss the - # overridden values (and possibly modify the base). Use ourselves - # and hope the dict-like methods below are sufficient. + # Proxied environment methods don't know (nor should they have to) that + # they could be called with an OverrideEnvironment as 'self' and may + # access the _dict construction variable dict directly, so we need to + # pretend to have one, and not serve up the one from the subject, or it + # will miss the overridden values (and possibly modify the base). Use + # ourselves and hope the dict-like methods below are sufficient. if name == '_dict': return self From f72ce398663eedd1e57ac3483781a5a5fc390752 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 14 Apr 2022 08:41:45 -0600 Subject: [PATCH 127/386] Update docs and usage of TEMPFILE/TempFileMunge tempfiles are now cleaned up via registering a cleanups with atexit(), instead of trying to squeeze removing the file into the command line. On Windows that caused the file to get deleted too early (did not work well with interactive mode), and on Linux it didn't remove the file at all. The Platform test expected to be able to read the tempfilename as the last argument of the "command", but this is no longer provided as the "rm filename" is no longer added, so now it has to chop off the prefix from the command-file argument to get the filename. Unrelatedly, two syntax warnings that turn up in the test output where some TeX syntax was listed in a docstring in a test are fixed by making that a raw string - got tired of seeing these. Fixes #4595 Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 + RELEASE.txt | 2 + SCons/Platform/Platform.xml | 57 +++++++++++++++++++++----- SCons/Platform/PlatformTests.py | 35 +++++++++------- SCons/Platform/__init__.py | 44 ++++++++++---------- test/TEX/glossaries.py | 2 +- test/TEX/subdir_variantdir_include2.py | 2 +- 7 files changed, 96 insertions(+), 48 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1ac704339e..2964a1cdc4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -23,6 +23,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER the test fails. The rest is cleanup and type annotations. Be more careful that the returns from stderr() and stdout(), which *can* return None, are not used without checking. + - Temporary files created by TempFileMunge() are now cleaned up on + scons exit, instead of at the time they're used. Fixes #4595. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 196103cfbc..fcc4f46a6f 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -36,6 +36,8 @@ FIXES if the variable is used on the command line with one of the enabling string as the value: the variable's default value is produced (previously it always produced True in this case). +- Temporary files created by TempFileMunge() are now cleaned up on + scons exit, instead of at the time they're used. Fixes #4595. IMPROVEMENTS ------------ diff --git a/SCons/Platform/Platform.xml b/SCons/Platform/Platform.xml index 0beafd1fdc..809cd9c431 100644 --- a/SCons/Platform/Platform.xml +++ b/SCons/Platform/Platform.xml @@ -298,16 +298,41 @@ The suffix used for shared object file names. -A callable object used to handle overly long command line strings, -since operations which call out to a shell will fail -if the line is longer than the shell can accept. -This tends to particularly impact linking. -The tempfile object stores the command line in a temporary -file in the appropriate format, and returns -an alternate command line so the invoked tool will make -use of the contents of the temporary file. -If you need to replace the default tempfile object, -the callable should take into account the settings of +Holds a callable object which will be invoked to transform long +command lines (string or list) into an alternate form. +Length limits on various operating systems +may cause long command lines to fail when calling out to +a shell to run the command. +Most often affects linking, when there are many object files and/or +libraries to be linked, but may also affect other +compilation steps which have many arguments. +&cv-TEMPFILE; is not called directly, +but rather is typically embedded in another +&consvar;, to be expanded when used. Example: + + + +env["TEMPFILE"] = TempFileMunge +env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES', '$LINKCOMSTR')}" + + + +The SCons default value for &cv-TEMPFILE;, +TempFileMunge, +performs command substitution on the passed command line, +calculates whether modification is needed, +then puts all but the first word (assumed to be the command name) +of the resulting list into a temporary file +(sometimes called a response file or command file), +and returns a new command line consisting of the +the command name and an appropriately formatted reference +to the temporary file. + + + +A replacement for the default tempfile object would need +to do fundamentally the same thing, including taking into account +the values of &cv-link-MAXLINELENGTH;, &cv-link-TEMPFILEPREFIX;, &cv-link-TEMPFILESUFFIX;, @@ -315,6 +340,11 @@ the callable should take into account the settings of &cv-link-TEMPFILEDIR; and &cv-link-TEMPFILEARGESCFUNC;. +If a particular use case requires a different transformation +than the default, it is recommended to copy the mechanism and +define a new &consvar; and rewrite the relevant *COM +variable(s) to use it, to avoid possibly disrupting existing uses +of &cv-TEMPFILE;. @@ -324,6 +354,8 @@ and The prefix for the name of the temporary file used to store command lines exceeding &cv-link-MAXLINELENGTH;. +The prefix must include the compiler syntax to actually +include and process the file. The default prefix is '@', which works for the &MSVC; and GNU toolchains on Windows. Set this appropriately for other toolchains, @@ -338,7 +370,7 @@ or '-via' for ARM toolchain. The suffix for the name of the temporary file used to store command lines exceeding &cv-link-MAXLINELENGTH;. -The suffix should include the dot ('.') if one is wanted as +The suffix should include the dot ('.') if one is needed as it will not be added automatically. The default is .lnk. @@ -363,6 +395,9 @@ Note this value is used literally and not expanded by the subst logic. The directory to create the long-lines temporary file in. +If unset, some suitable default should be chosen. +The default tempfile object lets the &Python; +tempfile module choose. diff --git a/SCons/Platform/PlatformTests.py b/SCons/Platform/PlatformTests.py index c786aa7d8d..691aed2598 100644 --- a/SCons/Platform/PlatformTests.py +++ b/SCons/Platform/PlatformTests.py @@ -207,9 +207,7 @@ def test_MAXLINELENGTH(self) -> None: assert cmd != defined_cmd, cmd def test_TEMPFILEARGJOINBYTE(self) -> None: - """ - Test argument join byte TEMPFILEARGJOINBYTE - """ + """Test argument join byte TEMPFILEARGJOINBYTE.""" # Init class with cmd, such that the fully expanded # string reads "a test command line". @@ -232,19 +230,24 @@ def test_TEMPFILEARGJOINBYTE(self) -> None: SCons.Action.print_actions = 0 env['MAXLINELENGTH'] = len(expanded_cmd)-1 cmd = t(None, None, env, 0) - # print("CMD is:%s"%cmd) + # print(f"[CMD is: {cmd}]") - with open(cmd[-1],'rb') as f: + if cmd[-1].startswith('@'): + tempfile = cmd[-1][1:] + else: + tempfile = cmd[-1] + with open(tempfile, 'rb') as f: file_content = f.read() - # print("Content is:[%s]"%file_content) + # print(f"[Content of {tempfile} is:{file_content}]") # ...and restoring its setting. SCons.Action.print_actions = old_actions - assert file_content != env['TEMPFILEARGJOINBYTE'].join(['test','command','line']) + assert file_content != bytearray( + env['TEMPFILEARGJOINBYTE'].join(['test', 'command', 'line']), + encoding='utf-8', + ) def test_TEMPFILEARGESCFUNC(self) -> None: - """ - Test a custom TEMPFILEARGESCFUNC - """ + """Test a custom TEMPFILEARGESCFUNC.""" def _tempfile_arg_esc_func(arg): return str(arg).replace("line", "newarg") @@ -262,12 +265,16 @@ def _tempfile_arg_esc_func(arg): SCons.Action.print_actions = 0 env['TEMPFILEARGESCFUNC'] = _tempfile_arg_esc_func cmd = t(None, None, env, 0) - # print("CMD is: %s"%cmd) + # print(f"[CMD is: {cmd}]") - with open(cmd[-1], 'rb') as f: + if cmd[-1].startswith('@'): + tempfile = cmd[-1][1:] + else: + tempfile = cmd[-1] + with open(tempfile, 'rb') as f: file_content = f.read() - # print("Content is:[%s]"%file_content) - # # ...and restoring its setting. + # print(f"[Content of {tempfile} is:{file_content}]") + # ...and restoring its setting. SCons.Action.print_actions = old_actions assert b"newarg" in file_content diff --git a/SCons/Platform/__init__.py b/SCons/Platform/__init__.py index 77eea5c099..aec1a8989a 100644 --- a/SCons/Platform/__init__.py +++ b/SCons/Platform/__init__.py @@ -42,6 +42,7 @@ import SCons.compat +import atexit import importlib import os import sys @@ -150,7 +151,7 @@ class TempFileMunge: the length of command lines. Example:: env["TEMPFILE"] = TempFileMunge - env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES','$LINKCOMSTR')}" + env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES', '$LINKCOMSTR')}" By default, the name of the temporary file used begins with a prefix of '@'. This may be configured for other tool chains by @@ -258,31 +259,27 @@ def __call__(self, target, source, env, for_signature): fd, tmp = tempfile.mkstemp(suffix, dir=tempfile_dir, text=True) native_tmp = SCons.Util.get_native_path(tmp) + # arrange for cleanup on exit: + + def tmpfile_cleanup(file) -> None: + os.remove(file) + + atexit.register(tmpfile_cleanup, tmp) + if env.get('SHELL', None) == 'sh': # The sh shell will try to escape the backslashes in the # path, so unescape them. native_tmp = native_tmp.replace('\\', r'\\\\') - # In Cygwin, we want to use rm to delete the temporary - # file, because del does not exist in the sh shell. - rm = env.Detect('rm') or 'del' - else: - # Don't use 'rm' if the shell is not sh, because rm won't - # work with the Windows shells (cmd.exe or command.com) or - # Windows path names. - rm = 'del' if 'TEMPFILEPREFIX' in env: prefix = env.subst('$TEMPFILEPREFIX') else: - prefix = '@' + prefix = "@" tempfile_esc_func = env.get('TEMPFILEARGESCFUNC', SCons.Subst.quote_spaces) - args = [ - tempfile_esc_func(arg) - for arg in cmd[1:] - ] + args = [tempfile_esc_func(arg) for arg in cmd[1:]] join_char = env.get('TEMPFILEARGJOIN', ' ') - os.write(fd, bytearray(join_char.join(args) + "\n", 'utf-8')) + os.write(fd, bytearray(join_char.join(args) + "\n", encoding="utf-8")) os.close(fd) # XXX Using the SCons.Action.print_actions value directly @@ -301,15 +298,20 @@ def __call__(self, target, source, env, for_signature): # purity get in the way of just being helpful, so we'll # reach into SCons.Action directly. if SCons.Action.print_actions: - cmdstr = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, - source) if self.cmdstr is not None else '' + cmdstr = ( + env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, source) + if self.cmdstr is not None + else '' + ) # Print our message only if XXXCOMSTR returns an empty string - if len(cmdstr) == 0 : - cmdstr = ("Using tempfile "+native_tmp+" for command line:\n"+ - str(cmd[0]) + " " + " ".join(args)) + if not cmdstr: + cmdstr = ( + f"Using tempfile {native_tmp} for command line:\n" + f'{cmd[0]} {" ".join(args)}' + ) self._print_cmd_str(target, source, env, cmdstr) - cmdlist = [cmd[0], prefix + native_tmp + '\n' + rm, native_tmp] + cmdlist = [cmd[0], prefix + native_tmp] # Store the temporary file command list into the target Node.attributes # to avoid creating two temporary files one for print and one for execute. diff --git a/test/TEX/glossaries.py b/test/TEX/glossaries.py index 069fcc3769..d418ee266e 100644 --- a/test/TEX/glossaries.py +++ b/test/TEX/glossaries.py @@ -23,7 +23,7 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -""" +r""" Validate that use of \glossaries in TeX source files causes SCons to be aware of the necessary created glossary files. diff --git a/test/TEX/subdir_variantdir_include2.py b/test/TEX/subdir_variantdir_include2.py index 953a2293e1..2b615f194d 100644 --- a/test/TEX/subdir_variantdir_include2.py +++ b/test/TEX/subdir_variantdir_include2.py @@ -23,7 +23,7 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -""" +r""" Verify that we execute TeX in a subdirectory (if that's where the document resides) by checking that all the auxiliary files get created there and not in the top-level directory. Test this when variantDir is used From 23ccbabb3e14eab68de76eb020fde62d0ac28087 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 16 Sep 2024 08:39:05 -0600 Subject: [PATCH 128/386] Fix short-option processing Override the _process_short_opts method from optparse so it behaves better. This fix is lifted directly from #3799, leaving an additional part to apply later. Fixes #3798 Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 ++ RELEASE.txt | 5 +++ SCons/Script/SConsOptions.py | 73 +++++++++++++++++++++++++++++++++--- test/AddOption/basic.py | 3 +- test/SCONSFLAGS.py | 8 ++-- 5 files changed, 81 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 249bccf68d..8648c161df 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,9 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release. RELEASE VERSION/DATE TO BE FILLED IN LATER + From Dillan Mills: + - Fix support for short options (`-x`). + From Mats Wichmann: - PackageVariable now does what the documentation always said it does if the variable is used on the command line with one of the enabling diff --git a/RELEASE.txt b/RELEASE.txt index c65e830e57..d86b342a49 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -43,6 +43,11 @@ FIXES it always produced True in this case). - Temporary files created by TempFileMunge() are now cleaned up on scons exit, instead of at the time they're used. Fixes #4595. +- AddOption now correctly adds short (single-character) options. + Previously an added short option would always report as unknown, + while long option names for the same option worked. Short options + that take a value require the user to specify the value immediately + following the option, with no spaces (e.g. -j5 and not -j 5). IMPROVEMENTS ------------ diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index c9c7aa0542..08531814f5 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -342,12 +342,13 @@ def error(self, msg: str) -> None: def _process_long_opt(self, rargs, values) -> None: """SCons-specific processing of long options. - This is copied directly from the normal - ``optparse._process_long_opt()`` method, except that, if configured - to do so, we catch the exception thrown when an unknown option - is encountered and just stick it back on the "leftover" arguments - for later (re-)processing. This is because we may see the option - definition later, while processing SConscript files. + This is copied directly from the normal Optparse + :meth:`~optparse.OptionParser._process_long_opt` method, except + that, if configured to do so, we catch the exception thrown + when an unknown option is encountered and just stick it back + on the "leftover" arguments for later (re-)processing. This is + because we may see the option definition later, while processing + SConscript files. """ arg = rargs.pop(0) @@ -414,6 +415,66 @@ def _process_long_opt(self, rargs, values) -> None: option.process(opt, value, values, self) + + def _process_short_opts(self, rargs, values) -> None: + """SCons-specific processing of short options. + + This is copied directly from the normal Optparse + :meth:`~optparse.OptionParser._process_short_opts` method, except + that, if configured to do so, we catch the exception thrown + when an unknown option is encountered and just stick it back + on the "leftover" arguments for later (re-)processing. This is + because we may see the option definition later, while processing + SConscript files. + """ + arg = rargs.pop(0) + stop = False + i = 1 + for ch in arg[1:]: + opt = "-" + ch + option = self._short_opt.get(opt) + i += 1 # we have consumed a character + + try: + if not option: + raise optparse.BadOptionError(opt) + except optparse.BadOptionError: + # SCons addition: if requested, add unknown options to + # the "leftover arguments" list for later processing. + if self.preserve_unknown_options: + self.largs.append(arg) + return + raise + + if option.takes_value(): + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + rargs.insert(0, arg[i:]) + stop = True + + nargs = option.nargs + if len(rargs) < nargs: + if nargs == 1: + self.error(_("%s option requires an argument") % opt) + else: + self.error(_("%s option requires %d arguments") + % (opt, nargs)) + elif nargs == 1: + value = rargs.pop(0) + else: + value = tuple(rargs[0:nargs]) + del rargs[0:nargs] + + else: # option doesn't take a value + value = None + + option.process(opt, value, values, self) + + if stop: + break + + def reparse_local_options(self) -> None: """Re-parse the leftover command-line options. diff --git a/test/AddOption/basic.py b/test/AddOption/basic.py index 83297a8277..f488c704cb 100644 --- a/test/AddOption/basic.py +++ b/test/AddOption/basic.py @@ -38,7 +38,7 @@ DefaultEnvironment(tools=[]) env = Environment(tools=[]) AddOption( - '--force', + '-F', '--force', action="store_true", help='force installation (overwrite any existing files)', ) @@ -74,6 +74,7 @@ test.run('-Q -q .', stdout="None\nNone\n") test.run('-Q -q . --force', stdout="True\nNone\n") +test.run('-Q -q . -F', stdout="True\nNone\n") test.run('-Q -q . --prefix=/home/foo', stdout="None\n/home/foo\n") test.run('-Q -q . -- --prefix=/home/foo --force', status=1, stdout="None\nNone\n") # check that SetOption works on prefix... diff --git a/test/SCONSFLAGS.py b/test/SCONSFLAGS.py index 4a44f30b07..ffccf1f2f8 100644 --- a/test/SCONSFLAGS.py +++ b/test/SCONSFLAGS.py @@ -62,15 +62,15 @@ test.must_not_contain_any_line(test.stdout(), ['Help text.']) test.must_contain_all_lines(test.stdout(), ['-H, --help-options']) -os.environ['SCONSFLAGS'] = '-Z' - expect = r"""usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: no such option: -Z """ -test.run(arguments = "-H", status = 2, - stderr = expect, match=TestSCons.match_exact) +test.run(arguments="-Z", status=2, stderr=expect, match=TestSCons.match_exact) + +os.environ['SCONSFLAGS'] = '-Z' +test.run(status=2, stderr=expect, match=TestSCons.match_exact) test.pass_test() From 4082db59a481fad7fe4f4e91847a187044c1db93 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 16 Sep 2024 15:12:22 -0600 Subject: [PATCH 129/386] Fix initialization of compilation_db emitters Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ RELEASE.txt | 2 ++ SCons/Tool/compilation_db.py | 17 +++++++++++------ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 249bccf68d..78c36f5d9a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -39,6 +39,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER will now not be deleted from the base environment. Override Environments now also pretend to have a _dict attribute so that regular environment methods don't have a problem if passed an OE instance. + - Fix a problem with compilation_db component initialization - the + entries for assembler files were not being set up correctly. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index c65e830e57..f21f93ede5 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -43,6 +43,8 @@ FIXES it always produced True in this case). - Temporary files created by TempFileMunge() are now cleaned up on scons exit, instead of at the time they're used. Fixes #4595. +- Fix a problem with compilation_db component initialization - the + entries for assembler files were not being set up correctly. IMPROVEMENTS ------------ diff --git a/SCons/Tool/compilation_db.py b/SCons/Tool/compilation_db.py index e17b5dc661..2b1bfb56d9 100644 --- a/SCons/Tool/compilation_db.py +++ b/SCons/Tool/compilation_db.py @@ -43,6 +43,8 @@ from .cc import CSuffixes from .asm import ASSuffixes, ASPPSuffixes +DEFAULT_DB_NAME = 'compile_commands.json' + # TODO: Is there a better way to do this than this global? Right now this exists so that the # emitter we add can record all of the things it emits, so that the scanner for the top level # compilation database can access the complete list, and also so that the writer has easy @@ -189,9 +191,8 @@ def compilation_db_emitter(target, source, env): if not target and len(source) == 1: target = source - # Default target name is compilation_db.json if not target: - target = ['compile_commands.json', ] + target = [DEFAULT_DB_NAME] # No source should have been passed. Drop it. if source: @@ -224,13 +225,17 @@ def generate(env, **kwargs) -> None: ), itertools.product( ASSuffixes, - [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASCOM")], - [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASCOM")], + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$ASCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASCOM") + ], ), itertools.product( ASPPSuffixes, - [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASPPCOM")], - [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASPPCOM")], + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$ASPPCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASPPCOM") + ], ), ) From f3b0a00430abc6d7eb35e143824481be37cad8f8 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 19 Sep 2024 10:52:46 -0600 Subject: [PATCH 130/386] Add clang, clang++ to default tool search SCons will now automatically find clang (clang++) on Linux and Windows, in each case looked for just after gcc (g++). Signed-off-by: Mats Wichmann --- CHANGES.txt | 6 ++++++ RELEASE.txt | 7 +++++++ SCons/Tool/__init__.py | 8 ++++---- doc/man/scons.xml | 3 +++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 78c36f5d9a..8d58fd4335 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -41,6 +41,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER methods don't have a problem if passed an OE instance. - Fix a problem with compilation_db component initialization - the entries for assembler files were not being set up correctly. + - Add clang and clang++ to the default tool search orders for POSIX + and Windows platforms. These will be searched for after gcc and g++, + respectively. Does not affect expliclity requested tool lists. + Note: on Windows, SCons currently only has builtin support for + clang, not for clang-cl, the version of the frontend that uses + cl.exe-compatible command line switches. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index f21f93ede5..785ef953e5 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -58,6 +58,13 @@ IMPROVEMENTS Calling AddOption with the full set of arguments (option names and attributes) to set up the option is still the recommended approach. +- Add clang and clang++ to the default tool search orders for POSIX + and Windows platforms. These will be searched for after gcc and g++, + respectively. Does not affect expliclity requested tool lists. Note: + on Windows, SCons currently only has builtin support for clang, not + for clang-cl, the version of the frontend that uses cl.exe-compatible + command line switches. + PACKAGING --------- diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py index 474414e3dc..faa92a78b2 100644 --- a/SCons/Tool/__init__.py +++ b/SCons/Tool/__init__.py @@ -691,8 +691,8 @@ def tool_list(platform, env): if str(platform) == 'win32': "prefer Microsoft tools on Windows" linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32'] - c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32'] - cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'cxx', 'bcc32'] + c_compilers = ['msvc', 'mingw', 'gcc', 'clang', 'intelc', 'icl', 'icc', 'cc', 'bcc32'] + cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'clang++', 'cxx', 'bcc32'] assemblers = ['masm', 'nasm', 'gas', '386asm'] fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran'] ars = ['mslib', 'ar', 'tlib'] @@ -757,8 +757,8 @@ def tool_list(platform, env): else: "prefer GNU tools on all other platforms" linkers = ['gnulink', 'ilink'] - c_compilers = ['gcc', 'intelc', 'icc', 'cc'] - cxx_compilers = ['g++', 'intelc', 'icc', 'cxx'] + c_compilers = ['gcc', 'clang', 'intelc', 'icc', 'cc'] + cxx_compilers = ['g++', 'clang++', 'intelc', 'icc', 'cxx'] assemblers = ['gas', 'nasm', 'masm'] fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77'] ars = ['ar', ] diff --git a/doc/man/scons.xml b/doc/man/scons.xml index ca1937260a..8cdd7bf347 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -451,6 +451,8 @@ searches in order for the &MSVC; tools, the MinGW tool chain, the Intel compiler tools, +the GCC tools, +the LLVM/clang tools, and the PharLap ETS compiler. On Windows system which identify as cygwin (that is, if &scons; is invoked from a cygwin shell), @@ -472,6 +474,7 @@ including POSIX (Linux and UNIX) and macOS platforms, &scons; searches in order for the GCC tool chain, +the LLVM/clang tools, and the Intel compiler tools. The defaul tool selection can be pre-empted through the use of the tools From f4d0a235b9a6a5de838fd6a1ea44a4cdbeed7bde Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 20 Sep 2024 08:39:54 -0600 Subject: [PATCH 131/386] Clean up manpage entries for gettext and pdf builders [skip appveyor] Minor wording cleanup, formatting for examples, etc. in the gettext-family (inc. msginit, msgformat, msgmerge) and the pdf and dvips builders. No code changes, no test changes. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 2 + SCons/Tool/dvips.xml | 8 +- SCons/Tool/gettext.xml | 145 ++++++++++++++------------- SCons/Tool/msgfmt.xml | 57 ++++++----- SCons/Tool/msginit.xml | 107 ++++++++++---------- SCons/Tool/msgmerge.xml | 113 ++++++++++----------- SCons/Tool/pdf.xml | 8 +- SCons/Tool/xgettext.xml | 213 ++++++++++++++++++++-------------------- 9 files changed, 334 insertions(+), 320 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8d58fd4335..218c0c66eb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -47,6 +47,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Note: on Windows, SCons currently only has builtin support for clang, not for clang-cl, the version of the frontend that uses cl.exe-compatible command line switches. + - Some manpage cleanup for the gettext and pdf/ps builders. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 785ef953e5..cbf061d177 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -77,6 +77,8 @@ DOCUMENTATION typo fixes, even if they're mentioned in src/CHANGES.txt to give the contributor credit) +- Some manpage cleanup for the gettext and pdf/ps builders. + DEVELOPMENT ----------- diff --git a/SCons/Tool/dvips.xml b/SCons/Tool/dvips.xml index 4df22ef195..d8a69e8961 100644 --- a/SCons/Tool/dvips.xml +++ b/SCons/Tool/dvips.xml @@ -54,15 +54,17 @@ or The suffix specified by the &cv-link-PSSUFFIX; construction variable (.ps by default) is added automatically to the target -if it is not already present. Example: +if it is not already present. +&b-PostScript; is a single-source builder. +Example: - + # builds from aaa.tex env.PostScript(target = 'aaa.ps', source = 'aaa.tex') # builds bbb.ps from bbb.dvi env.PostScript(target = 'bbb', source = 'bbb.dvi') - + diff --git a/SCons/Tool/gettext.xml b/SCons/Tool/gettext.xml index 1606129939..a2b7c98735 100644 --- a/SCons/Tool/gettext.xml +++ b/SCons/Tool/gettext.xml @@ -27,27 +27,27 @@ This file is processed by the bin/SConsDoc.py module. -This is actually a toolset, which supports internationalization and -localization of software being constructed with SCons. The toolset loads -following tools: +A toolset supporting internationalization and +localization of software being constructed with &SCons;. +The toolset loads the following tools: - &t-link-xgettext; - to extract internationalized messages from source code to - POT file(s), + &t-link-xgettext; - extract internationalized messages from source code to + POT file(s). - &t-link-msginit; - may be optionally used to initialize PO - files, + &t-link-msginit; - initialize PO + files during initial tranlation of a project. - &t-link-msgmerge; - to update PO files, that already contain + &t-link-msgmerge; - update PO files that already contain translated messages, - &t-link-msgfmt; - to compile textual PO file to binary - installable MO file. + &t-link-msgfmt; - compile textual PO files to binary + installable MO files. @@ -65,12 +65,12 @@ may be however interested in top-level -To use &t-gettext; tools add 'gettext' tool to your -environment: +To use the &t-gettext; tools, add the 'gettext' tool to your +&consenv;: - - env = Environment( tools = ['default', 'gettext'] ) - + +env = Environment(tools=['default', 'gettext']) + @@ -82,10 +82,11 @@ environment: -This pseudo-builder belongs to &t-link-gettext; toolset. The builder extracts -internationalized messages from source files, updates POT -template (if necessary) and then updates PO translations (if -necessary). If &cv-link-POAUTOINIT; is set, missing PO files +This pseudo-Builder is part of the &t-link-gettext; toolset. +The builder extracts internationalized messages from source files, +updates the POT template (if necessary) +and then updates PO translations (if necessary). +If &cv-link-POAUTOINIT; is set, missing PO files will be automatically created (i.e. without translator person intervention). The variables &cv-link-LINGUAS_FILE; and &cv-link-POTDOMAIN; are taken into acount too. All other construction variables used by &b-link-POTUpdate;, and @@ -95,41 +96,42 @@ acount too. All other construction variables used by &b-link-POTUpdate;, and Example 1. The simplest way is to specify input files and output languages inline in -a SCons script when invoking &b-Translate; +a SCons script when invoking &b-Translate;: - + # SConscript in 'po/' directory -env = Environment( tools = ["default", "gettext"] ) -env['POAUTOINIT'] = 1 -env.Translate(['en','pl'], ['../a.cpp','../b.cpp']) - +env = Environment(tools=["default", "gettext"]) +env['POAUTOINIT'] = True +env.Translate(['en', 'pl'], ['../a.cpp', '../b.cpp']) + Example 2. -If you wish, you may also stick to conventional style known from +If you wish, you may also stick to the conventional style known from autotools, i.e. using POTFILES.in and LINGUAS files +to specify the targets and sources: - + # LINGUAS en pl -#end - +# end + - + # POTFILES.in a.cpp b.cpp # end - + - + # SConscript -env = Environment( tools = ["default", "gettext"] ) -env['POAUTOINIT'] = 1 +env = Environment(tools=["default", "gettext"]) +env['POAUTOINIT'] = True env['XGETTEXTPATH'] = ['../'] -env.Translate(LINGUAS_FILE = 1, XGETTEXTFROM = 'POTFILES.in') - +env.Translate(LINGUAS_FILE=True, XGETTEXTFROM='POTFILES.in') + The last approach is perhaps the recommended one. It allows easily split @@ -152,7 +154,7 @@ project in their usual way. Example 3. Let's prepare a development tree as below - + project/ + SConstruct + build/ @@ -162,52 +164,55 @@ Let's prepare a development tree as below + SConscript.i18n + POTFILES.in + LINGUAS - + -with build being variant directory. Write the top-level +with build being the variant directory. +Write the top-level SConstruct script as follows - - # SConstruct - env = Environment( tools = ["default", "gettext"] ) - VariantDir('build', 'src', duplicate = 0) - env['POAUTOINIT'] = 1 - SConscript('src/po/SConscript.i18n', exports = 'env') - SConscript('build/po/SConscript', exports = 'env') - + +# SConstruct +env = Environment(tools=["default", "gettext"]) +VariantDir('build', 'src', duplicate=False) +env['POAUTOINIT'] = True +SConscript('src/po/SConscript.i18n', exports='env') +SConscript('build/po/SConscript', exports='env') + the src/po/SConscript.i18n as - - # src/po/SConscript.i18n - Import('env') - env.Translate(LINGUAS_FILE=1, XGETTEXTFROM='POTFILES.in', XGETTEXTPATH=['../']) - + +# src/po/SConscript.i18n +Import('env') +env.Translate(LINGUAS_FILE=True, XGETTEXTFROM='POTFILES.in', XGETTEXTPATH=['../']) + and the src/po/SConscript - - # src/po/SConscript - Import('env') - env.MOFiles(LINGUAS_FILE = 1) - + +# src/po/SConscript +Import('env') +env.MOFiles(LINGUAS_FILE=True) + -Such setup produces POT and PO files -under source tree in src/po/ and binary -MO files under variant tree in +Such a setup produces POT and PO files +under the source tree in src/po/ and binary +MO files under the variant tree in build/po/. This way the POT and PO files are separated from other output files, which must not be committed back to source repositories (e.g. MO files). - -In above example, the PO files are not updated, -nor created automatically when you issue scons '.' command. -The files must be updated (created) by hand via scons -po-update and then MO files can be compiled by -running scons '.'. - +In the above example, +the PO files are not updated, +nor created automatically when you issue the command +scons .. +The files must be updated (created) by hand via +scons po-update +and then MO files can be compiled by +running scons .. + @@ -245,10 +250,10 @@ them). The &cv-LINGUAS_FILE; defines file(s) containing list of additional linguas to be processed by &b-link-POInit;, &b-link-POUpdate; or &b-link-MOFiles; builders. It also affects &b-link-Translate; builder. If the variable contains -a string, it defines name of the list file. The &cv-LINGUAS_FILE; may be a +a string, it defines the name of the list file. The &cv-LINGUAS_FILE; may be a list of file names as well. If &cv-LINGUAS_FILE; is set to -True (or non-zero numeric value), the list will be read from -default file named +a non-string truthy value, the list will be read from +the file named LINGUAS. diff --git a/SCons/Tool/msgfmt.xml b/SCons/Tool/msgfmt.xml index e56c12c419..f92830ff3b 100644 --- a/SCons/Tool/msgfmt.xml +++ b/SCons/Tool/msgfmt.xml @@ -27,10 +27,13 @@ This file is processed by the bin/SConsDoc.py module. -This scons tool is a part of scons &t-link-gettext; toolset. It provides scons -interface to msgfmt(1) command, which generates binary -message catalog (MO) from a textual translation description -(PO). +This tool is a part of the &t-link-gettext; toolset. +It provides &SCons; +an interface to the msgfmt(1) command +by setting up the &b-link-MOFiles; builder, +which generates binary message catalog (MO) files +from a textual translation description +(PO files). @@ -49,8 +52,12 @@ message catalog (MO) from a textual translation description -This builder belongs to &t-link-msgfmt; tool. The builder compiles +This builder is set up by the &t-link-msgfmt; tool. +The builder compiles PO files to MO files. +&b-MOFiles; is a single-source builder. +The source parameter +can also be omitted if &cv-link-LINGUAS_FILE; is set. @@ -58,19 +65,17 @@ This builder belongs to &t-link-msgfmt; tool. The builder compiles Create pl.mo and en.mo by compiling pl.po and en.po: - - # ... - env.MOFiles(['pl', 'en']) - + +env.MOFiles(['pl', 'en']) + Example 2. Compile files for languages defined in LINGUAS file: - - # ... - env.MOFiles(LINGUAS_FILE = 1) - + +env.MOFiles(LINGUAS_FILE=True) + Example 3. @@ -78,24 +83,22 @@ Create pl.mo and en.mo by compiling pl.po and en.po plus files for languages defined in LINGUAS file: - - # ... - env.MOFiles(['pl', 'en'], LINGUAS_FILE = 1) - + +env.MOFiles(['pl', 'en'], LINGUAS_FILE=True) + Example 4. Compile files for languages defined in LINGUAS file (another version): - - # ... - env['LINGUAS_FILE'] = 1 - env.MOFiles() - + +env['LINGUAS_FILE'] = True +env.MOFiles() + - + @@ -104,7 +107,7 @@ See &t-link-msgfmt; tool and &b-link-MOFiles; builder. - + @@ -114,7 +117,7 @@ See &t-link-msgfmt; tool and &b-link-MOFiles; builder. - + @@ -123,7 +126,7 @@ See &t-link-msgfmt; tool and &b-link-MOFiles; builder. - + @@ -133,7 +136,7 @@ See &t-link-msgfmt; tool and &b-link-MOFiles; builder. - + diff --git a/SCons/Tool/msginit.xml b/SCons/Tool/msginit.xml index 667ed54b61..225074d308 100644 --- a/SCons/Tool/msginit.xml +++ b/SCons/Tool/msginit.xml @@ -27,10 +27,12 @@ This file is processed by the bin/SConsDoc.py module. -This scons tool is a part of scons &t-link-gettext; toolset. It provides -scons interface to msginit(1) program, which creates new +This tool is a part of scons &t-link-gettext; toolset. It provides +&SCons; an interface to the msginit(1) program, +by setting up the &b-link-POInit; builder, +which creates a new PO file, initializing the meta information with values from -user's environment (or options). +the &consenv; (or options). @@ -54,26 +56,31 @@ user's environment (or options). -This builder belongs to &t-link-msginit; tool. The builder initializes missing -PO file(s) if &cv-link-POAUTOINIT; is set. If -&cv-link-POAUTOINIT; is not set (default), &b-POInit; prints instruction for -user (that is supposed to be a translator), telling how the -PO file should be initialized. In normal projects +This builder is set up by the &t-link-msginit; tool. +The builder initializes missing +PO file(s) if &cv-link-POAUTOINIT; is set. +If &cv-link-POAUTOINIT; is not set (the default), +&b-POInit; prints instruction for the user (such as a translator), +telling how the PO file should be initialized. +In normal projects you should not use &b-POInit; and use &b-link-POUpdate; instead. &b-link-POUpdate; chooses intelligently between msgmerge(1) and msginit(1). &b-POInit; always uses msginit(1) and should be regarded as builder for special purposes or for temporary use (e.g. for quick, one time initialization of a bunch of PO files) or for tests. +&b-POInit; is a single-source builder. +The source parameter +can also be omitted if &cv-link-LINGUAS_FILE; is set. Target nodes defined through &b-POInit; are not built by default (they're Ignored from '.' node) but are added to -special Alias ('po-create' by default). +special &f-link-Alias; ('po-create' by default). The alias name may be changed through the &cv-link-POCREATE_ALIAS; -construction variable. All PO files defined through -&b-POInit; may be easily initialized by scons po-create. +&consvar;. All PO files defined through +&b-POInit; may be easily initialized by scons po-create. @@ -81,31 +88,27 @@ construction variable. All PO files defined through Initialize en.po and pl.po from messages.pot: - - # ... - env.POInit(['en', 'pl']) # messages.pot --> [en.po, pl.po] - + +env.POInit(['en', 'pl']) # messages.pot --> [en.po, pl.po] + Example 2. Initialize en.po and pl.po from foo.pot: - - # ... - env.POInit(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.po] - + +env.POInit(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.po] + Example 3. Initialize en.po and pl.po from -foo.pot but using &cv-link-POTDOMAIN; construction -variable: +foo.pot but using the &cv-link-POTDOMAIN; &consvar;: - - # ... - env.POInit(['en', 'pl'], POTDOMAIN='foo') # foo.pot --> [en.po, pl.po] - + +env.POInit(['en', 'pl'], POTDOMAIN='foo') # foo.pot --> [en.po, pl.po] + Example 4. @@ -113,10 +116,9 @@ Initialize PO files for languages defined in LINGUAS file. The files will be initialized from template messages.pot: - - # ... - env.POInit(LINGUAS_FILE = 1) # needs 'LINGUAS' file - + +env.POInit(LINGUAS_FILE=True) # needs 'LINGUAS' file + Example 5. @@ -125,34 +127,30 @@ Initialize en.po and pl.pl LINGUAS file. The files will be initialized from template messages.pot: - - # ... - env.POInit(['en', 'pl'], LINGUAS_FILE = 1) - + +env.POInit(['en', 'pl'], LINGUAS_FILE=True) + Example 6. You may preconfigure your environment first, and then initialize PO files: - - # ... - env['POAUTOINIT'] = 1 - env['LINGUAS_FILE'] = 1 - env['POTDOMAIN'] = 'foo' - env.POInit() - + +env['POAUTOINIT'] = True +env['LINGUAS_FILE'] = True +env['POTDOMAIN'] = 'foo' +env.POInit() + which has same efect as: - - # ... - env.POInit(POAUTOINIT = 1, LINGUAS_FILE = 1, POTDOMAIN = 'foo') - + +env.POInit(POAUTOINIT=True, LINGUAS_FILE=True, POTDOMAIN='foo') + - @@ -162,7 +160,7 @@ See &t-link-msginit; tool and &b-link-POInit; builder. - + @@ -171,17 +169,17 @@ See &t-link-msginit; tool and &b-link-POInit; builder. - + Path to msginit(1) program (found via -Detect()). +&f-link-Detect;). See &t-link-msginit; tool and &b-link-POInit; builder. - + @@ -190,17 +188,18 @@ See &t-link-msginit; tool and &b-link-POInit; builder. - + -String to display when msginit(1) is invoked -(default: '', which means ``print &cv-link-MSGINITCOM;''). +String to display when msginit(1) is invoked. +The default is an empty string, +which will print the command line (&cv-link-MSGINITCOM;). See &t-link-msginit; tool and &b-link-POInit; builder. - + @@ -210,7 +209,7 @@ See &t-link-msginit; tool and &b-link-POInit; builder. - + diff --git a/SCons/Tool/msgmerge.xml b/SCons/Tool/msgmerge.xml index 1f0437cf0f..f318d47050 100644 --- a/SCons/Tool/msgmerge.xml +++ b/SCons/Tool/msgmerge.xml @@ -27,8 +27,10 @@ This file is processed by the bin/SConsDoc.py module. -This scons tool is a part of scons &t-link-gettext; toolset. It provides -scons interface to msgmerge(1) command, which merges two +This tool is a part of scons &t-link-gettext; toolset. It provides +&SCons; an interface to the msgmerge(1) command, +by setting up the &b-link-POUpdate; builder, +which merges two Uniform style .po files together. @@ -51,23 +53,29 @@ Uniform style .po files together. -The builder belongs to &t-link-msgmerge; tool. The builder updates +The builder is set up by the &t-link-msgmerge; tool. +part of the &t-link-gettext; toolset. +The builder updates PO files with msgmerge(1), or initializes -missing PO files as described in documentation of -&t-link-msginit; tool and &b-link-POInit; builder (see also -&cv-link-POAUTOINIT;). Note, that &b-POUpdate; does not add its -targets to po-create alias as &b-link-POInit; -does. +missing PO files as described in the documentation of the +&t-link-msginit; tool and the &b-link-POInit; builder (see also +&cv-link-POAUTOINIT;). +&b-POUpdate; is a single-source builder. +The source parameter +can also be omitted if &cv-link-LINGUAS_FILE; is set. -Target nodes defined through &b-POUpdate; are not built by default -(they're Ignored from '.' node). Instead, -they are added automatically to special Alias +The target nodes are not +selected for building by default (e.g. scons .). +Instead, they are added automatically to special &f-link-Alias; ('po-update' by default). The alias name may be changed -through the &cv-link-POUPDATE_ALIAS; construction variable. You can easily -update PO files in your project by scons -po-update. +through the &cv-link-POUPDATE_ALIAS; &consvar;. You can easily +update PO files in your project by +scons po-update. +Note that &b-POUpdate; does not add its +targets to the po-create alias as &b-link-POInit; +does. @@ -77,49 +85,44 @@ Update en.po and pl.po from assuming that the later one exists or there is rule to build it (see &b-link-POTUpdate;): - - # ... - env.POUpdate(['en','pl']) # messages.pot --> [en.po, pl.po] - + +env.POUpdate(['en','pl']) # messages.pot --> [en.po, pl.po] + Example 2. Update en.po and pl.po from foo.pot template: - - # ... - env.POUpdate(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.pl] - + +env.POUpdate(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.pl] + Example 3. Update en.po and pl.po from foo.pot (another version): - - # ... - env.POUpdate(['en', 'pl'], POTDOMAIN='foo') # foo.pot -- > [en.po, pl.pl] - + +env.POUpdate(['en', 'pl'], POTDOMAIN='foo') # foo.pot -- > [en.po, pl.pl] + Example 4. Update files for languages defined in LINGUAS file. The files are updated from messages.pot template: - - # ... - env.POUpdate(LINGUAS_FILE = 1) # needs 'LINGUAS' file - + +env.POUpdate(LINGUAS_FILE=True) # needs 'LINGUAS' file + Example 5. Same as above, but update from foo.pot template: - - # ... - env.POUpdate(LINGUAS_FILE = 1, source = ['foo']) - + +env.POUpdate(LINGUAS_FILE=True, source=['foo']) + Example 6. @@ -127,20 +130,19 @@ Update en.po and pl.po plus files for languages defined in LINGUAS file. The files are updated from messages.pot template: - - # produce 'en.po', 'pl.po' + files defined in 'LINGUAS': - env.POUpdate(['en', 'pl' ], LINGUAS_FILE = 1) - + +# produce 'en.po', 'pl.po' + files defined in 'LINGUAS': +env.POUpdate(['en', 'pl' ], LINGUAS_FILE=True) + Example 7. Use &cv-link-POAUTOINIT; to automatically initialize PO file if it doesn't exist: - - # ... - env.POUpdate(LINGUAS_FILE = 1, POAUTOINIT = 1) - + +env.POUpdate(LINGUAS_FILE=True, POAUTOINIT=True) + Example 8. @@ -149,18 +151,16 @@ Update PO files for languages defined in foo.pot template. All necessary settings are pre-configured via environment. - - # ... - env['POAUTOINIT'] = 1 - env['LINGUAS_FILE'] = 1 - env['POTDOMAIN'] = 'foo' - env.POUpdate() - + +env['POAUTOINIT'] = True +env['LINGUAS_FILE'] = True +env['POTDOMAIN'] = 'foo' +env.POUpdate() + - @@ -170,7 +170,7 @@ See &t-link-msgmerge; tool and &b-link-POUpdate; builder. - + @@ -180,7 +180,7 @@ See &t-link-msgmerge; tool and &b-link-POUpdate; builder. - + @@ -189,17 +189,18 @@ See &t-link-msgmerge; tool and &b-link-POUpdate; builder. - + -String to be displayed when msgmerge(1) is invoked -(default: '', which means ``print &cv-link-MSGMERGECOM;''). +String to be displayed when msgmerge(1) is invoked. +The default is an empty string, +which will print the command line (&cv-link-MSGMERGECOM;). See &t-link-msgmerge; tool and &b-link-POUpdate; builder. - + diff --git a/SCons/Tool/pdf.xml b/SCons/Tool/pdf.xml index 95c5ac3013..71dbdda2d7 100644 --- a/SCons/Tool/pdf.xml +++ b/SCons/Tool/pdf.xml @@ -48,15 +48,17 @@ or The suffix specified by the &cv-link-PDFSUFFIX; construction variable (.pdf by default) is added automatically to the target -if it is not already present. Example: +if it is not already present. +&b-PDF; is a single-source builder. +Example: - + # builds from aaa.tex env.PDF(target = 'aaa.pdf', source = 'aaa.tex') # builds bbb.pdf from bbb.dvi env.PDF(target = 'bbb', source = 'bbb.dvi') - + diff --git a/SCons/Tool/xgettext.xml b/SCons/Tool/xgettext.xml index f8b8bb8bab..10bec3a86e 100644 --- a/SCons/Tool/xgettext.xml +++ b/SCons/Tool/xgettext.xml @@ -27,10 +27,10 @@ This file is processed by the bin/SConsDoc.py module. -This scons tool is a part of scons &t-link-gettext; toolset. It provides -scons interface to xgettext(1) -program, which extracts internationalized messages from source code. The tool -provides &b-POTUpdate; builder to make PO +This tool is a part of the &t-link-gettext; toolset. It provides +&SCons; an interface to the xgettext(1) +program, which extracts internationalized messages from source code. +The tool sets up the &b-POTUpdate; builder to make PO Template files. @@ -58,15 +58,18 @@ provides &b-POTUpdate; builder to make PO -The builder belongs to &t-link-xgettext; tool. The builder updates target -POT file if exists or creates one if it doesn't. The node is -not built by default (i.e. it is Ignored from -'.'), but only on demand (i.e. when given -POT file is required or when special alias is invoked). This -builder adds its targe node (messages.pot, say) to a +The builder is set up by the &t-link-xgettext; tool, +part of the &t-link-gettext; toolset. +The builder updates the target +POT file if exists or creates it if it doesn't. +The target node is not +selected for building by default (e.g. scons .), +but only on demand (i.e. when the given +POT file is required or when special alias is invoked). +This builder adds its target node (messages.pot, say) to a special alias (pot-update by default, see &cv-link-POTUPDATE_ALIAS;) so you can update/create them easily with -scons pot-update. The file is not written until there is no +scons pot-update. The file is not written until there is no real change in internationalized messages (or in comments that enter POT file). @@ -86,86 +89,87 @@ not. Let's create po/ directory and place following SConstruct script there: - - # SConstruct in 'po/' subdir - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(['foo'], ['../a.cpp', '../b.cpp']) - env.POTUpdate(['bar'], ['../c.cpp', '../d.cpp']) - + +# SConstruct in 'po/' subdir +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(['foo'], ['../a.cpp', '../b.cpp']) +env.POTUpdate(['bar'], ['../c.cpp', '../d.cpp']) + Then invoke scons few times: - - user@host:$ scons # Does not create foo.pot nor bar.pot - user@host:$ scons foo.pot # Updates or creates foo.pot - user@host:$ scons pot-update # Updates or creates foo.pot and bar.pot - user@host:$ scons -c # Does not clean foo.pot nor bar.pot. - + +$ scons # Does not create foo.pot nor bar.pot +$ scons foo.pot # Updates or creates foo.pot +$ scons pot-update # Updates or creates foo.pot and bar.pot +$ scons -c # Does not clean foo.pot nor bar.pot. + the results shall be as the comments above say. Example 2. -The &b-POTUpdate; builder may be used with no target specified, in which -case default target messages.pot will be used. The -default target may also be overridden by setting &cv-link-POTDOMAIN; construction -variable or providing it as an override to &b-POTUpdate; builder: - - - # SConstruct script - env = Environment( tools = ['default', 'xgettext'] ) - env['POTDOMAIN'] = "foo" - env.POTUpdate(source = ["a.cpp", "b.cpp"]) # Creates foo.pot ... - env.POTUpdate(POTDOMAIN = "bar", source = ["c.cpp", "d.cpp"]) # and bar.pot - +The target argument can be omitted, in which +case the default target name messages.pot is used. +The target may also be overridden by setting the &cv-link-POTDOMAIN; +&consvar; or providing it as an override to the &b-POTUpdate; builder: + + +# SConstruct script +env = Environment(tools=['default', 'xgettext']) +env['POTDOMAIN'] = "foo" +env.POTUpdate(source=["a.cpp", "b.cpp"]) # Creates foo.pot ... +env.POTUpdate(POTDOMAIN="bar", source=["c.cpp", "d.cpp"]) # and bar.pot + Example 3. -The sources may be specified within separate file, for example +The source parameter may also be omitted, +if it is specified in a separate file, for example POTFILES.in: - - # POTFILES.in in 'po/' subdirectory - ../a.cpp - ../b.cpp - # end of file - + +# POTFILES.in in 'po/' subdirectory +../a.cpp +../b.cpp +# end of file + The name of the file (POTFILES.in) containing the list of sources is provided via &cv-link-XGETTEXTFROM;: - - # SConstruct file in 'po/' subdirectory - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(XGETTEXTFROM = 'POTFILES.in') - + +# SConstruct file in 'po/' subdirectory +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(XGETTEXTFROM='POTFILES.in') + Example 4. -You may use &cv-link-XGETTEXTPATH; to define source search path. Assume, for -example, that you have files a.cpp, +You can use &cv-link-XGETTEXTPATH; to define the source search path. +Assume, for example, that you have files a.cpp, b.cpp, po/SConstruct, -po/POTFILES.in. Then your POT-related -files could look as below: - - - # POTFILES.in in 'po/' subdirectory - a.cpp - b.cpp - # end of file - +po/POTFILES.in. +Then your POT-related files could look like this: + + +# POTFILES.in in 'po/' subdirectory +a.cpp +b.cpp +# end of file + - - # SConstruct file in 'po/' subdirectory - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH='../') - + +# SConstruct file in 'po/' subdirectory +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(XGETTEXTFROM='POTFILES.in', XGETTEXTPATH='../') + Example 5. -Multiple search directories may be defined within a list, i.e. -XGETTEXTPATH = ['dir1', 'dir2', ...]. The order in the list +Multiple search directories may be defined as a list, i.e. +XGETTEXTPATH=['dir1', 'dir2', ...]. The order in the list determines the search order of source files. The path to the first file found is used. @@ -173,44 +177,44 @@ is used. Let's create 0/1/po/SConstruct script: - - # SConstruct file in '0/1/po/' subdirectory - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../', '../../']) - + +# SConstruct file in '0/1/po/' subdirectory +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(XGETTEXTFROM='POTFILES.in', XGETTEXTPATH=['../', '../../']) + and 0/1/po/POTFILES.in: - - # POTFILES.in in '0/1/po/' subdirectory - a.cpp - # end of file - + +# POTFILES.in in '0/1/po/' subdirectory +a.cpp +# end of file + Write two *.cpp files, the first one is 0/a.cpp: - - /* 0/a.cpp */ - gettext("Hello from ../../a.cpp") - + +/* 0/a.cpp */ +gettext("Hello from ../../a.cpp") + and the second is 0/1/a.cpp: - - /* 0/1/a.cpp */ - gettext("Hello from ../a.cpp") - + +/* 0/1/a.cpp */ +gettext("Hello from ../a.cpp") + then run scons. You'll obtain 0/1/po/messages.pot with the message "Hello from ../a.cpp". When you reverse order in $XGETTEXTFOM, i.e. when you write SConscript as - - # SConstruct file in '0/1/po/' subdirectory - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../../', '../']) - + +# SConstruct file in '0/1/po/' subdirectory +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(XGETTEXTFROM='POTFILES.in', XGETTEXTPATH=['../../', '../']) + then the messages.pot will contain msgid "Hello from ../../a.cpp" line and not @@ -220,7 +224,6 @@ then the messages.pot will contain - @@ -229,7 +232,7 @@ See &t-link-xgettext; tool and &b-link-POTUpdate; builder. - + @@ -239,7 +242,7 @@ See &t-link-xgettext; tool and &b-link-POTUpdate; builder. - + @@ -249,7 +252,7 @@ See &t-link-xgettext; tool and &b-link-POTUpdate; builder. - + @@ -258,7 +261,7 @@ See &t-link-xgettext; tool and &b-link-POTUpdate; builder. - + @@ -268,7 +271,7 @@ See &t-link-xgettext; tool and &b-link-POTUpdate; builder. - + @@ -277,7 +280,7 @@ See &t-link-xgettext; tool and &b-link-POTUpdate; builder. - + @@ -290,7 +293,7 @@ See &t-link-xgettext; tool and &b-link-POTUpdate; builder. - + @@ -303,7 +306,7 @@ See also &t-link-xgettext; tool and &b-link-POTUpdate; builder. - + @@ -313,7 +316,7 @@ This flag is used to add single search path to - + @@ -321,7 +324,7 @@ This flag is used to add single search path to - + @@ -331,7 +334,7 @@ This flag is used to add single &cv-link-XGETTEXTFROM; file to - + @@ -339,7 +342,7 @@ This flag is used to add single &cv-link-XGETTEXTFROM; file to - + @@ -348,7 +351,7 @@ form source and target (default: '${TARGET.filebase}'). - + @@ -357,7 +360,7 @@ from the &cv-link-XGETTEXTPATH; list. - + @@ -367,8 +370,4 @@ from &cv-link-XGETTEXTFROM;. - - From d0358e71f366f6d1bf668aef23cf9f58920a7d98 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 21 Sep 2024 11:39:18 -0600 Subject: [PATCH 132/386] Tweak User Guide environment descriptions Reword/clarify the section on `AllowSubstExceptions` and the sidebar on Python dictionaries. Add an additonal piece to `env.Replace`. Also, some entity replacements, and more explicit `id` tags added to `section` elements. One unittest change to make sure passing a dict to `env.Replace` works. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 2 + SCons/EnvironmentTests.py | 15 +- SCons/Subst.xml | 15 +- doc/generated/functions.gen | 28 +++- doc/generated/variables.gen | 57 ++++++-- doc/scons.mod | 6 +- doc/user/command-line.xml | 2 +- doc/user/environments.xml | 277 ++++++++++++++++++++---------------- doc/user/parseconfig.xml | 4 +- doc/user/preface.xml | 28 ++-- 11 files changed, 267 insertions(+), 168 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8d58fd4335..58fb9cf454 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -47,6 +47,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Note: on Windows, SCons currently only has builtin support for clang, not for clang-cl, the version of the frontend that uses cl.exe-compatible command line switches. + - Some clarifications in the User Guide "Environments" chapter. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 785ef953e5..cbf061d177 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -77,6 +77,8 @@ DOCUMENTATION typo fixes, even if they're mentioned in src/CHANGES.txt to give the contributor credit) +- Some manpage cleanup for the gettext and pdf/ps builders. + DEVELOPMENT ----------- diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index 6a69e3cd1e..361dda9722 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -1037,7 +1037,8 @@ def test_BuilderWrapper_attributes(self) -> None: # underlying method it tests (Environment.BuilderWrapper.execute()) # is necessary, but we're leaving the code here for now in case # that's mistaken. - def _DO_NOT_test_Builder_execs(self) -> None: + @unittest.skip("BuilderWrapper.execute method not needed") + def test_Builder_execs(self) -> None: """Test Builder execution through different environments One environment is initialized with a single @@ -1291,10 +1292,14 @@ def RDirs(pathlist, fs=env.fs): ] assert flags == expect, flags - env.Replace(F77PATH = [ 'foo', '$FOO/bar', blat ], - INCPREFIX = 'foo ', - INCSUFFIX = 'bar', - FOO = 'baz') + # do a Replace using the dict form + newvalues = { + "F77PATH": ['foo', '$FOO/bar', blat], + "INCPREFIX": 'foo ', + "INCSUFFIX": 'bar', + "FOO": 'baz', + } + env.Replace(**newvalues) flags = env.subst_list('$_F77INCFLAGS', 1)[0] expect = [ '$(', normalize_path('foo'), diff --git a/SCons/Subst.xml b/SCons/Subst.xml index 00ed135ce4..4ac4f7d0e4 100644 --- a/SCons/Subst.xml +++ b/SCons/Subst.xml @@ -31,16 +31,16 @@ This file is processed by the bin/SConsDoc.py module. -Specifies the exceptions that will be allowed -when expanding construction variables. +Specifies the exceptions that will be ignored +when expanding &consvars;. By default, -any construction variable expansions that generate a -NameError +any &consvar; expansions that generate a +&NameError; or -IndexError +&IndexError; exception will expand to a '' -(an empty string) and not cause scons to fail. +(an empty string) and not cause &scons; to fail. All exceptions not in the specified list will generate an error message and terminate processing. @@ -51,7 +51,8 @@ If &f-AllowSubstExceptions; is called multiple times, each call completely overwrites the previous list -of allowed exceptions. +of ignored exceptions. +Calling it with no arguments means no exceptions will be ignored. diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index ff10f9df0d..8563d66acb 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -405,16 +405,16 @@ env.Alias('update', ['file1', 'file2'], "update_database $SOURCES") AllowSubstExceptions([exception, ...]) -Specifies the exceptions that will be allowed -when expanding construction variables. +Specifies the exceptions that will be ignored +when expanding &consvars;. By default, -any construction variable expansions that generate a -NameError +any &consvar; expansions that generate a +&NameError; or -IndexError +&IndexError; exception will expand to a '' -(an empty string) and not cause scons to fail. +(an empty string) and not cause &scons; to fail. All exceptions not in the specified list will generate an error message and terminate processing. @@ -425,7 +425,8 @@ If &f-AllowSubstExceptions; is called multiple times, each call completely overwrites the previous list -of allowed exceptions. +of ignored exceptions. +Calling it with no arguments means no exceptions will be ignored. @@ -1634,6 +1635,19 @@ Example: cvars = env.Dictionary() cc_values = env.Dictionary('CC', 'CCFLAGS', 'CCCOM') + + +The object returned by &f-link-env-Dictionary; should be treated +as a read-only view into the &consvars;. +Some &consvars; have special internal handling, +and making changes through the &f-env-Dictionary; object can bypass +that handling and cause data inconsistencies. +The primary use of &f-env-Dictionary; is for diagnostic purposes - +it is used widely by test cases specifically because +it bypasses the special handling so that behavior +can be verified. + + diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 68ac6db12a..26371cff20 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -9651,16 +9651,41 @@ The suffix used for tar file names. TEMPFILE -A callable object used to handle overly long command line strings, -since operations which call out to a shell will fail -if the line is longer than the shell can accept. -This tends to particularly impact linking. -The tempfile object stores the command line in a temporary -file in the appropriate format, and returns -an alternate command line so the invoked tool will make -use of the contents of the temporary file. -If you need to replace the default tempfile object, -the callable should take into account the settings of +Holds a callable object which will be invoked to transform long +command lines (string or list) into an alternate form. +Length limits on various operating systems +may cause long command lines to fail when calling out to +a shell to run the command. +Most often affects linking, when there are many object files and/or +libraries to be linked, but may also affect other +compilation steps which have many arguments. +&cv-TEMPFILE; is not called directly, +but rather is typically embedded in another +&consvar;, to be expanded when used. Example: + + + +env["TEMPFILE"] = TempFileMunge +env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES', '$LINKCOMSTR')}" + + + +The SCons default value for &cv-TEMPFILE;, +TempFileMunge, +performs command substitution on the passed command line, +calculates whether modification is needed, +then puts all but the first word (assumed to be the command name) +of the resulting list into a temporary file +(sometimes called a response file or command file), +and returns a new command line consisting of the +the command name and an appropriately formatted reference +to the temporary file. + + + +A replacement for the default tempfile object would need +to do fundamentally the same thing, including taking into account +the values of &cv-link-MAXLINELENGTH;, &cv-link-TEMPFILEPREFIX;, &cv-link-TEMPFILESUFFIX;, @@ -9668,6 +9693,11 @@ the callable should take into account the settings of &cv-link-TEMPFILEDIR; and &cv-link-TEMPFILEARGESCFUNC;. +If a particular use case requires a different transformation +than the default, it is recommended to copy the mechanism and +define a new &consvar; and rewrite the relevant *COM +variable(s) to use it, to avoid possibly disrupting existing uses +of &cv-TEMPFILE;. @@ -9726,6 +9756,9 @@ Note this value is used literally and not expanded by the subst logic. The directory to create the long-lines temporary file in. +If unset, some suitable default should be chosen. +The default tempfile object lets the &Python; +tempfile module choose. @@ -9736,6 +9769,8 @@ The directory to create the long-lines temporary file in. The prefix for the name of the temporary file used to store command lines exceeding &cv-link-MAXLINELENGTH;. +The prefix must include the compiler syntax to actually +include and process the file. The default prefix is '@', which works for the &MSVC; and GNU toolchains on Windows. Set this appropriately for other toolchains, @@ -9751,7 +9786,7 @@ or '-via' for ARM toolchain. The suffix for the name of the temporary file used to store command lines exceeding &cv-link-MAXLINELENGTH;. -The suffix should include the dot ('.') if one is wanted as +The suffix should include the dot ('.') if one is needed as it will not be added automatically. The default is .lnk. diff --git a/doc/scons.mod b/doc/scons.mod index 76d82dfbd6..ba70c056bf 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -308,8 +308,10 @@ This file is processed by the bin/SConsDoc.py module. -IndexError"> -NameError"> +AttributeError"> +IndexError"> +KeyError"> +NameError"> str"> zipfile"> diff --git a/doc/user/command-line.xml b/doc/user/command-line.xml index 0bdb9c5771..c0a3c0c55e 100644 --- a/doc/user/command-line.xml +++ b/doc/user/command-line.xml @@ -874,7 +874,7 @@ prog.c get method, so you can supply a default value if the variable is not given on the command line. Otherwise, the build - will fail with a KeyError + will fail with a &KeyError; if the variable is not set. diff --git a/doc/user/environments.xml b/doc/user/environments.xml index eeaa23f756..65d8edff12 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -410,7 +410,7 @@ environment, of directory names, suffixes, etc. - Unlike &Make;, &SCons; does not automatically + Unlike &Make;, &SCons; does not automatically copy or import values between different environments (with the exception of explicit clones of &consenvs;, which inherit the values from their parent). @@ -458,26 +458,39 @@ environment, of directory names, suffixes, etc. If you're not familiar with the &Python; programming language, - we need to talk a little bit about the &Python; dictionary data type. - A dictionary (also known by terms such as mapping, associative array - and key-value store) associates keys with values, such - that asking the dict about a key gives you back the associated value - and assigning to a key creates the association - either a new - setting if the key was unknown, or replacing the - previous association if the key was already in the dictionary. - Values can be retrieved using item access - (the key name in square brackets ([])), - and dictionaries also provide a - method named get which responds - with a default value, either None or a value - you supply as the second argument, if the key is not in the dictionary, - which avoids failing in that case. The syntax - for initializing a dictionary uses curly braces ({}). - Here are some simple examples (inspired by those in the official - Python tutorial) using syntax that indicates - interacting with the &Python; interpreter - (>>> is the interpreter prompt) - - you can try these out: + here is a short summary of the &Python; dictionary data type, + or "dict". You may also see the terms mapping, associative array + or key-value store used for this type of data structure, + which appears in many programming languages. + + + + + + A dictionary associates keys with values, + so asking the dict about a key gives you back the associated value. + Values can be retrieved using item access: + the key name string in square brackets (mydict["keyname"]). + If the key is not present, you get a &KeyError; exception. + Dicts also provide a get() method + which returns a default value if the key is not present, + so it does not fail in that case. + You can specity the default as a second argument to the + get call, otherwise it defaults to + None. + + + + + + Assigning to a key creates the association - either a new + key/value pair if the key was unknown, or replacing the + previous value if the key was already in the dictionary. + Initializing a dictionary uses curly braces ({}). + Here are some simple examples inspired by those in the official + &Python; tutorial, as you would see them if you typed these to + the interactive &Python; interpreter + (>>> is the interpreter prompt): @@ -503,7 +516,7 @@ None &Consenvs; are written to behave like a &Python; - dictionary, and the &cv-ENV; construction variable in + dictionary, and the &cv-ENV; &consvar; in a &consenv; is a &Python; dictionary. The os.environ value that &Python; uses to make available the external environment is also a @@ -527,7 +540,7 @@ None os.environ dictionary. That syntax means the environ attribute of the os module. - In Python, to access the contents of a module you must first + In &Python;, to access the contents of a module you must first import it - so you would include the import os statement to any &SConscript; file @@ -578,18 +591,18 @@ void main() { } A &consenv; is an object that has a number of associated &consvars;, each with a name and a value, just like a dictionary. - (A construction environment also has an attached + (A &consenv; also has an attached set of &Builder; methods, - about which we'll learn more later.) + which you'll learn more abour later.) -
- Creating a &ConsEnv;: the &Environment; Function +
+ Creating a &ConsEnv;: the &Environment; Function - A &consenv; is created by the &Environment; method: + A &consenv; is created by the &f-link-Environment; function: @@ -599,13 +612,12 @@ env = Environment() - By default, &SCons; initializes every - new construction environment + &SCons; initializes every new &consenv; with a set of &consvars; based on the tools that it finds on your system, plus the default set of builder methods necessary for using those tools. - The construction variables + The &consvars; are initialized with values describing the C compiler, the Fortran compiler, @@ -617,7 +629,7 @@ env = Environment() - When you initialize a construction environment + When you initialize a &consenv; you can set the values of the environment's &consvars; to control how a program is built. @@ -637,18 +649,18 @@ int main() { } - The construction environment in this example + The &consenv; in this example is still initialized with the same default - construction variable values, + &consvar; values, except that the user has explicitly specified use of the GNU C compiler &gcc;, and that the (optimization level two) flag should be used when compiling the object file. - In other words, the explicit initializations of + In other words, the explicit initialization of &cv-link-CC; and &cv-link-CCFLAGS; - override the default values in the newly-created - construction environment. + overrides the default values in the newly-created + &consenv;. So a run from this example would look like: @@ -659,13 +671,13 @@ int main() { }
-
+
Fetching Values From a &ConsEnv; You can fetch individual values, known as - Construction Variables, + &ConsVars;, using the same syntax used for accessing individual named items in a &Python; dictionary: @@ -685,8 +697,9 @@ print("LATEX is: %s" % env.get('LATEX', None)) for building any targets, but because it's still a valid &SConstruct; it will be evaluated and the &Python; print calls will output the values - of &cv-link-CC; and &cv-link-LATEX; for us (remember using the - .get() method for fetching means + of &cv-link-CC; and &cv-link-LATEX; for us (remember + from the sidebar that using the + get() method for access means we get a default value back, rather than a failure, if the variable is not set): @@ -764,7 +777,7 @@ print(env.Dump())
-
+
Expanding Values From a &ConsEnv;: the &subst; Method @@ -802,7 +815,7 @@ print("CC is: %s" % env.subst('$CC')) env = Environment(CCFLAGS='-DFOO') -print("CCCOM is: %s" % env['CCCOM']) +print("CCCOM is:", env['CCCOM']) @@ -828,13 +841,13 @@ scons: `.' is up to date. env = Environment(CCFLAGS='-DFOO') -print("CCCOM is: %s" % env.subst('$CCCOM')) +print("CCCOM is:", env.subst('$CCCOM')) Will recursively expand all of - the construction variables prefixed + the &consvars; prefixed with $ (dollar signs), showing us the final output: @@ -857,20 +870,20 @@ scons: `.' is up to date.
-
- Handling Problems With Value Expansion +
+ Handling Problems With Value Expansion (advanced topic) - - If a problem occurs when expanding a construction variable, - by default it is expanded to '' - (an empty string), and will not cause &scons; to fail. + If a problem occurs when expanding a &consvar;, + by default it is expanded to an empty string, + that is, "replaced with nothing" - + &scons; will not fail for unknown variables. env = Environment() -print("value is: %s"%env.subst( '->$MISSING<-' )) +print("value is:", env.subst('->$MISSING<-')) @@ -879,26 +892,33 @@ print("value is: %s"%env.subst( '->$MISSING<-' )) - This default behaviour can be changed using the &AllowSubstExceptions; - function. - When a problem occurs with a variable expansion it generates - an exception, and the &AllowSubstExceptions; function controls - which of these exceptions are actually fatal and which are - allowed to occur safely. By default, &NameError; and &IndexError; - are the two exceptions that are allowed to occur: so instead of - causing &scons; to fail, these are caught, the variable expanded to - '' - and &scons; execution continues. - To require that all construction variable names exist, and that - indexes out of range are not allowed, call &AllowSubstExceptions; - with no extra arguments. + Sometimes this behavior leads to surprises while the build + configuration is being developed, + for example a typo in a variable name isn't reported, + and the variable expression is just dropped (empty string). + &SCons; provides a &f-link-AllowSubstExceptions; function + to allow the behavior to be tuned. + Internally, when a problem occurs with a variable expansion, + it generates an exception, + but before letting that exception kill the build, + &scons; checks a list of exceptions to ignore - by default + &NameError; and &IndexError;. + You can call &AllowSubstExceptions; to set the list of ignored + exceptions to anything you wish, including none at all. + That way, when a variable fails to expand that you thought + should be expanding to something, the build will stop and + you'll get an error message that should help diagnose the problem. + You give &AllowSubstExceptions; as many exception name arguments + as you wish it to ignore, + or call it with no arguments to have all expansion exceptions + propagate and stop &scons;. AllowSubstExceptions() env = Environment() -print("value is: %s"%env.subst( '->$MISSING<-' )) +print("value is:", env.subst('->$MISSING<-')) @@ -908,8 +928,8 @@ print("value is: %s"%env.subst( '->$MISSING<-' )) This can also be used to allow other exceptions that might occur, - most usefully with the ${...} construction - variable syntax. For example, this would allow zero-division to + most usefully with the ${...} &consvar; + syntax. For example, this would allow zero-division to occur in a variable expansion in addition to the default exceptions allowed @@ -918,7 +938,7 @@ print("value is: %s"%env.subst( '->$MISSING<-' )) AllowSubstExceptions(IndexError, NameError, ZeroDivisionError) env = Environment() -print("value is: %s"%env.subst( '->${1 / 0}<-' )) +print("value is:", env.subst('->${1 / 0}<-')) @@ -933,7 +953,7 @@ print("value is: %s"%env.subst( '->${1 / 0}<-' ))
-
+
Controlling the Default &ConsEnv;: the &DefaultEnvironment; Function @@ -989,7 +1009,7 @@ DefaultEnvironment(CC='/usr/local/bin/gcc') previous example, setting the &cv-CC; variable to /usr/local/bin/gcc but as a separate step after - the default construction environment has been initialized: + the default &consenv; has been initialized: @@ -1016,7 +1036,7 @@ def_env['CC'] = '/usr/local/bin/gcc' that &SCons; performs by specifying some specific tool modules with which to - initialize the default construction environment: + initialize the default &consenv;: @@ -1168,29 +1188,29 @@ int main() { }
-
+
Making Copies of &ConsEnvs;: the &Clone; Method - Sometimes you want more than one construction environment + Sometimes you want more than one &consenv; to share the same values for one or more variables. Rather than always having to repeat all of the common - variables when you create each construction environment, + variables when you create each &consenv;, you can use the &f-link-env-Clone; method - to create a copy of a construction environment. + to create a copy of a &consenv;. - Like the &f-link-Environment; call that creates a construction environment, + Like the &f-link-Environment; call that creates a &consenv;, the &Clone; method takes &consvar; assignments, - which will override the values in the copied construction environment. + which will override the values in the copied &consenv;. For example, suppose we want to use &gcc; to create three versions of a program, one optimized, one debug, and one with neither. - We could do this by creating a "base" construction environment + We could do this by creating a "base" &consenv; that sets &cv-link-CC; to &gcc;, and then creating two copies, one which sets &cv-link-CCFLAGS; for optimization @@ -1229,12 +1249,12 @@ int main() { }
-
+
Replacing Values: the &Replace; Method - You can replace existing construction variable values + You can replace existing &consvar; values using the &f-link-env-Replace; method: @@ -1252,10 +1272,10 @@ int main() { } - The replacing value + The new value (-DDEFINE2 in the above example) - completely replaces the value in the - construction environment: + replaces the value in the &consenv; - it's + like a &Python; assignment statement for &consvars;. @@ -1265,9 +1285,8 @@ int main() { } - You can safely call &Replace; - for construction variables that - don't exist in the construction environment: + You can safely call &Replace; for &consvars; that + don't exist in the &consenv; @@ -1281,9 +1300,7 @@ print("NEW_VARIABLE = %s" % env['NEW_VARIABLE']) - In this case, - the construction variable simply - gets added to the construction environment: + In this case, the &consvar; simply gets added to the &consenv;. @@ -1293,8 +1310,26 @@ print("NEW_VARIABLE = %s" % env['NEW_VARIABLE']) + If you have a lot of variables to replace, it may be more + convenient to put them in a dictionary and pass that to the + &Replace; method. That might look like: + + +newvalues = { + "F77PATH": ['foo', '$FOO/bar', blat], + "INCPREFIX": 'foo ', + "INCSUFFIX": 'bar', + "FOO": 'baz', +} +env.Replace(**newvalues) + + + + + + Because the variables - aren't expanded until the construction environment + aren't expanded until the &consenv; is actually used to build the targets, and because &SCons; function and method calls are order-independent, @@ -1356,14 +1391,14 @@ int main() { }
-
+
Setting Values Only If They're Not Already Defined: the &SetDefault; Method Sometimes it's useful to be able to specify - that a construction variable should be - set to a value only if the construction environment + that a &consvar; should be + set to a value only if the &consenv; does not already have that variable defined You can do this with the &f-link-env-SetDefault; method, which behaves similarly to the setdefault @@ -1379,7 +1414,7 @@ env.SetDefault(SPECIAL_FLAG='-extra-option') This is especially useful when writing your own Tool modules - to apply variables to construction environments. + to apply variables to &consenvs;. -
+
Propagating &PATH; From the External Environment @@ -1919,7 +1954,7 @@ env = Environment(ENV=os.environ.copy())
-
+
Adding to <varname>PATH</varname> Values in the Execution Environment @@ -1970,11 +2005,11 @@ env.AppendENVPath('LIB', '/usr/local/lib')
Using the toolpath for external Tools -
+
The default tool search path - Normally when using a tool from the construction environment, + Normally when using a tool from the &consenv;, several different search locations are checked by default. This includes the SCons/Tools/ directory that is part of the &scons; distribution @@ -1996,7 +2031,7 @@ SCons/Tool/SomeTool/__init__.py
-
+
Providing an external directory to toolpath @@ -2027,8 +2062,8 @@ SCons/Tool/SomeTool/__init__.py
-
- Nested Tools within a toolpath +
+ Nested Tools within a toolpath (advanced topic) @@ -2060,8 +2095,8 @@ SCons/Tool/SubDir1/SubDir2/SomeTool/__init__.py
-
- Using sys.path within the toolpath +
+ Using <literal>sys.path</literal> within the toolpath If we want to access tools external to &scons; which are findable @@ -2109,7 +2144,7 @@ C:\Python35\Lib\site-packages\someinstalledpackage\SomeTool\__init__.py
-
+
Using the &PyPackageDir; function to add to the toolpath diff --git a/doc/user/parseconfig.xml b/doc/user/parseconfig.xml index aac3cf3f6e..ee52c92c76 100644 --- a/doc/user/parseconfig.xml +++ b/doc/user/parseconfig.xml @@ -55,7 +55,7 @@ This file is processed by the bin/SConsDoc.py module. - &SCons; &consvars; have a &f-link-ParseConfig; + &SCons; &consenvs; have a &f-link-ParseConfig; method that asks the host system to execute a command and then configures the appropriate &consvars; based on the output of that command. @@ -102,7 +102,7 @@ scons: `.' is up to date. In the example above, &SCons; has added the include directory to &cv-link-CPPPATH; - (Depending upon what other flags are emitted by the + (depending on what other flags are emitted by the pkg-config command, other variables may have been extended as well.) diff --git a/doc/user/preface.xml b/doc/user/preface.xml index bccad50915..8c0ba874f7 100644 --- a/doc/user/preface.xml +++ b/doc/user/preface.xml @@ -102,7 +102,7 @@ This file is processed by the bin/SConsDoc.py module. --> -
+
&SCons; Principles @@ -193,7 +193,7 @@ This file is processed by the bin/SConsDoc.py module. --> -
+
How to Use this Guide @@ -223,19 +223,23 @@ This file is processed by the bin/SConsDoc.py module. - It is often useful to keep &SCons; man page open in a separate + If you are viewing an html version of this Guide, there are many + hyperlinks present that you can follow to get more details + if you want them, as the User Guide intentionally does not attempt + to provide every detail, to allow smoother study of the basics. + It may also be useful to keep the &SCons; man page open in a separate browser tab or window to refer to as a complement to this Guide, - as the User Guide does not attempt to provide every detail. - While this Guide's Appendices A-D do duplicate information that appears - in the man page (this is to allow intra-document links to - definitions of &consvars;, builders, tools and environment methods - to work), the rest of the man page is unique content. + which can avoid some jumping back and forth. + The four important manpage sections describiing the + of &consvars;, builders, tools and environment methods + are actually duplicated as appendices in the User Guide, + to avoid inter-document links.
-
+
A Caveat About This Guide's Completeness @@ -272,7 +276,7 @@ This file is processed by the bin/SConsDoc.py module.
-
+
Acknowledgements @@ -395,7 +399,7 @@ This file is processed by the bin/SConsDoc.py module. And last, thanks to Guido van Rossum - for his elegant scripting language, + for his elegant scripting language &Python;, which is the basis not only for the &SCons; implementation, but for the interface itself. @@ -403,7 +407,7 @@ This file is processed by the bin/SConsDoc.py module.
-
+
Contact From a46eee90ec63cf31fa4d820a3bd8c8f2aca51623 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 21 Sep 2024 18:00:06 -0700 Subject: [PATCH 133/386] fix typo --- doc/user/environments.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/environments.xml b/doc/user/environments.xml index 65d8edff12..a630c659d5 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -593,7 +593,7 @@ void main() { } &consvars;, each with a name and a value, just like a dictionary. (A &consenv; also has an attached set of &Builder; methods, - which you'll learn more abour later.) + which you'll learn more about later.) From a1afc933262c8e873478bc777f72aa87cbe32024 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 21 Sep 2024 18:03:19 -0700 Subject: [PATCH 134/386] fix mistake in RELEASE.txt content --- RELEASE.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE.txt b/RELEASE.txt index cbf061d177..f831305f29 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -29,7 +29,7 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - List modifications to existing features, where the previous behavior wouldn't actually be considered a bug -- Override envirionments, created when giving construction environment +- Override environments, created when giving construction environment keyword arguments to Builder calls (or manually, through the undocumented Override method), were modified not to "leak" on item deletion. The item will now not be deleted from the base environment. @@ -77,7 +77,7 @@ DOCUMENTATION typo fixes, even if they're mentioned in src/CHANGES.txt to give the contributor credit) -- Some manpage cleanup for the gettext and pdf/ps builders. +- Some clarifications in the User Guide "Environments" chapter. DEVELOPMENT ----------- From b8e3b23ed6ae4c12888daadb92575496730d9926 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 21 Sep 2024 18:10:57 -0700 Subject: [PATCH 135/386] Fix typos --- SCons/Tool/gettext.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SCons/Tool/gettext.xml b/SCons/Tool/gettext.xml index a2b7c98735..4b3eb12eb7 100644 --- a/SCons/Tool/gettext.xml +++ b/SCons/Tool/gettext.xml @@ -40,7 +40,7 @@ The toolset loads the following tools: &t-link-msginit; - initialize PO - files during initial tranlation of a project. + files during initial translation of a project. &t-link-msgmerge; - update PO files that already contain @@ -53,7 +53,7 @@ The toolset loads the following tools: -When you enable &t-gettext;, it internally loads all abovementioned tools, +When you enable &t-gettext;, it internally loads all the above-mentioned tools, so you're encouraged to see their individual documentation. @@ -89,7 +89,7 @@ and then updates PO translations (if necessary). If &cv-link-POAUTOINIT; is set, missing PO files will be automatically created (i.e. without translator person intervention). The variables &cv-link-LINGUAS_FILE; and &cv-link-POTDOMAIN; are taken into -acount too. All other construction variables used by &b-link-POTUpdate;, and +account too. All other construction variables used by &b-link-POTUpdate;, and &b-link-POUpdate; work here too. @@ -144,7 +144,7 @@ factor" synchronizing these two scripts is then the content of LINGUAS file. Note, that the updated POT and PO files are usually going to be committed back to the repository, so they must be updated within the source -directory (and not in variant directories). Additionaly, the file listing of +directory (and not in variant directories). Additionally, the file listing of po/ directory contains LINGUAS file, so the source tree looks familiar to translators, and they may work with the project in their usual way. From 2d5e3a40a613225b329776ab9dbd9abcd2d24222 Mon Sep 17 00:00:00 2001 From: Alex James Date: Sun, 22 Sep 2024 18:07:21 -0500 Subject: [PATCH 136/386] Handle permission errors while generating paths on Darwin On Darwin, Nix may invoke SCons in a sandbox which lacks access to /etc/paths.d. Handle PermissionError while iterating through /etc/paths.d to support sandboxed environments such as Nix. --- CHANGES.txt | 5 +++++ RELEASE.txt | 4 ++++ SCons/Platform/darwin.py | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index b402b16110..e9f445d93a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -50,6 +50,11 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Some manpage cleanup for the gettext and pdf/ps builders. - Some clarifications in the User Guide "Environments" chapter. + From Alex James: + - On Darwin, PermissionErrors are now handled while trying to access + /etc/paths.d. This may occur if SCons is invoked in a sandboxed + environment (such as Nix). + RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 06a726ec16..1ccc056fbb 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -46,6 +46,10 @@ FIXES - Fix a problem with compilation_db component initialization - the entries for assembler files were not being set up correctly. +- On Darwin, PermissionErrors are now handled while trying to access + /etc/paths.d. This may occur if SCons is invoked in a sandboxed environment + (such as Nix). + IMPROVEMENTS ------------ diff --git a/SCons/Platform/darwin.py b/SCons/Platform/darwin.py index 381b54249d..4ab7466c60 100644 --- a/SCons/Platform/darwin.py +++ b/SCons/Platform/darwin.py @@ -46,7 +46,7 @@ def generate(env) -> None: # make sure this works on Macs with Tiger or earlier try: dirlist = os.listdir('/etc/paths.d') - except FileNotFoundError: + except (FileNotFoundError, PermissionError): dirlist = [] for file in dirlist: From c6987ca8568e2082a56063837d539b8045b85917 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:34:35 -0400 Subject: [PATCH 137/386] Validate the SCONS_MSCOMMON_DEBUG file name. Changes: * Issue warning and remove leading and trailing double quotes from SCONS_MSCOMMON_DEBUG file name when present. * Issue warning when SCONS_MSCOMMON_DEBUG file name is likely invalid. Known issues: * A false positive warning may be issued when a colon is detected in the file name. An output file and hidden alternate data stream file may be created. --- SCons/Tool/MSCommon/common.py | 177 +++++++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index af3afb5150..ba43f8a570 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -33,6 +33,7 @@ from contextlib import suppress from subprocess import DEVNULL, PIPE from pathlib import Path +from typing import Optional, Tuple import SCons.Util import SCons.Warnings @@ -40,9 +41,175 @@ class MSVCCacheInvalidWarning(SCons.Warnings.WarningOnByDefault): pass +class WindowsFileNameWarning(SCons.Warnings.WarningOnByDefault): + pass + +class _WindowsFileName: + + # Known Issues: + # * NTFS filenames with a colon are reported as invalid (false positive) + # given a file named "colonbeg:colonend.txt": + # * file "colonbeg" is created and appears empty + # * hidden alternate data stream file "colonbeg:colonend.txt:$DATA" is created + # * use "dir /R colonbeg" to display alternate data streams of files + + drive_prefix = r"([a-zA-Z][:])[\\]?(?P.*)$" + re_drive_spec = re.compile(drive_prefix) + + unc_prefix = r"(\\\\[^\\]+)[\\](?P.*)$" + re_unc_spec = re.compile(unc_prefix) + + re_illegal_chars = re.compile( + r'[<>:"/\\|?*\r\t\n]', + re.IGNORECASE + ) + + re_reserved_names = re.compile( + r"^(?PCON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])(\.|$)", + re.IGNORECASE, + ) + + @classmethod + def is_filename_invalid(cls, fileorig: str) -> Tuple[str, str]: + + filename = fileorig + if not filename: + reason = "file name is empty" + return reason, filename + + filename = os.fspath(filename) + filename = os.path.abspath(filename) + + pathspec, filespec = os.path.split(filename) + + if filespec and not filespec.strip(): + reason = f"file name is whitespace only: {filespec!r}" + return reason, filename + + suffix = "" + + do_once = True + while do_once: + do_once = False + + m = cls.re_drive_spec.match(filename) + if m: + # filename: r"c:\dir\filename.txt" + # prefix: r"c:" + # suffix: r"dir\filename.txt" + suffix = m.group("suffix") + break + + m = cls.re_unc_spec.match(filename) + if m: + # filename: r"\\server\share\filename.txt" + # prefix: r"\\server" + # suffix: r"share\filename.txt" + suffix = m.group("suffix") + break + + reason = "unrecognized path prefix" + return reason, filename + + if suffix: + + comps = suffix.split(os.path.sep) + for name in comps: + + if not name: + reason = "file name component is empty" + return reason, filename + + if name and not name.strip(): + reason = f"file name component is whitespace-only ({name!r})" + return reason, filename + + illegal_chars = cls.re_illegal_chars.findall(name) + if illegal_chars: + seen_chars = ', '.join(list( + {repr(s): s for s in illegal_chars}.keys() + )) + reason = f"file name contains illegal characters ({seen_chars})" + return reason, filename + + match = cls.re_reserved_names.match(name) + if match: + reserved = match.group("reserved") + reason = f"file name contains a reserved name ({reserved!r})" + return reason, filename + + if not os.path.exists(pathspec): + reason = "path to file name does not exist" + return reason, filename + + if os.path.exists(filename) and os.path.isdir(filename): + reason = "file name is a directory" + return reason, filename + + return "", filename + + @classmethod + def check_filename_invalid( + cls, + filename: str, + description: Optional[str] = None + ) -> bool: + reason, abspath = cls.is_filename_invalid(filename) + if reason: + if description: + msg_description = description + " " + else: + msg_description = "" + msg = ( + f"{msg_description}file name is invalid:\n" + f" filename: {filename!r}\n" + f" abspath: {abspath!r}\n" + f" reason: {reason}" + ) + SCons.Warnings.warn(WindowsFileNameWarning, msg) + return bool(reason) + + @classmethod + def process_filename( + cls, + filename: Optional[str], + description: Optional[str] = None + ) -> Optional[str]: + if filename is None: + return filename + if len(filename) < 2: + return filename + if filename[0] == '"' and filename[-1] == '"': + fileorig = filename + filename = filename[1:-1] + if description: + msg_description = description + " " + else: + msg_description = "" + msg = ( + f"{msg_description}file name is invalid:\n" + f" filename: {fileorig!r}\n" + f" modified: {filename!r}\n" + f" action: leading and trailing double quotes removed" + ) + SCons.Warnings.warn(WindowsFileNameWarning, msg) + return filename + + @classmethod + def get_filename_environ( + cls, + evar: str, + ) -> Optional[str]: + filename = cls.process_filename(os.environ.get(evar), description=evar) + return filename + +check_filename_invalid = _WindowsFileName.check_filename_invalid +get_filename_environ = _WindowsFileName.get_filename_environ + +_LOGFILE_EVAR = "SCONS_MSCOMMON_DEBUG" # SCONS_MSCOMMON_DEBUG is internal-use so undocumented: # set to '-' to print to console, else set to filename to log to -LOGFILE = os.environ.get('SCONS_MSCOMMON_DEBUG') +LOGFILE = get_filename_environ(_LOGFILE_EVAR) if LOGFILE: import logging @@ -128,8 +295,14 @@ def format(self, record): log_prefix = 'debug: ' log_handler = logging.StreamHandler(sys.stdout) else: + isinvalid = check_filename_invalid(LOGFILE, description=_LOGFILE_EVAR) log_prefix = '' - log_handler = logging.FileHandler(filename=LOGFILE) + try: + log_handler = logging.FileHandler(filename=LOGFILE) + except Exception as e: + if isinvalid: + raise e.with_traceback(None) + raise log_formatter = _CustomFormatter(log_prefix) log_handler.setFormatter(log_formatter) logger = logging.getLogger(name=__name__) From f1117e193c2598a5c6df6063b37b204766479e51 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 27 Sep 2024 14:58:00 -0400 Subject: [PATCH 138/386] Simplify log file handling. Changes: * Remove leading and trailing double quotes from SCONS_MSDEBUG_VALUE and issue a warning. * Catch logging module OSError and FileNotFoundError and wrap in SCons UserError. --- SCons/Tool/MSCommon/common.py | 189 ++++------------------------------ 1 file changed, 20 insertions(+), 169 deletions(-) diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index ba43f8a570..5ec92a22bf 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -33,183 +33,32 @@ from contextlib import suppress from subprocess import DEVNULL, PIPE from pathlib import Path -from typing import Optional, Tuple +import SCons.Errors import SCons.Util import SCons.Warnings class MSVCCacheInvalidWarning(SCons.Warnings.WarningOnByDefault): pass -class WindowsFileNameWarning(SCons.Warnings.WarningOnByDefault): +class MSCommonLogFileWarning(SCons.Warnings.WarningOnByDefault): pass -class _WindowsFileName: - - # Known Issues: - # * NTFS filenames with a colon are reported as invalid (false positive) - # given a file named "colonbeg:colonend.txt": - # * file "colonbeg" is created and appears empty - # * hidden alternate data stream file "colonbeg:colonend.txt:$DATA" is created - # * use "dir /R colonbeg" to display alternate data streams of files - - drive_prefix = r"([a-zA-Z][:])[\\]?(?P.*)$" - re_drive_spec = re.compile(drive_prefix) - - unc_prefix = r"(\\\\[^\\]+)[\\](?P.*)$" - re_unc_spec = re.compile(unc_prefix) - - re_illegal_chars = re.compile( - r'[<>:"/\\|?*\r\t\n]', - re.IGNORECASE - ) - - re_reserved_names = re.compile( - r"^(?PCON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])(\.|$)", - re.IGNORECASE, - ) - - @classmethod - def is_filename_invalid(cls, fileorig: str) -> Tuple[str, str]: - - filename = fileorig - if not filename: - reason = "file name is empty" - return reason, filename - - filename = os.fspath(filename) - filename = os.path.abspath(filename) - - pathspec, filespec = os.path.split(filename) - - if filespec and not filespec.strip(): - reason = f"file name is whitespace only: {filespec!r}" - return reason, filename - - suffix = "" - - do_once = True - while do_once: - do_once = False - - m = cls.re_drive_spec.match(filename) - if m: - # filename: r"c:\dir\filename.txt" - # prefix: r"c:" - # suffix: r"dir\filename.txt" - suffix = m.group("suffix") - break - - m = cls.re_unc_spec.match(filename) - if m: - # filename: r"\\server\share\filename.txt" - # prefix: r"\\server" - # suffix: r"share\filename.txt" - suffix = m.group("suffix") - break - - reason = "unrecognized path prefix" - return reason, filename - - if suffix: - - comps = suffix.split(os.path.sep) - for name in comps: - - if not name: - reason = "file name component is empty" - return reason, filename - - if name and not name.strip(): - reason = f"file name component is whitespace-only ({name!r})" - return reason, filename - - illegal_chars = cls.re_illegal_chars.findall(name) - if illegal_chars: - seen_chars = ', '.join(list( - {repr(s): s for s in illegal_chars}.keys() - )) - reason = f"file name contains illegal characters ({seen_chars})" - return reason, filename - - match = cls.re_reserved_names.match(name) - if match: - reserved = match.group("reserved") - reason = f"file name contains a reserved name ({reserved!r})" - return reason, filename - - if not os.path.exists(pathspec): - reason = "path to file name does not exist" - return reason, filename - - if os.path.exists(filename) and os.path.isdir(filename): - reason = "file name is a directory" - return reason, filename - - return "", filename - - @classmethod - def check_filename_invalid( - cls, - filename: str, - description: Optional[str] = None - ) -> bool: - reason, abspath = cls.is_filename_invalid(filename) - if reason: - if description: - msg_description = description + " " - else: - msg_description = "" - msg = ( - f"{msg_description}file name is invalid:\n" - f" filename: {filename!r}\n" - f" abspath: {abspath!r}\n" - f" reason: {reason}" - ) - SCons.Warnings.warn(WindowsFileNameWarning, msg) - return bool(reason) - - @classmethod - def process_filename( - cls, - filename: Optional[str], - description: Optional[str] = None - ) -> Optional[str]: - if filename is None: - return filename - if len(filename) < 2: - return filename - if filename[0] == '"' and filename[-1] == '"': - fileorig = filename - filename = filename[1:-1] - if description: - msg_description = description + " " - else: - msg_description = "" - msg = ( - f"{msg_description}file name is invalid:\n" - f" filename: {fileorig!r}\n" - f" modified: {filename!r}\n" - f" action: leading and trailing double quotes removed" +def _check_logfile(logfile): + if logfile and len(logfile) >= 2: + if logfile[0] == '"' and logfile[-1] == '"': + logfile = logfile[1:-1] + warn_msg = ( + "SCONS_MSCOMMON_DEBUG value enclosed in double quotes, doubles quotes removed\n" + f' original value="{logfile}"\n' + f" modified value={logfile}" ) - SCons.Warnings.warn(WindowsFileNameWarning, msg) - return filename - - @classmethod - def get_filename_environ( - cls, - evar: str, - ) -> Optional[str]: - filename = cls.process_filename(os.environ.get(evar), description=evar) - return filename + SCons.Warnings.warn(MSCommonLogFileWarning, warn_msg) + return logfile -check_filename_invalid = _WindowsFileName.check_filename_invalid -get_filename_environ = _WindowsFileName.get_filename_environ - -_LOGFILE_EVAR = "SCONS_MSCOMMON_DEBUG" # SCONS_MSCOMMON_DEBUG is internal-use so undocumented: # set to '-' to print to console, else set to filename to log to -LOGFILE = get_filename_environ(_LOGFILE_EVAR) +LOGFILE = _check_logfile(os.environ.get('SCONS_MSCOMMON_DEBUG')) if LOGFILE: import logging @@ -295,14 +144,16 @@ def format(self, record): log_prefix = 'debug: ' log_handler = logging.StreamHandler(sys.stdout) else: - isinvalid = check_filename_invalid(LOGFILE, description=_LOGFILE_EVAR) log_prefix = '' try: log_handler = logging.FileHandler(filename=LOGFILE) - except Exception as e: - if isinvalid: - raise e.with_traceback(None) - raise + except (OSError, FileNotFoundError) as e: + err_msg = ( + f"Could not create logfile, check SCONS_MSCOMMON_DEBUG\n" + f" SCONS_MSCOMMON_DEBUG={LOGFILE}\n" + f" {e.__class__.__name__}: {str(e)}" + ) + raise SCons.Errors.UserError(err_msg) log_formatter = _CustomFormatter(log_prefix) log_handler.setFormatter(log_formatter) logger = logging.getLogger(name=__name__) From 29fe1db319d04d7c371a5cc38c3677a53d47258c Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:54:35 -0400 Subject: [PATCH 139/386] Remove unnecessary f-string specification in ms common logging error message. --- SCons/Tool/MSCommon/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index 5ec92a22bf..3663f1181c 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -149,7 +149,7 @@ def format(self, record): log_handler = logging.FileHandler(filename=LOGFILE) except (OSError, FileNotFoundError) as e: err_msg = ( - f"Could not create logfile, check SCONS_MSCOMMON_DEBUG\n" + "Could not create logfile, check SCONS_MSCOMMON_DEBUG\n" f" SCONS_MSCOMMON_DEBUG={LOGFILE}\n" f" {e.__class__.__name__}: {str(e)}" ) From 4f3e3026bc455b3b47666567f273b2f84f932d32 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 29 Sep 2024 13:31:16 -0700 Subject: [PATCH 140/386] added request to add to release.txt in the PR template --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4c2d467669..423ba3f7b3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,5 +7,5 @@ https://scons.org/guidelines.html ## Contributor Checklist: * [ ] I have created a new test or updated the unit tests to cover the new/changed functionality. -* [ ] I have updated `CHANGES.txt` (and read the `README.rst`) +* [ ] I have updated `CHANGES.txt` and `RELEASE.txt` (and read the `README.rst`). * [ ] I have updated the appropriate documentation From 444f44c0f8caaf4508fdf31b58fd9647dca2618e Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:13:46 -0400 Subject: [PATCH 141/386] Detect double quotes in SCONS_MSCOMMON_DEBUG value and raise UserError immediately. --- SCons/Tool/MSCommon/common.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index 3663f1181c..eebf4c6945 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -45,15 +45,12 @@ class MSCommonLogFileWarning(SCons.Warnings.WarningOnByDefault): pass def _check_logfile(logfile): - if logfile and len(logfile) >= 2: - if logfile[0] == '"' and logfile[-1] == '"': - logfile = logfile[1:-1] - warn_msg = ( - "SCONS_MSCOMMON_DEBUG value enclosed in double quotes, doubles quotes removed\n" - f' original value="{logfile}"\n' - f" modified value={logfile}" - ) - SCons.Warnings.warn(MSCommonLogFileWarning, warn_msg) + if logfile and '"' in logfile: + err_msg = ( + "SCONS_MSCOMMON_DEBUG value contains double quote character(s)\n" + f" SCONS_MSCOMMON_DEBUG={logfile}" + ) + raise SCons.Errors.UserError(err_msg) return logfile # SCONS_MSCOMMON_DEBUG is internal-use so undocumented: From fab319152a9a6e7e71da740558dd76af681a0096 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 5 Oct 2024 08:24:55 -0600 Subject: [PATCH 142/386] Fix nasm test [skip appveyor] Fix reported problem of missing header (no declaration for exit()). Clean up to current styles. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 2 ++ test/AS/nasm.py | 51 +++++++++++++++++++++++-------------------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index cebc9728ff..85c494e7ba 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -52,6 +52,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER cl.exe-compatible command line switches. - Some manpage cleanup for the gettext and pdf/ps builders. - Some clarifications in the User Guide "Environments" chapter. + - Fix nasm test for missing include file, cleanup. From Alex James: - On Darwin, PermissionErrors are now handled while trying to access diff --git a/RELEASE.txt b/RELEASE.txt index d27c18ad6c..71c52eb5a4 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -55,6 +55,8 @@ FIXES /etc/paths.d. This may occur if SCons is invoked in a sandboxed environment (such as Nix). +- Fix nasm test for missing include file, cleanup. + IMPROVEMENTS ------------ diff --git a/test/AS/nasm.py b/test/AS/nasm.py index 4d93c7f897..09090b6e60 100644 --- a/test/AS/nasm.py +++ b/test/AS/nasm.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 correct use of the live 'nasm' assembler. @@ -39,7 +38,6 @@ test = TestSCons.TestSCons() nasm = test.where_is('nasm') - if not nasm: test.skip_test('nasm not found; skipping test\n') @@ -77,25 +75,26 @@ test.file_fixture('wrapper.py') -test.write('SConstruct', """ -eee = Environment(tools = ['gcc', 'gnulink', 'nasm'], - CFLAGS = ['-m32'], - LINKFLAGS = '-m32', - ASFLAGS = '-f %(nasm_format)s') -fff = eee.Clone(AS = r'%(_python_)s wrapper.py ' + WhereIs('nasm')) -eee.Program(target = 'eee', source = ['eee.asm', 'eee_main.c']) -fff.Program(target = 'fff', source = ['fff.asm', 'fff_main.c']) -""" % locals()) - -test.write('eee.asm', -""" +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +eee = Environment( + tools=['gcc', 'gnulink', 'nasm'], + CFLAGS=['-m32'], + LINKFLAGS='-m32', + ASFLAGS='-f {nasm_format}', +) +fff = eee.Clone(AS=r'{_python_} wrapper.py ' + WhereIs('nasm')) +eee.Program(target='eee', source=['eee.asm', 'eee_main.c']) +fff.Program(target='fff', source=['fff.asm', 'fff_main.c']) +""") + +test.write('eee.asm', """\ global name name: db 'eee.asm',0 """) -test.write('fff.asm', -""" +test.write('fff.asm', """\ global name name: db 'fff.asm',0 @@ -103,6 +102,8 @@ test.write('eee_main.c', r""" #include +#include + extern char name[]; int @@ -129,20 +130,16 @@ } """) -test.run(arguments = 'eee' + _exe, stderr = None) - -test.run(program = test.workpath('eee'), stdout = "eee_main.c eee.asm\n") +test.run(arguments='eee' + _exe, stderr=None) +test.run(program=test.workpath('eee'), stdout="eee_main.c eee.asm\n") test.must_not_exist('wrapper.out') -test.run(arguments = 'fff' + _exe) - -test.run(program = test.workpath('fff'), stdout = "fff_main.c fff.asm\n") +test.run(arguments='fff' + _exe) +test.run(program=test.workpath('fff'), stdout="fff_main.c fff.asm\n") test.must_match('wrapper.out', "wrapper.py\n") - - test.pass_test() # Local Variables: From 9e7f85a8e79058191d93122e4b7d3d22dae969b2 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:57:59 -0400 Subject: [PATCH 143/386] Fix MSVS tests and minor changes to Tool/msvs.py. testing/framework/TestSConsMSVS.py: * Add default project GUID * Pass MSVS_PROJECT_GUID via environment * Add AdditionalOptions Condition to expected vcx project file * Fix vs version number for vc version 14.3 * Fix expected platform toolset version SCons/Tool/msvs.py: * User environment MSVS_PROJECT_GUID when creating project files info * Fix writing Visual Studio 15 for VS2015 * Add .vcxprof as an expected suffix for assigning the name to the file base name Fix early exit after vc version 8.0 (exit at the end of first loop execution) in: * test/MSVS/vs-files.py * test/MSVS/vs-scc-files.py * test/MSVS/vs-scc-legacy-files.py * test/MSVS/vs-variant_dir.py Tests: * Modify tests using TestSConsMSVS to add MSVS_PROJECT_GUID to the environment in the generated SConstruct/SConscript files. * Fix: delete env['PYTHON_ROOT'] before next loop iteration in test/MSVS/vs-files.py. --- SCons/Tool/msvs.py | 11 +++++++--- test/MSVS/common-prefix.py | 14 +++++------- test/MSVS/runfile.py | 14 ++++++------ test/MSVS/vs-7.0-clean.py | 13 ++++------- test/MSVS/vs-7.0-files.py | 12 ++--------- test/MSVS/vs-7.0-scc-files.py | 7 ++---- test/MSVS/vs-7.0-scc-legacy-files.py | 7 ++---- test/MSVS/vs-7.0-variant_dir.py | 17 ++++----------- test/MSVS/vs-7.1-clean.py | 13 ++++------- test/MSVS/vs-7.1-files.py | 12 ++--------- test/MSVS/vs-7.1-scc-files.py | 7 ++---- test/MSVS/vs-7.1-scc-legacy-files.py | 7 ++---- test/MSVS/vs-7.1-variant_dir.py | 17 ++++----------- test/MSVS/vs-files.py | 5 +++++ test/MSVS/vs-scc-files.py | 8 ++++--- test/MSVS/vs-scc-legacy-files.py | 7 ++++-- test/MSVS/vs-variant_dir.py | 12 +++++------ testing/framework/TestSConsMSVS.py | 32 +++++++++++++++++++++++----- 18 files changed, 95 insertions(+), 120 deletions(-) diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index 12974489a4..eff1c8e3a4 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -1554,6 +1554,7 @@ def AddConfig(self, variant, dswfile=dswfile) -> None: if not (p.platform in seen or seen.add(p.platform))] def GenerateProjectFilesInfo(self) -> None: + project_guid = self.env.get('MSVS_PROJECT_GUID', '') for dspfile in self.dspfiles: dsp_folder_path, name = os.path.split(dspfile) dsp_folder_path = os.path.abspath(dsp_folder_path) @@ -1565,8 +1566,12 @@ def GenerateProjectFilesInfo(self) -> None: dsp_relative_file_path = name else: dsp_relative_file_path = os.path.join(dsp_relative_folder_path, name) + if not project_guid: + guid = _generateGUID(dspfile, '') + else: + guid = project_guid dspfile_info = {'NAME': name, - 'GUID': _generateGUID(dspfile, ''), + 'GUID': guid, 'FOLDER_PATH': dsp_folder_path, 'FILE_PATH': dspfile, 'SLN_RELATIVE_FOLDER_PATH': dsp_relative_folder_path, @@ -1615,7 +1620,7 @@ def PrintSolution(self) -> None: elif self.version_num >= 14.2: # Visual Studio 2019 is considered to be version 16. self.file.write('# Visual Studio 16\n') - elif self.version_num > 14.0: + elif self.version_num >= 14.0: # Visual Studio 2015 and 2017 are both considered to be version 15. self.file.write('# Visual Studio 15\n') elif self.version_num >= 12.0: @@ -1632,7 +1637,7 @@ def PrintSolution(self) -> None: for dspinfo in self.dspfiles_info: name = dspinfo['NAME'] base, suffix = SCons.Util.splitext(name) - if suffix == '.vcproj': + if suffix in ('.vcxproj', '.vcproj'): name = base self.file.write('Project("%s") = "%s", "%s", "%s"\n' % (external_makefile_guid, name, dspinfo['SLN_RELATIVE_FILE_PATH'], dspinfo['GUID'])) diff --git a/test/MSVS/common-prefix.py b/test/MSVS/common-prefix.py index ea95c032a0..90417a2a22 100644 --- a/test/MSVS/common-prefix.py +++ b/test/MSVS/common-prefix.py @@ -38,6 +38,8 @@ msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform test.skip_test(msg) +msvs_project_guid = TestSConsMSVS.MSVS_PROJECT_GUID + vcproj_template = """\ """ - - SConscript_contents = """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '8.0') +env=Environment(tools=['msvs'], + MSVS_VERSION = '8.0', + MSVS_PROJECT_GUID = '%(msvs_project_guid)s') testsrc = %(testsrc)s @@ -98,8 +100,6 @@ auto_build_solution = 0) """ - - test.subdir('work1') testsrc = repr([ @@ -142,8 +142,6 @@ # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.subdir('work2') testsrc = repr([ @@ -170,8 +168,6 @@ # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.pass_test() # Local Variables: diff --git a/test/MSVS/runfile.py b/test/MSVS/runfile.py index d2319f330a..bdd541d197 100644 --- a/test/MSVS/runfile.py +++ b/test/MSVS/runfile.py @@ -38,6 +38,8 @@ msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform test.skip_test(msg) +sconscript_dict = {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} + expected_vcprojfile = """\ """ - - SConscript_contents = """\ -env=Environment(tools=['msvs'], MSVS_VERSION = '8.0') +env=Environment(tools=['msvs'], + MSVS_VERSION = '8.0', + MSVS_PROJECT_GUID = '%(MSVS_PROJECT_GUID)s') env.MSVSProject(target = 'Test.vcproj', slnguid = '{SLNGUID}', @@ -104,11 +106,9 @@ auto_build_solution = 0) """ - - test.subdir('work1') -test.write(['work1', 'SConstruct'], SConscript_contents) +test.write(['work1', 'SConstruct'], SConscript_contents % sconscript_dict) test.run(chdir='work1', arguments="Test.vcproj") @@ -118,8 +118,6 @@ # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.0-clean.py b/test/MSVS/vs-7.0-clean.py index 1194cc1fac..04d8d1509d 100644 --- a/test/MSVS/vs-7.0-clean.py +++ b/test/MSVS/vs-7.0-clean.py @@ -34,20 +34,17 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() - # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.0'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_0 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 - - test.write('SConstruct', """\ env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='7.0',HOST_ARCH='%(HOST_ARCH)s') + MSVS_VERSION='7.0', + MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -69,7 +66,7 @@ slnguid = '{SLNGUID}', projects = [p], variant = 'Release') -"""%{'HOST_ARCH':host_arch}) +""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID}) test.run(arguments=".") @@ -104,8 +101,6 @@ test.must_not_exist(test.workpath('Test.vcproj')) - - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.0-files.py b/test/MSVS/vs-7.0-files.py index 9dc33b70d5..163f0a5e41 100644 --- a/test/MSVS/vs-7.0-files.py +++ b/test/MSVS/vs-7.0-files.py @@ -35,20 +35,16 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() - +sconscript_dict = {'HOST_ARCH': host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.0'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_0 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 SConscript_contents = TestSConsMSVS.SConscript_contents_7_0 - - -test.write('SConstruct', SConscript_contents%{'HOST_ARCH': host_arch}) +test.write('SConstruct', SConscript_contents % sconscript_dict) test.run(arguments="Test.vcproj") @@ -79,8 +75,6 @@ test.must_not_exist(test.workpath('Test.vcproj')) test.must_not_exist(test.workpath('Test.sln')) - - # Test that running SCons with $PYTHON_ROOT in the environment # changes the .vcproj output as expected. os.environ['PYTHON_ROOT'] = 'xyzzy' @@ -95,8 +89,6 @@ # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.0-scc-files.py b/test/MSVS/vs-7.0-scc-files.py index f1f77db2e9..a1f3c435de 100644 --- a/test/MSVS/vs-7.0-scc-files.py +++ b/test/MSVS/vs-7.0-scc-files.py @@ -37,12 +37,11 @@ # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.0'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_0 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 SConscript_contents = \ r"""env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0', + MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_CONNECTION_ROOT='.', @@ -65,7 +64,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" +""" % {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution @@ -89,7 +88,6 @@ \tSccProvider="MSSCCI:Perforce SCM" """ - test.write('SConstruct', SConscript_contents) test.run(arguments="Test.vcproj") @@ -108,7 +106,6 @@ # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.0-scc-legacy-files.py b/test/MSVS/vs-7.0-scc-legacy-files.py index a180b8ad6a..d5791029d1 100644 --- a/test/MSVS/vs-7.0-scc-legacy-files.py +++ b/test/MSVS/vs-7.0-scc-legacy-files.py @@ -37,12 +37,11 @@ # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.0'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_0 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0', + MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', @@ -63,14 +62,13 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" +""" % {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} expected_vcproj_sccinfo = """\ \tSccProjectName="Perforce Project" \tSccLocalPath="C:\\MyMsVsProjects" """ - test.write('SConstruct', SConscript_contents) test.run(arguments="Test.vcproj") @@ -88,7 +86,6 @@ # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.0-variant_dir.py b/test/MSVS/vs-7.0-variant_dir.py index 41c3cdcec2..5ec3b556ed 100644 --- a/test/MSVS/vs-7.0-variant_dir.py +++ b/test/MSVS/vs-7.0-variant_dir.py @@ -33,40 +33,33 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() - +sconscript_dict = {'HOST_ARCH': host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.0'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_0 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 SConscript_contents = TestSConsMSVS.SConscript_contents_7_0 - - test.subdir('src') test.write('SConstruct', """\ SConscript('src/SConscript', variant_dir='build') """) -test.write(['src', 'SConscript'], SConscript_contents%{'HOST_ARCH': host_arch}) +test.write(['src', 'SConscript'], SConscript_contents % sconscript_dict) test.run(arguments=".") -project_guid = "{CB4637F1-2205-50B7-B115-DCFA0DA68FB1}" vcproj = test.read(['src', 'Test.vcproj'], 'r') -expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct', - project_guid=project_guid) +expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct') # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) test.must_exist(test.workpath('src', 'Test.sln')) sln = test.read(['src', 'Test.sln'], 'r') -expect = test.msvs_substitute(expected_slnfile, '7.0', 'src', - project_guid=project_guid) +expect = test.msvs_substitute(expected_slnfile, '7.0', 'src') # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) @@ -84,8 +77,6 @@ """ % test.workpath('src', 'Test.sln'), mode='r') - - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.1-clean.py b/test/MSVS/vs-7.1-clean.py index 8c9cb87894..665871ca5a 100644 --- a/test/MSVS/vs-7.1-clean.py +++ b/test/MSVS/vs-7.1-clean.py @@ -34,20 +34,17 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() - # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.1'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_1 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 - - test.write('SConstruct', """\ env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='7.1',HOST_ARCH='%(HOST_ARCH)s') + MSVS_VERSION='7.1', + MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -69,7 +66,7 @@ slnguid = '{SLNGUID}', projects = [p], variant = 'Release') -"""%{'HOST_ARCH':host_arch}) +""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID}) test.run(arguments=".") @@ -104,8 +101,6 @@ test.must_not_exist(test.workpath('Test.vcproj')) - - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.1-files.py b/test/MSVS/vs-7.1-files.py index e2a40a813d..e372fe0ee2 100644 --- a/test/MSVS/vs-7.1-files.py +++ b/test/MSVS/vs-7.1-files.py @@ -35,20 +35,16 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() - +sconscript_dict = {'HOST_ARCH': host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.1'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_1 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 SConscript_contents = TestSConsMSVS.SConscript_contents_7_1 - - -test.write('SConstruct', SConscript_contents%{'HOST_ARCH': host_arch}) +test.write('SConstruct', SConscript_contents % sconscript_dict) test.run(arguments="Test.vcproj") @@ -79,8 +75,6 @@ test.must_not_exist(test.workpath('Test.vcproj')) test.must_not_exist(test.workpath('Test.sln')) - - # Test that running SCons with $PYTHON_ROOT in the environment # changes the .vcproj output as expected. os.environ['PYTHON_ROOT'] = 'xyzzy' @@ -95,8 +89,6 @@ # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.1-scc-files.py b/test/MSVS/vs-7.1-scc-files.py index 0b42930b51..fbc83de862 100644 --- a/test/MSVS/vs-7.1-scc-files.py +++ b/test/MSVS/vs-7.1-scc-files.py @@ -37,12 +37,11 @@ # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.1'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_1 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1', + MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_CONNECTION_ROOT='.', @@ -65,7 +64,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" +""" % {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution @@ -89,7 +88,6 @@ \tSccProvider="MSSCCI:Perforce SCM" """ - test.write('SConstruct', SConscript_contents) test.run(arguments="Test.vcproj") @@ -108,7 +106,6 @@ # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.1-scc-legacy-files.py b/test/MSVS/vs-7.1-scc-legacy-files.py index bb184d6dec..335ef8b85d 100644 --- a/test/MSVS/vs-7.1-scc-legacy-files.py +++ b/test/MSVS/vs-7.1-scc-legacy-files.py @@ -37,12 +37,11 @@ # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.1'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_1 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1', + MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', @@ -63,14 +62,13 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" +""" % {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} expected_vcproj_sccinfo = """\ \tSccProjectName="Perforce Project" \tSccLocalPath="C:\\MyMsVsProjects" """ - test.write('SConstruct', SConscript_contents) test.run(arguments="Test.vcproj") @@ -88,7 +86,6 @@ # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.1-variant_dir.py b/test/MSVS/vs-7.1-variant_dir.py index 64467def53..b4de4171ab 100644 --- a/test/MSVS/vs-7.1-variant_dir.py +++ b/test/MSVS/vs-7.1-variant_dir.py @@ -33,40 +33,33 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() - +sconscript_dict = {'HOST_ARCH': host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.1'] - - expected_slnfile = TestSConsMSVS.expected_slnfile_7_1 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 SConscript_contents = TestSConsMSVS.SConscript_contents_7_1 - - test.subdir('src') test.write('SConstruct', """\ SConscript('src/SConscript', variant_dir='build') """) -test.write(['src', 'SConscript'], SConscript_contents%{'HOST_ARCH': host_arch}) +test.write(['src', 'SConscript'], SConscript_contents % sconscript_dict) test.run(arguments=".") -project_guid = "{CB4637F1-2205-50B7-B115-DCFA0DA68FB1}" vcproj = test.read(['src', 'Test.vcproj'], 'r') -expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct', - project_guid=project_guid) +expect = test.msvs_substitute(expected_vcprojfile, '7.0', None, 'SConstruct') # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) test.must_exist(test.workpath('src', 'Test.sln')) sln = test.read(['src', 'Test.sln'], 'r') -expect = test.msvs_substitute(expected_slnfile, '7.0', 'src', - project_guid=project_guid) +expect = test.msvs_substitute(expected_slnfile, '7.0', 'src') # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) @@ -84,8 +77,6 @@ """ % test.workpath('src', 'Test.sln'), mode='r') - - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-files.py b/test/MSVS/vs-files.py index b330ce726f..54933565e4 100644 --- a/test/MSVS/vs-files.py +++ b/test/MSVS/vs-files.py @@ -33,6 +33,8 @@ import TestSConsMSVS +test = None + for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() @@ -103,6 +105,9 @@ # don't compare the pickled data assert vcxproj[:len(expect)] == expect, test.diff_substr(expect, vcxproj) + del os.environ['PYTHON_ROOT'] + +if test: test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-scc-files.py b/test/MSVS/vs-scc-files.py index 85fa27c392..1cbbd8c0cc 100644 --- a/test/MSVS/vs-scc-files.py +++ b/test/MSVS/vs-scc-files.py @@ -31,6 +31,8 @@ import TestSConsMSVS +test = None + for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): test = TestSConsMSVS.TestSConsMSVS() @@ -46,6 +48,7 @@ expected_vcprojfile = test.get_expected_proj_file_contents(vc_version, dirs, project_file) SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='{vc_version}', + MSVS_PROJECT_GUID='{project_guid}', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_CONNECTION_ROOT='.', @@ -66,7 +69,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""".format(vc_version=vc_version, project_file=project_file) +""".format(vc_version=vc_version, project_file=project_file, project_guid=TestSConsMSVS.MSVS_PROJECT_GUID) expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution @@ -97,7 +100,6 @@ \t\tMSSCCI:Perforce SCM """ - test.write('SConstruct', SConscript_contents) test.run(arguments=project_file) @@ -116,7 +118,7 @@ # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - +if test: test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-scc-legacy-files.py b/test/MSVS/vs-scc-legacy-files.py index 8e2ca15cc7..88efca8e35 100644 --- a/test/MSVS/vs-scc-legacy-files.py +++ b/test/MSVS/vs-scc-legacy-files.py @@ -31,6 +31,8 @@ import TestSConsMSVS +test = None + for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): test = TestSConsMSVS.TestSConsMSVS() @@ -47,6 +49,7 @@ SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='{vc_version}', + MSVS_PROJECT_GUID='{project_guid}', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', @@ -66,7 +69,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""".format(vc_version=vc_version, project_file=project_file) +""".format(vc_version=vc_version, project_file=project_file, project_guid=TestSConsMSVS.MSVS_PROJECT_GUID) if major < 10: # VC8 and VC9 used key-value pair format. @@ -98,7 +101,7 @@ # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - +if test: test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-variant_dir.py b/test/MSVS/vs-variant_dir.py index 7d4b9cbf3e..880a36edac 100644 --- a/test/MSVS/vs-variant_dir.py +++ b/test/MSVS/vs-variant_dir.py @@ -31,6 +31,8 @@ import TestSConsMSVS +test = None + for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() @@ -58,21 +60,18 @@ test.run(arguments=".") - project_guid = "{CB4637F1-2205-50B7-B115-DCFA0DA68FB1}" vcproj = test.read(['src', project_file], 'r') - expect = test.msvs_substitute(expected_vcprojfile, vc_version, None, 'SConstruct', - project_guid=project_guid) + expect = test.msvs_substitute(expected_vcprojfile, vc_version, None, 'SConstruct') # don't compare the pickled data assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) test.must_exist(test.workpath('src', 'Test.sln')) sln = test.read(['src', 'Test.sln'], 'r') - expect = test.msvs_substitute(expected_slnfile, '8.0', 'src', - project_guid=project_guid) + expect = test.msvs_substitute(expected_slnfile, '8.0', 'src') # don't compare the pickled data assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - test.must_match(['build', 'Test.vcproj'], """\ + test.must_match(['build', project_file], """\ This is just a placeholder file. The real project file is here: %s @@ -84,6 +83,7 @@ %s """ % test.workpath('src', 'Test.sln'), mode='r') +if test: test.pass_test() # Local Variables: diff --git a/testing/framework/TestSConsMSVS.py b/testing/framework/TestSConsMSVS.py index 91aa329d66..1a075e8b65 100644 --- a/testing/framework/TestSConsMSVS.py +++ b/testing/framework/TestSConsMSVS.py @@ -50,6 +50,8 @@ from TestSCons import __all__ +MSVS_PROJECT_GUID = "{00000000-0000-0000-0000-000000000000}" + expected_dspfile_6_0 = '''\ # Microsoft Developer Studio Project File - Name="Test" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 @@ -311,7 +313,9 @@ SConscript_contents_7_0 = """\ env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='7.0',HOST_ARCH='%(HOST_ARCH)s') + MSVS_VERSION='7.0', + MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -437,7 +441,9 @@ SConscript_contents_7_1 = """\ env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='7.1',HOST_ARCH='%(HOST_ARCH)s') + MSVS_VERSION='7.1', + MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -575,7 +581,7 @@ \t\t \t \t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} +\t\t%(MSVS_PROJECT_GUID)s \t\tTest \t\tMakeFileProj @@ -585,7 +591,7 @@ \t \t\tMakefile \t\tfalse -\t\tv100 +\t\t%(PLATFORM_TOOLSET)s \t \t \t @@ -605,6 +611,7 @@ \t\t$(NMakeForcedIncludes) \t\t$(NMakeAssemblySearchPath) \t\t$(NMakeForcedUsingAssemblies) +\t\t \t \t \t\t @@ -633,6 +640,7 @@ SConscript_contents_fmt = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='%(MSVS_VERSION)s', + MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], HOST_ARCH='%(HOST_ARCH)s') @@ -720,7 +728,7 @@ def msvs_substitute(self, input, msvs_ver, python = sys.executable if project_guid is None: - project_guid = "{B0CC4EE9-0174-51CD-A06A-41D0713E928A}" + project_guid = MSVS_PROJECT_GUID if 'SCONS_LIB_DIR' in os.environ: exec_script_main = f"from os.path import join; import sys; sys.path = [ r'{os.environ['SCONS_LIB_DIR']}' ] + sys.path; import SCons.Script; SCons.Script.main()" @@ -868,6 +876,8 @@ def _get_solution_file_vs_number(self, vc_version) -> str: return '15' elif major == 14 and minor == 2: return '16' + elif major == 14 and minor == 3: + return '17' else: raise SCons.Errors.UserError(f'Received unexpected VC version {vc_version}') @@ -902,6 +912,15 @@ def _get_vcxproj_file_tools_version(self, vc_version) -> str: else: raise SCons.Errors.UserError(f'Received unexpected VC version {vc_version}') + def _get_vcxproj_file_platform_toolset(self, vc_version) -> str: + """ + Returns the version entry expected in the project file. + For .vcxproj files, this goes is PlatformToolset. + For .vcproj files, not applicable. + """ + major, minor = self.parse_vc_version(vc_version) + return f"v{major}{minor}" + def _get_vcxproj_file_cpp_path(self, dirs): """Returns the include paths expected in the .vcxproj file""" return ';'.join([self.workpath(dir) for dir in dirs]) @@ -926,14 +945,17 @@ def get_expected_proj_file_contents(self, vc_version, dirs, project_file): else: fmt = expected_vcprojfile_fmt return fmt % { + 'MSVS_PROJECT_GUID': MSVS_PROJECT_GUID, 'TOOLS_VERSION': self._get_vcxproj_file_tools_version(vc_version), 'INCLUDE_DIRS': self._get_vcxproj_file_cpp_path(dirs), + 'PLATFORM_TOOLSET': self._get_vcxproj_file_platform_toolset(vc_version), } def get_expected_sconscript_file_contents(self, vc_version, project_file): return SConscript_contents_fmt % { 'HOST_ARCH': self.get_vs_host_arch(), 'MSVS_VERSION': vc_version, + 'MSVS_PROJECT_GUID': MSVS_PROJECT_GUID, 'PROJECT_FILE': project_file, } From 357ce53b076feba224507053641d3de175a82429 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 8 Oct 2024 06:18:33 -0400 Subject: [PATCH 144/386] Add safe HOST_ARCH to sconstruct/sconscript environment for MSVS test scripts. Scripts affected: * /test/MSVS/vs-7.0-scc-files.py * /test/MSVS/vs-7.0-scc-legacy-files.py * /test/MSVS/vs-7.1-scc-files.py * /test/MSVS/vs-7.1-scc-legacy-files.py * /test/MSVS/vs-scc-files.py * /test/MSVS/vs-scc-legacy-files.py --- test/MSVS/vs-7.0-scc-files.py | 6 ++++-- test/MSVS/vs-7.0-scc-legacy-files.py | 6 ++++-- test/MSVS/vs-7.1-scc-files.py | 6 ++++-- test/MSVS/vs-7.1-scc-legacy-files.py | 6 ++++-- test/MSVS/vs-scc-files.py | 9 +++++++-- test/MSVS/vs-scc-legacy-files.py | 9 +++++++-- 6 files changed, 30 insertions(+), 12 deletions(-) diff --git a/test/MSVS/vs-7.0-scc-files.py b/test/MSVS/vs-7.0-scc-files.py index a1f3c435de..b87bb9a855 100644 --- a/test/MSVS/vs-7.0-scc-files.py +++ b/test/MSVS/vs-7.0-scc-files.py @@ -33,6 +33,7 @@ import TestSConsMSVS test = TestSConsMSVS.TestSConsMSVS() +host_arch = test.get_vs_host_arch() # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.0'] @@ -47,7 +48,8 @@ MSVS_SCC_CONNECTION_ROOT='.', MSVS_SCC_PROVIDER='MSSCCI:Perforce SCM', MSVS_SCC_PROJECT_NAME='Perforce Project', - MSVS_SCC_AUX_PATH='AUX') + MSVS_SCC_AUX_PATH='AUX', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -64,7 +66,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" % {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution diff --git a/test/MSVS/vs-7.0-scc-legacy-files.py b/test/MSVS/vs-7.0-scc-legacy-files.py index d5791029d1..5b900f80ca 100644 --- a/test/MSVS/vs-7.0-scc-legacy-files.py +++ b/test/MSVS/vs-7.0-scc-legacy-files.py @@ -33,6 +33,7 @@ import TestSConsMSVS test = TestSConsMSVS.TestSConsMSVS() +host_arch = test.get_vs_host_arch() # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.0'] @@ -45,7 +46,8 @@ CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', - MSVS_SCC_PROJECT_NAME='Perforce Project') + MSVS_SCC_PROJECT_NAME='Perforce Project', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -62,7 +64,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" % {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} expected_vcproj_sccinfo = """\ \tSccProjectName="Perforce Project" diff --git a/test/MSVS/vs-7.1-scc-files.py b/test/MSVS/vs-7.1-scc-files.py index fbc83de862..3af668f8c7 100644 --- a/test/MSVS/vs-7.1-scc-files.py +++ b/test/MSVS/vs-7.1-scc-files.py @@ -33,6 +33,7 @@ import TestSConsMSVS test = TestSConsMSVS.TestSConsMSVS() +host_arch = test.get_vs_host_arch() # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.1'] @@ -47,7 +48,8 @@ MSVS_SCC_CONNECTION_ROOT='.', MSVS_SCC_PROVIDER='MSSCCI:Perforce SCM', MSVS_SCC_PROJECT_NAME='Perforce Project', - MSVS_SCC_AUX_PATH='AUX') + MSVS_SCC_AUX_PATH='AUX', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -64,7 +66,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" % {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution diff --git a/test/MSVS/vs-7.1-scc-legacy-files.py b/test/MSVS/vs-7.1-scc-legacy-files.py index 335ef8b85d..9e0d0ea3da 100644 --- a/test/MSVS/vs-7.1-scc-legacy-files.py +++ b/test/MSVS/vs-7.1-scc-legacy-files.py @@ -33,6 +33,7 @@ import TestSConsMSVS test = TestSConsMSVS.TestSConsMSVS() +host_arch = test.get_vs_host_arch() # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.1'] @@ -45,7 +46,8 @@ CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', - MSVS_SCC_PROJECT_NAME='Perforce Project') + MSVS_SCC_PROJECT_NAME='Perforce Project', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] @@ -62,7 +64,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" % {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} expected_vcproj_sccinfo = """\ \tSccProjectName="Perforce Project" diff --git a/test/MSVS/vs-scc-files.py b/test/MSVS/vs-scc-files.py index 1cbbd8c0cc..1b4ad228f8 100644 --- a/test/MSVS/vs-scc-files.py +++ b/test/MSVS/vs-scc-files.py @@ -35,6 +35,7 @@ for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): test = TestSConsMSVS.TestSConsMSVS() + host_arch = test.get_vs_host_arch() # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = [vc_version] @@ -53,7 +54,8 @@ CPPPATH=['inc1', 'inc2'], MSVS_SCC_CONNECTION_ROOT='.', MSVS_SCC_PROVIDER='MSSCCI:Perforce SCM', - MSVS_SCC_PROJECT_NAME='Perforce Project') + MSVS_SCC_PROJECT_NAME='Perforce Project', + HOST_ARCH='{host_arch}') testsrc = ['test1.cpp', 'test2.cpp'] testincs = [r'sdk_dir\\sdk.h'] @@ -69,7 +71,10 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""".format(vc_version=vc_version, project_file=project_file, project_guid=TestSConsMSVS.MSVS_PROJECT_GUID) +""".format( + vc_version=vc_version, project_file=project_file, + host_arch=host_arch, project_guid=TestSConsMSVS.MSVS_PROJECT_GUID, +) expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution diff --git a/test/MSVS/vs-scc-legacy-files.py b/test/MSVS/vs-scc-legacy-files.py index 88efca8e35..49ea2ac165 100644 --- a/test/MSVS/vs-scc-legacy-files.py +++ b/test/MSVS/vs-scc-legacy-files.py @@ -35,6 +35,7 @@ for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): test = TestSConsMSVS.TestSConsMSVS() + host_arch = test.get_vs_host_arch() # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = [vc_version] @@ -53,7 +54,8 @@ CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', - MSVS_SCC_PROJECT_NAME='Perforce Project') + MSVS_SCC_PROJECT_NAME='Perforce Project', + HOST_ARCH='{host_arch}') testsrc = ['test1.cpp', 'test2.cpp'] testincs = [r'sdk_dir\\sdk.h'] @@ -69,7 +71,10 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""".format(vc_version=vc_version, project_file=project_file, project_guid=TestSConsMSVS.MSVS_PROJECT_GUID) +""".format( + vc_version=vc_version, project_file=project_file, + host_arch=host_arch, project_guid=TestSConsMSVS.MSVS_PROJECT_GUID, +) if major < 10: # VC8 and VC9 used key-value pair format. From c255a95fa04015c7c5db3aa60697630c4633e4af Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 8 Oct 2024 10:17:38 -0400 Subject: [PATCH 145/386] Remove unused MSCommonLogFile warning. --- SCons/Tool/MSCommon/common.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index eebf4c6945..5f011052bd 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -41,9 +41,6 @@ class MSVCCacheInvalidWarning(SCons.Warnings.WarningOnByDefault): pass -class MSCommonLogFileWarning(SCons.Warnings.WarningOnByDefault): - pass - def _check_logfile(logfile): if logfile and '"' in logfile: err_msg = ( From 76839d932e31aeb092f31fd1d81922aba193b446 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:22:43 -0400 Subject: [PATCH 146/386] Update Tool/msvs.py, update MSVS tests; and add additional tests for MSVS multi-project and solution builds. Tool/msvs.py: * Add "projectguid" argument to MSVSProject to enable user-defined GUID assignment per-project for multiple project solutions. * Project GUID priority: (1) MSVSProject "projectguid" argument, (2) SCons env MSVS_PROJECT_GUID value, (3) internally generated GUID. SCons env MSVS_PROJECT_GUID value should not be used for multiple project solutions without using projectguid arguments (otherwise, multiple projects will use the same GUID). * Store the project GUID as a node Tag when generating the MSVSProject and retrieve the project GUIDs using GetTag when generating the solution. * Move project node processing to a function so it can be called from multiple code locations. * Filter out solution nodes from project list (bugfix: auto_build_solution enabled and returned value used as project list includes auto-generated solution as a Project in the solution file). * Check for variant directory build of MSVSSolution and adjust src node accordingly similar to MSVSProject code. Bug fix: the solution file is generated in the build directory instead of the source directory. The placeholder solution file is now generated in the build directory and the solution file is generated in the source folder similar to the handling of project files. * Add project dsp nodes to the dsw source node list. This appears to always cause the project files to be generated before the solution files which is necessary to retrieve the project GUIDs for use in the solution file. This does change the behavior of clean for a project generated with auto_build_solution disabled and explicit solution generation: when the solution file is cleaned, the project files are also cleaned. The tests for vs 6.0-7.1 were changed accordingly. testing/framework/TestSConsMSVS.py: * Add known project GUID for single project tests and two known project GUIDs for dual project tests. * Add projectguid to MSVSProject arguments for single project test SConstruct/SConscript files. * Add PROJECT_BASENAME replaceable field to expected generated project file templates. * Add dual project and single solution templates. Tests: * Use known project guid for most tests which prevents have to hard-code generated GUIDs. * Add 4 (2x2) tests using two projects for combinations of auto_build_solutions settings (2) and variant directories (2). * Add 2 tests using two projects which use default GUID generation with and without variant directories. --- SCons/Tool/msvs.py | 99 +++++++--- SCons/Tool/msvsTests.py | 3 + test/MSVS/common-prefix.py | 7 +- test/MSVS/runfile.py | 7 +- test/MSVS/vs-6.0-clean.py | 14 +- test/MSVS/vs-7.0-clean.py | 13 +- test/MSVS/vs-7.0-files.py | 2 +- test/MSVS/vs-7.0-scc-files.py | 4 +- test/MSVS/vs-7.0-scc-legacy-files.py | 4 +- test/MSVS/vs-7.0-variant_dir.py | 2 +- test/MSVS/vs-7.1-clean.py | 16 +- test/MSVS/vs-7.1-files.py | 2 +- test/MSVS/vs-7.1-scc-files.py | 4 +- test/MSVS/vs-7.1-scc-legacy-files.py | 4 +- test/MSVS/vs-7.1-variant_dir.py | 2 +- test/MSVS/vs-mult-auto-guid.py | 149 ++++++++++++++ test/MSVS/vs-mult-auto-vardir-guid.py | 185 ++++++++++++++++++ test/MSVS/vs-mult-auto-vardir.py | 177 +++++++++++++++++ test/MSVS/vs-mult-auto.py | 141 ++++++++++++++ test/MSVS/vs-mult-noauto-vardir.py | 141 ++++++++++++++ test/MSVS/vs-mult-noauto.py | 117 +++++++++++ test/MSVS/vs-scc-files.py | 4 +- test/MSVS/vs-scc-legacy-files.py | 4 +- testing/framework/TestSConsMSVS.py | 269 ++++++++++++++++++++++++-- 24 files changed, 1278 insertions(+), 92 deletions(-) create mode 100644 test/MSVS/vs-mult-auto-guid.py create mode 100644 test/MSVS/vs-mult-auto-vardir-guid.py create mode 100644 test/MSVS/vs-mult-auto-vardir.py create mode 100644 test/MSVS/vs-mult-auto.py create mode 100644 test/MSVS/vs-mult-noauto-vardir.py create mode 100644 test/MSVS/vs-mult-noauto.py diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index eff1c8e3a4..e26ec118ae 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -124,6 +124,24 @@ def _generateGUID(slnfile, name, namespace=external_makefile_guid): return '{' + str(solution).upper() + '}' +def _projectGUID(env, dspfile): + """Generates a GUID for the project file to use. + + In order of preference: + * MSVSProject projectguid argument + * MSVS_PROJECT_GUID in environment + * Generated from the project file name (dspfile) + + Returns (str) + """ + project_guid = env.get('projectguid') + if not project_guid: + project_guid = env.get('MSVS_PROJECT_GUID') + if not project_guid: + project_guid = _generateGUID(dspfile, '') + return project_guid + + version_re = re.compile(r'(\d+\.\d+)(.*)') @@ -428,6 +446,9 @@ class _DSPGenerator: 'misc'] def __init__(self, dspfile, source, env) -> None: + dspnode = env.File(dspfile) + self.project_guid = _projectGUID(env, dspfile) + dspnode.Tag('project_guid', self.project_guid) self.dspfile = str(dspfile) try: get_abspath = dspfile.get_abspath @@ -912,8 +933,9 @@ def __init__(self, dspfile, source, env) -> None: def PrintHeader(self) -> None: env = self.env - versionstr = self.versionstr name = self.name + versionstr = self.versionstr + project_guid = self.project_guid encoding = self.env.subst('$MSVSENCODING') scc_provider = env.get('MSVS_SCC_PROVIDER', '') scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '') @@ -923,9 +945,6 @@ def PrintHeader(self) -> None: scc_local_path_legacy = env.get('MSVS_SCC_LOCAL_PATH', '') scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) scc_local_path = os.path.relpath(scc_connection_root, os.path.dirname(self.dspabs)) - project_guid = env.get('MSVS_PROJECT_GUID', '') - if not project_guid: - project_guid = _generateGUID(self.dspfile, '') if scc_provider != '': scc_attrs = '\tSccProjectName="%s"\n' % scc_project_name if scc_aux_path != '': @@ -1209,8 +1228,8 @@ def PrintHeader(self) -> None: env = self.env name = self.name versionstr = self.versionstr + project_guid = self.project_guid encoding = env.subst('$MSVSENCODING') - project_guid = env.get('MSVS_PROJECT_GUID', '') scc_provider = env.get('MSVS_SCC_PROVIDER', '') scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '') scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '') @@ -1219,8 +1238,6 @@ def PrintHeader(self) -> None: scc_local_path_legacy = env.get('MSVS_SCC_LOCAL_PATH', '') scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) scc_local_path = os.path.relpath(scc_connection_root, os.path.dirname(self.dspabs)) - if not project_guid: - project_guid = _generateGUID(self.dspfile, '') if scc_provider != '': scc_attrs = '\t\t%s\n' % scc_project_name if scc_aux_path != '': @@ -1465,6 +1482,26 @@ def Build(self): _GenerateV10User.Build(self) +def _projectDSPNodes(env): + if 'projects' not in env: + raise SCons.Errors.UserError("You must specify a 'projects' argument to create an MSVSSolution.") + projects = env['projects'] + if not SCons.Util.is_List(projects): + raise SCons.Errors.InternalError("The 'projects' argument must be a list of nodes.") + projects = SCons.Util.flatten(projects) + if len(projects) < 1: + raise SCons.Errors.UserError("You must specify at least one project to create an MSVSSolution.") + sln_suffix = env.subst('$MSVSSOLUTIONSUFFIX') + dspnodes = [] + for p in projects: + node = env.File(p) + if str(node).endswith(sln_suffix): + continue + dspnodes.append(node) + if len(dspnodes) < 1: + raise SCons.Errors.UserError("You must specify at least one project node to create an MSVSSolution.") + return dspnodes + class _DSWGenerator: """ Base class for DSW generators """ def __init__(self, dswfile, source, env) -> None: @@ -1472,15 +1509,8 @@ def __init__(self, dswfile, source, env) -> None: self.dsw_folder_path = os.path.dirname(os.path.abspath(self.dswfile)) self.env = env - if 'projects' not in env: - raise SCons.Errors.UserError("You must specify a 'projects' argument to create an MSVSSolution.") - projects = env['projects'] - if not SCons.Util.is_List(projects): - raise SCons.Errors.InternalError("The 'projects' argument must be a list of nodes.") - projects = SCons.Util.flatten(projects) - if len(projects) < 1: - raise SCons.Errors.UserError("You must specify at least one project to create an MSVSSolution.") - self.dspfiles = list(map(str, projects)) + dspnodes = _projectDSPNodes(env) + self.dsp_srcnodes = [dspnode.srcnode() for dspnode in dspnodes] if 'name' in self.env: self.name = self.env['name'] @@ -1554,8 +1584,9 @@ def AddConfig(self, variant, dswfile=dswfile) -> None: if not (p.platform in seen or seen.add(p.platform))] def GenerateProjectFilesInfo(self) -> None: - project_guid = self.env.get('MSVS_PROJECT_GUID', '') - for dspfile in self.dspfiles: + for dspnode in self.dsp_srcnodes: + project_guid = dspnode.GetTag('project_guid') + dspfile = str(dspnode) dsp_folder_path, name = os.path.split(dspfile) dsp_folder_path = os.path.abspath(dsp_folder_path) if SCons.Util.splitext(name)[1] == '.filters': @@ -1567,11 +1598,9 @@ def GenerateProjectFilesInfo(self) -> None: else: dsp_relative_file_path = os.path.join(dsp_relative_folder_path, name) if not project_guid: - guid = _generateGUID(dspfile, '') - else: - guid = project_guid + project_guid = _generateGUID(dspfile, '') dspfile_info = {'NAME': name, - 'GUID': guid, + 'GUID': project_guid, 'FOLDER_PATH': dsp_folder_path, 'FILE_PATH': dspfile, 'SLN_RELATIVE_FOLDER_PATH': dsp_relative_folder_path, @@ -1650,7 +1679,7 @@ def PrintSolution(self) -> None: env = self.env if 'MSVS_SCC_PROVIDER' in env: - scc_number_of_projects = len(self.dspfiles) + 1 + scc_number_of_projects = len(self.dsp_srcnodes) + 1 slnguid = self.slnguid scc_provider = env.get('MSVS_SCC_PROVIDER', '').replace(' ', r'\u0020') scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '').replace(' ', r'\u0020') @@ -1782,7 +1811,7 @@ class _GenerateV6DSW(_DSWGenerator): def PrintWorkspace(self) -> None: """ writes a DSW file """ name = self.name - dspfile = os.path.relpath(self.dspfiles[0], self.dsw_folder_path) + dspfile = os.path.relpath(str(self.dsp_srcnodes[0]), self.dsw_folder_path) self.file.write(V6DSWHeader % locals()) def Build(self): @@ -1872,7 +1901,22 @@ def GenerateProject(target, source, env): GenerateDSW(dswfile, source, env) def GenerateSolution(target, source, env) -> None: - GenerateDSW(target[0], source, env) + + builddswfile = target[0] + dswfile = builddswfile.srcnode() + + if dswfile is not builddswfile: + + try: + bdsw = open(str(builddswfile), "w+") + except OSError as detail: + print('Unable to open "' + str(dspfile) + '" for writing:',detail,'\n') + raise + + bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath()) + bdsw.close() + + GenerateDSW(dswfile, source, env) def projectEmitter(target, source, env): """Sets up the DSP dependencies.""" @@ -1966,7 +2010,7 @@ def projectEmitter(target, source, env): sourcelist = source if env.get('auto_build_solution', 1): - env['projects'] = [env.File(t).srcnode() for t in targetlist] + env['projects'] = [env.File(t) for t in targetlist] t, s = solutionEmitter(target, target, env) targetlist = targetlist + t @@ -2033,6 +2077,9 @@ def solutionEmitter(target, source, env): source = source + ' "%s"' % str(target[0]) source = [SCons.Node.Python.Value(source)] + dsp_nodes = _projectDSPNodes(env) + source.extend(dsp_nodes) + return ([target[0]], source) projectAction = SCons.Action.Action(GenerateProject, None) diff --git a/SCons/Tool/msvsTests.py b/SCons/Tool/msvsTests.py index 7893cafdcc..dd423d4bd9 100644 --- a/SCons/Tool/msvsTests.py +++ b/SCons/Tool/msvsTests.py @@ -420,6 +420,9 @@ def get(self, name, value=None): def Dir(self, name): return self.fs.Dir(name) + def File(self, name): + return self.fs.File(name) + def subst(self, key): if key[0] == '$': key = key[1:] diff --git a/test/MSVS/common-prefix.py b/test/MSVS/common-prefix.py index 90417a2a22..f511edc383 100644 --- a/test/MSVS/common-prefix.py +++ b/test/MSVS/common-prefix.py @@ -38,7 +38,7 @@ msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform test.skip_test(msg) -msvs_project_guid = TestSConsMSVS.MSVS_PROJECT_GUID +project_guid = TestSConsMSVS.PROJECT_GUID vcproj_template = """\ @@ -86,13 +86,12 @@ """ SConscript_contents = """\ -env=Environment(tools=['msvs'], - MSVS_VERSION = '8.0', - MSVS_PROJECT_GUID = '%(msvs_project_guid)s') +env=Environment(tools=['msvs'], MSVS_VERSION = '8.0') testsrc = %(testsrc)s env.MSVSProject(target = 'Test.vcproj', + projectguid = '%(project_guid)s', slnguid = '{SLNGUID}', srcs = testsrc, buildtarget = 'Test.exe', diff --git a/test/MSVS/runfile.py b/test/MSVS/runfile.py index bdd541d197..78e2c0dce6 100644 --- a/test/MSVS/runfile.py +++ b/test/MSVS/runfile.py @@ -38,7 +38,7 @@ msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform test.skip_test(msg) -sconscript_dict = {'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +sconscript_dict = {'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID} expected_vcprojfile = """\ @@ -93,11 +93,10 @@ """ SConscript_contents = """\ -env=Environment(tools=['msvs'], - MSVS_VERSION = '8.0', - MSVS_PROJECT_GUID = '%(MSVS_PROJECT_GUID)s') +env=Environment(tools=['msvs'], MSVS_VERSION = '8.0') env.MSVSProject(target = 'Test.vcproj', + projectguid = '%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = ['test.cpp'], buildtarget = 'Test.exe', diff --git a/test/MSVS/vs-6.0-clean.py b/test/MSVS/vs-6.0-clean.py index 0cbadba2f4..7045daa320 100644 --- a/test/MSVS/vs-6.0-clean.py +++ b/test/MSVS/vs-6.0-clean.py @@ -34,18 +34,12 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() - - # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['6.0'] - - expected_dspfile = TestSConsMSVS.expected_dspfile_6_0 expected_dswfile = TestSConsMSVS.expected_dswfile_6_0 - - test.write('SConstruct', """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='6.0',HOST_ARCH='%(HOST_ARCH)s') @@ -95,16 +89,14 @@ test.must_exist(test.workpath('Test.dsp')) test.must_exist(test.workpath('Test.dsw')) -test.run(arguments='-c Test.dsw') - -test.must_exist(test.workpath('Test.dsp')) -test.must_not_exist(test.workpath('Test.dsw')) - test.run(arguments='-c Test.dsp') test.must_not_exist(test.workpath('Test.dsp')) +test.must_exist(test.workpath('Test.dsw')) +test.run(arguments='-c Test.dsw') +test.must_not_exist(test.workpath('Test.dsw')) test.pass_test() diff --git a/test/MSVS/vs-7.0-clean.py b/test/MSVS/vs-7.0-clean.py index 04d8d1509d..603ae89b94 100644 --- a/test/MSVS/vs-7.0-clean.py +++ b/test/MSVS/vs-7.0-clean.py @@ -43,7 +43,6 @@ test.write('SConstruct', """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0', - MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] @@ -53,6 +52,7 @@ testmisc = ['readme.txt'] p = env.MSVSProject(target = 'Test.vcproj', + projectguid='%(PROJECT_GUID)s', srcs = testsrc, incs = testincs, localincs = testlocalincs, @@ -66,7 +66,7 @@ slnguid = '{SLNGUID}', projects = [p], variant = 'Release') -""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID}) +""" % {'HOST_ARCH':host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID}) test.run(arguments=".") @@ -92,15 +92,14 @@ test.must_exist(test.workpath('Test.vcproj')) test.must_exist(test.workpath('Test.sln')) +test.run(arguments='-c Test.vcproj') +test.must_not_exist(test.workpath('Test.vcproj')) +test.must_exist(test.workpath('Test.sln')) + test.run(arguments='-c Test.sln') -test.must_exist(test.workpath('Test.vcproj')) test.must_not_exist(test.workpath('Test.sln')) -test.run(arguments='-c Test.vcproj') - -test.must_not_exist(test.workpath('Test.vcproj')) - test.pass_test() # Local Variables: diff --git a/test/MSVS/vs-7.0-files.py b/test/MSVS/vs-7.0-files.py index 163f0a5e41..202036ce90 100644 --- a/test/MSVS/vs-7.0-files.py +++ b/test/MSVS/vs-7.0-files.py @@ -35,7 +35,7 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() -sconscript_dict = {'HOST_ARCH': host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +sconscript_dict = {'HOST_ARCH': host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID} # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.0'] diff --git a/test/MSVS/vs-7.0-scc-files.py b/test/MSVS/vs-7.0-scc-files.py index b87bb9a855..a517610560 100644 --- a/test/MSVS/vs-7.0-scc-files.py +++ b/test/MSVS/vs-7.0-scc-files.py @@ -42,7 +42,6 @@ expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 SConscript_contents = \ r"""env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0', - MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_CONNECTION_ROOT='.', @@ -58,6 +57,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', + projectguid='%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -66,7 +66,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +""" % {'HOST_ARCH':host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID} expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution diff --git a/test/MSVS/vs-7.0-scc-legacy-files.py b/test/MSVS/vs-7.0-scc-legacy-files.py index 5b900f80ca..30f2a7c2ad 100644 --- a/test/MSVS/vs-7.0-scc-legacy-files.py +++ b/test/MSVS/vs-7.0-scc-legacy-files.py @@ -42,7 +42,6 @@ expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0', - MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', @@ -56,6 +55,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', + projectguid='%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -64,7 +64,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +""" % {'HOST_ARCH':host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID} expected_vcproj_sccinfo = """\ \tSccProjectName="Perforce Project" diff --git a/test/MSVS/vs-7.0-variant_dir.py b/test/MSVS/vs-7.0-variant_dir.py index 5ec3b556ed..f83d0abe5e 100644 --- a/test/MSVS/vs-7.0-variant_dir.py +++ b/test/MSVS/vs-7.0-variant_dir.py @@ -33,7 +33,7 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() -sconscript_dict = {'HOST_ARCH': host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +sconscript_dict = {'HOST_ARCH': host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID} # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.0'] diff --git a/test/MSVS/vs-7.1-clean.py b/test/MSVS/vs-7.1-clean.py index 665871ca5a..45615573b3 100644 --- a/test/MSVS/vs-7.1-clean.py +++ b/test/MSVS/vs-7.1-clean.py @@ -43,7 +43,6 @@ test.write('SConstruct', """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1', - MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] @@ -53,6 +52,7 @@ testmisc = ['readme.txt'] p = env.MSVSProject(target = 'Test.vcproj', + projectguid='%(PROJECT_GUID)s', srcs = testsrc, incs = testincs, localincs = testlocalincs, @@ -66,7 +66,7 @@ slnguid = '{SLNGUID}', projects = [p], variant = 'Release') -""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID}) +""" % {'HOST_ARCH':host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID}) test.run(arguments=".") @@ -92,14 +92,20 @@ test.must_exist(test.workpath('Test.vcproj')) test.must_exist(test.workpath('Test.sln')) -test.run(arguments='-c Test.sln') +test.run(arguments='-c Test.vcproj') + +test.must_not_exist(test.workpath('Test.vcproj')) +test.must_exist(test.workpath('Test.sln')) + +test.run(arguments='.') test.must_exist(test.workpath('Test.vcproj')) -test.must_not_exist(test.workpath('Test.sln')) +test.must_exist(test.workpath('Test.sln')) -test.run(arguments='-c Test.vcproj') +test.run(arguments='-c Test.sln') test.must_not_exist(test.workpath('Test.vcproj')) +test.must_not_exist(test.workpath('Test.sln')) test.pass_test() diff --git a/test/MSVS/vs-7.1-files.py b/test/MSVS/vs-7.1-files.py index e372fe0ee2..9fcea6d5f6 100644 --- a/test/MSVS/vs-7.1-files.py +++ b/test/MSVS/vs-7.1-files.py @@ -35,7 +35,7 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() -sconscript_dict = {'HOST_ARCH': host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +sconscript_dict = {'HOST_ARCH': host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID} # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.1'] diff --git a/test/MSVS/vs-7.1-scc-files.py b/test/MSVS/vs-7.1-scc-files.py index 3af668f8c7..7ffbd8c776 100644 --- a/test/MSVS/vs-7.1-scc-files.py +++ b/test/MSVS/vs-7.1-scc-files.py @@ -42,7 +42,6 @@ expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1', - MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_CONNECTION_ROOT='.', @@ -58,6 +57,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', + projectguid='%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -66,7 +66,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +""" % {'HOST_ARCH':host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID} expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution diff --git a/test/MSVS/vs-7.1-scc-legacy-files.py b/test/MSVS/vs-7.1-scc-legacy-files.py index 9e0d0ea3da..2178edab3c 100644 --- a/test/MSVS/vs-7.1-scc-legacy-files.py +++ b/test/MSVS/vs-7.1-scc-legacy-files.py @@ -42,7 +42,6 @@ expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1', - MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', @@ -56,6 +55,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', + projectguid='%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -64,7 +64,7 @@ misc = testmisc, buildtarget = 'Test.exe', variant = 'Release') -""" % {'HOST_ARCH':host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +""" % {'HOST_ARCH':host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID} expected_vcproj_sccinfo = """\ \tSccProjectName="Perforce Project" diff --git a/test/MSVS/vs-7.1-variant_dir.py b/test/MSVS/vs-7.1-variant_dir.py index b4de4171ab..df337ae41e 100644 --- a/test/MSVS/vs-7.1-variant_dir.py +++ b/test/MSVS/vs-7.1-variant_dir.py @@ -33,7 +33,7 @@ test = TestSConsMSVS.TestSConsMSVS() host_arch = test.get_vs_host_arch() -sconscript_dict = {'HOST_ARCH': host_arch, 'MSVS_PROJECT_GUID': TestSConsMSVS.MSVS_PROJECT_GUID} +sconscript_dict = {'HOST_ARCH': host_arch, 'PROJECT_GUID': TestSConsMSVS.PROJECT_GUID} # Make the test infrastructure think we have this version of MSVS installed. test._msvs_versions = ['7.1'] diff --git a/test/MSVS/vs-mult-auto-guid.py b/test/MSVS/vs-mult-auto-guid.py new file mode 100644 index 0000000000..a69629b9c9 --- /dev/null +++ b/test/MSVS/vs-mult-auto-guid.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test two project files (.vcxproj) and three solution (.sln) file. +""" + +import TestSConsMSVS + +test = None + +for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + solution_file_1 = 'Test_1.sln' + solution_file_2 = 'Test_2.sln' + + if major >= 10: + project_guid_1 = "{F7D7CE55-37BF-51DE-8942-9377B2BE8387}" + project_guid_2 = "{8D17BC73-09FD-5B69-BBBF-1E40E0C63456}" + else: + project_guid_1 = "{53EE9FA7-6300-55B8-8A0E-A3DC40983390}" + project_guid_2 = "{57358E9B-126D-53F6-AD5A-559AB4A8EE62}" + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + ) + + expected_slnfile_1 = test.get_expected_sln_file_contents( + vc_version, + project_file_1, + ) + + expected_slnfile_2 = test.get_expected_sln_file_contents( + vc_version, + project_file_2, + ) + + SConstruct_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=1, + default_guids=True, + ) + + test.write('SConstruct', SConstruct_contents) + + test.run(arguments=".") + + test.must_exist(test.workpath(project_file_1)) + vcproj = test.read(project_file_1, 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(project_file_2)) + vcproj = test.read(project_file_2, 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(solution_file)) + sln = test.read(solution_file, 'r') + expect = test.msvs_substitute_projects( + expected_slnfile, subdir='src', + project_guid_1=project_guid_1, project_guid_2=project_guid_2 + ) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath(solution_file_1)) + sln = test.read(solution_file_1, 'r') + expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath(solution_file_2)) + sln = test.read(solution_file_2, 'r') + expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath(filters_file_1)) + test.must_exist(test.workpath(filters_file_2)) + else: + test.must_not_exist(test.workpath(filters_file_1)) + test.must_not_exist(test.workpath(filters_file_2)) + + # TODO: clean tests + +if test: + test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/MSVS/vs-mult-auto-vardir-guid.py b/test/MSVS/vs-mult-auto-vardir-guid.py new file mode 100644 index 0000000000..f4d49799b9 --- /dev/null +++ b/test/MSVS/vs-mult-auto-vardir-guid.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test two project files (.vcxproj) and three solution (.sln) file. +""" + +import TestSConsMSVS + +test = None + +for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + solution_file_1 = 'Test_1.sln' + solution_file_2 = 'Test_2.sln' + + if major >= 10: + project_guid_1 = "{5A243E49-07F0-54C3-B3FD-1DBDF1BA5C9E}" + project_guid_2 = "{E20E17C7-251E-5246-8FD1-5D51978A0A5D}" + else: + project_guid_1 = "{AB46DD68-8CD8-5832-B784-65B216B94739}" + project_guid_2 = "{03EB0BC3-DA68-5825-9EBB-D8713304E739}" + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + ) + + expected_slnfile_1 = test.get_expected_sln_file_contents( + vc_version, + project_file_1, + ) + + expected_slnfile_2 = test.get_expected_sln_file_contents( + vc_version, + project_file_2, + ) + + SConscript_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=1, + default_guids=True, + ) + + test.subdir('src') + + test.write('SConstruct', """\ +SConscript('src/SConscript', variant_dir='build') +""") + + test.write(['src', 'SConscript'], SConscript_contents) + + test.run(arguments=".") + + test.must_exist(test.workpath('src', project_file_1)) + vcproj = test.read(['src', project_file_1], 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', project_file_2)) + vcproj = test.read(['src', project_file_2], 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', solution_file)) + sln = test.read(['src', solution_file], 'r') + expect = test.msvs_substitute_projects( + expected_slnfile, subdir='src', + project_guid_1=project_guid_1, project_guid_2=project_guid_2 + ) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath('src', solution_file_1)) + sln = test.read(['src', solution_file_1], 'r') + expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath('src', solution_file_2)) + sln = test.read(['src', solution_file_2], 'r') + expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath('src', filters_file_1)) + test.must_exist(test.workpath('src', filters_file_2)) + else: + test.must_not_exist(test.workpath('src', filters_file_1)) + test.must_not_exist(test.workpath('src', filters_file_2)) + + test.must_match(['build', project_file_1], """\ +This is just a placeholder file. +The real project file is here: +%s +""" % test.workpath('src', project_file_1), mode='r') + + test.must_match(['build', project_file_2], """\ +This is just a placeholder file. +The real project file is here: +%s +""" % test.workpath('src', project_file_2), mode='r') + + test.must_match(['build', solution_file], """\ +This is just a placeholder file. +The real workspace file is here: +%s +""" % test.workpath('src', solution_file), mode='r') + + test.must_match(['build', solution_file_1], """\ +This is just a placeholder file. +The real workspace file is here: +%s +""" % test.workpath('src', solution_file_1), mode='r') + + test.must_match(['build', solution_file_2], """\ +This is just a placeholder file. +The real workspace file is here: +%s +""" % test.workpath('src', solution_file_2), mode='r') + + # TODO: clean tests + +if test: + test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/MSVS/vs-mult-auto-vardir.py b/test/MSVS/vs-mult-auto-vardir.py new file mode 100644 index 0000000000..06aebdd13d --- /dev/null +++ b/test/MSVS/vs-mult-auto-vardir.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test two project files (.vcxproj) and three solution (.sln) file. +""" + +import TestSConsMSVS + +project_guid_1 = TestSConsMSVS.PROJECT_GUID_1 +project_guid_2 = TestSConsMSVS.PROJECT_GUID_2 + +test = None + +for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + solution_file_1 = 'Test_1.sln' + solution_file_2 = 'Test_2.sln' + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + ) + + expected_slnfile_1 = test.get_expected_sln_file_contents( + vc_version, + project_file_1, + ) + + expected_slnfile_2 = test.get_expected_sln_file_contents( + vc_version, + project_file_2, + ) + + SConscript_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=1, + ) + + test.subdir('src') + + test.write('SConstruct', """\ +SConscript('src/SConscript', variant_dir='build') +""") + + test.write(['src', 'SConscript'], SConscript_contents) + + test.run(arguments=".") + + test.must_exist(test.workpath('src', project_file_1)) + vcproj = test.read(['src', project_file_1], 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', project_file_2)) + vcproj = test.read(['src', project_file_2], 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', solution_file)) + sln = test.read(['src', solution_file], 'r') + expect = test.msvs_substitute_projects(expected_slnfile, subdir='src') + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath('src', solution_file_1)) + sln = test.read(['src', solution_file_1], 'r') + expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath('src', solution_file_2)) + sln = test.read(['src', solution_file_2], 'r') + expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath('src', filters_file_1)) + test.must_exist(test.workpath('src', filters_file_2)) + else: + test.must_not_exist(test.workpath('src', filters_file_1)) + test.must_not_exist(test.workpath('src', filters_file_2)) + + test.must_match(['build', project_file_1], """\ +This is just a placeholder file. +The real project file is here: +%s +""" % test.workpath('src', project_file_1), mode='r') + + test.must_match(['build', project_file_2], """\ +This is just a placeholder file. +The real project file is here: +%s +""" % test.workpath('src', project_file_2), mode='r') + + test.must_match(['build', solution_file], """\ +This is just a placeholder file. +The real workspace file is here: +%s +""" % test.workpath('src', solution_file), mode='r') + + test.must_match(['build', solution_file_1], """\ +This is just a placeholder file. +The real workspace file is here: +%s +""" % test.workpath('src', solution_file_1), mode='r') + + test.must_match(['build', solution_file_2], """\ +This is just a placeholder file. +The real workspace file is here: +%s +""" % test.workpath('src', solution_file_2), mode='r') + + # TODO: clean tests + +if test: + test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/MSVS/vs-mult-auto.py b/test/MSVS/vs-mult-auto.py new file mode 100644 index 0000000000..bb6a4c42eb --- /dev/null +++ b/test/MSVS/vs-mult-auto.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test two project files (.vcxproj) and three solution (.sln) file. +""" + +import TestSConsMSVS + +project_guid_1 = TestSConsMSVS.PROJECT_GUID_1 +project_guid_2 = TestSConsMSVS.PROJECT_GUID_2 + +test = None + +for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + solution_file_1 = 'Test_1.sln' + solution_file_2 = 'Test_2.sln' + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + ) + + expected_slnfile_1 = test.get_expected_sln_file_contents( + vc_version, + project_file_1, + ) + + expected_slnfile_2 = test.get_expected_sln_file_contents( + vc_version, + project_file_2, + ) + + SConstruct_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=1, + ) + + test.write('SConstruct', SConstruct_contents) + + test.run(arguments=".") + + test.must_exist(test.workpath(project_file_1)) + vcproj = test.read(project_file_1, 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(project_file_2)) + vcproj = test.read(project_file_2, 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(solution_file)) + sln = test.read(solution_file, 'r') + expect = test.msvs_substitute_projects(expected_slnfile, subdir='src') + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath(solution_file_1)) + sln = test.read(solution_file_1, 'r') + expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath(solution_file_2)) + sln = test.read(solution_file_2, 'r') + expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath(filters_file_1)) + test.must_exist(test.workpath(filters_file_2)) + else: + test.must_not_exist(test.workpath(filters_file_1)) + test.must_not_exist(test.workpath(filters_file_2)) + + # TODO: clean tests + +if test: + test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/MSVS/vs-mult-noauto-vardir.py b/test/MSVS/vs-mult-noauto-vardir.py new file mode 100644 index 0000000000..459d57aae6 --- /dev/null +++ b/test/MSVS/vs-mult-noauto-vardir.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test two project files (.vcxproj) and a solution (.sln) file. +""" + +import TestSConsMSVS + +project_guid_1 = TestSConsMSVS.PROJECT_GUID_1 +project_guid_2 = TestSConsMSVS.PROJECT_GUID_2 + +test = None + +for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + ) + + SConscript_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=0, + ) + + test.subdir('src') + + test.write('SConstruct', """\ +SConscript('src/SConscript', variant_dir='build') +""") + + test.write(['src', 'SConscript'], SConscript_contents) + + test.run(arguments=".") + + test.must_exist(test.workpath('src', project_file_1)) + vcproj = test.read(['src', project_file_1], 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', project_file_2)) + vcproj = test.read(['src', project_file_2], 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', solution_file)) + sln = test.read(['src', solution_file], 'r') + expect = test.msvs_substitute_projects(expected_slnfile, subdir='src') + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath('src', filters_file_1)) + test.must_exist(test.workpath('src', filters_file_2)) + else: + test.must_not_exist(test.workpath('src', filters_file_1)) + test.must_not_exist(test.workpath('src', filters_file_2)) + + test.must_match(['build', project_file_1], """\ +This is just a placeholder file. +The real project file is here: +%s +""" % test.workpath('src', project_file_1), mode='r') + + test.must_match(['build', project_file_2], """\ +This is just a placeholder file. +The real project file is here: +%s +""" % test.workpath('src', project_file_2), mode='r') + + test.must_match(['build', solution_file], """\ +This is just a placeholder file. +The real workspace file is here: +%s +""" % test.workpath('src', solution_file), mode='r') + + # TODO: clean tests + +if test: + test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/MSVS/vs-mult-noauto.py b/test/MSVS/vs-mult-noauto.py new file mode 100644 index 0000000000..7554673fb7 --- /dev/null +++ b/test/MSVS/vs-mult-noauto.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test two project files (.vcxproj) and a solution (.sln) file. +""" + +import TestSConsMSVS + +project_guid_1 = TestSConsMSVS.PROJECT_GUID_1 +project_guid_2 = TestSConsMSVS.PROJECT_GUID_2 + +test = None + +for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + ) + + SConstruct_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=0, + ) + + test.write('SConstruct', SConstruct_contents) + + test.run(arguments=".") + + test.must_exist(test.workpath(project_file_1)) + vcproj = test.read(project_file_1, 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(project_file_2)) + vcproj = test.read(project_file_2, 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(solution_file)) + sln = test.read(solution_file, 'r') + expect = test.msvs_substitute_projects(expected_slnfile, subdir='src') + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath(filters_file_1)) + test.must_exist(test.workpath(filters_file_2)) + else: + test.must_not_exist(test.workpath(filters_file_1)) + test.must_not_exist(test.workpath(filters_file_2)) + + # TODO: clean tests + +if test: + test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/MSVS/vs-scc-files.py b/test/MSVS/vs-scc-files.py index 1b4ad228f8..d5e66d56b9 100644 --- a/test/MSVS/vs-scc-files.py +++ b/test/MSVS/vs-scc-files.py @@ -49,7 +49,6 @@ expected_vcprojfile = test.get_expected_proj_file_contents(vc_version, dirs, project_file) SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='{vc_version}', - MSVS_PROJECT_GUID='{project_guid}', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_CONNECTION_ROOT='.', @@ -64,6 +63,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = '{project_file}', + projectguid='{project_guid}', srcs = testsrc, incs = testincs, localincs = testlocalincs, @@ -73,7 +73,7 @@ variant = 'Release') """.format( vc_version=vc_version, project_file=project_file, - host_arch=host_arch, project_guid=TestSConsMSVS.MSVS_PROJECT_GUID, + host_arch=host_arch, project_guid=TestSConsMSVS.PROJECT_GUID, ) expected_sln_sccinfo = """\ diff --git a/test/MSVS/vs-scc-legacy-files.py b/test/MSVS/vs-scc-legacy-files.py index 49ea2ac165..cb2cca2e5e 100644 --- a/test/MSVS/vs-scc-legacy-files.py +++ b/test/MSVS/vs-scc-legacy-files.py @@ -50,7 +50,6 @@ SConscript_contents = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='{vc_version}', - MSVS_PROJECT_GUID='{project_guid}', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', @@ -64,6 +63,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = '{project_file}', + projectguid='{project_guid}', srcs = testsrc, incs = testincs, localincs = testlocalincs, @@ -73,7 +73,7 @@ variant = 'Release') """.format( vc_version=vc_version, project_file=project_file, - host_arch=host_arch, project_guid=TestSConsMSVS.MSVS_PROJECT_GUID, + host_arch=host_arch, project_guid=TestSConsMSVS.PROJECT_GUID, ) if major < 10: diff --git a/testing/framework/TestSConsMSVS.py b/testing/framework/TestSConsMSVS.py index 1a075e8b65..910dfd21e4 100644 --- a/testing/framework/TestSConsMSVS.py +++ b/testing/framework/TestSConsMSVS.py @@ -50,7 +50,9 @@ from TestSCons import __all__ -MSVS_PROJECT_GUID = "{00000000-0000-0000-0000-000000000000}" +PROJECT_GUID = "{00000000-0000-0000-0000-000000000000}" +PROJECT_GUID_1 = "{11111111-1111-1111-1111-111111111111}" +PROJECT_GUID_2 = "{22222222-2222-2222-2222-222222222222}" expected_dspfile_6_0 = '''\ # Microsoft Developer Studio Project File - Name="Test" - Package Owner=<4> @@ -314,7 +316,6 @@ SConscript_contents_7_0 = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0', - MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] @@ -324,6 +325,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', + projectguid = '%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -442,7 +444,6 @@ SConscript_contents_7_1 = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1', - MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] @@ -452,6 +453,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', + projectguid = '%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -488,9 +490,9 @@ \tKeyword="MakeFileProj"> \t @@ -508,10 +510,10 @@ \t\t\t> \t\t\t \t \t -\t\t%(MSVS_PROJECT_GUID)s +\t\t%(PROJECT_GUID)s -\t\tTest +\t\t%(PROJECT_BASENAME)s \t\tMakeFileProj \t\tNoUpgrade \t @@ -602,10 +604,10 @@ \t \t \t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "%(PROJECT_BASENAME)s.exe" +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "%(PROJECT_BASENAME)s.exe" +\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "%(PROJECT_BASENAME)s.exe" +\t\t%(PROJECT_BASENAME)s.exe \t\tDEF1;DEF2;DEF3=1234 \t\t%(INCLUDE_DIRS)s \t\t$(NMakeForcedIncludes) @@ -640,7 +642,6 @@ SConscript_contents_fmt = """\ env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='%(MSVS_VERSION)s', - MSVS_PROJECT_GUID='%(MSVS_PROJECT_GUID)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], HOST_ARCH='%(HOST_ARCH)s') @@ -652,6 +653,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = '%(PROJECT_FILE)s', + projectguid = '%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -662,6 +664,129 @@ variant = 'Release') """ +expected_projects_slnfile_fmt = """\ +Microsoft Visual Studio Solution File, Format Version %(FORMAT_VERSION)s +# Visual Studio %(VS_NUMBER)s +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%(PROJECT_NAME_1)s", "%(PROJECT_FILE_1)s", "" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%(PROJECT_NAME_2)s", "%(PROJECT_FILE_2)s", "" +EndProject +Global + +\tGlobalSection(SolutionConfigurationPlatforms) = preSolution +\t\tRelease|Win32 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(ProjectConfigurationPlatforms) = postSolution +\t\t.Release|Win32.ActiveCfg = Release|Win32 +\t\t.Release|Win32.Build.0 = Release|Win32 +\t\t.Release|Win32.ActiveCfg = Release|Win32 +\t\t.Release|Win32.Build.0 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(SolutionProperties) = preSolution +\t\tHideSolutionNode = FALSE +\tEndGlobalSection +EndGlobal +""" + +SConscript_projects_contents_fmt = """\ +env=Environment( + platform='win32', + tools=['msvs'], + MSVS_VERSION='%(MSVS_VERSION)s', + CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], + CPPPATH=['inc1', 'inc2'], + HOST_ARCH='%(HOST_ARCH)s', +) + +testsrc = ['test1.cpp', 'test2.cpp'] +testincs = [r'sdk_dir\\sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +p1 = env.MSVSProject( + target = '%(PROJECT_FILE_1)s', + projectguid = '%(PROJECT_GUID_1)s', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test_1.exe', + variant = 'Release', + auto_build_solution = %(AUTOBUILD_SOLUTION)s, +) + +p2 = env.MSVSProject( + target = '%(PROJECT_FILE_2)s', + projectguid = '%(PROJECT_GUID_2)s', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test_2.exe', + variant = 'Release', + auto_build_solution = %(AUTOBUILD_SOLUTION)s, +) + +env.MSVSSolution( + target = '%(SOLUTION_FILE)s', + projects = [p1, p2], + variant = 'Release', +) +""" + +SConscript_projects_defaultguids_contents_fmt = """\ +env=Environment( + platform='win32', + tools=['msvs'], + MSVS_VERSION='%(MSVS_VERSION)s', + CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], + CPPPATH=['inc1', 'inc2'], + HOST_ARCH='%(HOST_ARCH)s', +) + +testsrc = ['test1.cpp', 'test2.cpp'] +testincs = [r'sdk_dir\\sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +p1 = env.MSVSProject( + target = '%(PROJECT_FILE_1)s', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test_1.exe', + variant = 'Release', + auto_build_solution = %(AUTOBUILD_SOLUTION)s, +) + +p2 = env.MSVSProject( + target = '%(PROJECT_FILE_2)s', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test_2.exe', + variant = 'Release', + auto_build_solution = %(AUTOBUILD_SOLUTION)s, +) + +env.MSVSSolution( + target = '%(SOLUTION_FILE)s', + projects = [p1, p2], + variant = 'Release', +) +""" def get_tested_proj_file_vc_versions(): """ @@ -728,7 +853,7 @@ def msvs_substitute(self, input, msvs_ver, python = sys.executable if project_guid is None: - project_guid = MSVS_PROJECT_GUID + project_guid = PROJECT_GUID if 'SCONS_LIB_DIR' in os.environ: exec_script_main = f"from os.path import join; import sys; sys.path = [ r'{os.environ['SCONS_LIB_DIR']}' ] + sys.path; import SCons.Script; SCons.Script.main()" @@ -944,8 +1069,11 @@ def get_expected_proj_file_contents(self, vc_version, dirs, project_file): fmt = expected_vcxprojfile_fmt else: fmt = expected_vcprojfile_fmt + project_filename = os.path.split(project_file)[-1] + project_basename = os.path.splitext(project_filename)[0] return fmt % { - 'MSVS_PROJECT_GUID': MSVS_PROJECT_GUID, + 'PROJECT_BASENAME': project_basename, + 'PROJECT_GUID': PROJECT_GUID, 'TOOLS_VERSION': self._get_vcxproj_file_tools_version(vc_version), 'INCLUDE_DIRS': self._get_vcxproj_file_cpp_path(dirs), 'PLATFORM_TOOLSET': self._get_vcxproj_file_platform_toolset(vc_version), @@ -955,10 +1083,113 @@ def get_expected_sconscript_file_contents(self, vc_version, project_file): return SConscript_contents_fmt % { 'HOST_ARCH': self.get_vs_host_arch(), 'MSVS_VERSION': vc_version, - 'MSVS_PROJECT_GUID': MSVS_PROJECT_GUID, + 'PROJECT_GUID': PROJECT_GUID, 'PROJECT_FILE': project_file, } + def msvs_substitute_projects( + self, input, *, + subdir=None, + sconscript=None, + python=None, + project_guid_1=None, + project_guid_2=None, + vcproj_sccinfo: str='', + sln_sccinfo: str='' + ): + if not hasattr(self, '_msvs_versions'): + self.msvs_versions() + + if subdir: + workpath = self.workpath(subdir) + else: + workpath = self.workpath() + + if sconscript is None: + sconscript = self.workpath('SConstruct') + + if python is None: + python = sys.executable + + if project_guid_1 is None: + project_guid_1 = PROJECT_GUID_1 + + if project_guid_2 is None: + project_guid_2 = PROJECT_GUID_2 + + if 'SCONS_LIB_DIR' in os.environ: + exec_script_main = f"from os.path import join; import sys; sys.path = [ r'{os.environ['SCONS_LIB_DIR']}' ] + sys.path; import SCons.Script; SCons.Script.main()" + else: + exec_script_main = f"from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-{self.scons_version}'), join(sys.prefix, 'scons-{self.scons_version}'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" + exec_script_main_xml = exec_script_main.replace("'", "'") + + result = input.replace(r'', workpath) + result = result.replace(r'', python) + result = result.replace(r'', sconscript) + result = result.replace(r'', exec_script_main) + result = result.replace(r'', exec_script_main_xml) + result = result.replace(r'', project_guid_1) + result = result.replace(r'', project_guid_2) + result = result.replace('\n', vcproj_sccinfo) + result = result.replace('\n', sln_sccinfo) + return result + + def get_expected_projects_proj_file_contents(self, vc_version, dirs, project_file, project_guid): + """Returns the expected .vcxproj file contents""" + if project_file.endswith('.vcxproj'): + fmt = expected_vcxprojfile_fmt + else: + fmt = expected_vcprojfile_fmt + project_filename = os.path.split(project_file)[-1] + project_basename = os.path.splitext(project_filename)[0] + return fmt % { + 'PROJECT_BASENAME': project_basename, + 'PROJECT_GUID': project_guid, + 'TOOLS_VERSION': self._get_vcxproj_file_tools_version(vc_version), + 'INCLUDE_DIRS': self._get_vcxproj_file_cpp_path(dirs), + 'PLATFORM_TOOLSET': self._get_vcxproj_file_platform_toolset(vc_version), + } + + def get_expected_projects_sln_file_contents( + self, vc_version, + project_file_1, project_file_2, + ): + return expected_projects_slnfile_fmt % { + 'FORMAT_VERSION': self._get_solution_file_format_version(vc_version), + 'VS_NUMBER': self._get_solution_file_vs_number(vc_version), + 'PROJECT_NAME_1': project_file_1.split('.')[0], + 'PROJECT_FILE_1': project_file_1, + 'PROJECT_NAME_2': project_file_2.split('.')[0], + 'PROJECT_FILE_2': project_file_2, + } + + def get_expected_projects_sconscript_file_contents( + self, vc_version, + project_file_1, project_file_2, solution_file, + autobuild_solution=0, + default_guids=False, + ): + + values = { + 'HOST_ARCH': self.get_vs_host_arch(), + 'MSVS_VERSION': vc_version, + 'PROJECT_FILE_1': project_file_1, + 'PROJECT_FILE_2': project_file_2, + 'SOLUTION_FILE': solution_file, + "AUTOBUILD_SOLUTION": autobuild_solution, + } + + if default_guids: + format = SConscript_projects_defaultguids_contents_fmt + else: + format = SConscript_projects_contents_fmt + + values.update({ + 'PROJECT_GUID_1': PROJECT_GUID_1, + 'PROJECT_GUID_2': PROJECT_GUID_2, + }) + return format % values + # Local Variables: # tab-width:4 # indent-tabs-mode:nil From 2dac3e085897f6bae0d5fdef34bf1791c65d04b9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 12 Oct 2024 13:34:19 -0700 Subject: [PATCH 147/386] Added changes/release blurb --- CHANGES.txt | 4 ++++ RELEASE.txt | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index cebc9728ff..55a3257213 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,10 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release. RELEASE VERSION/DATE TO BE FILLED IN LATER + From Joseph Brill: + - Added error handling when creating MS VC detection debug log file specified by + SCONS_MSCOMMON_DEBUG + From Dillan Mills: - Fix support for short options (`-x`). diff --git a/RELEASE.txt b/RELEASE.txt index d27c18ad6c..40dfb2d49c 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -50,10 +50,11 @@ FIXES following the option, with no spaces (e.g. -j5 and not -j 5). - Fix a problem with compilation_db component initialization - the entries for assembler files were not being set up correctly. - - On Darwin, PermissionErrors are now handled while trying to access /etc/paths.d. This may occur if SCons is invoked in a sandboxed environment (such as Nix). +- Added error handling when creating MS VC detection debug log file specified by + SCONS_MSCOMMON_DEBUG IMPROVEMENTS ------------ From dc9673c41f68d2c86e8c2af2e9e55ff39b230d5e Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 12 Oct 2024 17:03:05 -0400 Subject: [PATCH 148/386] Update MSVS new test file headers based on the template test file header. [ci skip] --- test/MSVS/vs-mult-auto-guid.py | 7 +++---- test/MSVS/vs-mult-auto-vardir-guid.py | 7 +++---- test/MSVS/vs-mult-auto-vardir.py | 7 +++---- test/MSVS/vs-mult-auto.py | 7 +++---- test/MSVS/vs-mult-noauto-vardir.py | 7 +++---- test/MSVS/vs-mult-noauto.py | 7 +++---- 6 files changed, 18 insertions(+), 24 deletions(-) diff --git a/test/MSVS/vs-mult-auto-guid.py b/test/MSVS/vs-mult-auto-guid.py index a69629b9c9..1328f54b61 100644 --- a/test/MSVS/vs-mult-auto-guid.py +++ b/test/MSVS/vs-mult-auto-guid.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__" """ Test two project files (.vcxproj) and three solution (.sln) file. diff --git a/test/MSVS/vs-mult-auto-vardir-guid.py b/test/MSVS/vs-mult-auto-vardir-guid.py index f4d49799b9..026a2e8064 100644 --- a/test/MSVS/vs-mult-auto-vardir-guid.py +++ b/test/MSVS/vs-mult-auto-vardir-guid.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__" """ Test two project files (.vcxproj) and three solution (.sln) file. diff --git a/test/MSVS/vs-mult-auto-vardir.py b/test/MSVS/vs-mult-auto-vardir.py index 06aebdd13d..9a834749f1 100644 --- a/test/MSVS/vs-mult-auto-vardir.py +++ b/test/MSVS/vs-mult-auto-vardir.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__" """ Test two project files (.vcxproj) and three solution (.sln) file. diff --git a/test/MSVS/vs-mult-auto.py b/test/MSVS/vs-mult-auto.py index bb6a4c42eb..4b071c23ed 100644 --- a/test/MSVS/vs-mult-auto.py +++ b/test/MSVS/vs-mult-auto.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__" """ Test two project files (.vcxproj) and three solution (.sln) file. diff --git a/test/MSVS/vs-mult-noauto-vardir.py b/test/MSVS/vs-mult-noauto-vardir.py index 459d57aae6..18aacba887 100644 --- a/test/MSVS/vs-mult-noauto-vardir.py +++ b/test/MSVS/vs-mult-noauto-vardir.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__" """ Test two project files (.vcxproj) and a solution (.sln) file. diff --git a/test/MSVS/vs-mult-noauto.py b/test/MSVS/vs-mult-noauto.py index 7554673fb7..ab0bb6dc62 100644 --- a/test/MSVS/vs-mult-noauto.py +++ b/test/MSVS/vs-mult-noauto.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__" """ Test two project files (.vcxproj) and a solution (.sln) file. From d536184b92cfc5047cd4781eeb75c636292f453a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 12 Oct 2024 20:29:57 -0400 Subject: [PATCH 149/386] Remove MSVSProject argument projectguid and replace with MSVS_PROJECT_GUID in all test files. --- SCons/Tool/msvs.py | 5 +---- test/MSVS/common-prefix.py | 2 +- test/MSVS/runfile.py | 2 +- test/MSVS/vs-7.0-clean.py | 2 +- test/MSVS/vs-7.0-scc-files.py | 2 +- test/MSVS/vs-7.0-scc-legacy-files.py | 2 +- test/MSVS/vs-7.1-clean.py | 2 +- test/MSVS/vs-7.1-scc-files.py | 2 +- test/MSVS/vs-7.1-scc-legacy-files.py | 2 +- test/MSVS/vs-scc-files.py | 2 +- test/MSVS/vs-scc-legacy-files.py | 2 +- testing/framework/TestSConsMSVS.py | 10 +++++----- 12 files changed, 16 insertions(+), 19 deletions(-) diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index e26ec118ae..8835a118b7 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -128,15 +128,12 @@ def _projectGUID(env, dspfile): """Generates a GUID for the project file to use. In order of preference: - * MSVSProject projectguid argument * MSVS_PROJECT_GUID in environment * Generated from the project file name (dspfile) Returns (str) """ - project_guid = env.get('projectguid') - if not project_guid: - project_guid = env.get('MSVS_PROJECT_GUID') + project_guid = env.get('MSVS_PROJECT_GUID') if not project_guid: project_guid = _generateGUID(dspfile, '') return project_guid diff --git a/test/MSVS/common-prefix.py b/test/MSVS/common-prefix.py index f511edc383..070ce0d0e4 100644 --- a/test/MSVS/common-prefix.py +++ b/test/MSVS/common-prefix.py @@ -91,7 +91,7 @@ testsrc = %(testsrc)s env.MSVSProject(target = 'Test.vcproj', - projectguid = '%(project_guid)s', + MSVS_PROJECT_GUID = '%(project_guid)s', slnguid = '{SLNGUID}', srcs = testsrc, buildtarget = 'Test.exe', diff --git a/test/MSVS/runfile.py b/test/MSVS/runfile.py index 78e2c0dce6..08c8f31633 100644 --- a/test/MSVS/runfile.py +++ b/test/MSVS/runfile.py @@ -96,7 +96,7 @@ env=Environment(tools=['msvs'], MSVS_VERSION = '8.0') env.MSVSProject(target = 'Test.vcproj', - projectguid = '%(PROJECT_GUID)s', + MSVS_PROJECT_GUID = '%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = ['test.cpp'], buildtarget = 'Test.exe', diff --git a/test/MSVS/vs-7.0-clean.py b/test/MSVS/vs-7.0-clean.py index 603ae89b94..91a8cb2a79 100644 --- a/test/MSVS/vs-7.0-clean.py +++ b/test/MSVS/vs-7.0-clean.py @@ -52,7 +52,7 @@ testmisc = ['readme.txt'] p = env.MSVSProject(target = 'Test.vcproj', - projectguid='%(PROJECT_GUID)s', + MSVS_PROJECT_GUID='%(PROJECT_GUID)s', srcs = testsrc, incs = testincs, localincs = testlocalincs, diff --git a/test/MSVS/vs-7.0-scc-files.py b/test/MSVS/vs-7.0-scc-files.py index a517610560..bb45e5535d 100644 --- a/test/MSVS/vs-7.0-scc-files.py +++ b/test/MSVS/vs-7.0-scc-files.py @@ -57,7 +57,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', - projectguid='%(PROJECT_GUID)s', + MSVS_PROJECT_GUID='%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, diff --git a/test/MSVS/vs-7.0-scc-legacy-files.py b/test/MSVS/vs-7.0-scc-legacy-files.py index 30f2a7c2ad..d6eb24780f 100644 --- a/test/MSVS/vs-7.0-scc-legacy-files.py +++ b/test/MSVS/vs-7.0-scc-legacy-files.py @@ -55,7 +55,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', - projectguid='%(PROJECT_GUID)s', + MSVS_PROJECT_GUID='%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, diff --git a/test/MSVS/vs-7.1-clean.py b/test/MSVS/vs-7.1-clean.py index 45615573b3..515a9e649d 100644 --- a/test/MSVS/vs-7.1-clean.py +++ b/test/MSVS/vs-7.1-clean.py @@ -52,7 +52,7 @@ testmisc = ['readme.txt'] p = env.MSVSProject(target = 'Test.vcproj', - projectguid='%(PROJECT_GUID)s', + MSVS_PROJECT_GUID='%(PROJECT_GUID)s', srcs = testsrc, incs = testincs, localincs = testlocalincs, diff --git a/test/MSVS/vs-7.1-scc-files.py b/test/MSVS/vs-7.1-scc-files.py index 7ffbd8c776..9fdf5e1449 100644 --- a/test/MSVS/vs-7.1-scc-files.py +++ b/test/MSVS/vs-7.1-scc-files.py @@ -57,7 +57,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', - projectguid='%(PROJECT_GUID)s', + MSVS_PROJECT_GUID='%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, diff --git a/test/MSVS/vs-7.1-scc-legacy-files.py b/test/MSVS/vs-7.1-scc-legacy-files.py index 2178edab3c..8bb6b96fb0 100644 --- a/test/MSVS/vs-7.1-scc-legacy-files.py +++ b/test/MSVS/vs-7.1-scc-legacy-files.py @@ -55,7 +55,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', - projectguid='%(PROJECT_GUID)s', + MSVS_PROJECT_GUID='%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, diff --git a/test/MSVS/vs-scc-files.py b/test/MSVS/vs-scc-files.py index d5e66d56b9..2c16da2233 100644 --- a/test/MSVS/vs-scc-files.py +++ b/test/MSVS/vs-scc-files.py @@ -63,7 +63,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = '{project_file}', - projectguid='{project_guid}', + MSVS_PROJECT_GUID='{project_guid}', srcs = testsrc, incs = testincs, localincs = testlocalincs, diff --git a/test/MSVS/vs-scc-legacy-files.py b/test/MSVS/vs-scc-legacy-files.py index cb2cca2e5e..7badd612de 100644 --- a/test/MSVS/vs-scc-legacy-files.py +++ b/test/MSVS/vs-scc-legacy-files.py @@ -63,7 +63,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = '{project_file}', - projectguid='{project_guid}', + MSVS_PROJECT_GUID='{project_guid}', srcs = testsrc, incs = testincs, localincs = testlocalincs, diff --git a/testing/framework/TestSConsMSVS.py b/testing/framework/TestSConsMSVS.py index 910dfd21e4..e4ac3a9353 100644 --- a/testing/framework/TestSConsMSVS.py +++ b/testing/framework/TestSConsMSVS.py @@ -325,7 +325,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', - projectguid = '%(PROJECT_GUID)s', + MSVS_PROJECT_GUID = '%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -453,7 +453,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = 'Test.vcproj', - projectguid = '%(PROJECT_GUID)s', + MSVS_PROJECT_GUID = '%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -653,7 +653,7 @@ testmisc = ['readme.txt'] env.MSVSProject(target = '%(PROJECT_FILE)s', - projectguid = '%(PROJECT_GUID)s', + MSVS_PROJECT_GUID = '%(PROJECT_GUID)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -706,7 +706,7 @@ p1 = env.MSVSProject( target = '%(PROJECT_FILE_1)s', - projectguid = '%(PROJECT_GUID_1)s', + MSVS_PROJECT_GUID = '%(PROJECT_GUID_1)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -720,7 +720,7 @@ p2 = env.MSVSProject( target = '%(PROJECT_FILE_2)s', - projectguid = '%(PROJECT_GUID_2)s', + MSVS_PROJECT_GUID = '%(PROJECT_GUID_2)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, From dacb14146cd717bdc3da3b90acb585a41b4968a2 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 18 Oct 2024 10:56:23 -0600 Subject: [PATCH 150/386] Minor test cleanup - EnvironmentTests Convert two huge blocks of test parameters to iterate-over-list from former while-then-del strategy. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 + SCons/EnvironmentTests.py | 361 +++++++++++++++++++------------------- 2 files changed, 181 insertions(+), 183 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 98326479b1..b9467a1fd1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -57,6 +57,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Some manpage cleanup for the gettext and pdf/ps builders. - Some clarifications in the User Guide "Environments" chapter. - Fix nasm test for missing include file, cleanup. + - Change long-standing irritant in Environment tests - instead of using + a while loop to pull test info from a list of tests and then delete + the test, structure the test data as a list of tuples and iterate it. From Alex James: - On Darwin, PermissionErrors are now handled while trying to access diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index 361dda9722..a37e424646 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -1564,7 +1564,7 @@ def test__stripixes(self) -> None: def test_gvars(self) -> None: - """Test the Environment gvars() method""" + """Test the Environment gvars() method.""" env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z') gvars = env.gvars() assert gvars['XXX'] == 'x', gvars['XXX'] @@ -1572,7 +1572,7 @@ def test_gvars(self) -> None: assert gvars['ZZZ'] == 'z', gvars['ZZZ'] def test__update(self) -> None: - """Test the _update() method""" + """Test the _update() method.""" env = self.TestEnvironment(X = 'x', Y = 'y', Z = 'z') assert env['X'] == 'x', env['X'] assert env['Y'] == 'y', env['Y'] @@ -1592,106 +1592,103 @@ def test__update(self) -> None: assert env['SOURCES'] == 'sss', env['SOURCES'] def test_Append(self) -> None: - """Test appending to construction variables in an Environment - """ - + """Test appending to construction variables in an Environment.""" b1 = Environment()['BUILDERS'] b2 = Environment()['BUILDERS'] assert b1 == b2, diff_dict(b1, b2) cases = [ - 'a1', 'A1', 'a1A1', - 'a2', ['A2'], ['a2', 'A2'], - 'a3', UL(['A3']), UL(['a', '3', 'A3']), - 'a4', '', 'a4', - 'a5', [], ['a5'], - 'a6', UL([]), UL(['a', '6']), - 'a7', [''], ['a7', ''], - 'a8', UL(['']), UL(['a', '8', '']), - - ['e1'], 'E1', ['e1', 'E1'], - ['e2'], ['E2'], ['e2', 'E2'], - ['e3'], UL(['E3']), UL(['e3', 'E3']), - ['e4'], '', ['e4'], - ['e5'], [], ['e5'], - ['e6'], UL([]), UL(['e6']), - ['e7'], [''], ['e7', ''], - ['e8'], UL(['']), UL(['e8', '']), - - UL(['i1']), 'I1', UL(['i1', 'I', '1']), - UL(['i2']), ['I2'], UL(['i2', 'I2']), - UL(['i3']), UL(['I3']), UL(['i3', 'I3']), - UL(['i4']), '', UL(['i4']), - UL(['i5']), [], UL(['i5']), - UL(['i6']), UL([]), UL(['i6']), - UL(['i7']), [''], UL(['i7', '']), - UL(['i8']), UL(['']), UL(['i8', '']), - - {'d1':1}, 'D1', {'d1':1, 'D1':None}, - {'d2':1}, ['D2'], {'d2':1, 'D2':None}, - {'d3':1}, UL(['D3']), {'d3':1, 'D3':None}, - {'d4':1}, {'D4':1}, {'d4':1, 'D4':1}, - {'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1}), - - UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None}), - UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None}), - UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None}), - UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1}), - UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}), - - '', 'M1', 'M1', - '', ['M2'], ['M2'], - '', UL(['M3']), UL(['M3']), - '', '', '', - '', [], [], - '', UL([]), UL([]), - '', [''], [''], - '', UL(['']), UL(['']), - - [], 'N1', ['N1'], - [], ['N2'], ['N2'], - [], UL(['N3']), UL(['N3']), - [], '', [], - [], [], [], - [], UL([]), UL([]), - [], [''], [''], - [], UL(['']), UL(['']), - - UL([]), 'O1', ['O', '1'], - UL([]), ['O2'], ['O2'], - UL([]), UL(['O3']), UL(['O3']), - UL([]), '', UL([]), - UL([]), [], UL([]), - UL([]), UL([]), UL([]), - UL([]), [''], UL(['']), - UL([]), UL(['']), UL(['']), - - [''], 'P1', ['', 'P1'], - [''], ['P2'], ['', 'P2'], - [''], UL(['P3']), UL(['', 'P3']), - [''], '', [''], - [''], [], [''], - [''], UL([]), UL(['']), - [''], [''], ['', ''], - [''], UL(['']), UL(['', '']), - - UL(['']), 'Q1', ['', 'Q', '1'], - UL(['']), ['Q2'], ['', 'Q2'], - UL(['']), UL(['Q3']), UL(['', 'Q3']), - UL(['']), '', UL(['']), - UL(['']), [], UL(['']), - UL(['']), UL([]), UL(['']), - UL(['']), [''], UL(['', '']), - UL(['']), UL(['']), UL(['', '']), + ('a1', 'A1', 'a1A1'), + ('a2', ['A2'], ['a2', 'A2']), + ('a3', UL(['A3']), UL(['a', '3', 'A3'])), + ('a4', '', 'a4'), + ('a5', [], ['a5']), + ('a6', UL([]), UL(['a', '6'])), + ('a7', [''], ['a7', '']), + ('a8', UL(['']), UL(['a', '8', ''])), + + (['e1'], 'E1', ['e1', 'E1']), + (['e2'], ['E2'], ['e2', 'E2']), + (['e3'], UL(['E3']), UL(['e3', 'E3'])), + (['e4'], '', ['e4']), + (['e5'], [], ['e5']), + (['e6'], UL([]), UL(['e6'])), + (['e7'], [''], ['e7', '']), + (['e8'], UL(['']), UL(['e8', ''])), + + (UL(['i1']), 'I1', UL(['i1', 'I', '1'])), + (UL(['i2']), ['I2'], UL(['i2', 'I2'])), + (UL(['i3']), UL(['I3']), UL(['i3', 'I3'])), + (UL(['i4']), '', UL(['i4'])), + (UL(['i5']), [], UL(['i5'])), + (UL(['i6']), UL([]), UL(['i6'])), + (UL(['i7']), [''], UL(['i7', ''])), + (UL(['i8']), UL(['']), UL(['i8', ''])), + + ({'d1':1}, 'D1', {'d1':1, 'D1':None}), + ({'d2':1}, ['D2'], {'d2':1, 'D2':None}), + ({'d3':1}, UL(['D3']), {'d3':1, 'D3':None}), + ({'d4':1}, {'D4':1}, {'d4':1, 'D4':1}), + ({'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1})), + + (UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None})), + (UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None})), + (UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None})), + (UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1})), + ((UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}))), + + ('', 'M1', 'M1'), + ('', ['M2'], ['M2']), + ('', UL(['M3']), UL(['M3'])), + ('', '', ''), + ('', [], []), + ('', UL([]), UL([])), + ('', [''], ['']), + ('', UL(['']), UL([''])), + + ([], 'N1', ['N1']), + ([], ['N2'], ['N2']), + ([], UL(['N3']), UL(['N3'])), + ([], '', []), + ([], [], []), + ([], UL([]), UL([])), + ([], [''], ['']), + ([], UL(['']), UL([''])), + + (UL([]), 'O1', ['O', '1']), + (UL([]), ['O2'], ['O2']), + (UL([]), UL(['O3']), UL(['O3'])), + (UL([]), '', UL([])), + (UL([]), [], UL([])), + (UL([]), UL([]), UL([])), + (UL([]), [''], UL([''])), + (UL([]), UL(['']), UL([''])), + + ([''], 'P1', ['', 'P1']), + ([''], ['P2'], ['', 'P2']), + ([''], UL(['P3']), UL(['', 'P3'])), + ([''], '', ['']), + ([''], [], ['']), + ([''], UL([]), UL([''])), + ([''], [''], ['', '']), + ([''], UL(['']), UL(['', ''])), + + (UL(['']), 'Q1', ['', 'Q', '1']), + (UL(['']), ['Q2'], ['', 'Q2']), + (UL(['']), UL(['Q3']), UL(['', 'Q3'])), + (UL(['']), '', UL([''])), + (UL(['']), [], UL([''])), + (UL(['']), UL([]), UL([''])), + (UL(['']), [''], UL(['', ''])), + (UL(['']), UL(['']), UL(['', ''])), ] env = Environment() failed = 0 - while cases: - input, append, expect = cases[:3] + for input, append, expect in cases: env['XXX'] = copy.copy(input) try: - env.Append(XXX = append) + env.Append(XXX=append) except Exception as e: if failed == 0: print() print(" %s Append %s exception: %s" % \ @@ -1703,8 +1700,7 @@ def test_Append(self) -> None: if failed == 0: print() print(" %s Append %s => %s did not match %s" % \ (repr(input), repr(append), repr(result), repr(expect))) - failed = failed + 1 - del cases[:3] + failed += 1 assert failed == 0, "%d Append() cases failed" % failed env['UL'] = UL(['foo']) @@ -2376,101 +2372,99 @@ def test_Platform(self) -> None: assert env['OBJSUFFIX'] == '.obj', env['OBJSUFFIX'] def test_Prepend(self) -> None: - """Test prepending to construction variables in an Environment - """ + """Test prepending to construction variables in an Environment.""" cases = [ - 'a1', 'A1', 'A1a1', - 'a2', ['A2'], ['A2', 'a2'], - 'a3', UL(['A3']), UL(['A3', 'a', '3']), - 'a4', '', 'a4', - 'a5', [], ['a5'], - 'a6', UL([]), UL(['a', '6']), - 'a7', [''], ['', 'a7'], - 'a8', UL(['']), UL(['', 'a', '8']), - - ['e1'], 'E1', ['E1', 'e1'], - ['e2'], ['E2'], ['E2', 'e2'], - ['e3'], UL(['E3']), UL(['E3', 'e3']), - ['e4'], '', ['e4'], - ['e5'], [], ['e5'], - ['e6'], UL([]), UL(['e6']), - ['e7'], [''], ['', 'e7'], - ['e8'], UL(['']), UL(['', 'e8']), - - UL(['i1']), 'I1', UL(['I', '1', 'i1']), - UL(['i2']), ['I2'], UL(['I2', 'i2']), - UL(['i3']), UL(['I3']), UL(['I3', 'i3']), - UL(['i4']), '', UL(['i4']), - UL(['i5']), [], UL(['i5']), - UL(['i6']), UL([]), UL(['i6']), - UL(['i7']), [''], UL(['', 'i7']), - UL(['i8']), UL(['']), UL(['', 'i8']), - - {'d1':1}, 'D1', {'d1':1, 'D1':None}, - {'d2':1}, ['D2'], {'d2':1, 'D2':None}, - {'d3':1}, UL(['D3']), {'d3':1, 'D3':None}, - {'d4':1}, {'D4':1}, {'d4':1, 'D4':1}, - {'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1}), - - UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None}), - UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None}), - UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None}), - UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1}), - UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}), - - '', 'M1', 'M1', - '', ['M2'], ['M2'], - '', UL(['M3']), UL(['M3']), - '', '', '', - '', [], [], - '', UL([]), UL([]), - '', [''], [''], - '', UL(['']), UL(['']), - - [], 'N1', ['N1'], - [], ['N2'], ['N2'], - [], UL(['N3']), UL(['N3']), - [], '', [], - [], [], [], - [], UL([]), UL([]), - [], [''], [''], - [], UL(['']), UL(['']), - - UL([]), 'O1', UL(['O', '1']), - UL([]), ['O2'], UL(['O2']), - UL([]), UL(['O3']), UL(['O3']), - UL([]), '', UL([]), - UL([]), [], UL([]), - UL([]), UL([]), UL([]), - UL([]), [''], UL(['']), - UL([]), UL(['']), UL(['']), - - [''], 'P1', ['P1', ''], - [''], ['P2'], ['P2', ''], - [''], UL(['P3']), UL(['P3', '']), - [''], '', [''], - [''], [], [''], - [''], UL([]), UL(['']), - [''], [''], ['', ''], - [''], UL(['']), UL(['', '']), - - UL(['']), 'Q1', UL(['Q', '1', '']), - UL(['']), ['Q2'], UL(['Q2', '']), - UL(['']), UL(['Q3']), UL(['Q3', '']), - UL(['']), '', UL(['']), - UL(['']), [], UL(['']), - UL(['']), UL([]), UL(['']), - UL(['']), [''], UL(['', '']), - UL(['']), UL(['']), UL(['', '']), + ('a1', 'A1', 'A1a1'), + ('a2', ['A2'], ['A2', 'a2']), + ('a3', UL(['A3']), UL(['A3', 'a', '3'])), + ('a4', '', 'a4'), + ('a5', [], ['a5']), + ('a6', UL([]), UL(['a', '6'])), + ('a7', [''], ['', 'a7']), + ('a8', UL(['']), UL(['', 'a', '8'])), + + (['e1'], 'E1', ['E1', 'e1']), + (['e2'], ['E2'], ['E2', 'e2']), + (['e3'], UL(['E3']), UL(['E3', 'e3'])), + (['e4'], '', ['e4']), + (['e5'], [], ['e5']), + (['e6'], UL([]), UL(['e6'])), + (['e7'], [''], ['', 'e7']), + (['e8'], UL(['']), UL(['', 'e8'])), + + (UL(['i1']), 'I1', UL(['I', '1', 'i1'])), + (UL(['i2']), ['I2'], UL(['I2', 'i2'])), + (UL(['i3']), UL(['I3']), UL(['I3', 'i3'])), + (UL(['i4']), '', UL(['i4'])), + (UL(['i5']), [], UL(['i5'])), + (UL(['i6']), UL([]), UL(['i6'])), + (UL(['i7']), [''], UL(['', 'i7'])), + (UL(['i8']), UL(['']), UL(['', 'i8'])), + + ({'d1':1}, 'D1', {'d1':1, 'D1':None}), + ({'d2':1}, ['D2'], {'d2':1, 'D2':None}), + ({'d3':1}, UL(['D3']), {'d3':1, 'D3':None}), + ({'d4':1}, {'D4':1}, {'d4':1, 'D4':1}), + ({'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1})), + + (UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None})), + (UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None})), + (UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None})), + (UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1})), + (UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1})), + + ('', 'M1', 'M1'), + ('', ['M2'], ['M2']), + ('', UL(['M3']), UL(['M3'])), + ('', '', ''), + ('', [], []), + ('', UL([]), UL([])), + ('', [''], ['']), + ('', UL(['']), UL([''])), + + ([], 'N1', ['N1']), + ([], ['N2'], ['N2']), + ([], UL(['N3']), UL(['N3'])), + ([], '', []), + ([], [], []), + ([], UL([]), UL([])), + ([], [''], ['']), + ([], UL(['']), UL([''])), + + (UL([]), 'O1', UL(['O', '1'])), + (UL([]), ['O2'], UL(['O2'])), + (UL([]), UL(['O3']), UL(['O3'])), + (UL([]), '', UL([])), + (UL([]), [], UL([])), + (UL([]), UL([]), UL([])), + (UL([]), [''], UL([''])), + (UL([]), UL(['']), UL([''])), + + ([''], 'P1', ['P1', '']), + ([''], ['P2'], ['P2', '']), + ([''], UL(['P3']), UL(['P3', ''])), + ([''], '', ['']), + ([''], [], ['']), + ([''], UL([]), UL([''])), + ([''], [''], ['', '']), + ([''], UL(['']), UL(['', ''])), + + (UL(['']), 'Q1', UL(['Q', '1', ''])), + (UL(['']), ['Q2'], UL(['Q2', ''])), + (UL(['']), UL(['Q3']), UL(['Q3', ''])), + (UL(['']), '', UL([''])), + (UL(['']), [], UL([''])), + (UL(['']), UL([]), UL([''])), + (UL(['']), [''], UL(['', ''])), + (UL(['']), UL(['']), UL(['', ''])), ] env = Environment() failed = 0 - while cases: - input, prepend, expect = cases[:3] + for input, prepend, expect in cases: env['XXX'] = copy.copy(input) try: - env.Prepend(XXX = prepend) + env.Prepend(XXX=prepend) except Exception as e: if failed == 0: print() print(" %s Prepend %s exception: %s" % \ @@ -2482,8 +2476,7 @@ def test_Prepend(self) -> None: if failed == 0: print() print(" %s Prepend %s => %s did not match %s" % \ (repr(input), repr(prepend), repr(result), repr(expect))) - failed = failed + 1 - del cases[:3] + failed += 1 assert failed == 0, "%d Prepend() cases failed" % failed env['UL'] = UL(['foo']) @@ -2597,8 +2590,10 @@ def test_PrependUnique(self) -> None: env.PrependUnique(DDD1='b', delete_existing=True) assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front + env.PrependUnique(DDD1=['a', 'c'], delete_existing=True) assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front + env.PrependUnique(DDD1=['d', 'e', 'd'], delete_existing=True) assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1'] From 80ff4c7530e9b01afa896423fa025f15fe91cb4a Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 6 Oct 2024 11:34:37 -0600 Subject: [PATCH 151/386] Adjust tests in case running as root Although validation tests are not normally run as root, there may be cicrumstances when it happens - one known case is when the test suite is run as part of a particular Linux distros package construction. It isn't too hard to avoid the few places where we counted on something failing because of permissions, which don't if the user is root - added a few skips. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ RELEASE.txt | 8 ++++++ SCons/CacheDirTests.py | 41 +++++++++++++++++++--------- SCons/Node/FSTests.py | 40 +++++++++++++++++---------- SCons/Variables/PathVariableTests.py | 32 ++++++++++++++++++++-- template/RELEASE.txt | 2 +- test/Install/Install.py | 21 ++++++++------ test/VariantDir/errors.py | 16 +++++++---- testing/framework/TestCmd.py | 10 +++++-- 9 files changed, 125 insertions(+), 47 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b9467a1fd1..defd0e385e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -60,6 +60,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Change long-standing irritant in Environment tests - instead of using a while loop to pull test info from a list of tests and then delete the test, structure the test data as a list of tuples and iterate it. + - Skip running a few validation tests if the user is root and the test is + not designed to work for the root user. From Alex James: - On Darwin, PermissionErrors are now handled while trying to access diff --git a/RELEASE.txt b/RELEASE.txt index 76ae1d54a9..2e1769e91c 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -37,17 +37,22 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY FIXES ----- +- List fixes of outright bugs + - PackageVariable now does what the documentation always said it does if the variable is used on the command line with one of the enabling string as the value: the variable's default value is produced (previously it always produced True in this case). + - Temporary files created by TempFileMunge() are now cleaned up on scons exit, instead of at the time they're used. Fixes #4595. + - AddOption now correctly adds short (single-character) options. Previously an added short option would always report as unknown, while long option names for the same option worked. Short options that take a value require the user to specify the value immediately following the option, with no spaces (e.g. -j5 and not -j 5). + - Fix a problem with compilation_db component initialization - the entries for assembler files were not being set up correctly. - On Darwin, PermissionErrors are now handled while trying to access @@ -58,6 +63,9 @@ FIXES - Fix nasm test for missing include file, cleanup. +- Skip running a few validation tests if the user is root and the test is + not designed to work for the root user. + IMPROVEMENTS ------------ diff --git a/SCons/CacheDirTests.py b/SCons/CacheDirTests.py index 6ec9e84abb..e90478839d 100644 --- a/SCons/CacheDirTests.py +++ b/SCons/CacheDirTests.py @@ -34,6 +34,13 @@ built_it = None +IS_WINDOWS = sys.platform == 'win32' +try: + IS_ROOT = os.geteuid() == 0 +except AttributeError: + IS_ROOT = False + + class Action: def __call__(self, targets, sources, env, **kw) -> int: global built_it @@ -85,7 +92,6 @@ def File(self, name, bsig=None, action=Action()): def tearDown(self) -> None: os.remove(os.path.join(self._CacheDir.path, 'config')) os.rmdir(self._CacheDir.path) - # Should that be shutil.rmtree? class CacheDirTestCase(BaseTestCase): """ @@ -124,20 +130,29 @@ def setUp(self) -> None: def tearDown(self) -> None: shutil.rmtree(self.tmpdir) - @unittest.skipIf(sys.platform.startswith("win"), "This fixture will not trigger an OSError on Windows") + @unittest.skipIf( + IS_WINDOWS, + "Skip privileged CacheDir test on Windows, cannot change directory rights", + ) + @unittest.skipIf( + IS_ROOT, + "Skip privileged CacheDir test if running as root.", + ) def test_throws_correct_on_OSError(self) -> None: - """Test that the correct error is thrown when cache directory cannot be created.""" + """Test for correct error when cache directory cannot be created.""" + test = TestCmd() privileged_dir = os.path.join(self.tmpdir, "privileged") - try: - os.mkdir(privileged_dir) - os.chmod(privileged_dir, stat.S_IREAD) - cd = SCons.CacheDir.CacheDir(os.path.join(privileged_dir, "cache")) - assert False, "Should have raised exception and did not" - except SCons.Errors.SConsEnvironmentError as e: - assert str(e) == "Failed to create cache directory {}".format(os.path.join(privileged_dir, "cache")) - finally: - os.chmod(privileged_dir, stat.S_IWRITE | stat.S_IEXEC | stat.S_IREAD) - shutil.rmtree(privileged_dir) + cachedir_path = os.path.join(privileged_dir, "cache") + os.makedirs(privileged_dir, exist_ok=True) + test.writable(privileged_dir, False) + with self.assertRaises(SCons.Errors.SConsEnvironmentError) as cm: + cd = SCons.CacheDir.CacheDir(cachedir_path) + self.assertEqual( + str(cm.exception), + "Failed to create cache directory " + cachedir_path + ) + test.writable(privileged_dir, True) + shutil.rmtree(privileged_dir) def test_throws_correct_when_failed_to_write_configfile(self) -> None: diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py index 2036f9266b..0f20cd8307 100644 --- a/SCons/Node/FSTests.py +++ b/SCons/Node/FSTests.py @@ -44,6 +44,11 @@ scanner_count = 0 +try: + IS_ROOT = os.geteuid() == 0 +except AttributeError: + IS_ROOT = False + class Scanner: def __init__(self, node=None) -> None: @@ -958,6 +963,8 @@ def test_runTest(self): This test case handles all of the file system node tests in one environment, so we don't have to set up a complicated directory structure for each test individually. + This isn't ideal: normally you want to separate tests a bit + more to make it easier to debug and not fail too fast. """ test = self.test @@ -1550,27 +1557,30 @@ def nonexistent(method, s): assert r, r assert not os.path.islink(symlink), "symlink was not removed" - test.write('can_not_remove', "can_not_remove\n") - test.writable(test.workpath('.'), 0) - fp = open(test.workpath('can_not_remove')) - - f = fs.File('can_not_remove') - exc_caught = 0 - try: - r = f.remove() - except OSError: - exc_caught = 1 - - fp.close() - - assert exc_caught, "Should have caught an OSError, r = " + str(r) - f = fs.Entry('foo/bar/baz') assert f.for_signature() == 'baz', f.for_signature() assert f.get_string(0) == os.path.normpath('foo/bar/baz'), \ f.get_string(0) assert f.get_string(1) == 'baz', f.get_string(1) + + @unittest.skipIf(IS_ROOT, "Skip file removal in protected dir if running as root.") + def test_remove_fail(self) -> None: + """Test failure when removing a file where permissions don't allow. + + Split from :math:`test_runTest` to be able to skip on root. + We want to be able to skip only this one testcase and run the rest. + """ + test = self.test + fs = SCons.Node.FS.FS() + test.write('can_not_remove', "can_not_remove\n") + test.writable(test.workpath('.'), False) + with open(test.workpath('can_not_remove')): + f = fs.File('can_not_remove') + with self.assertRaises(OSError, msg="Should have caught an OSError"): + r = f.remove() + + def test_drive_letters(self) -> None: """Test drive-letter look-ups""" diff --git a/SCons/Variables/PathVariableTests.py b/SCons/Variables/PathVariableTests.py index 7fa8da18da..87d6bdffa4 100644 --- a/SCons/Variables/PathVariableTests.py +++ b/SCons/Variables/PathVariableTests.py @@ -30,6 +30,12 @@ import TestCmd from TestCmd import IS_WINDOWS +try: + IS_ROOT = os.geteuid() == 0 +except AttributeError: + IS_ROOT = False + + class PathVariableTestCase(unittest.TestCase): def test_PathVariable(self) -> None: """Test PathVariable creation""" @@ -93,7 +99,7 @@ def test_PathIsDir(self): self.assertEqual(str(e), f"Directory path for variable 'X' does not exist: {dne}") def test_PathIsDirCreate(self): - """Test the PathIsDirCreate validator""" + """Test the PathIsDirCreate validator.""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test build variable help', @@ -115,6 +121,27 @@ def test_PathIsDirCreate(self): e = cm.exception self.assertEqual(str(e), f"Path for variable 'X' is a file, not a directory: {f}") + + @unittest.skipIf(IS_ROOT, "Skip creating bad directory if running as root.") + def test_PathIsDirCreate_bad_dir(self): + """Test the PathIsDirCreate validator with an uncreatable dir. + + Split from :meth:`test_PathIsDirCreate` to be able to skip on root. + We want to be able to skip only this one testcase and run the rest. + """ + opts = SCons.Variables.Variables() + opts.Add( + SCons.Variables.PathVariable( + 'test', + 'test build variable help', + '/default/path', + SCons.Variables.PathVariable.PathIsDirCreate, + ) + ) + + test = TestCmd.TestCmd(workdir='') + o = opts.options[0] + # pick a directory path that can't be mkdir'd if IS_WINDOWS: f = r'\\noserver\noshare\yyy\zzz' @@ -125,8 +152,9 @@ def test_PathIsDirCreate(self): e = cm.exception self.assertEqual(str(e), f"Path for variable 'X' could not be created: {f}") + def test_PathIsFile(self): - """Test the PathIsFile validator""" + """Test the PathIsFile validator.""" opts = SCons.Variables.Variables() opts.Add(SCons.Variables.PathVariable('test', 'test build variable help', diff --git a/template/RELEASE.txt b/template/RELEASE.txt index 439c8630d8..77ce83b327 100755 --- a/template/RELEASE.txt +++ b/template/RELEASE.txt @@ -6,7 +6,7 @@ Past official release announcements appear at: ================================================================== -A new SCons release, 4.4.1, is now available on the SCons download page: +A new SCons release, NEXT_RELEASE, is now available on the SCons download page: https://scons.org/pages/download.html diff --git a/test/Install/Install.py b/test/Install/Install.py index 2857c72901..65725ba62e 100644 --- a/test/Install/Install.py +++ b/test/Install/Install.py @@ -131,16 +131,21 @@ def my_install(dest, source, env): # if a target can not be unlinked before building it: test.write(['work', 'f1.in'], "f1.in again again\n") -os.chmod(test.workpath('work', 'export'), 0o555) -with open(f1_out, 'rb'): - expect = [ - "Permission denied", - "The process cannot access the file because it is being used by another process", - "Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird", - ] +# This test is not designed to work if running as root +try: + IS_ROOT = os.geteuid() == 0 +except AttributeError: + IS_ROOT = False +if not IS_ROOT: + os.chmod(test.workpath('work', 'export'), 0o555) + with open(f1_out, 'rb'): + expect = [ + "Permission denied", + "The process cannot access the file because it is being used by another process", + "Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird", + ] test.run(chdir='work', arguments=f1_out, stderr=None, status=2) - test.must_contain_any_line(test.stderr(), expect) test.pass_test() diff --git a/test/VariantDir/errors.py b/test/VariantDir/errors.py index 26ef4a2312..531beea774 100644 --- a/test/VariantDir/errors.py +++ b/test/VariantDir/errors.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__" """ Validate successful handling of errors when duplicating things in @@ -37,6 +36,13 @@ test = TestSCons.TestSCons() +try: + IS_ROOT = os.geteuid() == 0 +except AttributeError: + IS_ROOT = False +if IS_ROOT: + test.skip_test('SConscript permissions meaningless when running as root; skipping test.\n') + for dir in ['normal', 'ro-dir', 'ro-SConscript', 'ro-src']: test.subdir(dir, [dir, 'src']) @@ -44,7 +50,7 @@ import os.path VariantDir('build', 'src') SConscript(os.path.join('build', 'SConscript')) -""") +""") test.write([dir, 'src', 'SConscript'], """\ def fake_scan(node, env, target): diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index cada7be602..240aa6c359 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -2003,11 +2003,15 @@ def do_chmod(fname) -> None: do_chmod(os.path.join(dirpath, name)) do_chmod(top) - def writable(self, top, write: bool=True) -> None: + def writable(self, top, write: bool = True) -> None: """Make the specified directory tree writable or unwritable. Tree is made writable if `write` evaluates True (the default), else it is made not writable. + + Note on Windows the only thing we can do is and/remove the + "readable" setting without resorting to PyWin32 - and that, + only as Administrator, so this is kind of pointless there. """ if sys.platform == 'win32': @@ -2034,7 +2038,7 @@ def do_chmod(fname) -> None: except OSError: pass else: - os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE] | 0o200)) + os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE] | stat.S_IWRITE)) else: def do_chmod(fname) -> None: try: @@ -2043,7 +2047,7 @@ def do_chmod(fname) -> None: pass else: os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~0o200)) + st[stat.ST_MODE] & ~stat.S_IWRITE)) if os.path.isfile(top): do_chmod(top) From b97cba4c79de819eef64cd8fb521d3216a235aa3 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 13 Oct 2024 12:50:53 -0600 Subject: [PATCH 152/386] Fix indent problem in Install test fix Signed-off-by: Mats Wichmann --- test/Install/Install.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/Install/Install.py b/test/Install/Install.py index 65725ba62e..8df9276447 100644 --- a/test/Install/Install.py +++ b/test/Install/Install.py @@ -144,9 +144,8 @@ def my_install(dest, source, env): "The process cannot access the file because it is being used by another process", "Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird", ] - - test.run(chdir='work', arguments=f1_out, stderr=None, status=2) - test.must_contain_any_line(test.stderr(), expect) + test.run(chdir='work', arguments=f1_out, stderr=None, status=2) + test.must_contain_any_line(test.stderr(), expect) test.pass_test() From c25c7b1d727f0da3288fa6c3cad6646e708d11e3 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 14 Oct 2024 02:17:39 -0600 Subject: [PATCH 153/386] Use IS_WINDOWS from test framework Signed-off-by: Mats Wichmann --- SCons/CacheDirTests.py | 3 +-- SCons/Node/FSTests.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/SCons/CacheDirTests.py b/SCons/CacheDirTests.py index e90478839d..7b6c5aa271 100644 --- a/SCons/CacheDirTests.py +++ b/SCons/CacheDirTests.py @@ -28,13 +28,12 @@ import tempfile import stat -from TestCmd import TestCmd +from TestCmd import TestCmd, IS_WINDOWS import SCons.CacheDir built_it = None -IS_WINDOWS = sys.platform == 'win32' try: IS_ROOT = os.geteuid() == 0 except AttributeError: diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py index 0f20cd8307..14a2b5691b 100644 --- a/SCons/Node/FSTests.py +++ b/SCons/Node/FSTests.py @@ -518,7 +518,7 @@ def copy(self, src, dest) -> None: # Disable symlink and link for now in win32. # We don't have a consistant plan to make these work as yet # They are only supported with PY3 - if sys.platform == 'win32': + if IS_WINDOWS: real_symlink = None real_link = None @@ -711,7 +711,7 @@ def test_isfile(self) -> None: nonexistent = fs.Entry('nonexistent') assert not nonexistent.isfile() - @unittest.skipUnless(sys.platform != 'win32' and hasattr(os, 'symlink'), + @unittest.skipIf(IS_WINDOWS or not hasattr(os, 'symlink'), "symlink is not used on Windows") def test_islink(self) -> None: """Test the Base.islink() method""" @@ -1456,7 +1456,7 @@ def nonexistent(method, s): except SyntaxError: assert c == "" - if sys.platform != 'win32' and hasattr(os, 'symlink'): + if not IS_WINDOWS and hasattr(os, 'symlink'): os.symlink('nonexistent', test.workpath('dangling_symlink')) e = fs.Entry('dangling_symlink') c = e.get_contents() @@ -1548,7 +1548,7 @@ def nonexistent(method, s): assert r, r assert not os.path.exists(test.workpath('exists')), "exists was not removed" - if sys.platform != 'win32' and hasattr(os, 'symlink'): + if not IS_WINDOWS and hasattr(os, 'symlink'): symlink = test.workpath('symlink') os.symlink(test.workpath('does_not_exist'), symlink) assert os.path.islink(symlink) @@ -1857,7 +1857,7 @@ def test_lookup_abs(self) -> None: d = root._lookup_abs('/tmp/foo-nonexistent/nonexistent-dir', SCons.Node.FS.Dir) assert d.__class__ == SCons.Node.FS.Dir, str(d.__class__) - @unittest.skipUnless(sys.platform == "win32", "requires Windows") + @unittest.skipUnless(IS_WINDOWS, "requires Windows") def test_lookup_uncpath(self) -> None: """Testing looking up a UNC path on Windows""" test = self.test @@ -1869,13 +1869,13 @@ def test_lookup_uncpath(self) -> None: assert str(f) == r'\\servername\C$\foo', \ 'UNC path %s got looked up as %s' % (path, f) - @unittest.skipUnless(sys.platform.startswith == "win32", "requires Windows") + @unittest.skipUnless(IS_WINDOWS, "requires Windows") def test_unc_drive_letter(self) -> None: """Test drive-letter lookup for windows UNC-style directories""" share = self.fs.Dir(r'\\SERVER\SHARE\Directory') assert str(share) == r'\\SERVER\SHARE\Directory', str(share) - @unittest.skipUnless(sys.platform == "win32", "requires Windows") + @unittest.skipUnless(IS_WINDOWS, "requires Windows") def test_UNC_dirs_2689(self) -> None: """Test some UNC dirs that printed incorrectly and/or caused infinite recursion errors prior to r5180 (SCons 2.1).""" @@ -1938,7 +1938,7 @@ def test_rel_path(self) -> None: d1_d2_f, d3_d4_f, '../../d3/d4/f', ] - if sys.platform in ('win32',): + if IS_WINDOWS: x_d1 = fs.Dir(r'X:\d1') x_d1_d2 = x_d1.Dir('d2') y_d1 = fs.Dir(r'Y:\d1') From fb8932b837a2fe63dd5370e5b05a26f930db6f50 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 19 Oct 2024 08:24:33 -0600 Subject: [PATCH 154/386] Move IS_ROOT definition to framework Signed-off-by: Mats Wichmann --- SCons/CacheDirTests.py | 8 +------- SCons/Node/FSTests.py | 8 +------- SCons/Variables/PathVariableTests.py | 8 +------- test/Install/Install.py | 5 +---- test/VariantDir/errors.py | 5 +---- testing/framework/TestCmd.py | 4 ++++ 6 files changed, 9 insertions(+), 29 deletions(-) diff --git a/SCons/CacheDirTests.py b/SCons/CacheDirTests.py index 7b6c5aa271..3fbab4e245 100644 --- a/SCons/CacheDirTests.py +++ b/SCons/CacheDirTests.py @@ -28,18 +28,12 @@ import tempfile import stat -from TestCmd import TestCmd, IS_WINDOWS +from TestCmd import TestCmd, IS_WINDOWS, IS_ROOT import SCons.CacheDir built_it = None -try: - IS_ROOT = os.geteuid() == 0 -except AttributeError: - IS_ROOT = False - - class Action: def __call__(self, targets, sources, env, **kw) -> int: global built_it diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py index 14a2b5691b..83ceef28c7 100644 --- a/SCons/Node/FSTests.py +++ b/SCons/Node/FSTests.py @@ -31,7 +31,7 @@ import stat from typing import Optional -from TestCmd import TestCmd, IS_WINDOWS +from TestCmd import TestCmd, IS_WINDOWS, IS_ROOT import SCons.Errors import SCons.Node.FS @@ -44,12 +44,6 @@ scanner_count = 0 -try: - IS_ROOT = os.geteuid() == 0 -except AttributeError: - IS_ROOT = False - - class Scanner: def __init__(self, node=None) -> None: global scanner_count diff --git a/SCons/Variables/PathVariableTests.py b/SCons/Variables/PathVariableTests.py index 87d6bdffa4..b093053d74 100644 --- a/SCons/Variables/PathVariableTests.py +++ b/SCons/Variables/PathVariableTests.py @@ -28,13 +28,7 @@ import SCons.Variables import TestCmd -from TestCmd import IS_WINDOWS - -try: - IS_ROOT = os.geteuid() == 0 -except AttributeError: - IS_ROOT = False - +from TestCmd import IS_WINDOWS, IS_ROOT class PathVariableTestCase(unittest.TestCase): def test_PathVariable(self) -> None: diff --git a/test/Install/Install.py b/test/Install/Install.py index 8df9276447..802b10dae6 100644 --- a/test/Install/Install.py +++ b/test/Install/Install.py @@ -31,6 +31,7 @@ import time import TestSCons +from TestCmd import IS_ROOT test = TestSCons.TestSCons() @@ -132,10 +133,6 @@ def my_install(dest, source, env): test.write(['work', 'f1.in'], "f1.in again again\n") # This test is not designed to work if running as root -try: - IS_ROOT = os.geteuid() == 0 -except AttributeError: - IS_ROOT = False if not IS_ROOT: os.chmod(test.workpath('work', 'export'), 0o555) with open(f1_out, 'rb'): diff --git a/test/VariantDir/errors.py b/test/VariantDir/errors.py index 531beea774..1ff3be3c4d 100644 --- a/test/VariantDir/errors.py +++ b/test/VariantDir/errors.py @@ -33,13 +33,10 @@ import stat import sys import TestSCons +from TestCmd import IS_ROOT test = TestSCons.TestSCons() -try: - IS_ROOT = os.geteuid() == 0 -except AttributeError: - IS_ROOT = False if IS_ROOT: test.skip_test('SConscript permissions meaningless when running as root; skipping test.\n') diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 240aa6c359..243745d020 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -329,6 +329,10 @@ IS_MACOS = sys.platform == 'darwin' IS_64_BIT = sys.maxsize > 2**32 IS_PYPY = hasattr(sys, 'pypy_translation_info') +try: + IS_ROOT = os.geteuid() == 0 +except AttributeError: + IS_ROOT = False NEED_HELPER = os.environ.get('SCONS_NO_DIRECT_SCRIPT') # sentinel for cases where None won't do From 07feba378c9e320072081c8c2e8e1835182d106c Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 19 Oct 2024 13:00:32 -0400 Subject: [PATCH 155/386] Minor code order refactor in msvs.py. --- SCons/Tool/msvs.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index 8835a118b7..6d8b45ebaa 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -443,9 +443,6 @@ class _DSPGenerator: 'misc'] def __init__(self, dspfile, source, env) -> None: - dspnode = env.File(dspfile) - self.project_guid = _projectGUID(env, dspfile) - dspnode.Tag('project_guid', self.project_guid) self.dspfile = str(dspfile) try: get_abspath = dspfile.get_abspath @@ -454,6 +451,10 @@ def __init__(self, dspfile, source, env) -> None: else: self.dspabs = get_abspath() + self.project_guid = _projectGUID(env, self.dspfile) + dspnode = env.File(self.dspfile) + dspnode.Tag('project_guid', self.project_guid) + if 'variant' not in env: raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ "'Release') to create an MSVSProject.") From f3fdec1ae8fce182bfbf8b3cafe580a90e1dfdf3 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 19 Oct 2024 13:19:24 -0400 Subject: [PATCH 156/386] Remove win32 platform specification (i.e., platform = 'win32') from MSVS test Environments. --- test/MSVS/vs-6.0-clean.py | 5 +++-- test/MSVS/vs-7.0-clean.py | 2 +- test/MSVS/vs-7.0-scc-files.py | 17 +++++++++-------- test/MSVS/vs-7.0-scc-legacy-files.py | 3 ++- test/MSVS/vs-7.1-clean.py | 2 +- test/MSVS/vs-7.1-scc-files.py | 3 ++- test/MSVS/vs-7.1-scc-legacy-files.py | 3 ++- test/MSVS/vs-scc-files.py | 3 ++- test/MSVS/vs-scc-legacy-files.py | 3 ++- testing/framework/TestSConsMSVS.py | 14 +++++++------- 10 files changed, 31 insertions(+), 24 deletions(-) diff --git a/test/MSVS/vs-6.0-clean.py b/test/MSVS/vs-6.0-clean.py index 7045daa320..3f99d650d3 100644 --- a/test/MSVS/vs-6.0-clean.py +++ b/test/MSVS/vs-6.0-clean.py @@ -41,8 +41,9 @@ expected_dswfile = TestSConsMSVS.expected_dswfile_6_0 test.write('SConstruct', """\ -env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='6.0',HOST_ARCH='%(HOST_ARCH)s') +env=Environment(tools=['msvs'], + MSVS_VERSION='6.0', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test.c'] testincs = ['sdk.h'] diff --git a/test/MSVS/vs-7.0-clean.py b/test/MSVS/vs-7.0-clean.py index 91a8cb2a79..627c5addb9 100644 --- a/test/MSVS/vs-7.0-clean.py +++ b/test/MSVS/vs-7.0-clean.py @@ -41,7 +41,7 @@ expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 test.write('SConstruct', """\ -env=Environment(platform='win32', tools=['msvs'], +env=Environment(tools=['msvs'], MSVS_VERSION='7.0', HOST_ARCH='%(HOST_ARCH)s') diff --git a/test/MSVS/vs-7.0-scc-files.py b/test/MSVS/vs-7.0-scc-files.py index bb45e5535d..2c5c520e60 100644 --- a/test/MSVS/vs-7.0-scc-files.py +++ b/test/MSVS/vs-7.0-scc-files.py @@ -41,14 +41,15 @@ expected_slnfile = TestSConsMSVS.expected_slnfile_7_0 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 SConscript_contents = \ -r"""env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - MSVS_SCC_CONNECTION_ROOT='.', - MSVS_SCC_PROVIDER='MSSCCI:Perforce SCM', - MSVS_SCC_PROJECT_NAME='Perforce Project', - MSVS_SCC_AUX_PATH='AUX', - HOST_ARCH='%(HOST_ARCH)s') +r"""env=Environment(tools=['msvs'], + MSVS_VERSION='7.0', + CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], + CPPPATH=['inc1', 'inc2'], + MSVS_SCC_CONNECTION_ROOT='.', + MSVS_SCC_PROVIDER='MSSCCI:Perforce SCM', + MSVS_SCC_PROJECT_NAME='Perforce Project', + MSVS_SCC_AUX_PATH='AUX', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] testincs = ['sdk.h'] diff --git a/test/MSVS/vs-7.0-scc-legacy-files.py b/test/MSVS/vs-7.0-scc-legacy-files.py index d6eb24780f..00ce397d45 100644 --- a/test/MSVS/vs-7.0-scc-legacy-files.py +++ b/test/MSVS/vs-7.0-scc-legacy-files.py @@ -41,7 +41,8 @@ expected_slnfile = TestSConsMSVS.expected_slnfile_7_0 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_0 SConscript_contents = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.0', +env=Environment(tools=['msvs'], + MSVS_VERSION='7.0', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', diff --git a/test/MSVS/vs-7.1-clean.py b/test/MSVS/vs-7.1-clean.py index 515a9e649d..403463e0bb 100644 --- a/test/MSVS/vs-7.1-clean.py +++ b/test/MSVS/vs-7.1-clean.py @@ -41,7 +41,7 @@ expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 test.write('SConstruct', """\ -env=Environment(platform='win32', tools=['msvs'], +env=Environment(tools=['msvs'], MSVS_VERSION='7.1', HOST_ARCH='%(HOST_ARCH)s') diff --git a/test/MSVS/vs-7.1-scc-files.py b/test/MSVS/vs-7.1-scc-files.py index 9fdf5e1449..4f253e7172 100644 --- a/test/MSVS/vs-7.1-scc-files.py +++ b/test/MSVS/vs-7.1-scc-files.py @@ -41,7 +41,8 @@ expected_slnfile = TestSConsMSVS.expected_slnfile_7_1 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 SConscript_contents = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1', +env=Environment(tools=['msvs'], + MSVS_VERSION='7.1', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_CONNECTION_ROOT='.', diff --git a/test/MSVS/vs-7.1-scc-legacy-files.py b/test/MSVS/vs-7.1-scc-legacy-files.py index 8bb6b96fb0..96ec21d67f 100644 --- a/test/MSVS/vs-7.1-scc-legacy-files.py +++ b/test/MSVS/vs-7.1-scc-legacy-files.py @@ -41,7 +41,8 @@ expected_slnfile = TestSConsMSVS.expected_slnfile_7_1 expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_7_1 SConscript_contents = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='7.1', +env=Environment(tools=['msvs'], + MSVS_VERSION='7.1', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', diff --git a/test/MSVS/vs-scc-files.py b/test/MSVS/vs-scc-files.py index 2c16da2233..4e79e274f5 100644 --- a/test/MSVS/vs-scc-files.py +++ b/test/MSVS/vs-scc-files.py @@ -48,7 +48,8 @@ expected_slnfile = test.get_expected_sln_file_contents(vc_version, project_file) expected_vcprojfile = test.get_expected_proj_file_contents(vc_version, dirs, project_file) SConscript_contents = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='{vc_version}', +env=Environment(tools=['msvs'], + MSVS_VERSION='{vc_version}', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_CONNECTION_ROOT='.', diff --git a/test/MSVS/vs-scc-legacy-files.py b/test/MSVS/vs-scc-legacy-files.py index 7badd612de..9943d3d7ce 100644 --- a/test/MSVS/vs-scc-legacy-files.py +++ b/test/MSVS/vs-scc-legacy-files.py @@ -49,7 +49,8 @@ expected_vcprojfile = test.get_expected_proj_file_contents(vc_version, dirs, project_file) SConscript_contents = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='{vc_version}', +env=Environment(tools=['msvs'], + MSVS_VERSION='{vc_version}', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], MSVS_SCC_LOCAL_PATH=r'C:\\MyMsVsProjects', diff --git a/testing/framework/TestSConsMSVS.py b/testing/framework/TestSConsMSVS.py index e4ac3a9353..7d2524187c 100644 --- a/testing/framework/TestSConsMSVS.py +++ b/testing/framework/TestSConsMSVS.py @@ -193,8 +193,9 @@ ''' SConscript_contents_6_0 = """\ -env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='6.0',HOST_ARCH='%(HOST_ARCH)s') +env=Environment(tools=['msvs'], + MSVS_VERSION='6.0', + HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test.c'] testincs = ['sdk.h'] @@ -314,7 +315,7 @@ """ SConscript_contents_7_0 = """\ -env=Environment(platform='win32', tools=['msvs'], +env=Environment(tools=['msvs'], MSVS_VERSION='7.0', HOST_ARCH='%(HOST_ARCH)s') @@ -442,7 +443,7 @@ """ SConscript_contents_7_1 = """\ -env=Environment(platform='win32', tools=['msvs'], +env=Environment(tools=['msvs'], MSVS_VERSION='7.1', HOST_ARCH='%(HOST_ARCH)s') @@ -641,7 +642,8 @@ """ SConscript_contents_fmt = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='%(MSVS_VERSION)s', +env=Environment(tools=['msvs'], + MSVS_VERSION='%(MSVS_VERSION)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], HOST_ARCH='%(HOST_ARCH)s') @@ -690,7 +692,6 @@ SConscript_projects_contents_fmt = """\ env=Environment( - platform='win32', tools=['msvs'], MSVS_VERSION='%(MSVS_VERSION)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], @@ -741,7 +742,6 @@ SConscript_projects_defaultguids_contents_fmt = """\ env=Environment( - platform='win32', tools=['msvs'], MSVS_VERSION='%(MSVS_VERSION)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], From 705f579da7245e0fbb8a8640bebe909bd58d8f9c Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 19 Oct 2024 13:40:41 -0400 Subject: [PATCH 157/386] Bug fix in msvs.py: the value for the construction value 'nokeep' was inadvertently querying the value of env['variant']. --- SCons/Tool/msvs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index 6d8b45ebaa..557590deba 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -590,7 +590,7 @@ def GetKeyFromEnv(env, key, variants): self.configs = {} self.nokeep = 0 - if 'nokeep' in env and env['variant'] != 0: + if 'nokeep' in env and env['nokeep'] != 0: self.nokeep = 1 if self.nokeep == 0 and os.path.exists(self.dspabs): @@ -1547,7 +1547,7 @@ def __init__(self, dswfile, source, env) -> None: self.configs = {} self.nokeep = 0 - if 'nokeep' in env and env['variant'] != 0: + if 'nokeep' in env and env['nokeep'] != 0: self.nokeep = 1 if self.nokeep == 0 and os.path.exists(self.dswfile): From 864da93e558a4cc6992f87453c967c87ebf936db Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 19 Oct 2024 17:35:38 -0700 Subject: [PATCH 158/386] [ci skip] Fix typos in CHANGES.txt --- CHANGES.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index cebc9728ff..31152d685d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -32,11 +32,11 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER a single arg consisting of a premade option object. Because optparse detects that case based on seeing zero kwargs and we always added at least one (default=) that would fail for AddOption. Fix - for consistency, but don't advertise it further - not addewd to - manpage synoposis/description. + for consistency, but don't advertise it further - not added to + manpage synopsis/description. - Temporary files created by TempFileMunge() are now cleaned up on scons exit, instead of at the time they're used. Fixes #4595. - - Override envirionments, created when giving construction environment + - Override environments, created when giving construction environment keyword arguments to Builder calls (or manually, through the undocumented Override method), were modified not to "leak" on item deletion. The item will now not be deleted from the base environment. Override Environments @@ -46,7 +46,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER entries for assembler files were not being set up correctly. - Add clang and clang++ to the default tool search orders for POSIX and Windows platforms. These will be searched for after gcc and g++, - respectively. Does not affect expliclity requested tool lists. + respectively. Does not affect explicitly requested tool lists. Note: on Windows, SCons currently only has builtin support for clang, not for clang-cl, the version of the frontend that uses cl.exe-compatible command line switches. @@ -87,7 +87,7 @@ RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 module (`Variables/__init__.py`) to control what is made available in a star import. However, there was existing usage of doing `from SCons.Variables import *` which expected the variable *types* - to be avaiable. While we never advertised this usage, there's no + to be available. While we never advertised this usage, there's no real reason it shouldn't keep working - add to `__all__`. - Switch SCons build to use setuptools' supported version fetcher from the old homegrown one. @@ -176,7 +176,7 @@ RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700 consistent behavior. NOTE: With this change, user created Actions should now catch and handle expected exceptions (whereas previously many of these were silently - caught and suppressed by the SCons Action exection code). + caught and suppressed by the SCons Action execution code). From Ryan Carsten Schmidt: - Teach ParseFlags to put a --stdlib=libname argument into CXXFLAGS. From da65616846b54e1e84d508823b58740dcbf9d3ca Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 20 Oct 2024 07:11:33 -0400 Subject: [PATCH 159/386] Fix: normalize case for solution file extension and file name for string endswith test. --- SCons/Tool/msvs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index 557590deba..9cb28aff36 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -1489,11 +1489,11 @@ def _projectDSPNodes(env): projects = SCons.Util.flatten(projects) if len(projects) < 1: raise SCons.Errors.UserError("You must specify at least one project to create an MSVSSolution.") - sln_suffix = env.subst('$MSVSSOLUTIONSUFFIX') + sln_suffix = os.path.normcase(env.subst('$MSVSSOLUTIONSUFFIX')) dspnodes = [] for p in projects: node = env.File(p) - if str(node).endswith(sln_suffix): + if os.path.normcase(str(node)).endswith(sln_suffix): continue dspnodes.append(node) if len(dspnodes) < 1: From c9d898304f2cec39c81a4e91801dd14868546365 Mon Sep 17 00:00:00 2001 From: "Keith F. Prussing" Date: Mon, 21 Oct 2024 08:25:24 -0400 Subject: [PATCH 160/386] Add test for beamer files The branch adds support for tracking Beamer theme files. This defines the test to show the scanning failed without modification, but succeeds with the modification. --- SCons/Scanner/LaTeXTests.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/SCons/Scanner/LaTeXTests.py b/SCons/Scanner/LaTeXTests.py index ae3ae6659c..a3f0263475 100644 --- a/SCons/Scanner/LaTeXTests.py +++ b/SCons/Scanner/LaTeXTests.py @@ -63,6 +63,18 @@ \only<2>{\includegraphics{inc7.png}} """) +test.write('test5.latex',r""" +\usetheme{scons} +""") +test.write('beamerthemescons.sty',r""" +\usecolortheme[option]{scons} +\usefonttheme{scons} +\useinnertheme{scons} +\useoutertheme{scons} +""") +for theme in ('color', 'font', 'inner', 'outer'): + test.write('beamer' + theme + 'themescons.sty', "\n") + test.subdir('subdir') test.write('inc1.tex',"\n") @@ -167,6 +179,16 @@ def runTest(self) -> None: files = ['inc1.tex', 'inc2.tex', 'inc5.xyz', 'inc7.png'] deps_match(self, deps, files) +class LaTeXScannerTestCase5(unittest.TestCase): + def runTest(self) -> None: + env = DummyEnvironment(TEXINPUTS=[test.workpath("subdir")],LATEXSUFFIXES = [".tex", ".ltx", ".latex"]) + s = SCons.Scanner.LaTeX.LaTeXScanner() + path = s.path(env) + deps = s(env.File('test5.latex'), env, path) + files = ['beamer' + _ + 'themescons.sty' for _ in + ('color', 'font', 'inner', 'outer', '')] + deps_match(self, deps, files) + if __name__ == "__main__": unittest.main() From b2391119dc5203ebdc267334c864eb32079d3382 Mon Sep 17 00:00:00 2001 From: "Keith F. Prussing" Date: Fri, 18 Oct 2024 20:01:56 -0400 Subject: [PATCH 161/386] Add support for tracking beamer themes This adds a regular expression to check for beamer theme files for LaTeX that may be part of the user's build tree. Similar to package files found via `\usepackage`, this will not report a missing file because the user may just be relying on the system version. --- CHANGES.txt | 3 +++ RELEASE.txt | 1 + SCons/Scanner/LaTeX.py | 11 ++++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0e16c1c2f7..12ef14e0ee 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -66,6 +66,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER /etc/paths.d. This may occur if SCons is invoked in a sandboxed environment (such as Nix). + From Keith F Prussing: + - Added support for tracking beamer themes in the LaTeX scanner. + RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 76ae1d54a9..4abe25391c 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -33,6 +33,7 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY keyword arguments to Builder calls (or manually, through the undocumented Override method), were modified not to "leak" on item deletion. The item will now not be deleted from the base environment. +- Added support for tracking beamer themes in the LaTeX scanner. FIXES ----- diff --git a/SCons/Scanner/LaTeX.py b/SCons/Scanner/LaTeX.py index 4412aee64a..93c2752ca1 100644 --- a/SCons/Scanner/LaTeX.py +++ b/SCons/Scanner/LaTeX.py @@ -169,6 +169,11 @@ class LaTeX(ScannerBase): 'addsectionbib': 'BIBINPUTS', 'makeindex': 'INDEXSTYLE', 'usepackage': 'TEXINPUTS', + 'usetheme': 'TEXINPUTS', + 'usecolortheme': 'TEXINPUTS', + 'usefonttheme': 'TEXINPUTS', + 'useinnertheme': 'TEXINPUTS', + 'useoutertheme': 'TEXINPUTS', 'lstinputlisting': 'TEXINPUTS'} env_variables = SCons.Util.unique(list(keyword_paths.values())) two_arg_commands = ['import', 'subimport', @@ -193,6 +198,7 @@ def __init__(self, name, suffixes, graphics_extensions, *args, **kwargs) -> None | addglobalbib | addsectionbib | usepackage + | use(?:|color|font|inner|outer)theme(?:\s*\[[^\]]+\])? ) \s*{([^}]*)} # first arg (?: \s*{([^}]*)} )? # maybe another arg @@ -362,6 +368,9 @@ def scan(self, node, subdir: str='.'): if inc_type in self.two_arg_commands: inc_subdir = os.path.join(subdir, include[1]) inc_list = include[2].split(',') + elif re.match('use(|color|font|inner|outer)theme', inc_type): + inc_list = [re.sub('use', 'beamer', inc_type) + _ + '.sty' for _ in + include[1].split(',')] else: inc_list = include[1].split(',') for inc in inc_list: @@ -411,7 +420,7 @@ def scan_recurse(self, node, path=()): if n is None: # Do not bother with 'usepackage' warnings, as they most # likely refer to system-level files - if inc_type != 'usepackage': + if inc_type != 'usepackage' or re.match("use(|color|font|inner|outer)theme", inc_type): SCons.Warnings.warn( SCons.Warnings.DependencyWarning, "No dependency generated for file: %s " From 7368acafdb37d2fe4e809b713dd766b0acde63dd Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 24 Oct 2024 07:36:21 -0600 Subject: [PATCH 162/386] Improve Repository docs [skip appveyor] Give a few more details. Add example for another usecase to the User Guide. At the same time - convert more "construction environment" and "construction variable" to entities in files already being edited. Normalize on NEXT_RELEASE (since 4.8.1, some changed had used NEXT_RELEASE and some on NEXT_VERSION). --- CHANGES.txt | 1 + RELEASE.txt | 3 + SCons/Environment.py | 2 +- SCons/Environment.xml | 127 +++++++++++++++++--------------------- SCons/EnvironmentTests.py | 6 +- doc/man/scons.xml | 13 ++-- doc/user/repositories.xml | 113 +++++++++++++++++++++++---------- 7 files changed, 155 insertions(+), 110 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b9467a1fd1..ea0ed50fe0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -60,6 +60,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Change long-standing irritant in Environment tests - instead of using a while loop to pull test info from a list of tests and then delete the test, structure the test data as a list of tuples and iterate it. + - Clarify documentation of Repository() in manpage and user guide. From Alex James: - On Darwin, PermissionErrors are now handled while trying to access diff --git a/RELEASE.txt b/RELEASE.txt index 76ae1d54a9..d02841d1fa 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -90,8 +90,11 @@ DOCUMENTATION the contributor credit) - Some manpage cleanup for the gettext and pdf/ps builders. + - Some clarifications in the User Guide "Environments" chapter. +- Clarify documentation of Repository() in manpage and user guide. + DEVELOPMENT ----------- diff --git a/SCons/Environment.py b/SCons/Environment.py index 72572db94a..ad5045633b 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -1749,7 +1749,7 @@ def Dump(self, *key: str, format: str = 'pretty') -> str: Raises: ValueError: *format* is not a recognized serialization format. - .. versionchanged:: NEXT_VERSION + .. versionchanged:: NEXT_RELEASE *key* is no longer limited to a single construction variable name. If *key* is supplied, a formatted dictionary is generated like the no-arg case - previously a single *key* displayed just the value. diff --git a/SCons/Environment.xml b/SCons/Environment.xml index 97d034364e..403d6b8e65 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -155,7 +155,7 @@ for more information. A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -166,7 +166,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -177,7 +177,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -188,7 +188,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -199,7 +199,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -210,7 +210,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -221,7 +221,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -232,7 +232,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -279,13 +279,12 @@ for a complete explanation of the arguments and behavior. Note that the &f-env-Action; form of the invocation will expand -construction variables in any argument strings, +&consvars; in any argument strings, including the action argument, at the time it is called -using the construction variables in the -env -construction environment through which +using the &consvars; in the +&consenv; through which &f-env-Action; was called. The &f-Action; global function form delays all variable expansion @@ -862,14 +861,14 @@ for a complete explanation of the arguments and behavior. Note that the env.Builder() form of the invocation will expand -construction variables in any arguments strings, +&consvars; in any arguments strings, including the action argument, at the time it is called -using the construction variables in the +using the &consvars; in the env -construction environment through which +&consenv; through which &f-env-Builder; was called. The &f-Builder; @@ -903,12 +902,12 @@ disables derived file caching. Calling the environment method &f-link-env-CacheDir; limits the effect to targets built -through the specified construction environment. +through the specified &consenv;. Calling the global function &f-link-CacheDir; sets a global default that will be used by all targets built -through construction environments +through &consenvs; that do not set up environment-specific caching by calling &f-env-CacheDir;. @@ -1034,7 +1033,7 @@ either as separate arguments to the &f-Clean; method, or as a list. &f-Clean; -will also accept the return value of any of the construction environment +will also accept the return value of any of the &consenv; Builder methods. Examples: @@ -1327,13 +1326,11 @@ for a complete explanation of the arguments and behavior. Specifies that all up-to-date decisions for -targets built through this construction environment -will be handled by the specified -function. +targets built through this &consenv; +will be handled by function. function can be the name of a function or one of the following strings -that specify the predefined decision function -that will be applied: +that specify a predefined decider function: @@ -1440,7 +1437,7 @@ Examples: Decider('timestamp-match') # Use hash content signatures for any targets built -# with the attached construction environment. +# with the attached &consenv;. env.Decider('content') @@ -1746,7 +1743,7 @@ only those keys and their values are serialized. -Changed in NEXT_VERSION: +Changed in NEXT_RELEASE: More than one key can be specified. The returned string always looks like a dict (or JSON equivalent); previously a single key serialized only the value, @@ -2269,14 +2266,14 @@ is non-zero, adds the names of the default builders (Program, Library, etc.) to the global name space -so they can be called without an explicit construction environment. +so they can be called without an explicit &consenv;. (This is the default.) When flag is zero, the names of the default builders are removed from the global name space -so that an explicit construction environment is required +so that an explicit &consenv; is required to call all builders. @@ -2334,7 +2331,7 @@ env.Ignore('bar', 'bar/foobar.obj') The specified string will be preserved as-is -and not have construction variables expanded. +and not have &consvars; expanded. @@ -2493,7 +2490,7 @@ either as separate arguments to the &f-NoCache; method, or as a list. &f-NoCache; -will also accept the return value of any of the construction environment +will also accept the return value of any of the &consenv; Builder methods. @@ -2544,7 +2541,7 @@ either as separate arguments to the &f-NoClean; method, or as a list. &f-NoClean; -will also accept the return value of any of the construction environment +will also accept the return value of any of the &consenv; Builder methods. @@ -2589,7 +2586,7 @@ is omitted or None, &f-link-env-MergeFlags; is used. By default, duplicate values are not -added to any construction variables; +added to any &consvars;; you can specify unique=False to allow duplicate values to be added. @@ -2613,7 +2610,7 @@ in order to distribute it to appropriate &consvars;. &f-env-MergeFlags; uses a separate function to do that processing - see &f-link-env-ParseFlags; for the details, including a -a table of options and corresponding construction variables. +a table of options and corresponding &consvars;. To provide alternative processing of the output of command, you can suppply a custom @@ -2696,12 +2693,12 @@ function. Parses one or more strings containing typical command-line flags for GCC-style tool chains and returns a dictionary with the flag values -separated into the appropriate SCons construction variables. +separated into the appropriate SCons &consvars;. Intended as a companion to the &f-link-env-MergeFlags; method, but allows for the values in the returned dictionary to be modified, if necessary, -before merging them into the construction environment. +before merging them into the &consenv;. (Note that &f-env-MergeFlags; will call this method if its argument is not a dictionary, @@ -2730,7 +2727,7 @@ See &f-link-ParseConfig; for more details. Flag values are translated according to the prefix found, -and added to the following construction variables: +and added to the following &consvars;: @@ -2770,8 +2767,7 @@ and added to the following construction variables: Any other strings not associated with options are assumed to be the names of libraries and added to the -&cv-LIBS; -construction variable. +&cv-LIBS; &consvar;. @@ -2802,7 +2798,7 @@ selected by plat (defaults to the detected platform for the current system) that can be used to initialize -a construction environment by passing it as the +a &consenv; by passing it as the platform keyword argument to the &f-link-Environment; function. @@ -2999,7 +2995,7 @@ env = Environment( -Replaces construction variables in the Environment +Replaces &consvars; in the Environment with the specified keyword arguments. @@ -3019,50 +3015,43 @@ env.Replace(CCFLAGS='-g', FOO='foo.xxx') -Specifies that +Sets directory -is a repository to be searched for files. +as a repository to be searched for files contributing to the build. Multiple calls to &f-Repository; -are legal, -and each one adds to the list of -repositories that will be searched. +are allowed, +with repositories searched in the given order. +Repositories specified via command-line option +have higher priority. -To +In &scons;, -a repository is a copy of the source tree, -from the top-level directory on down, -which may contain -both source files and derived files +a repository is partial or complete copy of the source tree, +from the top-level directory down, +containing source files that can be used to build targets in -the local source tree. -The canonical example would be an -official source tree maintained by an integrator. -If the repository contains derived files, -then the derived files should have been built using -&scons;, -so that the repository contains the necessary -signature information to allow -&scons; -to figure out when it is appropriate to -use the repository copy of a derived file, -instead of building one locally. +the current worktree. +Repositories can also contain derived files. +An example might be an official source tree maintained by an integrator. +If a repository contains derived files, +they should be the result of building with &SCons;, +so a signature database (sconsign) is present +in the repository, +allowing better decisions on whether they are +up-to-date or not. Note that if an up-to-date derived file already exists in a repository, -&scons; -will +&scons; will not make a copy in the local directory tree. -In order to guarantee that a local copy -will be made, -use the -&f-link-Local; -method. +If you need a local copy to be made, +use the &f-link-Local; method. @@ -3267,7 +3256,7 @@ SConsignFile(dbm_module=dbm.gnu) -Sets construction variables to default values specified with the keyword +Sets &consvars; to default values specified with the keyword arguments if (and only if) the variables are not already set. The following statements are equivalent: diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index a37e424646..62238c7575 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -3202,7 +3202,7 @@ def test_Dump(self) -> None: """Test the Dump() method""" env = self.TestEnvironment(FOO='foo', FOOFLAGS=CLVar('--bar --baz')) - # changed in NEXT_VERSION: single arg now displays as a dict, + # changed in NEXT_RELEASE: single arg now displays as a dict, # not a bare value; more than one arg is allowed. with self.subTest(): # one-arg version self.assertEqual(env.Dump('FOO'), "{'FOO': 'foo'}") @@ -3842,7 +3842,7 @@ def test___delitem__(self) -> None: """Test deleting variables from an OverrideEnvironment""" env, env2, env3 = self.envs - # changed in NEXT_VERSION: delete does not cascade to underlying envs + # changed in NEXT_RELEASE: delete does not cascade to underlying envs # XXX is in all three, del from env3 should affect only it del env3['XXX'] with self.subTest(): @@ -3931,7 +3931,7 @@ def test_Dictionary(self) -> None: # test deletion in top override del env3['XXX'] self.assertRaises(KeyError, env3.Dictionary, 'XXX') - # changed in NEXT_VERSION: *not* deleted from underlying envs + # changed in NEXT_RELEASE: *not* deleted from underlying envs assert 'XXX' in env2.Dictionary() assert 'XXX' in env.Dictionary() diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 8cdd7bf347..022f44c536 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -2464,12 +2464,15 @@ These warnings are enabled by default. -Search the specified repository + +Search repository for any input and target -files not found in the local directory hierarchy. Multiple - -options may be specified, in which case the -repositories are searched in the order specified. +files not found in the local directory hierarchy. +Multiple +options may be specified, +with repositories searched in the given order. +See &f-link-Repository; for more information. + diff --git a/doc/user/repositories.xml b/doc/user/repositories.xml index c59df78277..87616b7373 100644 --- a/doc/user/repositories.xml +++ b/doc/user/repositories.xml @@ -41,7 +41,7 @@ This file is processed by the bin/SConsDoc.py module. -
+
The &Repository; Method -
+
Limitations on <literal>#include</literal> files in repositories @@ -332,7 +332,7 @@ coming into existence.) - As we've seen, + As you've seen, &SCons; will compile the &hello_c; file from the repository if it doesn't exist in the local directory. @@ -364,7 +364,7 @@ main(int argc, char *argv[]) -env = Environment(CPPPATH = ['.']) +env = Environment(CPPPATH=['.']) env.Program('hello.c') Repository('__ROOT__/usr/repository1') @@ -400,9 +400,9 @@ int main() { printf("Hello, world!\n"); } Some modern versions of C compilers do have an option to disable or control this behavior. If so, add that option to &cv-link-CFLAGS; - (or &cv-link-CXXFLAGS; or both) in your construction environment(s). - Make sure the option is used for all construction - environments that use C preprocessing! + (or &cv-link-CXXFLAGS;, or both) in your &consenvs;. + Make sure the option is used for all &consenv; + that use C preprocessing! @@ -441,7 +441,7 @@ int main() { printf("Hello, world!\n"); }
-
+
Finding the &SConstruct; file in repositories @@ -472,6 +472,18 @@ int main() { printf("Hello, world!\n"); } + + + Note that while other files are searched through the chain of + repositories, &SConstruct; is special - it must be found either in + the current directory or the first directory + specified using the -Y + (or the --repository or + --srcdir synonyms) + command line option, or the build will abort. + + +
@@ -482,9 +494,9 @@ int main() { printf("Hello, world!\n"); } If a repository contains not only source files, but also derived files (such as object files, libraries, or executables), &SCons; will perform - its normal MD5 signature calculation to + its normal signature calculation to decide if a derived file in a repository is up-to-date, - or the derived file must be rebuilt in the local build directory. + or if it needs to be rebuilt in the local build directory. For the &SCons; signature calculation to work correctly, a repository tree must contain the &sconsigndb; files that &SCons; uses to keep track of signature information. @@ -535,8 +547,8 @@ int f2() { printf("file2.c\n"); } Now, with the repository populated, - we only need to create the one local source file - we're interested in working with at the moment, + you only need to create the one local source file + you're interested in working with at the moment, and use the -Y option to tell &SCons; to fetch any other files it needs from the repository: @@ -575,7 +587,7 @@ cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o If the repository tree contains the complete results of a build, - and we try to build from the repository + and you try to build from the repository without any files in our local tree, something moderately surprising happens: @@ -593,8 +605,7 @@ scons: `hello' is up-to-date. Why does &SCons; say that the &hello; program is up-to-date when there is no &hello; program in the local build directory? - Because the repository (not the local directory) - contains the up-to-date &hello; program, + Because the repository contains the &hello; program, and &SCons; correctly determines that nothing needs to be done to rebuild that up-to-date copy of the file. @@ -603,11 +614,17 @@ scons: `hello' is up-to-date. - There are, however, many times when you want to ensure that a + There are, however, times when you want to ensure that a local copy of a file always exists. - A packaging or testing script, for example, - may assume that certain generated files exist locally. - To tell &SCons; to make a copy of any up-to-date repository + For example, if you are pacakging the result of the build, + all the files used in the package need to be present locally, + and the packaging tool is unlikely to know anything about + &SCons; repositories. Similarly, if you build a unit test + program, and then expect to run after the build, + it doesn't help if the test program is somewhere else + and wasn't rebuilt into the local directory. + In these cases, you can tell + &SCons; to make a copy of any up-to-date repository file in the local build directory, use the &Local; function: @@ -626,7 +643,7 @@ int main() { printf("Hello, world!\n"); } - If we then run the same command, + Now, if you run the same command, &SCons; will make a local copy of the program from the repository copy, and tell you that it is doing so: @@ -649,4 +666,36 @@ scons: `hello' is up-to-date.
+
+ Using Repository to separate source and build. + + + + If you want to just do a build where the build artifacts don't + pollute the source directory, the repository mechanism can help with that. + Here's an example: checkout or unpack your project in the + directory src, + and then build it in build: + + + + +$ mkdir build +$ cd build +$ scons -Q -Y ../src +gcc -o foo.o -I. -I/path/to/src -c /path/to/src/foo.c +gcc -o foo foo.o +$ ls +foo foo.o + + + + + It can become awakward to keep having to type + -Y path-to-repo repeatedly. + If so, the option can be placed in &SCONSFLAGS;. + + +
+ From 590b9bd95db057a6a7b2f176d0128a59e5848155 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 24 Oct 2024 16:33:25 -0700 Subject: [PATCH 163/386] [ci skip] Fix CHANGES.txt author ordering --- CHANGES.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 12ef14e0ee..b969bdfc62 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,10 +15,18 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Joseph Brill: - Added error handling when creating MS VC detection debug log file specified by SCONS_MSCOMMON_DEBUG + + From Alex James: + - On Darwin, PermissionErrors are now handled while trying to access + /etc/paths.d. This may occur if SCons is invoked in a sandboxed + environment (such as Nix). From Dillan Mills: - Fix support for short options (`-x`). + From Keith F Prussing: + - Added support for tracking beamer themes in the LaTeX scanner. + From Mats Wichmann: - PackageVariable now does what the documentation always said it does if the variable is used on the command line with one of the enabling @@ -61,14 +69,6 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER a while loop to pull test info from a list of tests and then delete the test, structure the test data as a list of tuples and iterate it. - From Alex James: - - On Darwin, PermissionErrors are now handled while trying to access - /etc/paths.d. This may occur if SCons is invoked in a sandboxed - environment (such as Nix). - - From Keith F Prussing: - - Added support for tracking beamer themes in the LaTeX scanner. - RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 From f13babbe48114770effba65ed39f528afa56fcce Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 26 Oct 2024 07:36:57 -0400 Subject: [PATCH 164/386] Update CHANGES.txt and RELEASE.txt [ci skip] --- CHANGES.txt | 39 +++++++++++++++++++++++++++++++++++++-- RELEASE.txt | 22 +++++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0e16c1c2f7..5940fcb942 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -13,8 +13,43 @@ NOTE: Python 3.6 support is deprecated and will be dropped in a future release. RELEASE VERSION/DATE TO BE FILLED IN LATER From Joseph Brill: - - Added error handling when creating MS VC detection debug log file specified by - SCONS_MSCOMMON_DEBUG + - Added error handling when creating MSVC detection debug log file specified by + SCONS_MSCOMMON_DEBUG. + - MSVS: Added default HOST_ARCH values to sconstruct/sconscript environment for + select msvs test scripts to allow tests to run on platforms not recognized by + the msvs/msvc tool implementations. Fixes #4608. + - MSVS: Fix early exit after the first msvc version loop execution in select msvs + test scripts. Select msvs test scripts were being invoked for msvc version 8.0 + only. Fixes #4609. + - MSVS: Additional minor select msvs test script fixes as a result of the msvs + tests being invoked for all msvc versions: fix vs version number for vc version + 14.3, fix expected platform toolset version, add and use a default known + project GUID for some select tests, add AdditionalOptions Condition to expected + vcx project file. + - MSVS: Additional minor changes to the msvs tool as a result of the msvs tests + being invoked for all msvc versions: use environment MSVS_PROJECT_GUID when + generating project files information, fix the visual studio string for VS2015, + add .vcxproj as an expected suffix for assigning the name to the file basename. + - MSVS: Add additional msvs tests for multi-project and solution builds. + - MSVS: Check for variant directory build of MSVSSolution and adjust the source + node similar to the handling for MSVSProject. The solution was generated in + the build directory instead of the source directory. The placeholder solution + file is not generated in the build directory and the solution file is generated + in the source directory similar to the handling for project files. + Fixes #4612. + - MSVS: Add project dsp nodes to the dsw source node list in the msvs tool. This + appears to always cause the project files to be generated before the solution + files which is necessary to retrieve the project GUIDs for use in the solution + file. This does change the behavior of clean for a project generated with + auto_build_solution disabled and explicit solution generation: when the + solution files are cleaned, the project files are also cleaned. The tests for + vs 6.0-7.1 were changed accordingly. + - MSVS: Filter out solution nodes from the project list in the msvs tool. When + auto_build_solution is enabled and the MSVSProject return value is used in the + MSVSSolution project specification, the auto-generated solution file is + generated as a Project record in the solution file. Fixes #4613. + - MSVS: Remove the platform specification (i.e., platform = 'win32') from select + test script environments. The platform specification appears superfluous. From Dillan Mills: - Fix support for short options (`-x`). diff --git a/RELEASE.txt b/RELEASE.txt index 76ae1d54a9..61cdd2d85b 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -33,6 +33,11 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY keyword arguments to Builder calls (or manually, through the undocumented Override method), were modified not to "leak" on item deletion. The item will now not be deleted from the base environment. +- MSVS: msvs project files are always generated before the corresponding + msvs solution files. This changes the behavior of clean for a project + generated with auto_build_solution disabled and explicit solution + generation: when the solution files are cleaned, the project files are + also cleaned. The tests for vs 6.0-7.1 were updated accordingly. FIXES ----- @@ -55,7 +60,22 @@ FIXES (such as Nix). - Added error handling when creating MS VC detection debug log file specified by SCONS_MSCOMMON_DEBUG - +- MSVS: Modify select msvs test scripts to run on platforms not supported by + the msvs/msvc tool implementation via a default host architecture for + unsupported platforms. +- MSVS: Fixed early loop exit in select msvs test scripts. Select msvs test + scripts were being invoked for msvc version 8.0 only. Additional msvs + tool and test changes due to the msvs test scripts being run for all msvc + versions (i.e., minor test and tool issues went undetected). +- MSVS: msvs solution files are no longer included when generating the + project records for a solution file. +- MSVS: for variant build configurations, msvs solution files are + generated in the source directory and a placeholder file is generated in + the variant build directory. This mirrors the behavior of generated + msvs project files. +- MSVS: msvs project files are generated before the corresponding msvs + solution file. User-specified project GUIDs should now be correctly + written to the solution file. - Fix nasm test for missing include file, cleanup. IMPROVEMENTS From 8606b497e222d105dbd4cde4191a38a02f752c7f Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 27 Oct 2024 07:47:40 -0600 Subject: [PATCH 165/386] Fix two typos for PR 4622 [skip appveyor] Signed-off-by: Mats Wichmann --- doc/user/repositories.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/user/repositories.xml b/doc/user/repositories.xml index 87616b7373..7adb85c740 100644 --- a/doc/user/repositories.xml +++ b/doc/user/repositories.xml @@ -102,7 +102,7 @@ int main() { printf("Hello, world!\n"); }
-
+
Finding source files in repositories @@ -235,7 +235,7 @@ hello.c:1: hello.h: No such file or directory In order to inform the C compiler about the repositories, &SCons; will add appropriate source file inclusion - directived (-I or /I flags) + directives (-I or /I flags) to the compilation commands for each directory in the &cv-CPPPATH; list. So if you add the current directory to the &consenv; &cv-CPPPATH;: From 09f47f5a286253eefb1d6deb0411cc54271aebfd Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 27 Oct 2024 13:32:27 -0700 Subject: [PATCH 166/386] [ci skip] fix typos --- doc/user/repositories.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/user/repositories.xml b/doc/user/repositories.xml index 7adb85c740..e05f5c2f59 100644 --- a/doc/user/repositories.xml +++ b/doc/user/repositories.xml @@ -616,7 +616,7 @@ scons: `hello' is up-to-date. There are, however, times when you want to ensure that a local copy of a file always exists. - For example, if you are pacakging the result of the build, + For example, if you are packaging the result of the build, all the files used in the package need to be present locally, and the packaging tool is unlikely to know anything about &SCons; repositories. Similarly, if you build a unit test @@ -691,7 +691,7 @@ foo foo.o - It can become awakward to keep having to type + It can become awkward to keep having to type -Y path-to-repo repeatedly. If so, the option can be placed in &SCONSFLAGS;. From c33cfb36e230ac42c252dc22249b2c49a3ad8151 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 27 Oct 2024 13:36:28 -0700 Subject: [PATCH 167/386] [ci skip] fix typos --- RELEASE.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/RELEASE.txt b/RELEASE.txt index 47ac8fc597..9f60ef22cb 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -37,9 +37,6 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY FIXES ----- - -- List fixes of outright bugs - - PackageVariable now does what the documentation always said it does if the variable is used on the command line with one of the enabling string as the value: the variable's default value is produced (previously @@ -75,13 +72,13 @@ IMPROVEMENTS under which they would be observed), or major code cleanups - For consistency with the optparse "add_option" method, AddOption accepts - an SConsOption object as a single argument (this failed previouly). + an SConsOption object as a single argument (this failed previously). Calling AddOption with the full set of arguments (option names and attributes) to set up the option is still the recommended approach. - Add clang and clang++ to the default tool search orders for POSIX and Windows platforms. These will be searched for after gcc and g++, - respectively. Does not affect expliclity requested tool lists. Note: + respectively. Does not affect explicitly requested tool lists. Note: on Windows, SCons currently only has builtin support for clang, not for clang-cl, the version of the frontend that uses cl.exe-compatible command line switches. From 8a3c2a38280633191817a6ac8db11cd293973c11 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 25 Sep 2024 08:21:49 -0600 Subject: [PATCH 168/386] Add a tag to the cachedir Since there are now two files to make when a cachedir is created, use the temporary-dir -> rename technique. CacheDir tests no longer pre-create the cache directory, they should be verifying it's created properly upon request (one unit test still makes sure passing an existing empty directory works, too). Signed-off-by: Mats Wichmann --- CHANGES.txt | 5 +- RELEASE.txt | 5 + SCons/CacheDir.py | 134 ++++++++++++++++++--------- SCons/CacheDirTests.py | 70 +++++++------- test/CacheDir/CacheDir.py | 10 +- test/CacheDir/CacheDir_TryCompile.py | 2 +- test/CacheDir/NoCache.py | 2 +- test/CacheDir/SideEffect.py | 2 +- test/CacheDir/VariantDir.py | 2 +- test/CacheDir/debug.py | 2 +- test/CacheDir/environment.py | 9 +- test/CacheDir/multi-targets.py | 2 +- test/CacheDir/multiple-targets.py | 2 - test/CacheDir/option--cd.py | 2 +- test/CacheDir/option--cf.py | 4 +- test/CacheDir/option--cr.py | 2 +- test/CacheDir/option--cs.py | 4 +- test/CacheDir/scanner-target.py | 2 - test/CacheDir/source-scanner.py | 2 +- test/CacheDir/up-to-date-q.py | 6 +- test/CacheDir/value_dependencies.py | 1 - 21 files changed, 169 insertions(+), 101 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4161b1b0e5..c53fce3284 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,7 +15,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Joseph Brill: - Added error handling when creating MS VC detection debug log file specified by SCONS_MSCOMMON_DEBUG - + From Alex James: - On Darwin, PermissionErrors are now handled while trying to access /etc/paths.d. This may occur if SCons is invoked in a sandboxed @@ -71,6 +71,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Skip running a few validation tests if the user is root and the test is not designed to work for the root user. - Clarify documentation of Repository() in manpage and user guide. + - Add a tag to each CacheDir to let systems ignore backing it up + (per https://bford.info/cachedir/). Update the way a CacheDir + is created, since it now has to create two files. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 9f60ef22cb..baa9d1dbf7 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -33,8 +33,13 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY keyword arguments to Builder calls (or manually, through the undocumented Override method), were modified not to "leak" on item deletion. The item will now not be deleted from the base environment. + - Added support for tracking beamer themes in the LaTeX scanner. +- Add a tag to each CacheDir to let systems ignore backing it up + (per https://bford.info/cachedir/). Update the way a CacheDir + is created, since it now has to create two files. + FIXES ----- - PackageVariable now does what the documentation always said it does diff --git a/SCons/CacheDir.py b/SCons/CacheDir.py index 0174793df5..7f8deb55e1 100644 --- a/SCons/CacheDir.py +++ b/SCons/CacheDir.py @@ -29,6 +29,7 @@ import os import stat import sys +import tempfile import uuid import SCons.Action @@ -36,6 +37,12 @@ import SCons.Warnings import SCons.Util +CACHE_PREFIX_LEN = 2 # first two characters used as subdirectory name +CACHE_TAG = ( + b"Signature: 8a477f597d28d172789f06886806bc55\n" + b"# SCons cache directory - see https://bford.info/cachedir/\n" +) + cache_enabled = True cache_debug = False cache_force = False @@ -67,20 +74,20 @@ def CacheRetrieveFunc(target, source, env) -> int: fs.chmod(t.get_internal_path(), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) return 0 -def CacheRetrieveString(target, source, env) -> None: +def CacheRetrieveString(target, source, env) -> str: t = target[0] fs = t.fs cd = env.get_CacheDir() cachedir, cachefile = cd.cachepath(t) if t.fs.exists(cachefile): return "Retrieved `%s' from cache" % t.get_internal_path() - return None + return "" CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) -def CachePushFunc(target, source, env): +def CachePushFunc(target, source, env) -> None: if cache_readonly: return @@ -134,8 +141,7 @@ def CachePushFunc(target, source, env): class CacheDir: def __init__(self, path) -> None: - """ - Initialize a CacheDir object. + """Initialize a CacheDir object. The cache configuration is stored in the object. It is read from the config file in the supplied path if @@ -147,53 +153,97 @@ def __init__(self, path) -> None: self.path = path self.current_cache_debug = None self.debugFP = None - self.config = dict() - if path is None: - return - - self._readconfig(path) + self.config = {} + if path is not None: + self._readconfig(path) + + def _add_config(self, path: str) -> None: + """Create the cache config file in *path*. + + Locking isn't necessary in the normal case - when the cachedir is + being created - because it's written to a unique directory first, + before the directory is renamed. But it is legal to call CacheDir + with an existing directory, which may be missing the config file, + and in that case we do need locking. Simpler to always lock. + """ + config_file = os.path.join(path, 'config') + # TODO: this breaks the "unserializable config object" test which + # does some crazy stuff, so for now don't use setdefault. It does + # seem like it would be better to preserve an exisiting value. + # self.config.setdefault('prefix_len', CACHE_PREFIX_LEN) + self.config['prefix_len'] = CACHE_PREFIX_LEN + with SCons.Util.FileLock(config_file, timeout=5, writer=True), open( + config_file, "x" + ) as config: + try: + json.dump(self.config, config) + except Exception: + msg = "Failed to write cache configuration for " + path + raise SCons.Errors.SConsEnvironmentError(msg) + # Add the tag file "carelessly" - the contents are not used by SCons + # so we don't care about the chance of concurrent writes. + try: + tagfile = os.path.join(path, "CACHEDIR.TAG") + with open(tagfile, 'xb') as cachedir_tag: + cachedir_tag.write(CACHE_TAG) + except FileExistsError: + pass - def _readconfig(self, path): - """ - Read the cache config. + def _mkdir_atomic(self, path: str) -> bool: + """Create cache directory at *path*. - If directory or config file do not exist, create. Take advantage - of Py3 capability in os.makedirs() and in file open(): just try - the operation and handle failure appropriately. + Uses directory renaming to avoid races. If we are actually + creating the dir, populate it with the metadata files at the + same time as that's the safest way. But it's not illegal to point + CacheDir at an existing directory that wasn't a cache previously, + so we may have to do that elsewhere, too. - Omit the check for old cache format, assume that's old enough - there will be none of those left to worry about. + Returns: + ``True`` if it we created the dir, ``False`` if already existed, - :param path: path to the cache directory + Raises: + SConsEnvironmentError: if we tried and failed to create the cache. """ - config_file = os.path.join(path, 'config') + directory = os.path.abspath(path) + if os.path.exists(directory): + return False + try: - # still use a try block even with exist_ok, might have other fails - os.makedirs(path, exist_ok=True) - except OSError: + tempdir = tempfile.TemporaryDirectory(dir=os.path.dirname(directory)) + except OSError as e: msg = "Failed to create cache directory " + path - raise SCons.Errors.SConsEnvironmentError(msg) + raise SCons.Errors.SConsEnvironmentError(msg) from e + self._add_config(tempdir.name) + with tempdir: + try: + os.rename(tempdir.name, directory) + return True + except Exception as e: + # did someone else get there first? + if os.path.isdir(directory): + return False + msg = "Failed to create cache directory " + path + raise SCons.Errors.SConsEnvironmentError(msg) from e + + def _readconfig(self, path: str) -> None: + """Read the cache config from *path*. + If directory or config file do not exist, create and populate. + """ + config_file = os.path.join(path, 'config') + created = self._mkdir_atomic(path) + if not created and not os.path.isfile(config_file): + # Could have been passed an empty directory + self._add_config(path) try: - with SCons.Util.FileLock(config_file, timeout=5, writer=True), open( - config_file, "x" + with SCons.Util.FileLock(config_file, timeout=5, writer=False), open( + config_file ) as config: - self.config['prefix_len'] = 2 - try: - json.dump(self.config, config) - except Exception: - msg = "Failed to write cache configuration for " + path - raise SCons.Errors.SConsEnvironmentError(msg) - except FileExistsError: - try: - with SCons.Util.FileLock(config_file, timeout=5, writer=False), open( - config_file - ) as config: - self.config = json.load(config) - except (ValueError, json.decoder.JSONDecodeError): - msg = "Failed to read cache configuration for " + path - raise SCons.Errors.SConsEnvironmentError(msg) + self.config = json.load(config) + except (ValueError, json.decoder.JSONDecodeError): + msg = "Failed to read cache configuration for " + path + raise SCons.Errors.SConsEnvironmentError(msg) def CacheDebug(self, fmt, target, cachefile) -> None: if cache_debug != self.current_cache_debug: @@ -252,7 +302,7 @@ def is_enabled(self) -> bool: def is_readonly(self) -> bool: return cache_readonly - def get_cachedir_csig(self, node): + def get_cachedir_csig(self, node) -> str: cachedir, cachefile = self.cachepath(node) if cachefile and os.path.exists(cachefile): return SCons.Util.hash_file_signature(cachefile, SCons.Node.FS.File.hash_chunksize) diff --git a/SCons/CacheDirTests.py b/SCons/CacheDirTests.py index 3fbab4e245..0ecd502a13 100644 --- a/SCons/CacheDirTests.py +++ b/SCons/CacheDirTests.py @@ -31,6 +31,7 @@ from TestCmd import TestCmd, IS_WINDOWS, IS_ROOT import SCons.CacheDir +import SCons.Node.FS built_it = None @@ -62,15 +63,11 @@ def get_CacheDir(self): return self.cachedir class BaseTestCase(unittest.TestCase): - """ - Base fixtures common to our other unittest classes. - """ + """Base fixtures common to our other unittest classes.""" + def setUp(self) -> None: self.test = TestCmd(workdir='') - - import SCons.Node.FS self.fs = SCons.Node.FS.FS() - self._CacheDir = SCons.CacheDir.CacheDir('cache') def File(self, name, bsig=None, action=Action()): @@ -83,13 +80,11 @@ def File(self, name, bsig=None, action=Action()): return node def tearDown(self) -> None: - os.remove(os.path.join(self._CacheDir.path, 'config')) - os.rmdir(self._CacheDir.path) + shutil.rmtree(self._CacheDir.path) class CacheDirTestCase(BaseTestCase): - """ - Test calling CacheDir code directly. - """ + """Test calling CacheDir code directly.""" + def test_cachepath(self) -> None: """Test the cachepath() method""" @@ -97,6 +92,7 @@ def test_cachepath(self) -> None: # of the file in cache. def my_collect(list, hash_format=None): return list[0] + save_collect = SCons.Util.hash_collect SCons.Util.hash_collect = my_collect @@ -111,6 +107,21 @@ def my_collect(list, hash_format=None): finally: SCons.Util.hash_collect = save_collect +class CacheDirExistsTestCase(unittest.TestCase): + """Test passing an existing but not setup cache directory.""" + + def setUp(self) -> None: + self.test = TestCmd(workdir='') + self.test.subdir('ex-cache') # force an empty dir + cache = self.test.workpath('ex-cache') + self.fs = SCons.Node.FS.FS() + self._CacheDir = SCons.CacheDir.CacheDir(cache) + + def test_existing_cachedir(self) -> None: + """Test the setup happened even though cache already existed.""" + assert os.path.exists(self.test.workpath('ex-cache', 'config')) + assert os.path.exists(self.test.workpath('ex-cache', 'CACHEDIR.TAG')) + class ExceptionTestCase(unittest.TestCase): """Test that the correct exceptions are thrown by CacheDir.""" @@ -147,13 +158,14 @@ def test_throws_correct_on_OSError(self) -> None: test.writable(privileged_dir, True) shutil.rmtree(privileged_dir) - def test_throws_correct_when_failed_to_write_configfile(self) -> None: + """Test for correct error if cache config file cannot be created.""" + class Unserializable: - """A class which the JSON should not be able to serialize""" + """A class which the JSON module should not be able to serialize.""" def __init__(self, oldconfig) -> None: - self.something = 1 # Make the object unserializable + self.something = 1 # Make the object unserializable # Pretend to be the old config just enough self.__dict__["prefix_len"] = oldconfig["prefix_len"] @@ -168,16 +180,17 @@ def __setitem__(self, name, value) -> None: oldconfig = self._CacheDir.config self._CacheDir.config = Unserializable(oldconfig) + # Remove the config file that got created on object creation # so that _readconfig* will try to rewrite it old_config = os.path.join(self._CacheDir.path, "config") os.remove(old_config) - - try: + with self.assertRaises(SCons.Errors.SConsEnvironmentError) as cm: self._CacheDir._readconfig(self._CacheDir.path) - assert False, "Should have raised exception and did not" - except SCons.Errors.SConsEnvironmentError as e: - assert str(e) == "Failed to write cache configuration for {}".format(self._CacheDir.path) + self.assertEqual( + str(cm.exception), + "Failed to write cache configuration for " + self._CacheDir.path, + ) def test_raise_environment_error_on_invalid_json(self) -> None: config_file = os.path.join(self._CacheDir.path, "config") @@ -188,17 +201,16 @@ def test_raise_environment_error_on_invalid_json(self) -> None: with open(config_file, "w") as cfg: cfg.write(content) - try: - # Construct a new cache dir that will try to read the invalid config + with self.assertRaises(SCons.Errors.SConsEnvironmentError) as cm: + # Construct a new cachedir that will try to read the invalid config new_cache_dir = SCons.CacheDir.CacheDir(self._CacheDir.path) - assert False, "Should have raised exception and did not" - except SCons.Errors.SConsEnvironmentError as e: - assert str(e) == "Failed to read cache configuration for {}".format(self._CacheDir.path) + self.assertEqual( + str(cm.exception), + "Failed to read cache configuration for " + self._CacheDir.path, + ) class FileTestCase(BaseTestCase): - """ - Test calling CacheDir code through Node.FS.File interfaces. - """ + """Test calling CacheDir code through Node.FS.File interfaces.""" # These tests were originally in Nodes/FSTests.py and got moved # when the CacheDir support was refactored into its own module. # Look in the history for Node/FSTests.py if any of this needs @@ -274,9 +286,7 @@ def test_CacheRetrieveSilent(self) -> None: def test_CachePush(self) -> None: """Test the CachePush() function""" - save_CachePush = SCons.CacheDir.CachePush - SCons.CacheDir.CachePush = self.push try: @@ -309,7 +319,6 @@ def test_CachePush(self) -> None: def test_warning(self) -> None: """Test raising a warning if we can't copy a file to cache.""" - test = TestCmd(workdir='') save_copy2 = shutil.copy2 @@ -337,7 +346,6 @@ def mkdir(dir, mode: int=0) -> None: def test_no_strfunction(self) -> None: """Test handling no strfunction() for an action.""" - save_CacheRetrieveSilent = SCons.CacheDir.CacheRetrieveSilent f8 = self.File("cd.f8", 'f8_bsig') diff --git a/test/CacheDir/CacheDir.py b/test/CacheDir/CacheDir.py index bd8d674af3..05134e613a 100644 --- a/test/CacheDir/CacheDir.py +++ b/test/CacheDir/CacheDir.py @@ -41,7 +41,7 @@ src_cat_out = test.workpath('src', 'cat.out') src_all = test.workpath('src', 'all') -test.subdir('cache', 'src') +test.subdir('src') test.write(['src', 'SConstruct'], """\ DefaultEnvironment(tools=[]) @@ -84,8 +84,12 @@ def cat(env, source, target): test.must_not_exist(src_ccc_out) test.must_not_exist(src_all) # Even if you do -n, the cache will be configured. -test.fail_test(os.listdir(cache) != ['config']) - +expect = ['CACHEDIR.TAG', 'config'] +found = sorted(os.listdir(cache)) +test.fail_test( + expect != found, + message=f"expected cachedir contents {expect}, found {found}", +) # Verify that a normal build works correctly, and clean up. # This should populate the cache with our derived files. test.run(chdir = 'src', arguments = '.') diff --git a/test/CacheDir/CacheDir_TryCompile.py b/test/CacheDir/CacheDir_TryCompile.py index b00c5d4956..c9ab12643f 100644 --- a/test/CacheDir/CacheDir_TryCompile.py +++ b/test/CacheDir/CacheDir_TryCompile.py @@ -38,7 +38,7 @@ cache = test.workpath('cache') -test.subdir('cache', 'src') +test.subdir('src') test.write(['src', 'SConstruct'], """\ DefaultEnvironment(tools=[]) diff --git a/test/CacheDir/NoCache.py b/test/CacheDir/NoCache.py index e1cecee351..611b555b5c 100644 --- a/test/CacheDir/NoCache.py +++ b/test/CacheDir/NoCache.py @@ -31,7 +31,7 @@ test = TestSCons.TestSCons() -test.subdir('cache', 'alpha', 'beta') +test.subdir('alpha', 'beta') sconstruct = """ DefaultEnvironment(tools=[]) diff --git a/test/CacheDir/SideEffect.py b/test/CacheDir/SideEffect.py index 61c9bbcf6e..ee808d6e40 100644 --- a/test/CacheDir/SideEffect.py +++ b/test/CacheDir/SideEffect.py @@ -31,7 +31,7 @@ test = TestSCons.TestSCons() -test.subdir('cache', 'work') +test.subdir('work') cache = test.workpath('cache') diff --git a/test/CacheDir/VariantDir.py b/test/CacheDir/VariantDir.py index 9fc82c9cb0..8c449173a5 100644 --- a/test/CacheDir/VariantDir.py +++ b/test/CacheDir/VariantDir.py @@ -33,7 +33,7 @@ test = TestSCons.TestSCons() -test.subdir('cache', 'src') +test.subdir('src') cache = test.workpath('cache') cat_out = test.workpath('cat.out') diff --git a/test/CacheDir/debug.py b/test/CacheDir/debug.py index 0ba6bbba2c..a8c4e835d3 100644 --- a/test/CacheDir/debug.py +++ b/test/CacheDir/debug.py @@ -36,7 +36,7 @@ test = TestSCons.TestSCons(match=TestSCons.match_re) -test.subdir('cache', 'src') +test.subdir('src') cache = test.workpath('cache') debug_out = test.workpath('cache-debug.out') diff --git a/test/CacheDir/environment.py b/test/CacheDir/environment.py index 17a4f26e0b..23e35087ac 100644 --- a/test/CacheDir/environment.py +++ b/test/CacheDir/environment.py @@ -42,7 +42,7 @@ src_cat_out = test.workpath('src', 'cat.out') src_all = test.workpath('src', 'all') -test.subdir('cache', 'src') +test.subdir('src') test.write(['src', 'SConstruct'], """\ DefaultEnvironment(tools=[]) @@ -87,7 +87,12 @@ def cat(env, source, target): test.must_not_exist(src_ccc_out) test.must_not_exist(src_all) # Even if you do -n, the cache will be configured. -test.fail_test(os.listdir(cache) != ['config']) +expect = ['CACHEDIR.TAG', 'config'] +found = sorted(os.listdir(cache)) +test.fail_test( + expect != found, + message=f"expected cachedir contents {expect}, found {found}", +) # Verify that a normal build works correctly, and clean up. # This should populate the cache with our derived files. diff --git a/test/CacheDir/multi-targets.py b/test/CacheDir/multi-targets.py index 72c7e66a2b..7c606afdf9 100644 --- a/test/CacheDir/multi-targets.py +++ b/test/CacheDir/multi-targets.py @@ -31,7 +31,7 @@ test = TestSCons.TestSCons() -test.subdir('cache', 'multiple') +test.subdir('multiple') cache = test.workpath('cache') diff --git a/test/CacheDir/multiple-targets.py b/test/CacheDir/multiple-targets.py index 1218e9b794..dfde453df4 100644 --- a/test/CacheDir/multiple-targets.py +++ b/test/CacheDir/multiple-targets.py @@ -32,8 +32,6 @@ test = TestSCons.TestSCons() -test.subdir('cache') - test.write('SConstruct', """\ DefaultEnvironment(tools=[]) def touch(env, source, target): diff --git a/test/CacheDir/option--cd.py b/test/CacheDir/option--cd.py index 692207d973..df9ab47041 100644 --- a/test/CacheDir/option--cd.py +++ b/test/CacheDir/option--cd.py @@ -33,7 +33,7 @@ test = TestSCons.TestSCons() -test.subdir('cache', 'src') +test.subdir('src') test.write(['src', 'SConstruct'], """ DefaultEnvironment(tools=[]) diff --git a/test/CacheDir/option--cf.py b/test/CacheDir/option--cf.py index 4750b40b99..b34d706b77 100644 --- a/test/CacheDir/option--cf.py +++ b/test/CacheDir/option--cf.py @@ -34,7 +34,7 @@ test = TestSCons.TestSCons() -test.subdir('cache', 'src') +test.subdir('src') test.write(['src', 'SConstruct'], """ DefaultEnvironment(tools=[]) @@ -88,7 +88,6 @@ def cat(env, source, target): # Blow away and recreate the CacheDir, then verify that --cache-force # repopulates the cache with the local built targets. DO NOT CLEAN UP. shutil.rmtree(test.workpath('cache')) -test.subdir('cache') test.run(chdir = 'src', arguments = '--cache-force .') @@ -106,7 +105,6 @@ def cat(env, source, target): # Blow away and recreate the CacheDir, then verify that --cache-populate # repopulates the cache with the local built targets. DO NOT CLEAN UP. shutil.rmtree(test.workpath('cache')) -test.subdir('cache') test.run(chdir = 'src', arguments = '--cache-populate .') diff --git a/test/CacheDir/option--cr.py b/test/CacheDir/option--cr.py index 6ff6974b92..4c423cfd19 100644 --- a/test/CacheDir/option--cr.py +++ b/test/CacheDir/option--cr.py @@ -33,7 +33,7 @@ test = TestSCons.TestSCons() -test.subdir('cache', 'src') +test.subdir('src') test.write(['src', 'SConstruct'], """ DefaultEnvironment(tools=[]) diff --git a/test/CacheDir/option--cs.py b/test/CacheDir/option--cs.py index ce48ac0417..62c0026f76 100644 --- a/test/CacheDir/option--cs.py +++ b/test/CacheDir/option--cs.py @@ -38,7 +38,7 @@ test = TestSCons.TestSCons() -test.subdir('cache', 'src1', 'src2') +test.subdir('src1', 'src2') test.write(['src1', 'build.py'], r""" import sys @@ -63,7 +63,7 @@ def cat(env, source, target): f.write(f2.read()) DefaultEnvironment(tools=[]) # test speedup -env = Environment(tools=[], +env = Environment(tools=[], BUILDERS={'Internal':Builder(action=cat), 'External':Builder(action=r'%(_python_)s build.py $TARGET $SOURCES')}) env.External('aaa.out', 'aaa.in') diff --git a/test/CacheDir/scanner-target.py b/test/CacheDir/scanner-target.py index dd8791d031..4aa36923fe 100644 --- a/test/CacheDir/scanner-target.py +++ b/test/CacheDir/scanner-target.py @@ -35,8 +35,6 @@ test = TestSCons.TestSCons() -test.subdir('cache') - test.write('SConstruct', """\ DefaultEnvironment(tools=[]) import SCons diff --git a/test/CacheDir/source-scanner.py b/test/CacheDir/source-scanner.py index 0dda4f43c8..2ff19ce0ba 100644 --- a/test/CacheDir/source-scanner.py +++ b/test/CacheDir/source-scanner.py @@ -39,7 +39,7 @@ cache = test.workpath('cache') -test.subdir('cache', 'subdir') +test.subdir('subdir') test.write(['subdir', 'SConstruct'], """\ DefaultEnvironment(tools=[]) diff --git a/test/CacheDir/up-to-date-q.py b/test/CacheDir/up-to-date-q.py index c8fa1e3805..5c71ab97ae 100644 --- a/test/CacheDir/up-to-date-q.py +++ b/test/CacheDir/up-to-date-q.py @@ -39,19 +39,19 @@ # 1. Set up two identical C project directories called 'alpha' and # 'beta', which use the same cache # 2. Invoke scons on 'alpha' -# 3. Invoke scons on 'beta', which successfully draws output +# 3. Invoke scons on 'beta', which successfully draws output # files from the cache # 4. Invoke scons again, asserting (with -q) that 'beta' is up to date # # Step 4 failed in 0.96.93. In practice, this problem would lead to -# lots of unecessary fetches from the cache during incremental +# lots of unecessary fetches from the cache during incremental # builds (because they behaved like non-incremental builds). import TestSCons test = TestSCons.TestSCons() -test.subdir('cache', 'alpha', 'beta') +test.subdir('alpha', 'beta') foo_c = """ int main(void){ return 0; } diff --git a/test/CacheDir/value_dependencies.py b/test/CacheDir/value_dependencies.py index b34401dc9c..37c6153a17 100644 --- a/test/CacheDir/value_dependencies.py +++ b/test/CacheDir/value_dependencies.py @@ -37,7 +37,6 @@ test = TestSCons.TestSCons() test.dir_fixture('value_dependencies') -test.subdir('cache') # First build, populates the cache test.run(arguments='.') From 55849419b8a1365d5918386c085be55bc9cddd3a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:54:42 -0400 Subject: [PATCH 169/386] Add optional keyword argument auto_filter_projects to MSVSSolution. Changes: * Detect solution file names and nodes in projects argument list. Behavior based on the auto_filter_projects value. By default, raise an exception. * Update TestSConsMSVS and multiple project auto_build_solution tests. * Update documentation, CHANGES.txt, and RELEASE.txt. --- CHANGES.txt | 17 +- RELEASE.txt | 15 +- SCons/Tool/msvs.py | 18 +- SCons/Tool/msvs.xml | 68 +++++++ test/MSVS/vs-mult-auto-guid.py | 228 +++++++++++++----------- test/MSVS/vs-mult-auto-vardir-guid.py | 244 ++++++++++++++------------ test/MSVS/vs-mult-auto-vardir.py | 222 ++++++++++++----------- test/MSVS/vs-mult-auto.py | 204 +++++++++++---------- testing/framework/TestSConsMSVS.py | 80 ++++++++- 9 files changed, 670 insertions(+), 426 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d08cb900d5..c0b3d57e80 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -44,10 +44,19 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER auto_build_solution disabled and explicit solution generation: when the solution files are cleaned, the project files are also cleaned. The tests for vs 6.0-7.1 were changed accordingly. - - MSVS: Filter out solution nodes from the project list in the msvs tool. When - auto_build_solution is enabled and the MSVSProject return value is used in the - MSVSSolution project specification, the auto-generated solution file is - generated as a Project record in the solution file. Fixes #4613. + - MSVS: Add an optional keyword argument, auto_filter_projects, to MSVSSolution. + Accepted values for auto_filter_projects are: + - None [default]: raise an exception when solution file names or nodes are + detected in the projects argument list. + - True or evaluates True: automatically remove solution file names and nodes + from the project argument list. + - False or evaluates False: leave solution file names and nodes in the project + argument list. An exception is not raised. + Solution file names and/or nodes in the project argument list cause erroneous + Project records to be produced in the generated solution file. As a + convenience, an end-user may elect to ignore solution file names and nodes in + the projects argument list rather than manually removing solution file names + and nodes from the MSVSProject return values. Resolves #4613. - MSVS: Remove the platform specification (i.e., platform = 'win32') from select test script environments. The platform specification appears superfluous. diff --git a/RELEASE.txt b/RELEASE.txt index fe4b05df48..8d4864425f 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -39,6 +39,19 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY generated with auto_build_solution disabled and explicit solution generation: when the solution files are cleaned, the project files are also cleaned. The tests for vs 6.0-7.1 were updated accordingly. +- MSVS: Add an optional keyword argument, auto_filter_projects, to + MSVSSolution. Accepted values for auto_filter_projects are: + - None [default]: raise an exception when solution file names or nodes + are detected in the projects argument list. + - True or evaluates True: automatically remove solution file names and + nodes from the project argument list. + - False or evaluates False: leave solution file names and nodes in the + project argument list. An exception is not raised. + Solution file names and/or nodes in the project argument list cause + erroneous Project records to be produced in the generated solution file. + As a convenience, a user may elect to ignore solution file names and nodes + in the projects argument list rather than manually removing solution file + names and nodes from the MSVSProject return values. FIXES ----- @@ -68,8 +81,6 @@ FIXES scripts were being invoked for msvc version 8.0 only. Additional msvs tool and test changes due to the msvs test scripts being run for all msvc versions (i.e., minor test and tool issues went undetected). -- MSVS: msvs solution files are no longer included when generating the - project records for a solution file. - MSVS: for variant build configurations, msvs solution files are generated in the source directory and a placeholder file is generated in the variant build directory. This mirrors the behavior of generated diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index 9cb28aff36..b327653160 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -1481,6 +1481,7 @@ def Build(self): _GenerateV10User.Build(self) def _projectDSPNodes(env): + auto_filter_projects = env.get('auto_filter_projects', None) if 'projects' not in env: raise SCons.Errors.UserError("You must specify a 'projects' argument to create an MSVSSolution.") projects = env['projects'] @@ -1494,7 +1495,22 @@ def _projectDSPNodes(env): for p in projects: node = env.File(p) if os.path.normcase(str(node)).endswith(sln_suffix): - continue + # solution node detected (possible issues when opening generated solution file with VS IDE) + if auto_filter_projects is None: + nodestr = str(node) + errmsg = ( + f"An msvs solution file was detected in the MSVSSolution 'projects' argument: {nodestr!r}.\n" + " Add MSVSSolution argument 'auto_filter_projects=True' to suppress project solution nodes.\n" + " Add MSVSSolution argument 'auto_filter_projects=False' to keep project solution nodes.\n" + " Refer to the MSVSSolution documentation for more information." + ) + raise SCons.Errors.UserError(errmsg) + elif auto_filter_projects: + # skip solution node + continue + else: + # keep solution node + pass dspnodes.append(node) if len(dspnodes) < 1: raise SCons.Errors.UserError("You must specify at least one project node to create an MSVSSolution.") diff --git a/SCons/Tool/msvs.xml b/SCons/Tool/msvs.xml index cc4220c787..ad3a7569be 100644 --- a/SCons/Tool/msvs.xml +++ b/SCons/Tool/msvs.xml @@ -540,6 +540,74 @@ env.MSVSProject( + + In addition to the mandatory arguments above, the following optional + values may be specified as keyword arguments: + + + + auto_filter_projects + + + Under certain circumstances, solution file names or + solution file nodes may be present in the + projects argument list. + When solution file names or nodes are present in the + projects argument list, the generated + solution file may contain erroneous Project records resulting + in VS IDE error messages when opening the generated solution + file. + By default, an exception is raised when a solution file + name or solution file node is detected in the + projects argument list. + + + The accepted values for auto_filter_projects + are: + + + + None + + + An exception is raised when a solution file name or solution + file node is detected in the projects + argument list. + + + None is the default value. + + + + + True or evaluates True + + + Automatically remove solution file names and solution file + nodes from the projects argument list. + + + + + False or evaluates False + + + Leave the solution file names and solution file nodes + in the projects argument list. + An exception is not raised. + + + When opening the generated solution file with the VS IDE, + the VS IDE will likely report that there are erroneous + Project records that are not supported or that need to be + modified. + + + + + + + Example Usage: env.MSVSSolution( diff --git a/test/MSVS/vs-mult-auto-guid.py b/test/MSVS/vs-mult-auto-guid.py index 1328f54b61..412c379ab7 100644 --- a/test/MSVS/vs-mult-auto-guid.py +++ b/test/MSVS/vs-mult-auto-guid.py @@ -32,111 +32,129 @@ test = None for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): - test = TestSConsMSVS.TestSConsMSVS() - - # Make the test infrastructure think we have this version of MSVS installed. - test._msvs_versions = [vc_version] - - dirs = ['inc1', 'inc2'] - major, minor = test.parse_vc_version(vc_version) - project_ext = '.vcproj' if major <= 9 else '.vcxproj' - - project_file_1 = 'Test_1' + project_ext - project_file_2 = 'Test_2' + project_ext - - filters_file_1 = project_file_1 + '.filters' - filters_file_2 = project_file_2 + '.filters' - filters_file_expected = major >= 10 - - solution_file = 'Test.sln' - solution_file_1 = 'Test_1.sln' - solution_file_2 = 'Test_2.sln' - - if major >= 10: - project_guid_1 = "{F7D7CE55-37BF-51DE-8942-9377B2BE8387}" - project_guid_2 = "{8D17BC73-09FD-5B69-BBBF-1E40E0C63456}" - else: - project_guid_1 = "{53EE9FA7-6300-55B8-8A0E-A3DC40983390}" - project_guid_2 = "{57358E9B-126D-53F6-AD5A-559AB4A8EE62}" - - expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( - vc_version, dirs, project_file_1, project_guid_1, - ) - - expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( - vc_version, dirs, project_file_2, project_guid_2, - ) - - expected_slnfile = test.get_expected_projects_sln_file_contents( - vc_version, - project_file_1, - project_file_2, - ) - - expected_slnfile_1 = test.get_expected_sln_file_contents( - vc_version, - project_file_1, - ) - - expected_slnfile_2 = test.get_expected_sln_file_contents( - vc_version, - project_file_2, - ) - - SConstruct_contents = test.get_expected_projects_sconscript_file_contents( - vc_version=vc_version, - project_file_1=project_file_1, - project_file_2=project_file_2, - solution_file=solution_file, - autobuild_solution=1, - default_guids=True, - ) - - test.write('SConstruct', SConstruct_contents) - - test.run(arguments=".") - - test.must_exist(test.workpath(project_file_1)) - vcproj = test.read(project_file_1, 'r') - expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) - # don't compare the pickled data - assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.must_exist(test.workpath(project_file_2)) - vcproj = test.read(project_file_2, 'r') - expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) - # don't compare the pickled data - assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.must_exist(test.workpath(solution_file)) - sln = test.read(solution_file, 'r') - expect = test.msvs_substitute_projects( - expected_slnfile, subdir='src', - project_guid_1=project_guid_1, project_guid_2=project_guid_2 - ) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - test.must_exist(test.workpath(solution_file_1)) - sln = test.read(solution_file_1, 'r') - expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - test.must_exist(test.workpath(solution_file_2)) - sln = test.read(solution_file_2, 'r') - expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - if filters_file_expected: - test.must_exist(test.workpath(filters_file_1)) - test.must_exist(test.workpath(filters_file_2)) - else: - test.must_not_exist(test.workpath(filters_file_1)) - test.must_not_exist(test.workpath(filters_file_2)) - - # TODO: clean tests + + for autofilter_projects in (None, False, True): + + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + solution_file_1 = 'Test_1.sln' + solution_file_2 = 'Test_2.sln' + + if major >= 10: + project_guid_1 = "{F7D7CE55-37BF-51DE-8942-9377B2BE8387}" + project_guid_2 = "{8D17BC73-09FD-5B69-BBBF-1E40E0C63456}" + else: + project_guid_1 = "{53EE9FA7-6300-55B8-8A0E-A3DC40983390}" + project_guid_2 = "{57358E9B-126D-53F6-AD5A-559AB4A8EE62}" + + if major >= 10: + solution_guid_1 = "{73ABD7C4-0A64-5192-97C3-252B70C8D1B1}" + solution_guid_2 = "{142F0D4F-9896-5808-AAB4-A80F5661B1FE}" + else: + solution_guid_1 = "{73ABD7C4-0A64-5192-97C3-252B70C8D1B1}" + solution_guid_2 = "{142F0D4F-9896-5808-AAB4-A80F5661B1FE}" + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + have_solution_project_nodes=True, + autofilter_solution_project_nodes=autofilter_projects, + ) + + expected_slnfile_1 = test.get_expected_sln_file_contents( + vc_version, + project_file_1, + ) + + expected_slnfile_2 = test.get_expected_sln_file_contents( + vc_version, + project_file_2, + ) + + SConstruct_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=1, + autofilter_projects=autofilter_projects, + default_guids=True, + ) + + test.write('SConstruct', SConstruct_contents) + + if autofilter_projects is None: + test.run(arguments=".", status=2, stderr=r"^.*scons: [*]{3} An msvs solution file was detected in the MSVSSolution 'projects' argument:.+", match=test.match_re_dotall) + continue + + test.run(arguments=".") + + test.must_exist(test.workpath(project_file_1)) + vcproj = test.read(project_file_1, 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(project_file_2)) + vcproj = test.read(project_file_2, 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(solution_file)) + sln = test.read(solution_file, 'r') + expect = test.msvs_substitute_projects( + expected_slnfile, subdir='src', + project_guid_1=project_guid_1, project_guid_2=project_guid_2, + solution_guid_1=solution_guid_1, solution_guid_2=solution_guid_2, + ) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath(solution_file_1)) + sln = test.read(solution_file_1, 'r') + expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath(solution_file_2)) + sln = test.read(solution_file_2, 'r') + expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath(filters_file_1)) + test.must_exist(test.workpath(filters_file_2)) + else: + test.must_not_exist(test.workpath(filters_file_1)) + test.must_not_exist(test.workpath(filters_file_2)) + + # TODO: clean tests if test: test.pass_test() diff --git a/test/MSVS/vs-mult-auto-vardir-guid.py b/test/MSVS/vs-mult-auto-vardir-guid.py index 026a2e8064..87d233220b 100644 --- a/test/MSVS/vs-mult-auto-vardir-guid.py +++ b/test/MSVS/vs-mult-auto-vardir-guid.py @@ -32,147 +32,165 @@ test = None for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): - test = TestSConsMSVS.TestSConsMSVS() - - # Make the test infrastructure think we have this version of MSVS installed. - test._msvs_versions = [vc_version] - - dirs = ['inc1', 'inc2'] - major, minor = test.parse_vc_version(vc_version) - project_ext = '.vcproj' if major <= 9 else '.vcxproj' - - project_file_1 = 'Test_1' + project_ext - project_file_2 = 'Test_2' + project_ext - - filters_file_1 = project_file_1 + '.filters' - filters_file_2 = project_file_2 + '.filters' - filters_file_expected = major >= 10 - - solution_file = 'Test.sln' - solution_file_1 = 'Test_1.sln' - solution_file_2 = 'Test_2.sln' - - if major >= 10: - project_guid_1 = "{5A243E49-07F0-54C3-B3FD-1DBDF1BA5C9E}" - project_guid_2 = "{E20E17C7-251E-5246-8FD1-5D51978A0A5D}" - else: - project_guid_1 = "{AB46DD68-8CD8-5832-B784-65B216B94739}" - project_guid_2 = "{03EB0BC3-DA68-5825-9EBB-D8713304E739}" - - expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( - vc_version, dirs, project_file_1, project_guid_1, - ) - - expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( - vc_version, dirs, project_file_2, project_guid_2, - ) - - expected_slnfile = test.get_expected_projects_sln_file_contents( - vc_version, - project_file_1, - project_file_2, - ) - - expected_slnfile_1 = test.get_expected_sln_file_contents( - vc_version, - project_file_1, - ) - - expected_slnfile_2 = test.get_expected_sln_file_contents( - vc_version, - project_file_2, - ) - - SConscript_contents = test.get_expected_projects_sconscript_file_contents( - vc_version=vc_version, - project_file_1=project_file_1, - project_file_2=project_file_2, - solution_file=solution_file, - autobuild_solution=1, - default_guids=True, - ) - - test.subdir('src') - - test.write('SConstruct', """\ + + for autofilter_projects in (None, False, True): + + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + solution_file_1 = 'Test_1.sln' + solution_file_2 = 'Test_2.sln' + + if major >= 10: + project_guid_1 = "{5A243E49-07F0-54C3-B3FD-1DBDF1BA5C9E}" + project_guid_2 = "{E20E17C7-251E-5246-8FD1-5D51978A0A5D}" + else: + project_guid_1 = "{AB46DD68-8CD8-5832-B784-65B216B94739}" + project_guid_2 = "{03EB0BC3-DA68-5825-9EBB-D8713304E739}" + + if major >= 10: + solution_guid_1 = "{5E5E4F5D-3E6A-5958-81C6-D4B8B8C633FA}" + solution_guid_2 = "{ECBCA12A-191D-54EC-BA78-F14249171130}" + else: + solution_guid_1 = "{5E5E4F5D-3E6A-5958-81C6-D4B8B8C633FA}" + solution_guid_2 = "{ECBCA12A-191D-54EC-BA78-F14249171130}" + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + have_solution_project_nodes=True, + autofilter_solution_project_nodes=autofilter_projects, + ) + + expected_slnfile_1 = test.get_expected_sln_file_contents( + vc_version, + project_file_1, + ) + + expected_slnfile_2 = test.get_expected_sln_file_contents( + vc_version, + project_file_2, + ) + + SConscript_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=1, + autofilter_projects=autofilter_projects, + default_guids=True, + ) + + test.subdir('src') + + test.write('SConstruct', """\ SConscript('src/SConscript', variant_dir='build') """) - test.write(['src', 'SConscript'], SConscript_contents) - - test.run(arguments=".") - - test.must_exist(test.workpath('src', project_file_1)) - vcproj = test.read(['src', project_file_1], 'r') - expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) - # don't compare the pickled data - assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.must_exist(test.workpath('src', project_file_2)) - vcproj = test.read(['src', project_file_2], 'r') - expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) - # don't compare the pickled data - assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.must_exist(test.workpath('src', solution_file)) - sln = test.read(['src', solution_file], 'r') - expect = test.msvs_substitute_projects( - expected_slnfile, subdir='src', - project_guid_1=project_guid_1, project_guid_2=project_guid_2 - ) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - test.must_exist(test.workpath('src', solution_file_1)) - sln = test.read(['src', solution_file_1], 'r') - expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - test.must_exist(test.workpath('src', solution_file_2)) - sln = test.read(['src', solution_file_2], 'r') - expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - if filters_file_expected: - test.must_exist(test.workpath('src', filters_file_1)) - test.must_exist(test.workpath('src', filters_file_2)) - else: - test.must_not_exist(test.workpath('src', filters_file_1)) - test.must_not_exist(test.workpath('src', filters_file_2)) - - test.must_match(['build', project_file_1], """\ + test.write(['src', 'SConscript'], SConscript_contents) + + if autofilter_projects is None: + test.run(arguments=".", status=2, stderr=r"^.*scons: [*]{3} An msvs solution file was detected in the MSVSSolution 'projects' argument:.+", match=test.match_re_dotall) + continue + + test.run(arguments=".") + + test.must_exist(test.workpath('src', project_file_1)) + vcproj = test.read(['src', project_file_1], 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', project_file_2)) + vcproj = test.read(['src', project_file_2], 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', solution_file)) + sln = test.read(['src', solution_file], 'r') + expect = test.msvs_substitute_projects( + expected_slnfile, subdir='src', + project_guid_1=project_guid_1, project_guid_2=project_guid_2, + solution_guid_1=solution_guid_1, solution_guid_2=solution_guid_2, + ) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath('src', solution_file_1)) + sln = test.read(['src', solution_file_1], 'r') + expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath('src', solution_file_2)) + sln = test.read(['src', solution_file_2], 'r') + expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath('src', filters_file_1)) + test.must_exist(test.workpath('src', filters_file_2)) + else: + test.must_not_exist(test.workpath('src', filters_file_1)) + test.must_not_exist(test.workpath('src', filters_file_2)) + + test.must_match(['build', project_file_1], """\ This is just a placeholder file. The real project file is here: %s """ % test.workpath('src', project_file_1), mode='r') - test.must_match(['build', project_file_2], """\ + test.must_match(['build', project_file_2], """\ This is just a placeholder file. The real project file is here: %s """ % test.workpath('src', project_file_2), mode='r') - test.must_match(['build', solution_file], """\ + test.must_match(['build', solution_file], """\ This is just a placeholder file. The real workspace file is here: %s """ % test.workpath('src', solution_file), mode='r') - test.must_match(['build', solution_file_1], """\ + test.must_match(['build', solution_file_1], """\ This is just a placeholder file. The real workspace file is here: %s """ % test.workpath('src', solution_file_1), mode='r') - test.must_match(['build', solution_file_2], """\ + test.must_match(['build', solution_file_2], """\ This is just a placeholder file. The real workspace file is here: %s """ % test.workpath('src', solution_file_2), mode='r') - # TODO: clean tests + # TODO: clean tests if test: test.pass_test() diff --git a/test/MSVS/vs-mult-auto-vardir.py b/test/MSVS/vs-mult-auto-vardir.py index 9a834749f1..b4d2109312 100644 --- a/test/MSVS/vs-mult-auto-vardir.py +++ b/test/MSVS/vs-mult-auto-vardir.py @@ -35,136 +35,156 @@ test = None for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): - test = TestSConsMSVS.TestSConsMSVS() - # Make the test infrastructure think we have this version of MSVS installed. - test._msvs_versions = [vc_version] - - dirs = ['inc1', 'inc2'] - major, minor = test.parse_vc_version(vc_version) - project_ext = '.vcproj' if major <= 9 else '.vcxproj' - - project_file_1 = 'Test_1' + project_ext - project_file_2 = 'Test_2' + project_ext - - filters_file_1 = project_file_1 + '.filters' - filters_file_2 = project_file_2 + '.filters' - filters_file_expected = major >= 10 - - solution_file = 'Test.sln' - solution_file_1 = 'Test_1.sln' - solution_file_2 = 'Test_2.sln' - - expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( - vc_version, dirs, project_file_1, project_guid_1, - ) - - expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( - vc_version, dirs, project_file_2, project_guid_2, - ) - - expected_slnfile = test.get_expected_projects_sln_file_contents( - vc_version, - project_file_1, - project_file_2, - ) - - expected_slnfile_1 = test.get_expected_sln_file_contents( - vc_version, - project_file_1, - ) - - expected_slnfile_2 = test.get_expected_sln_file_contents( - vc_version, - project_file_2, - ) - - SConscript_contents = test.get_expected_projects_sconscript_file_contents( - vc_version=vc_version, - project_file_1=project_file_1, - project_file_2=project_file_2, - solution_file=solution_file, - autobuild_solution=1, - ) - - test.subdir('src') - - test.write('SConstruct', """\ + for autofilter_projects in (None, False, True): + + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + solution_file_1 = 'Test_1.sln' + solution_file_2 = 'Test_2.sln' + + if major >= 10: + solution_guid_1 = "{5E5E4F5D-3E6A-5958-81C6-D4B8B8C633FA}" + solution_guid_2 = "{ECBCA12A-191D-54EC-BA78-F14249171130}" + else: + solution_guid_1 = "{5E5E4F5D-3E6A-5958-81C6-D4B8B8C633FA}" + solution_guid_2 = "{ECBCA12A-191D-54EC-BA78-F14249171130}" + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + have_solution_project_nodes=True, + autofilter_solution_project_nodes=autofilter_projects, + ) + + expected_slnfile_1 = test.get_expected_sln_file_contents( + vc_version, + project_file_1, + ) + + expected_slnfile_2 = test.get_expected_sln_file_contents( + vc_version, + project_file_2, + ) + + SConscript_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=1, + autofilter_projects=autofilter_projects, + ) + + test.subdir('src') + + test.write('SConstruct', """\ SConscript('src/SConscript', variant_dir='build') """) - test.write(['src', 'SConscript'], SConscript_contents) - - test.run(arguments=".") - - test.must_exist(test.workpath('src', project_file_1)) - vcproj = test.read(['src', project_file_1], 'r') - expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) - # don't compare the pickled data - assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.must_exist(test.workpath('src', project_file_2)) - vcproj = test.read(['src', project_file_2], 'r') - expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) - # don't compare the pickled data - assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.must_exist(test.workpath('src', solution_file)) - sln = test.read(['src', solution_file], 'r') - expect = test.msvs_substitute_projects(expected_slnfile, subdir='src') - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - test.must_exist(test.workpath('src', solution_file_1)) - sln = test.read(['src', solution_file_1], 'r') - expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - test.must_exist(test.workpath('src', solution_file_2)) - sln = test.read(['src', solution_file_2], 'r') - expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - if filters_file_expected: - test.must_exist(test.workpath('src', filters_file_1)) - test.must_exist(test.workpath('src', filters_file_2)) - else: - test.must_not_exist(test.workpath('src', filters_file_1)) - test.must_not_exist(test.workpath('src', filters_file_2)) - - test.must_match(['build', project_file_1], """\ + test.write(['src', 'SConscript'], SConscript_contents) + + if autofilter_projects is None: + test.run(arguments=".", status=2, stderr=r"^.*scons: [*]{3} An msvs solution file was detected in the MSVSSolution 'projects' argument:.+", match=test.match_re_dotall) + continue + + test.run(arguments=".") + + test.must_exist(test.workpath('src', project_file_1)) + vcproj = test.read(['src', project_file_1], 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', project_file_2)) + vcproj = test.read(['src', project_file_2], 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath('src', solution_file)) + sln = test.read(['src', solution_file], 'r') + expect = test.msvs_substitute_projects( + expected_slnfile, subdir='src', + solution_guid_1=solution_guid_1, solution_guid_2=solution_guid_2, + ) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath('src', solution_file_1)) + sln = test.read(['src', solution_file_1], 'r') + expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath('src', solution_file_2)) + sln = test.read(['src', solution_file_2], 'r') + expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath('src', filters_file_1)) + test.must_exist(test.workpath('src', filters_file_2)) + else: + test.must_not_exist(test.workpath('src', filters_file_1)) + test.must_not_exist(test.workpath('src', filters_file_2)) + + test.must_match(['build', project_file_1], """\ This is just a placeholder file. The real project file is here: %s """ % test.workpath('src', project_file_1), mode='r') - test.must_match(['build', project_file_2], """\ + test.must_match(['build', project_file_2], """\ This is just a placeholder file. The real project file is here: %s """ % test.workpath('src', project_file_2), mode='r') - test.must_match(['build', solution_file], """\ + test.must_match(['build', solution_file], """\ This is just a placeholder file. The real workspace file is here: %s """ % test.workpath('src', solution_file), mode='r') - test.must_match(['build', solution_file_1], """\ + test.must_match(['build', solution_file_1], """\ This is just a placeholder file. The real workspace file is here: %s """ % test.workpath('src', solution_file_1), mode='r') - test.must_match(['build', solution_file_2], """\ + test.must_match(['build', solution_file_2], """\ This is just a placeholder file. The real workspace file is here: %s """ % test.workpath('src', solution_file_2), mode='r') - # TODO: clean tests + # TODO: clean tests if test: test.pass_test() diff --git a/test/MSVS/vs-mult-auto.py b/test/MSVS/vs-mult-auto.py index 4b071c23ed..57f1145fd1 100644 --- a/test/MSVS/vs-mult-auto.py +++ b/test/MSVS/vs-mult-auto.py @@ -35,98 +35,118 @@ test = None for vc_version in TestSConsMSVS.get_tested_proj_file_vc_versions(): - test = TestSConsMSVS.TestSConsMSVS() - - # Make the test infrastructure think we have this version of MSVS installed. - test._msvs_versions = [vc_version] - - dirs = ['inc1', 'inc2'] - major, minor = test.parse_vc_version(vc_version) - project_ext = '.vcproj' if major <= 9 else '.vcxproj' - - project_file_1 = 'Test_1' + project_ext - project_file_2 = 'Test_2' + project_ext - - filters_file_1 = project_file_1 + '.filters' - filters_file_2 = project_file_2 + '.filters' - filters_file_expected = major >= 10 - - solution_file = 'Test.sln' - solution_file_1 = 'Test_1.sln' - solution_file_2 = 'Test_2.sln' - - expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( - vc_version, dirs, project_file_1, project_guid_1, - ) - - expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( - vc_version, dirs, project_file_2, project_guid_2, - ) - - expected_slnfile = test.get_expected_projects_sln_file_contents( - vc_version, - project_file_1, - project_file_2, - ) - - expected_slnfile_1 = test.get_expected_sln_file_contents( - vc_version, - project_file_1, - ) - - expected_slnfile_2 = test.get_expected_sln_file_contents( - vc_version, - project_file_2, - ) - - SConstruct_contents = test.get_expected_projects_sconscript_file_contents( - vc_version=vc_version, - project_file_1=project_file_1, - project_file_2=project_file_2, - solution_file=solution_file, - autobuild_solution=1, - ) - - test.write('SConstruct', SConstruct_contents) - - test.run(arguments=".") - - test.must_exist(test.workpath(project_file_1)) - vcproj = test.read(project_file_1, 'r') - expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) - # don't compare the pickled data - assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.must_exist(test.workpath(project_file_2)) - vcproj = test.read(project_file_2, 'r') - expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) - # don't compare the pickled data - assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) - - test.must_exist(test.workpath(solution_file)) - sln = test.read(solution_file, 'r') - expect = test.msvs_substitute_projects(expected_slnfile, subdir='src') - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - test.must_exist(test.workpath(solution_file_1)) - sln = test.read(solution_file_1, 'r') - expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - test.must_exist(test.workpath(solution_file_2)) - sln = test.read(solution_file_2, 'r') - expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) - # don't compare the pickled data - assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) - - if filters_file_expected: - test.must_exist(test.workpath(filters_file_1)) - test.must_exist(test.workpath(filters_file_2)) - else: - test.must_not_exist(test.workpath(filters_file_1)) - test.must_not_exist(test.workpath(filters_file_2)) + + for autofilter_projects in (None, False, True): + + test = TestSConsMSVS.TestSConsMSVS() + + # Make the test infrastructure think we have this version of MSVS installed. + test._msvs_versions = [vc_version] + + dirs = ['inc1', 'inc2'] + major, minor = test.parse_vc_version(vc_version) + project_ext = '.vcproj' if major <= 9 else '.vcxproj' + + project_file_1 = 'Test_1' + project_ext + project_file_2 = 'Test_2' + project_ext + + filters_file_1 = project_file_1 + '.filters' + filters_file_2 = project_file_2 + '.filters' + filters_file_expected = major >= 10 + + solution_file = 'Test.sln' + solution_file_1 = 'Test_1.sln' + solution_file_2 = 'Test_2.sln' + + if major >= 10: + solution_guid_1 = "{73ABD7C4-0A64-5192-97C3-252B70C8D1B1}" + solution_guid_2 = "{142F0D4F-9896-5808-AAB4-A80F5661B1FE}" + else: + solution_guid_1 = "{73ABD7C4-0A64-5192-97C3-252B70C8D1B1}" + solution_guid_2 = "{142F0D4F-9896-5808-AAB4-A80F5661B1FE}" + + expected_vcprojfile_1 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_1, project_guid_1, + ) + + expected_vcprojfile_2 = test.get_expected_projects_proj_file_contents( + vc_version, dirs, project_file_2, project_guid_2, + ) + + expected_slnfile = test.get_expected_projects_sln_file_contents( + vc_version, + project_file_1, + project_file_2, + have_solution_project_nodes=True, + autofilter_solution_project_nodes=autofilter_projects, + ) + + expected_slnfile_1 = test.get_expected_sln_file_contents( + vc_version, + project_file_1, + ) + + expected_slnfile_2 = test.get_expected_sln_file_contents( + vc_version, + project_file_2, + ) + + SConstruct_contents = test.get_expected_projects_sconscript_file_contents( + vc_version=vc_version, + project_file_1=project_file_1, + project_file_2=project_file_2, + solution_file=solution_file, + autobuild_solution=1, + autofilter_projects=autofilter_projects, + ) + + test.write('SConstruct', SConstruct_contents) + + if autofilter_projects is None: + test.run(arguments=".", status=2, stderr=r"^.*scons: [*]{3} An msvs solution file was detected in the MSVSSolution 'projects' argument:.+", match=test.match_re_dotall) + continue + + test.run(arguments=".") + + test.must_exist(test.workpath(project_file_1)) + vcproj = test.read(project_file_1, 'r') + expect = test.msvs_substitute(expected_vcprojfile_1, vc_version, None, 'SConstruct', project_guid=project_guid_1) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(project_file_2)) + vcproj = test.read(project_file_2, 'r') + expect = test.msvs_substitute(expected_vcprojfile_2, vc_version, None, 'SConstruct', project_guid=project_guid_2) + # don't compare the pickled data + assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj) + + test.must_exist(test.workpath(solution_file)) + sln = test.read(solution_file, 'r') + expect = test.msvs_substitute_projects( + expected_slnfile, subdir='src', + solution_guid_1=solution_guid_1, solution_guid_2=solution_guid_2, + ) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath(solution_file_1)) + sln = test.read(solution_file_1, 'r') + expect = test.msvs_substitute(expected_slnfile_1, vc_version, subdir='src', project_guid=project_guid_1) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + test.must_exist(test.workpath(solution_file_2)) + sln = test.read(solution_file_2, 'r') + expect = test.msvs_substitute(expected_slnfile_2, vc_version, subdir='src', project_guid=project_guid_2) + # don't compare the pickled data + assert sln[:len(expect)] == expect, test.diff_substr(expect, sln) + + if filters_file_expected: + test.must_exist(test.workpath(filters_file_1)) + test.must_exist(test.workpath(filters_file_2)) + else: + test.must_not_exist(test.workpath(filters_file_1)) + test.must_not_exist(test.workpath(filters_file_2)) # TODO: clean tests diff --git a/testing/framework/TestSConsMSVS.py b/testing/framework/TestSConsMSVS.py index 7d2524187c..0951b4626a 100644 --- a/testing/framework/TestSConsMSVS.py +++ b/testing/framework/TestSConsMSVS.py @@ -54,6 +54,9 @@ PROJECT_GUID_1 = "{11111111-1111-1111-1111-111111111111}" PROJECT_GUID_2 = "{22222222-2222-2222-2222-222222222222}" +SOLUTION_GUID_1 = "{88888888-8888-8888-8888-888888888888}" +SOLUTION_GUID_2 = "{99999999-9999-9999-9999-999999999999}" + expected_dspfile_6_0 = '''\ # Microsoft Developer Studio Project File - Name="Test" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 @@ -690,6 +693,38 @@ EndGlobal """ +expected_projects_slnfile_fmt_slnnodes = """\ +Microsoft Visual Studio Solution File, Format Version %(FORMAT_VERSION)s +# Visual Studio %(VS_NUMBER)s +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%(PROJECT_NAME_1)s", "%(PROJECT_FILE_1)s", "" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%(SOLUTION_FILE_1)s", "%(SOLUTION_FILE_1)s", "" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%(PROJECT_NAME_2)s", "%(PROJECT_FILE_2)s", "" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%(SOLUTION_FILE_2)s", "%(SOLUTION_FILE_2)s", "" +EndProject +Global + +\tGlobalSection(SolutionConfigurationPlatforms) = preSolution +\t\tRelease|Win32 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(ProjectConfigurationPlatforms) = postSolution +\t\t.Release|Win32.ActiveCfg = Release|Win32 +\t\t.Release|Win32.Build.0 = Release|Win32 +\t\t.Release|Win32.ActiveCfg = Release|Win32 +\t\t.Release|Win32.Build.0 = Release|Win32 +\t\t.Release|Win32.ActiveCfg = Release|Win32 +\t\t.Release|Win32.Build.0 = Release|Win32 +\t\t.Release|Win32.ActiveCfg = Release|Win32 +\t\t.Release|Win32.Build.0 = Release|Win32 +\tEndGlobalSection +\tGlobalSection(SolutionProperties) = preSolution +\t\tHideSolutionNode = FALSE +\tEndGlobalSection +EndGlobal +""" + SConscript_projects_contents_fmt = """\ env=Environment( tools=['msvs'], @@ -737,6 +772,7 @@ target = '%(SOLUTION_FILE)s', projects = [p1, p2], variant = 'Release', + auto_filter_projects = %(AUTOFILTER_PROJECTS)s, ) """ @@ -785,6 +821,7 @@ target = '%(SOLUTION_FILE)s', projects = [p1, p2], variant = 'Release', + auto_filter_projects = %(AUTOFILTER_PROJECTS)s, ) """ @@ -1094,6 +1131,8 @@ def msvs_substitute_projects( python=None, project_guid_1=None, project_guid_2=None, + solution_guid_1=None, + solution_guid_2=None, vcproj_sccinfo: str='', sln_sccinfo: str='' ): @@ -1117,6 +1156,12 @@ def msvs_substitute_projects( if project_guid_2 is None: project_guid_2 = PROJECT_GUID_2 + if solution_guid_1 is None: + solution_guid_1 = SOLUTION_GUID_1 + + if solution_guid_2 is None: + solution_guid_2 = SOLUTION_GUID_2 + if 'SCONS_LIB_DIR' in os.environ: exec_script_main = f"from os.path import join; import sys; sys.path = [ r'{os.environ['SCONS_LIB_DIR']}' ] + sys.path; import SCons.Script; SCons.Script.main()" else: @@ -1130,6 +1175,8 @@ def msvs_substitute_projects( result = result.replace(r'', exec_script_main_xml) result = result.replace(r'', project_guid_1) result = result.replace(r'', project_guid_2) + result = result.replace(r'', solution_guid_1) + result = result.replace(r'', solution_guid_2) result = result.replace('\n', vcproj_sccinfo) result = result.replace('\n', sln_sccinfo) return result @@ -1153,20 +1200,36 @@ def get_expected_projects_proj_file_contents(self, vc_version, dirs, project_fil def get_expected_projects_sln_file_contents( self, vc_version, project_file_1, project_file_2, + have_solution_project_nodes=False, + autofilter_solution_project_nodes=None, ): - return expected_projects_slnfile_fmt % { - 'FORMAT_VERSION': self._get_solution_file_format_version(vc_version), - 'VS_NUMBER': self._get_solution_file_vs_number(vc_version), - 'PROJECT_NAME_1': project_file_1.split('.')[0], - 'PROJECT_FILE_1': project_file_1, - 'PROJECT_NAME_2': project_file_2.split('.')[0], - 'PROJECT_FILE_2': project_file_2, - } + if not have_solution_project_nodes or autofilter_solution_project_nodes: + rval = expected_projects_slnfile_fmt % { + 'FORMAT_VERSION': self._get_solution_file_format_version(vc_version), + 'VS_NUMBER': self._get_solution_file_vs_number(vc_version), + 'PROJECT_NAME_1': project_file_1.split('.')[0], + 'PROJECT_FILE_1': project_file_1, + 'PROJECT_NAME_2': project_file_2.split('.')[0], + 'PROJECT_FILE_2': project_file_2, + } + else: + rval = expected_projects_slnfile_fmt_slnnodes % { + 'FORMAT_VERSION': self._get_solution_file_format_version(vc_version), + 'VS_NUMBER': self._get_solution_file_vs_number(vc_version), + 'PROJECT_NAME_1': project_file_1.split('.')[0], + 'PROJECT_FILE_1': project_file_1, + 'PROJECT_NAME_2': project_file_2.split('.')[0], + 'PROJECT_FILE_2': project_file_2, + 'SOLUTION_FILE_1': project_file_1.split('.')[0] + ".sln", + 'SOLUTION_FILE_2': project_file_2.split('.')[0] + ".sln", + } + return rval def get_expected_projects_sconscript_file_contents( self, vc_version, project_file_1, project_file_2, solution_file, autobuild_solution=0, + autofilter_projects=None, default_guids=False, ): @@ -1177,6 +1240,7 @@ def get_expected_projects_sconscript_file_contents( 'PROJECT_FILE_2': project_file_2, 'SOLUTION_FILE': solution_file, "AUTOBUILD_SOLUTION": autobuild_solution, + "AUTOFILTER_PROJECTS": autofilter_projects, } if default_guids: From 5b772399bfce5cc1bb6b1aa15cad6f979540a0b1 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:42:01 -0400 Subject: [PATCH 170/386] Update RELEASE.txt entries to be consistent with current formatting. --- RELEASE.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/RELEASE.txt b/RELEASE.txt index 96f4eb20c6..6ba7fc06f7 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -35,11 +35,13 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY The item will now not be deleted from the base environment. - Added support for tracking beamer themes in the LaTeX scanner. + - MSVS: msvs project files are always generated before the corresponding msvs solution files. This changes the behavior of clean for a project generated with auto_build_solution disabled and explicit solution generation: when the solution files are cleaned, the project files are also cleaned. The tests for vs 6.0-7.1 were updated accordingly. + - MSVS: Add an optional keyword argument, auto_filter_projects, to MSVSSolution. Accepted values for auto_filter_projects are: - None [default]: raise an exception when solution file names or nodes @@ -76,25 +78,32 @@ FIXES - Fix a problem with compilation_db component initialization - the entries for assembler files were not being set up correctly. + - On Darwin, PermissionErrors are now handled while trying to access /etc/paths.d. This may occur if SCons is invoked in a sandboxed environment (such as Nix). -- Added error handling when creating MS VC detection debug log file specified by - SCONS_MSCOMMON_DEBUG + +- Added error handling when creating MSVC detection debug log file specified + by SCONS_MSCOMMON_DEBUG. + - MSVS: Modify select msvs test scripts to run on platforms not supported by the msvs/msvc tool implementation via a default host architecture for unsupported platforms. + - MSVS: Fixed early loop exit in select msvs test scripts. Select msvs test scripts were being invoked for msvc version 8.0 only. Additional msvs tool and test changes due to the msvs test scripts being run for all msvc versions (i.e., minor test and tool issues went undetected). + - MSVS: for variant build configurations, msvs solution files are generated in the source directory and a placeholder file is generated in the variant build directory. This mirrors the behavior of generated msvs project files. + - MSVS: msvs project files are generated before the corresponding msvs solution file. User-specified project GUIDs should now be correctly written to the solution file. + - Fix nasm test for missing include file, cleanup. - Skip running a few validation tests if the user is root and the test is From 382d1e293a1ddf268d62e993e4f7ae3c75501633 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 10 Nov 2024 11:21:53 -0500 Subject: [PATCH 171/386] Fix CPP conditional scanner (cpp.py) and test suite (cppTests.py). Changes: - Preserve non-integer literals that contain valid integer specifications. - Add binary integer specifications - Add octal integer specification - Zero (0) is considered an octal number - Add negative lookbehind/lookahead for number specifications (text is not word/token based) - Add method to evaluate constant expression for define constant expressions - Replace int conversion with constant evaluation expression - int conversion failed for hex numbers due to default base 10 [int(s)] vs unknown base [int(s, 0)] - Add additional tests --- SCons/cpp.py | 52 ++++++-- SCons/cppTests.py | 297 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 332 insertions(+), 17 deletions(-) diff --git a/SCons/cpp.py b/SCons/cpp.py index 1093ae2ac2..63e49ae88c 100644 --- a/SCons/cpp.py +++ b/SCons/cpp.py @@ -146,13 +146,27 @@ def Cleanup_CPP_Expressions(ts): # ...and compile the expression. CPP_to_Python_Ops_Expression = re.compile(expr) +# integer specifications +int_suffix_opt = r'(?:[uU](?:l{1,2}|L{1,2}|[zZ]|wb|WB)?|(?:l{1,2}|L{1,2}|[zZ]|wb|WB)[uU]?)?' + +hex_integer = fr'(0[xX][0-9A-Fa-f]+){int_suffix_opt}' +bin_integer = fr'(0[bB][01]+){int_suffix_opt}' +oct_integer = fr'(0[0-7]*){int_suffix_opt}' +dec_integer = fr'([1-9][0-9]*){int_suffix_opt}' + +int_boundary = r'[a-zA-Z0-9_]' +int_neg_lookbehind = fr'(? None: def scons_current_file(self, t) -> None: self.current_file = t[1] + def eval_constant_expression(self, s): + """ + Evaluates a C preprocessor expression. + + This is done by converting it to a Python equivalent and + eval()ing it in the C preprocessor namespace we use to + track #define values. + + Returns None if the eval() result is not an integer. + """ + s = CPP_to_Python(s) + try: + rval = eval(s, self.constant_expression_namespace) + except (NameError, TypeError, SyntaxError) as e: + rval = None + if not isinstance(rval, int): + rval = None + return rval + def eval_expression(self, t): """ Evaluates a C preprocessor expression. @@ -507,9 +543,11 @@ def do_define(self, t) -> None: Default handling of a #define line. """ _, name, args, expansion = t - try: - expansion = int(expansion) - except (TypeError, ValueError): + + rval = self.eval_constant_expression(expansion) + if rval is not None: + expansion = rval + else: # handle "defined" chain "! (defined (A) || defined (B)" ... if "defined " in expansion: self.cpp_namespace[name] = self.eval_expression(t[2:]) diff --git a/SCons/cppTests.py b/SCons/cppTests.py index 85f01b7813..8b079ec742 100644 --- a/SCons/cppTests.py +++ b/SCons/cppTests.py @@ -151,7 +151,7 @@ #include #endif -#if ! (defined (DEFINED_A) || defined (DEFINED_B) +#if ! (defined (DEFINED_A) || defined (DEFINED_B)) #include #else #include @@ -236,11 +236,189 @@ #include #endif -#if 123456789UL || 0x13L -#include +#if (123456789UL || 0x13L) +#include #else -#include +#include #endif + +#if (123456789UL && 0x0LU) +#include +#else +#include +#endif + +#if 123456789UL +#include +#else +#include +#endif + +#if 1234U +#include +#else +#include +#endif + +#if 1234L +#include +#else +#include +#endif + +#if 1234ULL +#include +#else +#include +#endif + +#define X1234UL 1 +#if X1234UL +#include +#else +#include +#endif + +#define X1234U 1 +#if X1234U +#include +#else +#include +#endif + +#define X1234L 1 +#if X1234L +#include +#else +#include +#endif + +#define X1234ULL 1 +#if X1234ULL +#include +#else +#include +#endif + +#define DEC0 0 +#define HEX0 0x0 +#define HEXF 0xF + +#if DEC0 +#include +#else +#include +#endif + +#if ! DEC0 +#include +#else +#include +#endif + +#if (DEC0) +#include +#else +#include +#endif + +#if !(DEC0) +#include +#else +#include +#endif + +#if HEX0 +#include +#else +#include +#endif + +#if ! HEX0 +#include +#else +#include +#endif + +#if (HEX0) +#include +#else +#include +#endif + +#if !(HEX0) +#include +#else +#include +#endif + +#if HEXF +#include +#else +#include +#endif + +#if ! HEXF +#include +#else +#include +#endif + +#if (HEXF) +#include +#else +#include +#endif + +#if !(HEXF) +#include +#else +#include +#endif + +#if defined(DEC0) && (DEC0 & 0x1) +#include +#else +#include +#endif + +#if !(defined(HEXF) && (HEXF & 0x1)) +#include +#else +#include +#endif + +#define X2345ULL 1 +#if !(X2345ULL > 4567ull) +#include +#else +#include +#endif + +#if !0ull +#include +#else +#include +#endif + +#define X0U 0U +#if X0U +#include +#else +#include +#endif + +#define XF1 (0x0U & 0x1U) +#if XF1 +#include +#else +#include +#endif + +#define ABC00 0U +#define ABC01 1U +#define ABC_(a, b) ABC##a##b +#define ABC ABC_(ZERO, ZERO) """ @@ -303,8 +481,8 @@ #define FUNC39a(x0, y0) FILE39 #define FUNC40a(x0, y0) FILE40 -#define FUNC39b(x1, y2) FUNC39a(x1, y1) -#define FUNC40b(x1, y2) FUNC40a(x1, y1) +#define FUNC39b(x1, y1) FUNC39a(x1, y1) +#define FUNC40b(x1, y1) FUNC40a(x1, y1) #define FUNC39c(x2, y2) FUNC39b(x2, y2) #define FUNC40c(x2, y2) FUNC40b(x2, y2) @@ -480,7 +658,12 @@ def test_expression(self) -> None: """Test #if with arithmetic expressions""" expect = self.expression_expect result = self.cpp.process_contents(expression_input) - assert expect == result, (expect, result) + if expect != result: + for e,r in zip(expect, result): + if e != r: + print("ERROR->",end="") + print(f"{e}: {r}") + assert expect == result, f"\nexpect:{expect}\nresult:{result}" def test_undef(self) -> None: """Test #undef handling""" @@ -588,7 +771,41 @@ class PreProcessorTestCase(cppAllTestCase): ('include', '<', 'file28-yes'), ('include', '"', 'file29-yes'), ('include', '<', 'file30-yes'), - ('include', '<', 'file301-yes'), + + ('include', '<', 'file301or-yes'), + ('include', '<', 'file301and-no'), + ('include', '<', 'file301ul-yes'), + ('include', '<', 'file301u-yes'), + ('include', '<', 'file301l-yes'), + ('include', '<', 'file301ull-yes'), + + ('include', '<', 'file302-yes'), + ('include', '<', 'file303-yes'), + ('include', '<', 'file304-yes'), + ('include', '<', 'file305-yes'), + + ('include', '<', 'file401-no'), + ('include', '<', 'file402-yes'), + ('include', '<', 'file403-no'), + ('include', '<', 'file404-yes'), + + ('include', '<', 'file411-no'), + ('include', '<', 'file412-yes'), + ('include', '<', 'file413-no'), + ('include', '<', 'file414-yes'), + + ('include', '<', 'file421-yes'), + ('include', '<', 'file422-no'), + ('include', '<', 'file423-yes'), + ('include', '<', 'file424-no'), + + ('include', '<', 'file431-no'), + ('include', '<', 'file432-no'), + + ('include', '<', 'file501-yes'), + ('include', '<', 'file502-yes'), + ('include', '<', 'file503-no'), + ('include', '<', 'file504-no'), ] undef_expect = [ @@ -717,8 +934,68 @@ class DumbPreProcessorTestCase(cppAllTestCase): ('include', '"', 'file29-yes'), ('include', '<', 'file30-yes'), ('include', '<', 'file30-no'), - ('include', '<', 'file301-yes'), - ('include', '<', 'file301-no'), + + ('include', '<', 'file301or-yes'), + ('include', '<', 'file301or-no'), + ('include', '<', 'file301and-yes'), + ('include', '<', 'file301and-no'), + ('include', '<', 'file301ul-yes'), + ('include', '<', 'file301ul-no'), + ('include', '<', 'file301u-yes'), + ('include', '<', 'file301u-no'), + ('include', '<', 'file301l-yes'), + ('include', '<', 'file301l-no'), + ('include', '<', 'file301ull-yes'), + ('include', '<', 'file301ull-no'), + + ('include', '<', 'file302-yes'), + ('include', '<', 'file302-no'), + ('include', '<', 'file303-yes'), + ('include', '<', 'file303-no'), + ('include', '<', 'file304-yes'), + ('include', '<', 'file304-no'), + ('include', '<', 'file305-yes'), + ('include', '<', 'file305-no'), + + ('include', '<', 'file401-yes'), + ('include', '<', 'file401-no'), + ('include', '<', 'file402-yes'), + ('include', '<', 'file402-no'), + ('include', '<', 'file403-yes'), + ('include', '<', 'file403-no'), + ('include', '<', 'file404-yes'), + ('include', '<', 'file404-no'), + + ('include', '<', 'file411-yes'), + ('include', '<', 'file411-no'), + ('include', '<', 'file412-yes'), + ('include', '<', 'file412-no'), + ('include', '<', 'file413-yes'), + ('include', '<', 'file413-no'), + ('include', '<', 'file414-yes'), + ('include', '<', 'file414-no'), + + ('include', '<', 'file421-yes'), + ('include', '<', 'file421-no'), + ('include', '<', 'file422-yes'), + ('include', '<', 'file422-no'), + ('include', '<', 'file423-yes'), + ('include', '<', 'file423-no'), + ('include', '<', 'file424-yes'), + ('include', '<', 'file424-no'), + ('include', '<', 'file431-yes'), + ('include', '<', 'file431-no'), + ('include', '<', 'file432-yes'), + ('include', '<', 'file432-no'), + + ('include', '<', 'file501-yes'), + ('include', '<', 'file501-no'), + ('include', '<', 'file502-yes'), + ('include', '<', 'file502-no'), + ('include', '<', 'file503-yes'), + ('include', '<', 'file503-no'), + ('include', '<', 'file504-yes'), + ('include', '<', 'file504-no'), ] undef_expect = [ From e96cf6850a2f4b8ba9c6d3f26e3d92826c6eeb98 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 10 Nov 2024 20:18:46 -0500 Subject: [PATCH 172/386] Update CHANGES.txt and RELEASE.txt --- CHANGES.txt | 16 ++++++++++++++++ RELEASE.txt | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 098bba3c9b..b75f8560a7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -59,6 +59,22 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER and nodes from the MSVSProject return values. Resolves #4613. - MSVS: Remove the platform specification (i.e., platform = 'win32') from select test script environments. The platform specification appears superfluous. + - SCons C preprocessor changes: + - Preserve literals that contain valid integer substring specifications. + Previously, the integer suffix could be stripped from a symbol that contained + an integer and suffix substring. + - Update the optional integer suffixes to include the z|Z and wb|WB suffixes. + - Update the optional integer suffixes to include support for alternate + orderings of unsigned with long or long long as defined in the c/cpp + grammar. + - Update the optional integer suffixes for case insensitive specifications as + defined in the c/cpp grammar. + - Add support for binary integer constants. + - Add support for octal integer constants. Previously, octal integers were + evaluated as decimal integers. A literal zero (0) is treated as an octal + number. + - Change the attempted conversion of a define expansion from using int() to + a constant expression evaluation. From Alex James: - On Darwin, PermissionErrors are now handled while trying to access diff --git a/RELEASE.txt b/RELEASE.txt index 6ba7fc06f7..2cb6638066 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -56,12 +56,23 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY in the projects argument list rather than manually removing solution file names and nodes from the MSVSProject return values. +- SCons C preprocessor: + - Update the optional integer suffixes to include the z|Z and wb|WB + suffixes. + - Add support for binary integer constants. + - Add support for octal integer constants. Previously, octal integers + were evaluated as decimal integers. A literal zero (0) is treated as an + octal number. + - Change the method for attempted conversion of a define expansion value + to an integer from a literal to a constant expression evaluation. + - Add a tag to each CacheDir to let systems ignore backing it up (per https://bford.info/cachedir/). Update the way a CacheDir is created, since it now has to create two files. FIXES ----- + - PackageVariable now does what the documentation always said it does if the variable is used on the command line with one of the enabling string as the value: the variable's default value is produced (previously @@ -104,6 +115,17 @@ FIXES solution file. User-specified project GUIDs should now be correctly written to the solution file. +- SCons C preprocessor: Preserve literals that contain valid integer + substring specifications. Previously, the integer suffix could be + stripped from a symbol that contained an integer and suffix substring. + +- SCons C preprocessor: Update the optional integer suffixes to include + support for the alternate orderings of unsigned with long or long long as + defined in the c/cpp grammar. + +- SCons C preprocessor: Update the optional integer suffixes for case + insensitive specifications as defined in the c/cpp grammar. + - Fix nasm test for missing include file, cleanup. - Skip running a few validation tests if the user is root and the test is From c8ebf8ee8393e3471083920ea07387a30aa6ced1 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 12 Nov 2024 07:35:10 -0700 Subject: [PATCH 173/386] Add as_dict flag to env.Dictionary The Dictionary method now has an as_dict flag. If true, Dictionary always returns a dict. The default remains to return different types depending on whether zero (a dict of construction vars), one (that construction var's value), or multiple (a list of construction var values) arguments are given Fixes #4631 Signed-off-by: Mats Wichmann --- CHANGES.txt | 4 +++ RELEASE.txt | 4 +++ SCons/Environment.py | 58 ++++++++++++++++++-------------- SCons/Environment.xml | 71 ++++++++++++++++++++++----------------- SCons/EnvironmentTests.py | 20 +++++++++-- 5 files changed, 100 insertions(+), 57 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b75f8560a7..7d6ef004d9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -134,6 +134,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Add a tag to each CacheDir to let systems ignore backing it up (per https://bford.info/cachedir/). Update the way a CacheDir is created, since it now has to create two files. + - The Dictionary method now has an as_dict flag. If true, Dictionary + always returns a dict. The default remains to return different + types depending on whether zero, one, or multiple construction + variable names are given. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 2cb6638066..e71f535ef3 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -70,6 +70,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY (per https://bford.info/cachedir/). Update the way a CacheDir is created, since it now has to create two files. +- The Dictionary method now has an as_dict flag. If true, Dictionary + always returns a dict. The default remains to return different + types depending on whether zero, one, or multiple construction + FIXES ----- diff --git a/SCons/Environment.py b/SCons/Environment.py index ad5045633b..0c14468406 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -537,7 +537,7 @@ class actually becomes useful.) Special note: methods here and in actual child classes might be called via proxy from an :class:`OverrideEnvironment`, which isn't in the - Python inheritance chain. Take care that methods called with a *self* + class inheritance chain. Take care that methods called with a *self* that's really an ``OverrideEnvironment`` don't make bad assumptions. """ @@ -1708,23 +1708,33 @@ def Detect(self, progs): return None - def Dictionary(self, *args: str): + def Dictionary(self, *args: str, as_dict: bool = False): """Return construction variables from an environment. Args: - args (optional): variable names to look up + args (optional): construction variable names to select. + If omitted, all variables are selected and returned + as a dict. + as_dict: if true, and *args* is supplied, return the + variables and their values in a dict. If false + (the default), return a single value as a scalar, + or multiple values in a list. Returns: - If *args* omitted, the dictionary of all construction variables. - If one arg, the corresponding value is returned. - If more than one arg, a list of values is returned. + A dictionary of construction variables, or a single value + or list of values. Raises: KeyError: if any of *args* is not in the construction environment. + + .. versionchanged:: NEXT_RELEASE + Added the *as_dict* keyword arg to specify always returning a dict. """ if not args: return self._dict - dlist = [self._dict[x] for x in args] + if as_dict: + return {key: self._dict[key] for key in args} + dlist = [self._dict[key] for key in args] if len(dlist) == 1: return dlist[0] return dlist @@ -1754,13 +1764,10 @@ def Dump(self, *key: str, format: str = 'pretty') -> str: If *key* is supplied, a formatted dictionary is generated like the no-arg case - previously a single *key* displayed just the value. """ - if not key: - cvars = self.Dictionary() - elif len(key) == 1: - dkey = key[0] - cvars = {dkey: self[dkey]} + if len(key): + cvars = self.Dictionary(*key, as_dict=True) else: - cvars = dict(zip(key, self.Dictionary(*key))) + cvars = self.Dictionary() fmt = format.lower() @@ -2716,28 +2723,29 @@ def __contains__(self, key) -> bool: return False return key in self.__dict__['__subject'] - def Dictionary(self, *args): + def Dictionary(self, *args, as_dict: bool = False): """Return construction variables from an environment. - Returns all the visible variables, or just those in *args* if - specified. Obtains a Dictionary view of the subject env, then layers - the overrrides on top, after which any any deleted items are removed. - - Returns: - Like :meth:`Base.Dictionary`, returns a dict if *args* is - omitted; a single value if there is one arg; else a list of values. + Behavior is as described for :class:`SubstitutionEnvironment.Dictionary` + but understanda about the override. Raises: - KeyError: if any of *args* is not visible in the construction environment. + KeyError: if any of *args* is not in the construction environment. + + .. versionchanged: NEXT_RELEASE + Added the *as_dict* keyword arg to always return a dict. """ - d = self.__dict__['__subject'].Dictionary().copy() + d = {} + d.update(self.__dict__['__subject']) d.update(self.__dict__['overrides']) d = {k: v for k, v in d.items() if k not in self.__dict__['__deleted']} if not args: return d - dlist = [d[x] for x in args] + if as_dict: + return {key: d[key] for key in args} + dlist = [d[key] for key in args] if len(dlist) == 1: - dlist = dlist[0] + return dlist[0] return dlist def items(self): diff --git a/SCons/Environment.xml b/SCons/Environment.xml index 403d6b8e65..bc4c3b7629 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -1624,16 +1624,22 @@ but will not include any such extension in the return value. -([vars]) +([var, ...], [as_dict=]) -Returns a dictionary object -containing the &consvars; in the &consenv;. -If there are any arguments specified, -the values of the specified &consvars; -are returned as a string (if one -argument) or as a list of strings. +Return an object containing &consvars; from +env. +If var is omitted, +all the &consvars; with their values +are returned in a dict. +If var is specified, +and as_dict is true, +the specified &consvars; are returned in a dict; +otherwise (the default, for backwards compatibility), +values only are returned, +as a scalar if one var is given, +or as a list if multiples. @@ -1648,8 +1654,8 @@ cc_values = env.Dictionary('CC', 'CCFLAGS', 'CCCOM') The object returned by &f-link-env-Dictionary; should be treated as a read-only view into the &consvars;. -Some &consvars; have special internal handling, -and making changes through the &f-env-Dictionary; object can bypass +Some &consvars; require special internal handling, +and modifying them through the &f-env-Dictionary; object can bypass that handling and cause data inconsistencies. The primary use of &f-env-Dictionary; is for diagnostic purposes - it is used widely by test cases specifically because @@ -1657,6 +1663,11 @@ it bypasses the special handling so that behavior can be verified. + +Changed in NEXT_RELEASE: +as_dict added. + + @@ -1702,24 +1713,30 @@ for more information. -([key, ...], [format=]) +([var, ...], [format=TYPE]) -Serializes &consvars; from the current &consenv; -to a string. -The method supports the following formats specified by -format, -which must be used a a keyword argument: +Serialize &consvars; from env to a string. +If var is omitted, +all the &consvars; are serialized. +If one or more var values are supplied, +only those variables and their values are serialized. + + + +The optional format string +selects the serialization format: pretty -Returns a pretty-printed representation of the variables +Returns a pretty-printed representation +of the &consvars; - the result will look like a +&Python; dict (this is the default). -The variables will be presented in &Python; dict form. @@ -1727,31 +1744,25 @@ The variables will be presented in &Python; dict form. json -Returns a JSON-formatted string representation of the variables. +Returns a JSON-formatted representation of the variables. The variables will be presented as a JSON object literal, -the JSON equivalent of a &Python; dict. +the JSON equivalent of a &Python; dict.. - -If no key is supplied, -all the &consvars; are serialized. -If one or more keys are supplied, -only those keys and their values are serialized. - - Changed in NEXT_RELEASE: More than one key can be specified. -The returned string always looks like a dict (or JSON equivalent); +The returned string always looks like a dict +(or equivalent in other formats); previously a single key serialized only the value, not the key with the value. -This SConstruct: +Examples: this &SConstruct; @@ -1773,7 +1784,7 @@ will print something like: -While this SConstruct: +While this &SConstruct;: @@ -2022,7 +2033,7 @@ FindSourceFiles('src') -As you can see build support files (SConstruct in the above example) +As you can see build support files (&SConstruct; in the above example) will also be returned by this function. diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index 62238c7575..22b9074705 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -1832,8 +1832,10 @@ def test_AppendUnique(self) -> None: assert env['CCC1'] == 'c1', env['CCC1'] assert env['CCC2'] == ['c2'], env['CCC2'] assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1'] - assert env['LL1'] == [env.Literal('a literal'), \ - env.Literal('b literal')], env['LL1'] + assert env['LL1'] == [ + env.Literal('a literal'), + env.Literal('b literal'), + ], env['LL1'] assert env['LL2'] == [ env.Literal('c literal'), env.Literal('b literal'), @@ -2135,6 +2137,13 @@ def test_Dictionary(self) -> None: xxx, zzz = env.Dictionary('XXX', 'ZZZ') assert xxx == 'x' assert zzz == 'z' + # added in NEXT_RELEASE: as_dict flag + with self.subTest(): + expect = {'XXX': 'x'} + self.assertEqual(env.Dictionary('XXX', as_dict=True), expect) + with self.subTest(): + expect = {'XXX': 'x', 'YYY': 'y'} + self.assertEqual(env.Dictionary('XXX', 'YYY', as_dict=True), expect) assert 'BUILDERS' in env.Dictionary() assert 'CC' in env.Dictionary() assert 'CCFLAGS' in env.Dictionary() @@ -3927,6 +3936,13 @@ def test_Dictionary(self) -> None: xxx, yyy = env2.Dictionary('XXX', 'YYY') assert xxx == 'x2', xxx assert yyy == 'y', yyy + # added in NEXT_VERSION: as_dict flag + with self.subTest(): + expect = {'XXX': 'x3'} + self.assertEqual(env3.Dictionary('XXX', as_dict=True), expect) + with self.subTest(): + expect = {'XXX': 'x2', 'YYY': 'y'} + self.assertEqual(env2.Dictionary('XXX', 'YYY', as_dict=True), expect) # test deletion in top override del env3['XXX'] From 4d3912f705828c03a45875681fe881a137cdc5a6 Mon Sep 17 00:00:00 2001 From: "Yevhen Babiichuk (DustDFG)" Date: Tue, 12 Nov 2024 21:02:53 +0200 Subject: [PATCH 174/386] Docs: Add metnion of `no_progress` option when user passes -Q Accordingly to changes to 4.0.0 --- doc/user/command-line.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/user/command-line.xml b/doc/user/command-line.xml index c0a3c0c55e..a6a7e95755 100644 --- a/doc/user/command-line.xml +++ b/doc/user/command-line.xml @@ -556,6 +556,11 @@ foo.in + + no_progress + + + no_site_dir From 8aa16624a5dd789043fe0e1e99825d6711ad52b9 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 13:02:59 +0100 Subject: [PATCH 175/386] *.xml: Fix obvious typos etc. --- SCons/Action.xml | 2 +- SCons/Defaults.xml | 10 ++--- SCons/Environment.xml | 34 ++++++++-------- SCons/Script/Main.xml | 4 +- SCons/Script/SConscript.xml | 2 +- SCons/Subst.xml | 2 +- SCons/Tool/DCommon.xml | 2 +- SCons/Tool/Tool.xml | 2 +- SCons/Tool/c++.xml | 8 ++-- SCons/Tool/docbook/docbook.xml | 8 ++-- SCons/Tool/javac.xml | 2 +- SCons/Tool/link.xml | 4 +- SCons/Tool/msvc.xml | 4 +- SCons/Tool/msvs.xml | 2 +- SCons/Tool/ninja/ninja.xml | 2 +- SCons/Tool/packaging/packaging.xml | 6 +-- SCons/Tool/textfile.xml | 6 +-- SCons/Tool/tlib.xml | 2 +- SCons/Tool/xgettext.xml | 2 +- doc/man/scons-time.xml | 6 +-- doc/man/scons.xml | 64 +++++++++++++++--------------- doc/user/build-install.xml | 2 +- doc/user/builders-writing.xml | 6 +-- doc/user/caching.xml | 4 +- doc/user/depends.xml | 6 +-- doc/user/environments.xml | 4 +- doc/user/external.xml | 2 +- doc/user/file-removal.xml | 2 +- doc/user/gettext.xml | 6 +-- doc/user/hierarchy.xml | 2 +- doc/user/java.xml | 4 +- doc/user/less-simple.xml | 2 +- doc/user/misc.xml | 2 +- doc/user/parseconfig.xml | 2 +- doc/user/parseflags.xml | 2 +- doc/user/scanners.xml | 4 +- doc/user/sconf.xml | 2 +- doc/user/separate.xml | 2 +- doc/user/sideeffect.xml | 4 +- doc/user/troubleshoot.xml | 6 +-- doc/user/variants.xml | 2 +- 41 files changed, 120 insertions(+), 120 deletions(-) diff --git a/SCons/Action.xml b/SCons/Action.xml index c71c305eab..738df78208 100644 --- a/SCons/Action.xml +++ b/SCons/Action.xml @@ -59,7 +59,7 @@ greater than one, as that has a different meaning). Action strings can be segmented by the use of an AND operator, &&. -In a segemented string, each segment is a separate +In a segmented string, each segment is a separate command line, these are run sequentially until one fails or the entire sequence has been executed. If an diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml index 933fe9dc94..80ec4cdc62 100644 --- a/SCons/Defaults.xml +++ b/SCons/Defaults.xml @@ -147,7 +147,7 @@ and the second is the macro definition. If the definition is not omitted or None, the name and definition are combined into a single name=definition item -before the preending/appending. +before the prepending/appending. @@ -618,7 +618,7 @@ If &cv-link-LIBLITERALPREFIX; is set to a non-empty string, then a string-valued &cv-LIBS; entry that starts with &cv-link-LIBLITERALPREFIX; will cause the rest of the entry -to be searched for for unmodified, +to be searched for unmodified, but respecting normal library search paths (this is an exception to the guideline above about leaving off the prefix/suffix from the library name). @@ -649,12 +649,12 @@ env.Append(LIBS=File('/tmp/mylib.so')) For each &Builder; call that causes linking with libraries, &SCons; will add the libraries in the setting of &cv-LIBS; -in effect at that moment to the dependecy graph +in effect at that moment to the dependency graph as dependencies of the target being generated. -The library list will transformed to command line +The library list will be transformed to command line arguments through the automatically-generated &cv-link-_LIBFLAGS; &consvar; which is constructed by @@ -764,7 +764,7 @@ build phase begins, if has not already been done. However, calling it explicitly provides the opportunity to affect and examine its contents. Instantiation occurs even if nothing in the build system -appars to use it, due to internal uses. +appears to use it, due to internal uses. diff --git a/SCons/Environment.xml b/SCons/Environment.xml index bc4c3b7629..87d1e7ca30 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -572,14 +572,14 @@ named key, then key is simply stored with a value of val. Otherwise, val is -combinined with the existing value, +combined 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 +however, the contents of &cv-link-CPPDEFINES; will be postprocessed by adding a prefix and/or suffix to each entry when the command line is produced, so &SCons; keeps them separate - @@ -696,7 +696,7 @@ scons: `.' is up to date. Changed in version 4.5: -clarifined the use of tuples vs. other types, +clarified the use of tuples vs. other types, handling is now consistent across the four functions. @@ -723,7 +723,7 @@ See &cv-link-CPPDEFINES; for more details. Appending a string val -to a dictonary-typed &consvar; enters +to a dictionary-typed &consvar; enters val as the key in the dictionary, and None as its value. Using a tuple type to supply a key-value pair @@ -1146,7 +1146,7 @@ This is useful for "one-off" builds where a full Builder is not needed. Since the anonymous Builder is never hooked into the standard Builder framework, -an Action must always be specfied. +an Action must always be specified. See the &f-link-Command; function description for the calling syntax and details. @@ -1458,7 +1458,7 @@ The Node (file) which should cause the target to be rebuilt -if it has "changed" since the last tme +if it has "changed" since the last time target was built. @@ -1533,7 +1533,7 @@ otherwise not be rebuilt). Note that the decision can be made -using whatever criteria are appopriate. +using whatever criteria are appropriate. Ignoring some or all of the function arguments is perfectly normal. @@ -2117,8 +2117,8 @@ or (most commonly) relative to the directory of the current &f-Glob; matches both files stored on disk and Nodes which &SCons; already knows about, even if any corresponding file is not currently stored on disk. -The evironment method form (&f-env-Glob;) -performs string substition on +The environment method form (&f-env-Glob;) +performs string substitution on pattern and returns whatever matches the resulting expanded pattern. The results are sorted, unlike for the similar &Python; @@ -2209,7 +2209,7 @@ If the optional source argument evaluates true, and the local directory is a variant directory, -then &f-Glob; returnes Nodes from +then &f-Glob; returns Nodes from the corresponding source directory, rather than the local directory. @@ -2243,7 +2243,7 @@ directory.) The optional exclude argument may be set to a pattern or a list of patterns -descibing files or directories +describing files or directories to filter out of the match list. Elements matching a least one specified pattern will be excluded. These patterns use the same syntax as for @@ -2448,7 +2448,7 @@ In case of duplication, any &consvar; names that end in PATH keep the left-most value so the -path searcb order is not altered. +path search order is not altered. All other &consvars; keep the right-most value. If unique is false, @@ -2620,11 +2620,11 @@ the output produced by command in order to distribute it to appropriate &consvars;. &f-env-MergeFlags; uses a separate function to do that processing - -see &f-link-env-ParseFlags; for the details, including a +see &f-link-env-ParseFlags; for the details, including a table of options and corresponding &consvars;. To provide alternative processing of the output of command, -you can suppply a custom +you can supply a custom function, which must accept three arguments: the &consenv; to modify, @@ -2846,7 +2846,7 @@ See the manpage section "Construction Environments" for more details. Prepend values to &consvars; in the current &consenv;, -Works like &f-link-env-Append; (see for details), +works like &f-link-env-Append; (see for details), except that values are added to the front, rather than the end, of any existing value of the &consvar; @@ -3225,7 +3225,7 @@ in a separate .sconsign file in each directory, not in a single combined database file. -This is a backwards-compatibility meaure to support +This is a backwards-compatibility measure to support what was the default behavior prior to &SCons; 0.97 (i.e. before 2008). Use of this mode is discouraged and may be @@ -3602,7 +3602,7 @@ Returns a Node object representing the specified &Python; Value Nodes can be used as dependencies of targets. If the string representation of the Value Node changes between &SCons; runs, it is considered -out of date and any targets depending it will be rebuilt. +out of date and any targets depending on it will be rebuilt. Since Value Nodes have no filesystem representation, timestamps are not used; the timestamp deciders perform the same content-based up to date check. diff --git a/SCons/Script/Main.xml b/SCons/Script/Main.xml index 9dd9609a10..01a3b90b11 100644 --- a/SCons/Script/Main.xml +++ b/SCons/Script/Main.xml @@ -43,7 +43,7 @@ but with a few additional capabilities noted below. See the optparse documentation -for a thorough discussion of its option-processing capabities. +for a thorough discussion of its option-processing capabilities. All options added through &f-AddOption; are placed in a special "Local Options" option group. @@ -700,7 +700,7 @@ If the string contains the verbatim substring it will be replaced with the Node. Note that, for performance reasons, this is not -a regular SCons variable substition, +a regular SCons variable substitution, so you can not use other variables or use curly braces. The following example will print the name of diff --git a/SCons/Script/SConscript.xml b/SCons/Script/SConscript.xml index b64d9c3061..7ae0159dee 100644 --- a/SCons/Script/SConscript.xml +++ b/SCons/Script/SConscript.xml @@ -364,7 +364,7 @@ Import("*") Return to the calling SConscript, optionally returning the values of variables named in vars. -Multiple strings contaning variable names may be passed to +Multiple strings containing variable names may be passed to &f-Return;. A string containing white space is split into individual variable names. Returns the value if one variable is specified, diff --git a/SCons/Subst.xml b/SCons/Subst.xml index 4ac4f7d0e4..71aab2fc00 100644 --- a/SCons/Subst.xml +++ b/SCons/Subst.xml @@ -66,7 +66,7 @@ Example: AllowSubstExceptions() # Also allow a string containing a zero-division expansion -# like '${1 / 0}' to evalute to ''. +# like '${1 / 0}' to evaluate to ''. AllowSubstExceptions(IndexError, NameError, ZeroDivisionError) diff --git a/SCons/Tool/DCommon.xml b/SCons/Tool/DCommon.xml index c46ed43fa6..03a7096a21 100644 --- a/SCons/Tool/DCommon.xml +++ b/SCons/Tool/DCommon.xml @@ -286,7 +286,7 @@ DVERSUFFIX. The name of the compiler to use when compiling D source -destined to be in a shared objects. +destined to be in a shared object. See also &cv-link-DC; for compiling to static objects. diff --git a/SCons/Tool/Tool.xml b/SCons/Tool/Tool.xml index 5e996c7c4e..ba8b9afe9d 100644 --- a/SCons/Tool/Tool.xml +++ b/SCons/Tool/Tool.xml @@ -482,7 +482,7 @@ and as C++ files, and files with .mm -suffixes as Objective C++ files. +suffixes as Objective-C++ files. On case-sensitive systems (Linux, UNIX, and other POSIX-alikes), SCons also treats .C diff --git a/SCons/Tool/c++.xml b/SCons/Tool/c++.xml index af1e9e1ecb..4e08a3b4eb 100644 --- a/SCons/Tool/c++.xml +++ b/SCons/Tool/c++.xml @@ -56,7 +56,7 @@ Sets construction variables for generic POSIX C++ compilers. The C++ compiler. -See also &cv-link-SHCXX; for compiling to shared objects.. +See also &cv-link-SHCXX; for compiling to shared objects. @@ -68,7 +68,7 @@ The command line used to compile a C++ source file to an object file. Any options specified in the &cv-link-CXXFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. -See also &cv-link-SHCXXCOM; for compiling to shared objects.. +See also &cv-link-SHCXXCOM; for compiling to shared objects.
@@ -79,7 +79,7 @@ See also &cv-link-SHCXXCOM; for compiling to shared objects.. If set, the string displayed when a C++ source file is compiled to a (static) object file. If not set, then &cv-link-CXXCOM; (the command line) is displayed. -See also &cv-link-SHCXXCOMSTR; for compiling to shared objects.. +See also &cv-link-SHCXXCOMSTR; for compiling to shared objects. @@ -96,7 +96,7 @@ By default, this includes the value of &cv-link-CCFLAGS;, so that setting &cv-CCFLAGS; affects both C and C++ compilation. If you want to add C++-specific flags, you must set or override the value of &cv-link-CXXFLAGS;. -See also &cv-link-SHCXXFLAGS; for compiling to shared objects.. +See also &cv-link-SHCXXFLAGS; for compiling to shared objects.
diff --git a/SCons/Tool/docbook/docbook.xml b/SCons/Tool/docbook/docbook.xml index 79d2aead7a..464c1b2556 100644 --- a/SCons/Tool/docbook/docbook.xml +++ b/SCons/Tool/docbook/docbook.xml @@ -286,7 +286,7 @@ if one of them is installed (fop gets checked first). -Additonal command-line flags for the external executable +Additional command-line flags for the external executable xsltproc (or saxon, xalan). @@ -296,7 +296,7 @@ Additonal command-line flags for the external executable -Additonal command-line flags for the external executable +Additional command-line flags for the external executable xmllint. @@ -305,7 +305,7 @@ Additonal command-line flags for the external executable -Additonal command-line flags for the +Additional command-line flags for the PDF renderer fop or xep. @@ -314,7 +314,7 @@ PDF renderer fop or xep. -Additonal parameters that are not intended for the XSLT processor executable, but +Additional parameters that are not intended for the XSLT processor executable, but the XSL processing itself. By default, they get appended at the end of the command line for saxon and saxon-xslt, respectively. diff --git a/SCons/Tool/javac.xml b/SCons/Tool/javac.xml index d3be4f8e82..ccda8857b8 100644 --- a/SCons/Tool/javac.xml +++ b/SCons/Tool/javac.xml @@ -109,7 +109,7 @@ env.Java(target='classes', source=['File1.java', 'File2.java']) In this case, the user must specify the LANG environment variable to tell the compiler what encoding is used. - For portibility, it's best if the encoding is hard-coded + For portability, it's best if the encoding is hard-coded so that the compile will work if it is done on a system with a different encoding. diff --git a/SCons/Tool/link.xml b/SCons/Tool/link.xml index 4bf7969dc0..0b10768115 100644 --- a/SCons/Tool/link.xml +++ b/SCons/Tool/link.xml @@ -66,7 +66,7 @@ based on the types of source files. This construction variable automatically introduces &cv-link-_LDMODULEVERSIONFLAGS; -if &cv-link-LDMODULEVERSION; is set. Othervise it evaluates to an empty string. +if &cv-link-LDMODULEVERSION; is set. Otherwise it evaluates to an empty string. @@ -75,7 +75,7 @@ if &cv-link-LDMODULEVERSION; is set. Othervise it evaluates to an empty string. This construction variable automatically introduces &cv-link-_SHLIBVERSIONFLAGS; -if &cv-link-SHLIBVERSION; is set. Othervise it evaluates to an empty string. +if &cv-link-SHLIBVERSION; is set. Otherwise it evaluates to an empty string. diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 97edb2ced0..acf9c5be83 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -298,7 +298,7 @@ only if the &cv-link-PDB; &consvar; is set. This variable specifies how much of a source file is precompiled. This variable is ignored by tools other than &MSVC;, or when -the PCH variable is not being used. When this variable is define it +the PCH variable is not being used. When this variable is defined it must be a string that is the name of the header that is included at the end of the precompiled portion of the source files, or the empty string if the "#pragma hrdstop" construct is being used: @@ -621,7 +621,7 @@ Visual Studio It is only necessary to specify the Exp suffix to select the express edition when both express and non-express editions of the same product are installed - simulaneously. The Exp suffix is unnecessary, + simultaneously. The Exp suffix is unnecessary, but accepted, when only the express edition is installed. diff --git a/SCons/Tool/msvs.xml b/SCons/Tool/msvs.xml index ad3a7569be..dba3257e79 100644 --- a/SCons/Tool/msvs.xml +++ b/SCons/Tool/msvs.xml @@ -803,7 +803,7 @@ env.MSVSSolution( and &cv-MSVC_VERSION; is not, &cv-MSVC_VERSION; will be initialized to the value of &cv-MSVS_VERSION;. - An error is raised if If both are set and have different values, + An error is raised if both are set and have different values. diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml index 3919194dd2..65945d3402 100644 --- a/SCons/Tool/ninja/ninja.xml +++ b/SCons/Tool/ninja/ninja.xml @@ -358,7 +358,7 @@ python -m pip install ninja - The number of seconds for the SCons deamon launched by ninja to stay alive. + The number of seconds for the SCons daemon launched by ninja to stay alive. (Default: 180000) diff --git a/SCons/Tool/packaging/packaging.xml b/SCons/Tool/packaging/packaging.xml index 62ba558a5d..0c42462e39 100644 --- a/SCons/Tool/packaging/packaging.xml +++ b/SCons/Tool/packaging/packaging.xml @@ -55,7 +55,7 @@ env = Environment(tools=['default', 'packaging']) &SCons; can build packages in a number of well known packaging formats. The target package type may be selected with the -the &cv-link-PACKAGETYPE; construction variable +&cv-link-PACKAGETYPE; construction variable or the command line option. The package type may be a list, in which case &SCons; will attempt to build packages for each type in the list. Example: @@ -73,7 +73,7 @@ The currently supported packagers are: msiMicrosoft Installer package -rpmRPM Package Manger package +rpmRPM Package Manager package ipkgItsy Package Management package tarbz2bzip2-compressed tar file targzgzip-compressed tar file @@ -541,7 +541,7 @@ field in the RPM -A list used to supply extra defintions or flags +A list used to supply extra definitions or flags to be added to the RPM .spec file. Each item is added as-is with a carriage return appended. This is useful if some specific RPM feature not otherwise diff --git a/SCons/Tool/textfile.xml b/SCons/Tool/textfile.xml index 895fbeffcb..2539f5a1b0 100644 --- a/SCons/Tool/textfile.xml +++ b/SCons/Tool/textfile.xml @@ -82,7 +82,7 @@ env.Textfile(target='bar', source=['lalala', 'tanteratei'], LINESEPARATOR='|*') # nested lists are flattened automatically env.Textfile(target='blob', source=['lalala', ['Goethe', 42, 'Schiller'], 'tanteratei']) -# files may be used as input by wraping them in File() +# files may be used as input by wrapping them in File() env.Textfile( target='concat', # concatenate files with a marker between source=[File('concat1'), File('concat2')], @@ -154,7 +154,7 @@ it may be either a Python dictionary or a sequence of If it is a dictionary it is converted into a list of tuples with unspecified order, so if one key is a prefix of another key -or if one substitution could be further expanded by another subsitition, +or if one substitution could be further expanded by another substitution, it is unpredictable whether the expansion will occur. @@ -185,7 +185,7 @@ env.Substfile('foo.in', SUBST_DICT=bad_foo) good_foo = [('$foobar', '$foobar'), ('$foo', '$foo')] env.Substfile('foo.in', SUBST_DICT=good_foo) -# UNPREDICTABLE - one substitution could be futher expanded +# UNPREDICTABLE - one substitution could be further expanded bad_bar = {'@bar@': '@soap@', '@soap@': 'lye'} env.Substfile('bar.in', SUBST_DICT=bad_bar) diff --git a/SCons/Tool/tlib.xml b/SCons/Tool/tlib.xml index 7274890662..f186a177b3 100644 --- a/SCons/Tool/tlib.xml +++ b/SCons/Tool/tlib.xml @@ -27,7 +27,7 @@ This file is processed by the bin/SConsDoc.py module. -Sets construction variables for the Borlan +Sets construction variables for the Borland tib library archiver. diff --git a/SCons/Tool/xgettext.xml b/SCons/Tool/xgettext.xml index 10bec3a86e..fb9c6a32dd 100644 --- a/SCons/Tool/xgettext.xml +++ b/SCons/Tool/xgettext.xml @@ -355,7 +355,7 @@ form source and target (default: '${TARGET.filebase}'). -Internal "macro". Genrates list of -D<dir> flags +Internal "macro". Generates list of -D<dir> flags from the &cv-link-XGETTEXTPATH; list. diff --git a/doc/man/scons-time.xml b/doc/man/scons-time.xml index 794e593632..394beba7ed 100644 --- a/doc/man/scons-time.xml +++ b/doc/man/scons-time.xml @@ -703,7 +703,7 @@ and processing the SConscript files. Full build SCons is run to build everything specified in the configuration. -Specific targets to be passed in on the command l ine +Specific targets to be passed in on the command line may be specified by the targets keyword in a configuration file; see below for details. @@ -800,7 +800,7 @@ if the first argument is a directory, the default prefix is the name of the directory; if the first argument is an archive (tar or zip file), -the default prefix is the the base name of the archive, +the default prefix is the base name of the archive, that is, what remains after stripping the archive suffix (.tgz, .tar.gz or .zip). @@ -984,7 +984,7 @@ value: SConscripts (total execution time for the SConscript files themselves), SCons -(exectuion time in SCons code itself) +(execution time in SCons code itself) or commands (execution time of the commands and other actions diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 022f44c536..c915c172d4 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -172,7 +172,7 @@ The file is by default named &SConstruct;, although some variants of that, or a developer-chosen name, are also accepted (see ). -If found, the currrent directory +If found, the current directory is set as the project top directory. Certain command-line options specify alternate places to look for &SConstruct; @@ -284,7 +284,7 @@ To assure reproducible builds, &SCons; uses a restricted execution environment for running external commands used to build targets, -rather then propagating the full environment +rather than propagating the full environment in effect at the time &scons; was called. This helps avoid problems like picking up accidental or malicious settings, @@ -298,7 +298,7 @@ either by assigning the desired values, or by picking those values individually or collectively out of environment variables exposed by the &Python; os.environ dictionary -(as external program inputs they should be validate +(as external program inputs they should be validated before use). The execution environment for a given &consenv; is its &cv-link-ENV; value. @@ -476,10 +476,10 @@ searches in order for the GCC tool chain, the LLVM/clang tools, and the Intel compiler tools. -The defaul tool selection can be pre-empted +The default tool selection can be pre-empted through the use of the tools argument to &consenv; creation methods, -explcitly calling the &f-link-Tool; loader, +explicitly calling the &f-link-Tool; loader, the through the setting of various setting of &consvars;. @@ -789,7 +789,7 @@ This saves time by not running the same configuration tests every time you invoke scons, but will overlook changes in system header files or external commands (such as compilers) -if you don't specify those dependecies explicitly. +if you don't specify those dependencies explicitly. This is the default behavior. @@ -1629,7 +1629,7 @@ scons>>> exit -Specifies the maximum number of comcurrent jobs (commands) to run. +Specifies the maximum number of concurrent jobs (commands) to run. If there is more than one option, the last one is effective. @@ -1779,7 +1779,7 @@ added to the module search path sys.path, searched for a site_init.py file, or have their site_tools directory included in the tool search path. -Can be overridden by a subequent +Can be overridden by a subsequent option. @@ -2512,7 +2512,7 @@ the build don't accidentally step on each other. You have to be explicit about sharing information, by using the &f-link-Export; function or the &exports; argument to the &f-link-SConscript; function, as well as the &f-link-Return; function -in a called &SConscript; file, and comsume shared information by using the +in a called &SConscript; file, and consume shared information by using the &f-link-Import; function. @@ -2562,8 +2562,8 @@ env['FOO'] = 'foo' Note that certain settings which affect tool detection are -referenced only when the tools are initializided, -so you either need either to supply them as part of the call to +referenced only when the tools are initialized, +so you need either to supply them as part of the call to &f-link-Environment;, or defer tool initialization. For example, initializing the &MSVC; version you wish to use: @@ -2667,7 +2667,7 @@ one of the pre-defined platforms posix, sunos or win32), -or it may be be a callable platform object +or it may be a callable platform object returned by a call to &f-link-Platform; selecting a pre-defined platform, or it may be a user-supplied callable, @@ -2684,7 +2684,7 @@ env = Environment(platform=my_platform) Note that supplying a non-default platform or custom -fuction for initialization +function for initialization may bypass settings that should happen for the host system and should be used with care. It is most useful in the case where the platform is an alternative for @@ -2718,7 +2718,7 @@ See for details. which are used to help initialize the &consenv; prior to building, and more can be written to suit a particular purpose, or added from external sources (a repository of -constributed tools is available). +contributed tools is available). More information on writing custom tools can be found in the Extending SCons section and specifically Tool Modules. @@ -3601,7 +3601,7 @@ selects for building as a result of making the sure the specified targets are up to date, if those targets did not appear on the command line. The list is empty if neither -command line targets or &Default; calls are present. +command line targets nor &Default; calls are present. The elements of this list may be strings @@ -3641,7 +3641,7 @@ If there are no targets specified on the command line, the list is empty. The elements of this list are strings. This can be used, for example, to take specific actions only -when a certain targets is explicitly requested for building. +when a certain target is explicitly requested for building. Example: @@ -3679,7 +3679,7 @@ if 'foo' in [str(t) for t in DEFAULT_TARGETS]: The contents of the &DEFAULT_TARGETS; -list change on on each successive call to the +list changes on each successive call to the &Default; function: @@ -4234,7 +4234,7 @@ header files whose lines should precede the header line being checked for. A code fragment -(must be a a valid expression, including a trailing semicolon) +(must be a valid expression, including a trailing semicolon) to serve as the test can be supplied in call; if not supplied, @@ -4558,7 +4558,7 @@ can make use of:: Displays text -as an indicator of progess. +as an indicator of progress. For example: Checking for library X.... Usually called before the check is started. @@ -4788,8 +4788,8 @@ The key-value pairs from args will be added to those obtained from files, if any. Keys from args -take precendence over same-named keys from files. -If omittted, the default is the +take precedence over same-named keys from files. +If omitted, the default is the &ARGUMENTS; dictionary that holds build variables specified on the command line. @@ -5142,7 +5142,7 @@ vars.FormatVariableHelpText = my_format &SCons; provides five pre-defined variable types, -acessible through factory functions that generate +accessible through factory functions that generate a tuple appropriate for directly passing to the Add AddVariables @@ -5326,7 +5326,7 @@ specifies the descriptive part of the help text. Set up a variable named key to hold a path string. -The variable will have have a default value of +The variable will have a default value of default, and the help parameter will be used as the descriptive part of the help text. @@ -5608,7 +5608,7 @@ in the directory represented by If the original Node is a File Node, -these methods will place the the new Node in the same +these methods will place the new Node in the same directory as the one the original Node represents: @@ -5697,7 +5697,7 @@ which is used for disambiguation. If an alias source has a string valued name, it will be resolved to a filesystem entry Node, unless it is found in the alias namespace, -in which case it it resolved to the matching alias Node. +in which case it is resolved to the matching alias Node. As a result, the order of &f-Alias; calls is significant. An alias can refer to another alias, but only if the other alias has previously been created. @@ -7794,7 +7794,7 @@ Nodes for additional scanning. -Once created, a Scanner can added to an environment +Once created, a Scanner can be added to an environment by setting it in the &cv-link-SCANNERS; list, which automatically triggers &SCons; to also add it to the environment as a method. @@ -7968,7 +7968,7 @@ env = Environment(tools=[my_tool]) An element of the tools list may also be a two-element list or tuple of the form (toolname, kw_dict). -SCons searches for the a tool specification module +SCons searches for the tool specification module toolname as described above, and passes kw_dict, @@ -8067,7 +8067,7 @@ dialect setup (expressed as a set of &consvars;) depending on the file suffix. By default, all of these setups start out the same, but individual &consvars; can be modified as needed to tune a given dialect. -Each of these dialacts has a tool specification module +Each of these dialects has a tool specification module whose documentation describes the &consvars; associated with that dialect: .f (as well as .for and .ftn) @@ -8096,7 +8096,7 @@ code that was the only available option in FORTRAN 77 and earlier, and .f90 refers to free-format source code which became available as of the Fortran 90 standard. Some compilers recognize suffixes which correspond to Fortran -specifications later then F90 as equivalent to +specifications later than F90 as equivalent to .f90 for this purpose, while some do not - check the documentation for your compiler. An occasionally suggested policy suggestion is to use only @@ -8146,7 +8146,7 @@ which shall first be run through the standard C preprocessor. The lower-cased versions of these suffixes do not trigger this behavior. -On systems which do not distinguish between uppper +On systems which do not distinguish between upper and lower case in filenames, this behavior is not available, but files suffixed with either @@ -8226,8 +8226,8 @@ in particular the recommendation to use the msys2 version of scons.bat batch file, there are (at least) two ramifications. Note this is no longer the default - &scons; installed -via &Python;''s pip installer -will have an scons.exe which does +via &Python;'s pip installer +will have a scons.exe which does not have these limitations: diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml index d34512e01b..f0b400f967 100644 --- a/doc/user/build-install.xml +++ b/doc/user/build-install.xml @@ -121,7 +121,7 @@ Python 3.9.15 stating something like "command not found" (on UNIX or Linux) or "'python' is not recognized as an internal - or external command, operable progam or batch file" + or external command, operable program or batch file" (on Windows cmd). In that case, you need to either install &Python; or fix the search path diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml index b094537cab..d0938fab2a 100644 --- a/doc/user/builders-writing.xml +++ b/doc/user/builders-writing.xml @@ -42,7 +42,7 @@ This file is processed by the bin/SConsDoc.py module. for any custom file types you want to build. (In fact, the &SCons; interfaces for creating &Builder; objects are flexible enough and easy enough to use - that all of the the &SCons; built-in &Builder; objects + that all of the &SCons; built-in &Builder; objects are created using the mechanisms described in this section.) @@ -819,7 +819,7 @@ cat to the file specified by the option's argument. If this option is just supplied to the build, &SCons; will not consider the link map file a tracked target, - which has various undesirable efffects. + which has various undesirable effects. @@ -927,7 +927,7 @@ main() The site_scons directories give you a place to put &Python; modules and packages that you can import into your - &SConscript; files (at the top level) + &SConscript; files (at the top level), add-on tools that can integrate into &SCons; (in a site_tools subdirectory), and a site_scons/site_init.py file that diff --git a/doc/user/caching.xml b/doc/user/caching.xml index 85a54f072e..523781ff04 100644 --- a/doc/user/caching.xml +++ b/doc/user/caching.xml @@ -124,7 +124,7 @@ CacheDir('/usr/local/build_cache') - The use of the &buildsig; provides protection from concflicts: + The use of the &buildsig; provides protection from conflicts: if two developers have different setups, so they would produce built objects that are not identical, then because the difference in tools will show up in the &buildsig;, which is used as the @@ -337,7 +337,7 @@ Retrieved `hello' from cache - In this case, you can use the + In this case, you can use the option to tell &SCons; to put all derived files in the cache, even if the files already exist in your local tree diff --git a/doc/user/depends.xml b/doc/user/depends.xml index 095080638c..0c61e67118 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -437,7 +437,7 @@ cc -o hello hello.o will have been performed by simply looking at the modification time of the &hello_c; file, not by opening it and performing - a signature calcuation on its contents. + a signature calculation on its contents. This can significantly speed up many up-to-date builds. @@ -555,7 +555,7 @@ int main() { printf("Hello, world!\n"); } The &contentsig;: - a cryptgraphic hash, or checksum, of the file contents + a cryptographic hash, or checksum, of the file contents of the dependency file the last time the ⌖ was built. @@ -966,7 +966,7 @@ SetOption('implicit_cache', 1) When &implicit-cache; is used, &SCons; will ignore any changes that may have been made to search paths - (like &cv-CPPPATH; or &cv-LIBPATH;,). + (like &cv-CPPPATH; or &cv-LIBPATH;). This can lead to &SCons; not rebuilding a file if a change to &cv-CPPPATH; would normally cause a different, same-named file from a different directory to be used. diff --git a/doc/user/environments.xml b/doc/user/environments.xml index a630c659d5..80133e8ad6 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -1609,7 +1609,7 @@ print("NEW_VARIABLE = %s" % env['NEW_VARIABLE']) - Some times it's useful to add a new value + Sometimes it's useful to add a new value to the beginning of a &consvar; only if the existing value doesn't already contain the to-be-added value. @@ -2149,7 +2149,7 @@ C:\Python35\Lib\site-packages\someinstalledpackage\SomeTool\__init__.py In some cases you may want to use a tool - located within a installed external pip package. + located within an installed external pip package. This is possible by the use of sys.path with the toolpath. However in that situation you need to provide a prefix to the toolname diff --git a/doc/user/external.xml b/doc/user/external.xml index 71eb849896..4e021f6c3c 100644 --- a/doc/user/external.xml +++ b/doc/user/external.xml @@ -376,7 +376,7 @@ conda install -c conda-forge ninja When &ninja; runs the generated &ninja; build file, &ninja; will launch &scons; as a daemon and feed commands to that &scons; process which &ninja; is unable to build directly. This daemon will stay alive until - explicitly killed, or it times out. The timeout is set by &cv-link-NINJA_SCONS_DAEMON_KEEP_ALIVE; . + explicitly killed, or it times out. The timeout is set by &cv-link-NINJA_SCONS_DAEMON_KEEP_ALIVE;. diff --git a/doc/user/file-removal.xml b/doc/user/file-removal.xml index 7e5df666ac..082d17e010 100644 --- a/doc/user/file-removal.xml +++ b/doc/user/file-removal.xml @@ -34,7 +34,7 @@ This file is processed by the bin/SConsDoc.py module. There are two occasions when &SCons; will, by default, remove target files. The first is when &SCons; determines that - an target file needs to be rebuilt + a target file needs to be rebuilt and removes the existing version of the target before executing The second is when &SCons; is invoked with the diff --git a/doc/user/gettext.xml b/doc/user/gettext.xml index cabf883b79..515534501a 100644 --- a/doc/user/gettext.xml +++ b/doc/user/gettext.xml @@ -89,7 +89,7 @@ hello = Program(["hello.c"]) Now we'll convert the project to a multi-lingual one. If you don't already have GNU gettext - utilities installed, install them from your preffered + utilities installed, install them from your preferred package repository, or download from http://ftp.gnu.org/gnu/gettext/. For the purpose of this example, @@ -163,7 +163,7 @@ InstallAs(["locale/de/LC_MESSAGES/hello.mo"], ["de.mo"]) Generate the translation files with scons po-update. - You should see the output from SCons simillar to this: + You should see the output from SCons similar to this: user@host:$ scons po-update scons: Reading SConscript files ... @@ -350,7 +350,7 @@ gcc -o hello.o -c hello.c gcc -o hello hello.o scons: done building targets. - As you see, the internationalized messages ditn't change, so the + As you see, the internationalized messages didn't change, so the POT and the rest of translation files have not even been touched. diff --git a/doc/user/hierarchy.xml b/doc/user/hierarchy.xml index 84b082417e..c5cb9b8f73 100644 --- a/doc/user/hierarchy.xml +++ b/doc/user/hierarchy.xml @@ -371,7 +371,7 @@ x building up a tree of information, before starting to execute the needed builds in a second pass. This is quite different than some other build tools - which implement a heirarcical build by recursing. + which implement a hierarchical build by recursing. diff --git a/doc/user/java.xml b/doc/user/java.xml index a08b406a4a..f53fcf4f8a 100644 --- a/doc/user/java.xml +++ b/doc/user/java.xml @@ -597,11 +597,11 @@ public class Example3 - Note that the the javah command was + Note that the javah command was removed from the JDK as of JDK 10, and the approved method (available since JDK 8) is to use javac to generate native headers at the same time as the Java source - code is compiled.. As such the &b-link-JavaH; builder + code is compiled. As such the &b-link-JavaH; builder is of limited utility in later Java versions. diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml index f59a9053a3..4c1b14d545 100644 --- a/doc/user/less-simple.xml +++ b/doc/user/less-simple.xml @@ -376,7 +376,7 @@ Program('program', Split('main.c file1.c file2.c')) (If you're already familiar with &Python;, you'll have realized that this is similar to the split() method - of &Python; string objects.. + of &Python; string objects. Unlike the split() method, however, the &Split; function does not require a string as input diff --git a/doc/user/misc.xml b/doc/user/misc.xml index df507df7b3..f78fb35da5 100644 --- a/doc/user/misc.xml +++ b/doc/user/misc.xml @@ -252,7 +252,7 @@ hello.c Note that the &Exit; function is equivalent to calling the Python sys.exit function - (which the it actually calls), + (which it actually calls), but because &Exit; is a &SCons; function, you don't have to import the Python sys module to use it. diff --git a/doc/user/parseconfig.xml b/doc/user/parseconfig.xml index ee52c92c76..a4b4618779 100644 --- a/doc/user/parseconfig.xml +++ b/doc/user/parseconfig.xml @@ -34,7 +34,7 @@ This file is processed by the bin/SConsDoc.py module. libraries--especially shared libraries--that are available on POSIX systems can be complex. To help this situation, - various utilies with names that end in config + various utilities with names that end in config return the command-line options for the GNU Compiler Collection (GCC) that are needed to build and link against those libraries; for example, the command-line options diff --git a/doc/user/parseflags.xml b/doc/user/parseflags.xml index 90e166a8b9..eef242ddaf 100644 --- a/doc/user/parseflags.xml +++ b/doc/user/parseflags.xml @@ -148,7 +148,7 @@ void main() { return 0; } - If a string begins with a an exclamation mark (!), + If a string begins with an exclamation mark (!), the string is passed to the shell for execution. The output of the command is then parsed: diff --git a/doc/user/scanners.xml b/doc/user/scanners.xml index 6785771f39..5ead52683e 100644 --- a/doc/user/scanners.xml +++ b/doc/user/scanners.xml @@ -462,7 +462,7 @@ kscan = Scanner( One approach for introducing a &Scanner; into the build is in - conjunction with a &Builder;. There are two relvant optional + conjunction with a &Builder;. There are two relevant optional parameters we can use when creating a Builder: source_scanner and target_scanner. @@ -523,7 +523,7 @@ text to include Running this example would only show that the stub build_function is getting called, - so some debug prints were added to the scaner function, + so some debug prints were added to the scanner function, just to show the scanner is being invoked. diff --git a/doc/user/sconf.xml b/doc/user/sconf.xml index e2570925ea..3eb89c12bd 100644 --- a/doc/user/sconf.xml +++ b/doc/user/sconf.xml @@ -288,7 +288,7 @@ env = conf.Finish() You can also add a string that will be placed at the beginning of the test file that will be used to check for the &typedef;. - This provide a way to specify + This provides a way to specify files that must be included to find the &typedef;: diff --git a/doc/user/separate.xml b/doc/user/separate.xml index 4ba55585c0..4af3a5560e 100644 --- a/doc/user/separate.xml +++ b/doc/user/separate.xml @@ -498,7 +498,7 @@ int main() { printf("Hello, world!\n"); } to the use of &f-link-SConscript; with the variant_dir argument from earlier in this chapter, - but did require callng the SConscript using the already established + but did require calling the SConscript using the already established variant directory path to trigger that behavior. If you call SConscript('src/SConscript') you would get a normal in-place build in &src;. diff --git a/doc/user/sideeffect.xml b/doc/user/sideeffect.xml index 07d619fd6b..b17f391a1c 100644 --- a/doc/user/sideeffect.xml +++ b/doc/user/sideeffect.xml @@ -114,7 +114,7 @@ f1 = env.Command( In general, &SideEffect; is not intended for the case when a command produces extra target files (that is, files which - will be used as sources to other build steps). For example, the + will be used as sources to other build steps). For example, the &MSVC; compiler is capable of performing incremental linking, for which it uses a status file - such that linking foo.exe also produces @@ -245,7 +245,7 @@ cat - This makes sure the the two + This makes sure the two ./build steps are run sequentially, even with the --jobs=2 in the command line: diff --git a/doc/user/troubleshoot.xml b/doc/user/troubleshoot.xml index 72c6fd6e3e..710361c19d 100644 --- a/doc/user/troubleshoot.xml +++ b/doc/user/troubleshoot.xml @@ -193,7 +193,7 @@ file3.c This becomes even more helpful in identifying when a file is rebuilt due to a change in an implicit dependency, - such as an incuded .h file. + such as an included .h file. If the file1.c and file3.c files in our example @@ -321,7 +321,7 @@ print(env.Dump()) interested in, the &Dump; method allows you to specify a specific &consvar; - that you want to disply. + that you want to display. For example, it's not unusual to want to verify the external environment used to execute build commands, @@ -658,7 +658,7 @@ cc -o prog prog.o To get some insight into what library names &SCons; is searching for, and in which directories it is searching, - Use the &debug-findlibs; option. + use the &debug-findlibs; option. Given the following input &SConstruct; file: diff --git a/doc/user/variants.xml b/doc/user/variants.xml index 84a93bffa6..e2c7ba3d3d 100644 --- a/doc/user/variants.xml +++ b/doc/user/variants.xml @@ -128,7 +128,7 @@ int world() { printf "world.c\n"; } In order to build several variants at once when using the variant_dir argument to &SConscript;, - you can call the function repeatedely - this example + you can call the function repeatedly - this example does so in a loop. Note that the &f-link-SConscript; trick of passing a list of script files, or a list of source directories, does not work with variant_dir, From b4ab76fb8f37bb0d9c76c10c91c9365af0861f5d Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 13:39:26 +0100 Subject: [PATCH 176/386] *.xml: Add missing commas --- SCons/Action.xml | 2 +- SCons/Defaults.xml | 6 +++--- SCons/Environment.xml | 2 +- SCons/Script/Main.xml | 2 +- SCons/Tool/f03.xml | 2 +- SCons/Tool/f08.xml | 2 +- SCons/Tool/f77.xml | 2 +- SCons/Tool/f90.xml | 2 +- SCons/Tool/f95.xml | 2 +- SCons/Tool/fortran.xml | 2 +- SCons/Tool/jar.xml | 2 +- SCons/Tool/javac.xml | 2 +- SCons/Tool/link.xml | 4 ++-- SCons/Tool/msvc.xml | 2 +- SCons/Tool/msvs.xml | 4 ++-- SCons/Tool/textfile.xml | 4 ++-- SCons/Tool/zip.xml | 2 +- doc/man/scons-time.xml | 2 +- doc/man/scons.xml | 12 ++++++------ doc/user/builders-writing.xml | 4 ++-- doc/user/caching.xml | 4 ++-- doc/user/depends.xml | 2 +- doc/user/environments.xml | 2 +- doc/user/hierarchy.xml | 2 +- doc/user/output.xml | 2 +- doc/user/separate.xml | 2 +- doc/user/simple.xml | 2 +- doc/user/troubleshoot.xml | 4 ++-- 28 files changed, 41 insertions(+), 41 deletions(-) diff --git a/SCons/Action.xml b/SCons/Action.xml index 738df78208..906324cfcb 100644 --- a/SCons/Action.xml +++ b/SCons/Action.xml @@ -61,7 +61,7 @@ Action strings can be segmented by the use of an AND operator, &&. In a segmented string, each segment is a separate command line, these are run -sequentially until one fails or the entire +sequentially until one fails, or the entire sequence has been executed. If an action string is segmented, then the selected behavior of &cv-IMPLICIT_COMMAND_DEPENDENCIES; diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml index 80ec4cdc62..f6215281ed 100644 --- a/SCons/Defaults.xml +++ b/SCons/Defaults.xml @@ -261,7 +261,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. -In general it's not advised to put include directory directives +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 and the directories will not be searched by the dependency scanner. @@ -276,7 +276,7 @@ directory names in &cv-CPPPATH; will be looked-up relative to the directory of the SConscript file 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: @@ -548,7 +548,7 @@ directory names in &cv-LIBPATH; will be looked-up relative to the directory of the SConscript file 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: diff --git a/SCons/Environment.xml b/SCons/Environment.xml index 87d1e7ca30..be32fa50fa 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -2033,7 +2033,7 @@ FindSourceFiles('src') -As you can see build support files (&SConstruct; in the above example) +As you can see, build support files (&SConstruct; in the above example) will also be returned by this function. diff --git a/SCons/Script/Main.xml b/SCons/Script/Main.xml index 01a3b90b11..38d8dad2b5 100644 --- a/SCons/Script/Main.xml +++ b/SCons/Script/Main.xml @@ -200,7 +200,7 @@ Future versions of &SCons; will likely forbid such usage. -Allows setting options for SCons debug options. Currently the only supported value is +Allows setting options for SCons debug options. Currently, the only supported value is json which sets the path to the json file created when --debug=json is set. diff --git a/SCons/Tool/f03.xml b/SCons/Tool/f03.xml index e5df9ebec9..e18439c3c0 100644 --- a/SCons/Tool/f03.xml +++ b/SCons/Tool/f03.xml @@ -154,7 +154,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F03PATH; will be looked-up relative to the SConscript directory 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 #: You only need to set &cv-link-F03PATH; if you need to define a specific include path for Fortran 03 files. You should normally set the &cv-link-FORTRANPATH; variable, diff --git a/SCons/Tool/f08.xml b/SCons/Tool/f08.xml index e50f857cdb..dcf1b49898 100644 --- a/SCons/Tool/f08.xml +++ b/SCons/Tool/f08.xml @@ -154,7 +154,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F08PATH; will be looked-up relative to the SConscript directory 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 #: You only need to set &cv-link-F08PATH; if you need to define a specific include path for Fortran 08 files. You should normally set the &cv-link-FORTRANPATH; variable, diff --git a/SCons/Tool/f77.xml b/SCons/Tool/f77.xml index a98c110830..5420c62603 100644 --- a/SCons/Tool/f77.xml +++ b/SCons/Tool/f77.xml @@ -169,7 +169,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F77PATH; will be looked-up relative to the SConscript directory 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 #: You only need to set &cv-link-F77PATH; if you need to define a specific include path for Fortran 77 files. You should normally set the &cv-link-FORTRANPATH; variable, diff --git a/SCons/Tool/f90.xml b/SCons/Tool/f90.xml index 0a2f6a04da..4310ed8952 100644 --- a/SCons/Tool/f90.xml +++ b/SCons/Tool/f90.xml @@ -154,7 +154,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F90PATH; will be looked-up relative to the SConscript directory 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 #: You only need to set &cv-link-F90PATH; if you need to define a specific include path for Fortran 90 files. You should normally set the &cv-link-FORTRANPATH; variable, diff --git a/SCons/Tool/f95.xml b/SCons/Tool/f95.xml index 3e964840f5..91e5a48a2a 100644 --- a/SCons/Tool/f95.xml +++ b/SCons/Tool/f95.xml @@ -154,7 +154,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F95PATH; will be looked-up relative to the SConscript directory 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 #: You only need to set &cv-link-F95PATH; if you need to define a specific include path for Fortran 95 files. You should normally set the &cv-link-FORTRANPATH; variable, diff --git a/SCons/Tool/fortran.xml b/SCons/Tool/fortran.xml index 944eb3ed4c..799e1d6f46 100644 --- a/SCons/Tool/fortran.xml +++ b/SCons/Tool/fortran.xml @@ -239,7 +239,7 @@ non-portable and the directories will not be searched by the dependency scanner. Note: directory names in FORTRANPATH will be looked-up relative to the SConscript directory 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 #: diff --git a/SCons/Tool/jar.xml b/SCons/Tool/jar.xml index 011ce4e278..ba8b2dff22 100644 --- a/SCons/Tool/jar.xml +++ b/SCons/Tool/jar.xml @@ -130,7 +130,7 @@ env = Environment(JARCOMSTR="JARchiving $SOURCES into $TARGET") General options passed to the Java archive tool. -By default this is set to +By default, this is set to to create the necessary jar diff --git a/SCons/Tool/javac.xml b/SCons/Tool/javac.xml index ccda8857b8..b6b3f6ee6d 100644 --- a/SCons/Tool/javac.xml +++ b/SCons/Tool/javac.xml @@ -109,7 +109,7 @@ env.Java(target='classes', source=['File1.java', 'File2.java']) In this case, the user must specify the LANG environment variable to tell the compiler what encoding is used. - For portability, it's best if the encoding is hard-coded + For portability, it's best if the encoding is hard-coded, so that the compile will work if it is done on a system with a different encoding. diff --git a/SCons/Tool/link.xml b/SCons/Tool/link.xml index 0b10768115..63d499c011 100644 --- a/SCons/Tool/link.xml +++ b/SCons/Tool/link.xml @@ -66,7 +66,7 @@ based on the types of source files. This construction variable automatically introduces &cv-link-_LDMODULEVERSIONFLAGS; -if &cv-link-LDMODULEVERSION; is set. Otherwise it evaluates to an empty string. +if &cv-link-LDMODULEVERSION; is set. Otherwise, it evaluates to an empty string. @@ -75,7 +75,7 @@ if &cv-link-LDMODULEVERSION; is set. Otherwise it evaluates to an empty string. This construction variable automatically introduces &cv-link-_SHLIBVERSIONFLAGS; -if &cv-link-SHLIBVERSION; is set. Otherwise it evaluates to an empty string. +if &cv-link-SHLIBVERSION; is set. Otherwise, it evaluates to an empty string. diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index acf9c5be83..c6fa977723 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -298,7 +298,7 @@ only if the &cv-link-PDB; &consvar; is set. This variable specifies how much of a source file is precompiled. This variable is ignored by tools other than &MSVC;, or when -the PCH variable is not being used. When this variable is defined it +the PCH variable is not being used. When this variable is defined, it must be a string that is the name of the header that is included at the end of the precompiled portion of the source files, or the empty string if the "#pragma hrdstop" construct is being used: diff --git a/SCons/Tool/msvs.xml b/SCons/Tool/msvs.xml index dba3257e79..bf9cd525fe 100644 --- a/SCons/Tool/msvs.xml +++ b/SCons/Tool/msvs.xml @@ -292,7 +292,7 @@ This file is processed by the bin/SConsDoc.py module. it is important not to set Visual Studio to do parallel builds, as it will then launch the separate project builds in parallel, and &SCons; does not work well if called that way. - Instead you can set up the &SCons; build for parallel building - + Instead, you can set up the &SCons; build for parallel building - see the &f-link-SetOption; function for how to do this with num_jobs. @@ -747,7 +747,7 @@ env.MSVSSolution( SccProjectFilePathRelativizedFromConnection[i] (where [i] ranges from 0 to the number of projects in the solution) attributes of the GlobalSection(SourceCodeControl) - section of the Microsoft Visual Studio solution file. Similarly + section of the Microsoft Visual Studio solution file. Similarly, the relative solution file path is placed as the values of the SccLocalPath[i] (where [i] ranges from 0 to the number of projects in the solution) attributes of the diff --git a/SCons/Tool/textfile.xml b/SCons/Tool/textfile.xml index 2539f5a1b0..c16b3d039a 100644 --- a/SCons/Tool/textfile.xml +++ b/SCons/Tool/textfile.xml @@ -68,7 +68,7 @@ and &cv-link-TEXTFILESUFFIX; &consvars; are automatically added to the target if they are not already present. -By default the target file encoding is "utf-8" and can be changed by &cv-link-FILE_ENCODING; +By default, the target file encoding is "utf-8" and can be changed by &cv-link-FILE_ENCODING; Examples: @@ -131,7 +131,7 @@ are flattened. See also &b-link-Textfile;. -By default the target file encoding is "utf-8" and can be changed by &cv-link-FILE_ENCODING; +By default, the target file encoding is "utf-8" and can be changed by &cv-link-FILE_ENCODING; Examples: diff --git a/SCons/Tool/zip.xml b/SCons/Tool/zip.xml index 85fe19b1fd..9458a12e70 100644 --- a/SCons/Tool/zip.xml +++ b/SCons/Tool/zip.xml @@ -149,7 +149,7 @@ The suffix used for zip file names. An optional zip root directory (default empty). The filenames stored in the zip file will be relative to this directory, if given. -Otherwise the filenames are relative to the current directory of the +Otherwise, the filenames are relative to the current directory of the command. For instance: diff --git a/doc/man/scons-time.xml b/doc/man/scons-time.xml index 394beba7ed..c5bc13997f 100644 --- a/doc/man/scons-time.xml +++ b/doc/man/scons-time.xml @@ -1169,7 +1169,7 @@ arguments = ['project-1.2.tgz', 'project-SConscripts.tar'] # so tell scons-time to chdir there before building. subdir = 'project-1.2' -# Set the prefix so output log files and profiles are named: +# Set the prefix, so output log files and profiles are named: # project-000-[012].{log,prof} # project-001-[012].{log,prof} # etc. diff --git a/doc/man/scons.xml b/doc/man/scons.xml index c915c172d4..b8172ae418 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -138,7 +138,7 @@ to support additional input file types. Information about files involved in the build, including a cryptographic hash of the contents of source files, is cached for later reuse. -By default this hash (the &contentsig;) +By default, this hash (the &contentsig;) is used to decide if a file has changed since the last build, although other algorithms can be used by selecting an appropriate &f-link-Decider; function. @@ -757,7 +757,7 @@ built during this invocation. -When using a derived-file cache show the command +When using a derived-file cache, show the command that would have been executed to build the file (or the corresponding *COMSTR contents if set) @@ -1341,7 +1341,7 @@ to use the specified algorithm. If this option is omitted, the first supported hash format found is selected. -Typically this is MD5, however, on a FIPS-compliant system +Typically, this is MD5, however, on a FIPS-compliant system using a version of &Python; older than 3.9, SHA1 or SHA256 is chosen as the default. &Python; 3.9 and onwards clients always default to MD5, even in FIPS mode. @@ -2920,7 +2920,7 @@ of the &SConscript; file currently being processed. &SCons; also recognizes a third way to specify path strings: if the string begins with the # character it is -top-relative - it works like a relative path but the +top-relative - it works like a relative path, but the search follows down from the project top directory rather than from the current directory. The # can optionally be followed by a pathname separator, @@ -5864,7 +5864,7 @@ If the suffix is a string, then &scons; prepends a '.' to the suffix if it's not already there. The string returned by the callable object or obtained from the -dictionary is untouched and you need to manually prepend a '.' +dictionary is untouched, and you need to manually prepend a '.' if one is required. @@ -8848,7 +8848,7 @@ env.Program('MyApp', ['Foo.cpp', 'Bar.cpp']) In general, &scons; is not controlled by environment variables set in the shell used to invoke it, leaving it up to the &SConscript; file author to import those if desired. -However the following variables are imported by +However, the following variables are imported by &scons; itself if set: diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml index d0938fab2a..b9eb064bfe 100644 --- a/doc/user/builders-writing.xml +++ b/doc/user/builders-writing.xml @@ -132,7 +132,7 @@ env.Foo('file.foo', 'file.input') - Then when we run &SCons; it looks like: + Then, when we run &SCons; it looks like: @@ -1038,7 +1038,7 @@ env.AddHeader('tgt', 'src') to include in their &SConscript; files: just put them in site_scons/my_utils.py or any valid &Python; module name of your - choice. For instance you can do something like this in + choice. For instance, you can do something like this in site_scons/my_utils.py to add build_id and MakeWorkDir functions: diff --git a/doc/user/caching.xml b/doc/user/caching.xml index 523781ff04..885e6c3887 100644 --- a/doc/user/caching.xml +++ b/doc/user/caching.xml @@ -87,7 +87,7 @@ CacheDir('/usr/local/build_cache') on a shared or NFS-mounted file system. While &SCons; will create the specified cache directory as needed, in this multi user scenario it is usually best - to create it ahead of time so the access rights + to create it ahead of time, so the access rights can be set up correctly. @@ -240,7 +240,7 @@ hello.c - Then when you run &scons; after cleaning + Then, when you run &scons; after cleaning the built targets, it will recompile the object file locally (since it doesn't exist in the derived-file cache directory), diff --git a/doc/user/depends.xml b/doc/user/depends.xml index 0c61e67118..23ae612324 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -1026,7 +1026,7 @@ SetOption('implicit_cache', 1) - By default when caching dependencies, + By default, when caching dependencies, &SCons; notices when a file has been modified and re-scans the file for any updated implicit dependency information. diff --git a/doc/user/environments.xml b/doc/user/environments.xml index 80133e8ad6..9c490bd261 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -2152,7 +2152,7 @@ C:\Python35\Lib\site-packages\someinstalledpackage\SomeTool\__init__.py located within an installed external pip package. This is possible by the use of sys.path with the toolpath. - However in that situation you need to provide a prefix to the toolname + However, in that situation you need to provide a prefix to the toolname to indicate where it is located within sys.path. diff --git a/doc/user/hierarchy.xml b/doc/user/hierarchy.xml index c5cb9b8f73..8c6b4ca6b3 100644 --- a/doc/user/hierarchy.xml +++ b/doc/user/hierarchy.xml @@ -893,7 +893,7 @@ void bar(void) { printf("bar/bar.c\n"); } (The corresponding bar/SConscript file should be pretty obvious.) - Then when we run &SCons;, + Then, when we run &SCons;, the object files from the subsidiary subdirectories are all correctly archived in the desired library: diff --git a/doc/user/output.xml b/doc/user/output.xml index 4072e7f5da..abb6e90c80 100644 --- a/doc/user/output.xml +++ b/doc/user/output.xml @@ -31,7 +31,7 @@ This file is processed by the bin/SConsDoc.py module. A key aspect of creating a usable build configuration - is providing useful output from the build + is providing useful output from the build, so its users can readily understand what the build is doing and get information about how to control the build. diff --git a/doc/user/separate.xml b/doc/user/separate.xml index 4af3a5560e..9fc9ba0b76 100644 --- a/doc/user/separate.xml +++ b/doc/user/separate.xml @@ -219,7 +219,7 @@ int main() { printf("Hello, world!\n"); } - When you set up a variant directory &SCons; conceptually behaves as + When you set up a variant directory, &SCons; conceptually behaves as if you requested a build in that directory. As noted in the previous chapter, all builds actually happen from the top level directory, diff --git a/doc/user/simple.xml b/doc/user/simple.xml index 7a065136c1..9f57d33311 100644 --- a/doc/user/simple.xml +++ b/doc/user/simple.xml @@ -487,7 +487,7 @@ int main() { printf("Goodbye, world!\n"); } - Then when you execute &SCons;, + Then, when you execute &SCons;, you will see the output from calling the print function in between the messages about reading the &SConscript; files, diff --git a/doc/user/troubleshoot.xml b/doc/user/troubleshoot.xml index 710361c19d..763e55ce0f 100644 --- a/doc/user/troubleshoot.xml +++ b/doc/user/troubleshoot.xml @@ -426,7 +426,7 @@ inc.h - By default &SCons; uses "ASCII art" to draw the tree. It is + By default, &SCons; uses "ASCII art" to draw the tree. It is possible to use line-drawing characters (Unicode calls these Box Drawing) to make a nicer display. To do this, add the qualifier: @@ -877,7 +877,7 @@ prog.c - Sometimes SCons doesn't build the target you want + Sometimes SCons doesn't build the target you want, and it's difficult to figure out why. You can use the &debug-prepare; option to see all the targets &SCons; is considering, and whether From a277b0c959ab178ca660924ddce96e6b2a9dbf93 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 13:50:54 +0100 Subject: [PATCH 177/386] *.xml: Use en_US --- SCons/Tool/lex.xml | 2 +- doc/man/scons.xml | 2 +- doc/user/file-removal.xml | 2 +- doc/user/simple.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SCons/Tool/lex.xml b/SCons/Tool/lex.xml index e1e1190119..5bc0e30217 100644 --- a/SCons/Tool/lex.xml +++ b/SCons/Tool/lex.xml @@ -27,7 +27,7 @@ This file is processed by the bin/SConsDoc.py module. -Sets construction variables for the &lex; lexical analyser. +Sets construction variables for the &lex; lexical analyzer. diff --git a/doc/man/scons.xml b/doc/man/scons.xml index b8172ae418..19063a376c 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -3887,7 +3887,7 @@ The mechanism is portable across platforms. does not maintain an explicit cache of the tested values (this is different than &Autoconf;), but uses its normal dependency tracking to keep the checked values -up to date. However, users may override this behaviour with the +up to date. However, users may override this behavior with the command line option. diff --git a/doc/user/file-removal.xml b/doc/user/file-removal.xml index 082d17e010..a91d9e09a2 100644 --- a/doc/user/file-removal.xml +++ b/doc/user/file-removal.xml @@ -41,7 +41,7 @@ This file is processed by the bin/SConsDoc.py module. -c option to "clean" a tree of its built targets. - These behaviours can be suppressed with the + These behaviors can be suppressed with the &Precious; and &NoClean; functions, respectively. diff --git a/doc/user/simple.xml b/doc/user/simple.xml index 9f57d33311..88e3d8c7ff 100644 --- a/doc/user/simple.xml +++ b/doc/user/simple.xml @@ -308,7 +308,7 @@ public class Example1 in exactly the same way as you control what gets built. If you build the C example above and then invoke scons -c - afterwards, the output on POSIX looks like: + afterward, the output on POSIX looks like: From 9934570fda75537dc392b7b4fc2367606be2759b Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 13:54:27 +0100 Subject: [PATCH 178/386] *.xml: Fix hyphens One of the implemented rules is: > A compound adjective usually gets a hyphen when it comes before a > noun. --- SCons/Defaults.xml | 4 ++-- SCons/Environment.xml | 2 +- SCons/Tool/Tool.xml | 2 +- SCons/Tool/f03.xml | 2 +- SCons/Tool/fortran.xml | 2 +- SCons/Tool/msvs.xml | 2 +- SCons/Tool/swig.xml | 2 +- doc/man/scons.xml | 6 +++--- doc/user/caching.xml | 2 +- doc/user/environments.xml | 8 ++++---- doc/user/gettext.xml | 2 +- doc/user/make.xml | 2 +- doc/user/nodes.xml | 2 +- doc/user/separate.xml | 12 ++++++------ doc/user/sideeffect.xml | 2 +- 15 files changed, 26 insertions(+), 26 deletions(-) diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml index f6215281ed..ce24b3b986 100644 --- a/SCons/Defaults.xml +++ b/SCons/Defaults.xml @@ -276,7 +276,7 @@ directory names in &cv-CPPPATH; will be looked-up relative to the directory of the SConscript file 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: @@ -654,7 +654,7 @@ as dependencies of the target being generated. -The library list will be transformed to command line +The library list will be transformed to command-line arguments through the automatically-generated &cv-link-_LIBFLAGS; &consvar; which is constructed by diff --git a/SCons/Environment.xml b/SCons/Environment.xml index be32fa50fa..9054c2e273 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -580,7 +580,7 @@ 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 be postprocessed by adding a prefix and/or suffix +will be post-processed 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, diff --git a/SCons/Tool/Tool.xml b/SCons/Tool/Tool.xml index ba8b9afe9d..fcd33fe93d 100644 --- a/SCons/Tool/Tool.xml +++ b/SCons/Tool/Tool.xml @@ -567,7 +567,7 @@ When this &consvar; is defined, a versioned shared library is created by the &b-link-SharedLibrary; builder. This activates the &cv-link-_SHLIBVERSIONFLAGS; and thus modifies the &cv-link-SHLINKCOM; as required, adds the version number to the library name, and creates the symlinks -that are needed. &cv-link-SHLIBVERSION; versions should exist as alpha-numeric, +that are needed. &cv-link-SHLIBVERSION; versions should exist as alphanumeric, decimal-delimited values as defined by the regular expression "\w+[\.\w+]*". Example &cv-link-SHLIBVERSION; values include '1', '1.2.3', and '1.2.gitaa412c8b'. diff --git a/SCons/Tool/f03.xml b/SCons/Tool/f03.xml index e18439c3c0..eaa115d165 100644 --- a/SCons/Tool/f03.xml +++ b/SCons/Tool/f03.xml @@ -154,7 +154,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F03PATH; will be looked-up relative to the SConscript directory 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 #: You only need to set &cv-link-F03PATH; if you need to define a specific include path for Fortran 03 files. You should normally set the &cv-link-FORTRANPATH; variable, diff --git a/SCons/Tool/fortran.xml b/SCons/Tool/fortran.xml index 799e1d6f46..d9a84a574c 100644 --- a/SCons/Tool/fortran.xml +++ b/SCons/Tool/fortran.xml @@ -239,7 +239,7 @@ non-portable and the directories will not be searched by the dependency scanner. Note: directory names in FORTRANPATH will be looked-up relative to the SConscript directory 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 #: diff --git a/SCons/Tool/msvs.xml b/SCons/Tool/msvs.xml index bf9cd525fe..8ccd8d753e 100644 --- a/SCons/Tool/msvs.xml +++ b/SCons/Tool/msvs.xml @@ -279,7 +279,7 @@ This file is processed by the bin/SConsDoc.py module. "build" from the Visual Studio interface) you lose the direct control of target selection and command-line options you would have if launching the build directly from &SCons;, - because these will be hardcoded in the project file to the + because these will be hard-coded in the project file to the values specified in the &b-MSVSProject; call. You can regain some of this control by defining multiple variants, using multiple &b-MSVSProject; calls to arrange different build diff --git a/SCons/Tool/swig.xml b/SCons/Tool/swig.xml index 2d3550468f..ea5fabf4eb 100644 --- a/SCons/Tool/swig.xml +++ b/SCons/Tool/swig.xml @@ -208,7 +208,7 @@ will be looked-up relative to the SConscript directory 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 a top-relative path (#): diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 19063a376c..3477d2eec7 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -3273,7 +3273,7 @@ object_files.extend(Object('bar.c')) The path name for a Node's file may be used -by passing the Node to &Python;'s builtin +by passing the Node to &Python;'s built-in str function: @@ -5421,7 +5421,7 @@ vars.AddVariables( default="no", allowed_values=("yes", "no", "full"), map={}, - ignorecase=0, # case sensitive + ignorecase=0, # case-sensitive ), ListVariable( "shared", @@ -7637,7 +7637,7 @@ expensive for limited benefit - consider for example the C standard header file string.h. The scanner function is not passed any special information -to help make this choice, so the decision making encoded +to help make this choice, so the decision-making encoded in the scanner function must be carefully considered. diff --git a/doc/user/caching.xml b/doc/user/caching.xml index 885e6c3887..413c72da92 100644 --- a/doc/user/caching.xml +++ b/doc/user/caching.xml @@ -86,7 +86,7 @@ CacheDir('/usr/local/build_cache') this directory would typically be on a shared or NFS-mounted file system. While &SCons; will create the specified cache directory as needed, - in this multi user scenario it is usually best + in this multi-user scenario it is usually best to create it ahead of time, so the access rights can be set up correctly. diff --git a/doc/user/environments.xml b/doc/user/environments.xml index 9c490bd261..6807694ece 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -1842,7 +1842,7 @@ env['ENV']['PATH'] = ['/usr/local/bin', '/bin', '/usr/bin'] Note that &SCons; does allow you to define the directories in the &PATH; in a string with paths - separated by the pathname-separator character + separated by the pathname separator character for your system (':' on POSIX systems, ';' on Windows). @@ -2018,7 +2018,7 @@ env.AppendENVPath('LIB', '/usr/local/lib') -# Builtin tool or tool located within site_tools +# Built-in tool or tool located within site_tools env = Environment(tools=['SomeTool']) env.SomeTool(targets, sources) @@ -2068,11 +2068,11 @@ SCons/Tool/SomeTool/__init__.py Since &SCons; 3.0, a Builder may be located - within a sub-directory / sub-package of the toolpath. + within a subdirectory / sub-package of the toolpath. This is similar to namespacing within &Python;. With nested or namespaced tools we can use the dot notation - to specify a sub-directory that the tool is located under. + to specify a subdirectory that the tool is located under. diff --git a/doc/user/gettext.xml b/doc/user/gettext.xml index 515534501a..5abebf9ec2 100644 --- a/doc/user/gettext.xml +++ b/doc/user/gettext.xml @@ -86,7 +86,7 @@ hello = Program(["hello.c"]) - Now we'll convert the project to a multi-lingual one. If you don't + Now we'll convert the project to a multilingual one. If you don't already have GNU gettext utilities installed, install them from your preferred diff --git a/doc/user/make.xml b/doc/user/make.xml index 442dc1534f..a14153ff0c 100644 --- a/doc/user/make.xml +++ b/doc/user/make.xml @@ -43,7 +43,7 @@ original make utility and its derivatives have contributed to this tendency in a number of ways. Make is not good at dealing with systems that are spread over multiple directories. Various work-arounds are used to overcome this difficulty; the usual choice is for make to invoke itself recursively -for each sub-directory of a build. This leads to complicated code, in which +for each subdirectory of a build. This leads to complicated code, in which it is often unclear how a variable is set, or what effect the setting of a variable will have on the build as a whole. The make scripting language has gradually been extended to provide more possibilities, but these have diff --git a/doc/user/nodes.xml b/doc/user/nodes.xml index be0f9f6255..a652e79c79 100644 --- a/doc/user/nodes.xml +++ b/doc/user/nodes.xml @@ -301,7 +301,7 @@ int main() { printf("Hello, world!\n"); } is the name of the file. If you want to do something other than print the name of the file, - you can fetch it by using the builtin Python + you can fetch it by using the built-in Python &str; function. For example, if you want to use the Python os.path.exists diff --git a/doc/user/separate.xml b/doc/user/separate.xml index 9fc9ba0b76..effc5aa2c5 100644 --- a/doc/user/separate.xml +++ b/doc/user/separate.xml @@ -79,7 +79,7 @@ This file is processed by the bin/SConsDoc.py module. The key to making this separation work is the ability to do out-of-tree builds: building under a separate root than the sources being built. - You set up out of tree builds by establishing what &SCons; + You set up out-of-tree builds by establishing what &SCons; calls a variant directory, a place where you can build a single variant of your software (of course you can define more than one of these if you need to). @@ -208,7 +208,7 @@ int main() { printf("Hello, world!\n"); } except for the extra &variant_dir; argument in the &f-link-SConscript; call. &SCons; handles all the path adjustments for the - out of tree &build; directory while it processes that SConscript file. + out-of-tree &build; directory while it processes that SConscript file. @@ -224,11 +224,11 @@ int main() { printf("Hello, world!\n"); } As noted in the previous chapter, all builds actually happen from the top level directory, but as an aid to understanding how &SCons; operates, think - of it as build in place in the variant directory, - not build in source but send build artifacts + of it as build in-place in the variant directory, + not build in-source but send build artifacts to the variant directory. - It turns out in place builds are easier to get right than out - of tree builds - so by default &SCons; simulates an in place build + It turns out in-place builds are easier to get right than out-of-tree + builds - so by default &SCons; simulates an in-place build by making the variant directory look just like the source directory. The most straightforward way to do that is by making copies of the files needed for the build. diff --git a/doc/user/sideeffect.xml b/doc/user/sideeffect.xml index b17f391a1c..cff8985b59 100644 --- a/doc/user/sideeffect.xml +++ b/doc/user/sideeffect.xml @@ -121,7 +121,7 @@ f1 = env.Command( a foo.ilk, or uses it if it was already present, if the option was supplied. Specifying foo.ilk as a - side-effect of foo.exe + side effect of foo.exe is not a recommended use of &SideEffect; since foo.ilk is used by the link. &SCons; handles side-effect files From ff44c3b4649ca0c0814399ffe535540f3ba4f612 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 14:59:38 +0100 Subject: [PATCH 179/386] *.xml: Hyphenate `out of date` This seems to be a compound adjective which usually gets a hyphen. --- SCons/Environment.xml | 12 ++++++------ SCons/Tool/javac.xml | 2 +- doc/man/scons.xml | 18 +++++++++--------- doc/python10/summary.xml | 2 +- doc/user/build-install.xml | 2 +- doc/user/depends.xml | 2 +- doc/user/make.xml | 2 +- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/SCons/Environment.xml b/SCons/Environment.xml index 9054c2e273..924872183a 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -513,7 +513,7 @@ env.Alias('update', ['file1', 'file2'], "update_database $SOURCES") Marks each given target -so that it is always assumed to be out of date, +so that it is always assumed to be out-of-date, and will always be rebuilt if needed. Note, however, that &f-AlwaysBuild; @@ -1339,7 +1339,7 @@ that specify a predefined decider function: "content" -Specifies that a target shall be considered out of date and rebuilt +Specifies that a target shall be considered out-of-date and rebuilt if the dependency's content has changed since the last time the target was built, as determined by performing a checksum @@ -1361,7 +1361,7 @@ can still be used as a synonym, but is deprecated. "content-timestamp" -Specifies that a target shall be considered out of date and rebuilt +Specifies that a target shall be considered out-of-date and rebuilt if the dependency's content has changed since the last time the target was built, except that dependencies with a timestamp that matches @@ -1398,7 +1398,7 @@ can still be used as a synonym, but is deprecated. "timestamp-newer" -Specifies that a target shall be considered out of date and rebuilt +Specifies that a target shall be considered out-of-date and rebuilt if the dependency's timestamp is newer than the target file's timestamp. This is the behavior of the classic Make utility, and @@ -1412,7 +1412,7 @@ can be used a synonym for "timestamp-match" -Specifies that a target shall be considered out of date and rebuilt +Specifies that a target shall be considered out-of-date and rebuilt if the dependency's timestamp is different than the timestamp recorded the last time the target was built. This provides behavior very similar to the classic Make utility @@ -3602,7 +3602,7 @@ Returns a Node object representing the specified &Python; Value Nodes can be used as dependencies of targets. If the string representation of the Value Node changes between &SCons; runs, it is considered -out of date and any targets depending on it will be rebuilt. +out-of-date and any targets depending on it will be rebuilt. Since Value Nodes have no filesystem representation, timestamps are not used; the timestamp deciders perform the same content-based up to date check. diff --git a/SCons/Tool/javac.xml b/SCons/Tool/javac.xml index b6b3f6ee6d..af1fc31b47 100644 --- a/SCons/Tool/javac.xml +++ b/SCons/Tool/javac.xml @@ -364,7 +364,7 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES") that &SCons; expects will be generated by the &javac; compiler. Setting &cv-JAVAVERSION; to a version greater than 1.4 makes &SCons; realize that a build - with such a compiler is actually up to date. + with such a compiler is actually up-to-date. The default is 1.4. diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 3477d2eec7..656d34c0b3 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -800,7 +800,7 @@ This is the default behavior. If this mode is specified, all configuration tests will be re-run regardless of whether the -cached results are out of date. +cached results are out-of-date. This can be used to explicitly force the configuration tests to be updated in response to an otherwise unconfigured change @@ -1067,7 +1067,7 @@ without requiring .py suffix. is prepared for building. &scons; prints this for each target it considers, even if that -target is up to date (see also ). +target is up-to-date (see also ). This can help debug problems with targets that aren't being built; it shows whether &scons; @@ -1752,7 +1752,7 @@ if not GetOption("no_exec"): determine what would be built. For example, if a file generated by a builder action is also used as a source in the build, that file is not available to scan for dependencies at all -in an unbuilt tree, and may contain out of date information in a +in an unbuilt tree, and may contain out-of-date information in a previously built tree. @@ -1760,7 +1760,7 @@ previously built tree. as they would make changes to the filesystem (see &cv-link-CONFIGUREDIR; and &cv-link-CONFIGURELOG;). It can use stored information from a previous build, -if it is not out of date, +if it is not out-of-date, so a "priming" build may make subsequent no-exec runs more useful. @@ -1845,8 +1845,8 @@ The results may be analyzed using the &Python; Do not run any commands, or print anything. Just return an exit -status that is zero if the specified targets are already up to -date, non-zero otherwise. +status that is zero if the specified targets are already up-to-date, +non-zero otherwise. @@ -2827,7 +2827,7 @@ specified relationship into its internal dependency node graph, and only later makes the decision on whether anything is actually built, since this depends on command-line options, target selection rules, and whether the target(s) are -out of date with respect to the sources. +out-of-date with respect to the sources. @@ -5685,7 +5685,7 @@ Aliases are virtual objects - they will not themselves result in physical objects being constructed, but are entered into the dependency graph related to their sources. An alias is checked for up to date by checking if -its sources are up to date. +its sources are up-to-date. An alias is built by making sure its sources have been built, and if any building took place, applying any Actions that are defined as part of the alias. @@ -5707,7 +5707,7 @@ other alias has previously been created. The &f-link-Value; method returns a Value Node. Value nodes are often used for generated data that will not have any corresponding filesystem entry, -but will be used to determine whether a build target is out of date, +but will be used to determine whether a build target is out-of-date, or to include as part of a build Action. Common examples are timestamp strings, revision control version strings diff --git a/doc/python10/summary.xml b/doc/python10/summary.xml index a8f8767e97..0b611984b9 100644 --- a/doc/python10/summary.xml +++ b/doc/python10/summary.xml @@ -40,7 +40,7 @@ This paper has introduced &SCons;, a next-generation build tool with a modular, embeddable architecture and a direct Python interface. &SCons; has a global view of the dependencies in a source - tree, uses MD5 signatures to decide if derived files are out of date, + tree, uses MD5 signatures to decide if derived files are out-of-date, and automatically scans files for dependencies, all of which make &SCons; builds exceptionally reliable. The &SCons; development methodology has been described, notable for its emphasis on automated regression diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml index f0b400f967..61dc1d8147 100644 --- a/doc/user/build-install.xml +++ b/doc/user/build-install.xml @@ -155,7 +155,7 @@ Python 3.9.15 Recent versions of the Mac no longer come with &Python; - pre-installed; older versions came with a rather out of date + pre-installed; older versions came with a rather out-of-date version (based on &Python; 2.7) which is insufficient to run current &SCons;. The python.org installer can be used on the Mac, but there are diff --git a/doc/user/depends.xml b/doc/user/depends.xml index 23ae612324..d4083535f6 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -1001,7 +1001,7 @@ SetOption('implicit_cache', 1) external code that you use for compilation, the external header files will have changed and the previously-cached implicit dependencies - will be out of date. + will be out-of-date. You can update them by running &SCons; with the &implicit-deps-changed; option: diff --git a/doc/user/make.xml b/doc/user/make.xml index a14153ff0c..b50cbd624e 100644 --- a/doc/user/make.xml +++ b/doc/user/make.xml @@ -60,7 +60,7 @@ dependencies. Most often, an attempt is made to do a reasonable job of dependencies within a single directory, but no serious attempt is made to do the job between directories. Even when dependencies are working correctly, make's reliance on a simple time stamp comparison to determine whether a -file is out of date with respect to its dependents is not, in general, +file is out-of-date with respect to its dependents is not, in general, adequate for determining when a file should be rederived. If an external library, for example, is rebuilt and then ``snapped'' into place, the timestamps on its newly created files may well be earlier than the last From 8a43edd4bfb38fd379206dbc8b085fc259461ce6 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 14:04:41 +0100 Subject: [PATCH 180/386] *.xml: Capitalize proper nouns Refer to https://en.wikipedia.org/wiki/Proper_noun#Modern_English_capitalization_of_proper_nouns. > In modern English orthography, it is the norm for recognized proper > names to be capitalized. --- SCons/Tool/lex.xml | 2 +- SCons/Tool/msvs.xml | 2 +- doc/user/gettext.xml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SCons/Tool/lex.xml b/SCons/Tool/lex.xml index 5bc0e30217..028072b6e5 100644 --- a/SCons/Tool/lex.xml +++ b/SCons/Tool/lex.xml @@ -123,7 +123,7 @@ command-line option. Use this in preference to including -Used only on windows environments to set a lex flag to prevent 'unistd.h' from being included. The default value is '--nounistd'. +Used only in Windows environments to set a lex flag to prevent 'unistd.h' from being included. The default value is '--nounistd'. diff --git a/SCons/Tool/msvs.xml b/SCons/Tool/msvs.xml index 8ccd8d753e..392dd73c77 100644 --- a/SCons/Tool/msvs.xml +++ b/SCons/Tool/msvs.xml @@ -438,7 +438,7 @@ V10DebugSettings = { } # -# 3. Select the dictionary you want depending on the version of visual Studio +# 3. Select the dictionary you want depending on the version of Visual Studio # Files you want to generate. # if not env.GetOption('userfile'): diff --git a/doc/user/gettext.xml b/doc/user/gettext.xml index 5abebf9ec2..73cb1ae76a 100644 --- a/doc/user/gettext.xml +++ b/doc/user/gettext.xml @@ -95,7 +95,7 @@ hello = Program(["hello.c"]) http://ftp.gnu.org/gnu/gettext/. For the purpose of this example, you should have following three locales installed on your system: en_US, de_DE and - pl_PL. On debian, for example, you may enable certain + pl_PL. On Debian, for example, you may enable certain locales through dpkg-reconfigure locales. @@ -231,7 +231,7 @@ scons: done building targets. these files under locale folder. - Your program should be now ready. You may try it as follows (linux): + Your program should be now ready. You may try it as follows (Linux): user@host:$ LANG=en_US.UTF-8 ./hello Welcome to beautiful world From 39f1cb1092aa647c8322f6eeabae23932e987054 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 14:07:33 +0100 Subject: [PATCH 181/386] *.xml: Remove trailing whitespace --- SCons/Tool/msvs.xml | 2 +- doc/man/scons-time.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/msvs.xml b/SCons/Tool/msvs.xml index 392dd73c77..88aa854978 100644 --- a/SCons/Tool/msvs.xml +++ b/SCons/Tool/msvs.xml @@ -552,7 +552,7 @@ env.MSVSProject( Under certain circumstances, solution file names or solution file nodes may be present in the projects argument list. - When solution file names or nodes are present in the + When solution file names or nodes are present in the projects argument list, the generated solution file may contain erroneous Project records resulting in VS IDE error messages when opening the generated solution diff --git a/doc/man/scons-time.xml b/doc/man/scons-time.xml index c5bc13997f..2ce8c3bbab 100644 --- a/doc/man/scons-time.xml +++ b/doc/man/scons-time.xml @@ -41,7 +41,7 @@ - scons-time + scons-time subcommand options arguments @@ -129,7 +129,7 @@ DESCRIPTION -The +The scons-time command runs an SCons configuration through a standard set of profiled timings From e7848aa95663a4d89fe71fab90a8105a0c9d9fe0 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 14:09:34 +0100 Subject: [PATCH 182/386] *.xml: Don't abbreviate `directory` in normal text --- doc/user/add-method.xml | 4 ++-- doc/user/builders-writing.xml | 6 +++--- doc/user/troubleshoot.xml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/user/add-method.xml b/doc/user/add-method.xml index 071d654cdc..001a575004 100644 --- a/doc/user/add-method.xml +++ b/doc/user/add-method.xml @@ -54,14 +54,14 @@ This file is processed by the bin/SConsDoc.py module. def install_in_bin_dirs(env, source): - """Install source in both bin dirs""" + """Install source in both bin directories""" i1 = env.Install("$BIN", source) i2 = env.Install("$LOCALBIN", source) return [i1[0], i2[0]] # Return a list, like a normal builder env = Environment(BIN='__ROOT__/usr/bin', LOCALBIN='#install/bin') env.AddMethod(install_in_bin_dirs, "InstallInBinDirs") -env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin dirs +env.InstallInBinDirs(Program('hello.c')) # installs hello in both bin directories int main() { printf("Hello, world!\n"); } diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml index b9eb064bfe..8d2c948bf3 100644 --- a/doc/user/builders-writing.xml +++ b/doc/user/builders-writing.xml @@ -941,9 +941,9 @@ main() Each system type (Windows, Mac, Linux, etc.) searches a canonical set of directories for site_scons; see the man page for details. - The top-level SConstruct's site_scons dir + The top-level SConstruct's site_scons directory (that is, the one in the project) is always searched last, - and its dir is placed first in the tool path so it overrides all + and its directory is placed first in the tool path so it overrides all others. @@ -952,7 +952,7 @@ main() If you get a tool from somewhere (the &SCons; wiki or a third party, for instance) and you'd like to use it in your project, a - site_scons dir is the simplest place to put it. + site_scons directory is the simplest place to put it. Tools come in two flavors; either a &Python; function that operates on an &Environment; or a &Python; module or package containing two functions, exists() and generate(). diff --git a/doc/user/troubleshoot.xml b/doc/user/troubleshoot.xml index 763e55ce0f..e2b59a5107 100644 --- a/doc/user/troubleshoot.xml +++ b/doc/user/troubleshoot.xml @@ -893,7 +893,7 @@ prog.c - When using the &Duplicate; option to create variant dirs, + When using the &Duplicate; option to create variant directories, sometimes you may find files not getting linked or copied to where you expect (or not at all), or files mysteriously disappearing. These are usually because of a misconfiguration of some kind in the @@ -901,7 +901,7 @@ prog.c &debug-duplicate; option shows each time a variant file is unlinked and relinked from its source (or copied, depending on settings), and also shows a message for removing "stale" - variant-dir files that no longer have a corresponding source file. + variant-directory files that no longer have a corresponding source file. It also prints a line for each target that's removed just before building, since that can also be mistaken for the same thing. From 2cba4ceb8948f32f20de5b6ccf76cca63d280aa4 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 14:13:59 +0100 Subject: [PATCH 183/386] *.xml: Fix joined words --- SCons/Tool/ninja/ninja.xml | 2 +- SCons/Tool/xgettext.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml index 65945d3402..c833d441a2 100644 --- a/SCons/Tool/ninja/ninja.xml +++ b/SCons/Tool/ninja/ninja.xml @@ -339,7 +339,7 @@ python -m pip install ninja - If true, causes the build nodes to callback to scons instead of using + If true, causes the build nodes to call back to scons instead of using &ninja; to build them. This is intended to be passed to the environment on the builder invocation. It is useful if you have a build node which does something which is not easily translated into &ninja;. diff --git a/SCons/Tool/xgettext.xml b/SCons/Tool/xgettext.xml index fb9c6a32dd..940e24a45f 100644 --- a/SCons/Tool/xgettext.xml +++ b/SCons/Tool/xgettext.xml @@ -311,7 +311,7 @@ See also &t-link-xgettext; tool and &b-link-POTUpdate; builder. This flag is used to add single search path to -xgettext(1)'s commandline (default: +xgettext(1)'s command line (default: '-D'). @@ -329,7 +329,7 @@ This flag is used to add single search path to This flag is used to add single &cv-link-XGETTEXTFROM; file to -xgettext(1)'s commandline (default: +xgettext(1)'s command line (default: '-f'). From 73f1cd097a3e87c1aff5fdf30d744efdc7d54db2 Mon Sep 17 00:00:00 2001 From: Alex Thiessen Date: Fri, 15 Nov 2024 14:17:02 +0100 Subject: [PATCH 184/386] *.xml: Add miscellaneous language improvements --- SCons/Environment.xml | 4 ++-- SCons/Script/Main.xml | 2 +- SCons/Tool/gs.xml | 2 +- SCons/Tool/install.xml | 2 +- SCons/Tool/javac.xml | 2 +- SCons/Tool/ninja/ninja.xml | 2 +- SCons/Tool/packaging/packaging.xml | 2 +- SCons/Tool/qt3.xml | 2 +- SCons/Tool/tlib.xml | 2 +- doc/man/scons.xml | 6 +++--- doc/user/build-install.xml | 2 +- doc/user/command-line.xml | 2 +- doc/user/external.xml | 2 +- doc/user/hierarchy.xml | 2 +- doc/user/less-simple.xml | 2 +- doc/user/sideeffect.xml | 2 +- 16 files changed, 19 insertions(+), 19 deletions(-) diff --git a/SCons/Environment.xml b/SCons/Environment.xml index 924872183a..dafc6491e3 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -1033,7 +1033,7 @@ either as separate arguments to the &f-Clean; method, or as a list. &f-Clean; -will also accept the return value of any of the &consenv; +will also accept the return value of the &consenv; Builder methods. Examples: @@ -1065,7 +1065,7 @@ Clean(['foo', 'bar'], 'something_else_to_clean') In this example, installing the project creates a subdirectory for the documentation. This statement causes the subdirectory to be removed -if the project is deinstalled. +if the project is uninstalled. Clean(docdir, os.path.join(docdir, projectname)) diff --git a/SCons/Script/Main.xml b/SCons/Script/Main.xml index 38d8dad2b5..810e8aae50 100644 --- a/SCons/Script/Main.xml +++ b/SCons/Script/Main.xml @@ -201,7 +201,7 @@ Future versions of &SCons; will likely forbid such usage. Allows setting options for SCons debug options. Currently, the only supported value is - json which sets the path to the json file created when + json which sets the path to the JSON file created when --debug=json is set. diff --git a/SCons/Tool/gs.xml b/SCons/Tool/gs.xml index b984fd3aa8..2e0f46e03b 100644 --- a/SCons/Tool/gs.xml +++ b/SCons/Tool/gs.xml @@ -27,7 +27,7 @@ This file is processed by the bin/SConsDoc.py module. -This Tool sets the required construction variables for working with +This tool sets the required construction variables for working with the Ghostscript software. It also registers an appropriate Action with the &b-link-PDF; Builder, such that the conversion from PS/EPS to PDF happens automatically for the TeX/LaTeX toolchain. diff --git a/SCons/Tool/install.xml b/SCons/Tool/install.xml index 3069cad512..cdb044b695 100644 --- a/SCons/Tool/install.xml +++ b/SCons/Tool/install.xml @@ -72,7 +72,7 @@ in this case. If the command line option is given, the target directory will be prefixed by the directory path specified. -This is useful to test installs without installing to +This is useful to test installation behavior without installing to a "live" location in the system. diff --git a/SCons/Tool/javac.xml b/SCons/Tool/javac.xml index af1fc31b47..c43470cf11 100644 --- a/SCons/Tool/javac.xml +++ b/SCons/Tool/javac.xml @@ -110,7 +110,7 @@ env.Java(target='classes', source=['File1.java', 'File2.java']) LANG environment variable to tell the compiler what encoding is used. For portability, it's best if the encoding is hard-coded, - so that the compile will work if it is done on a system + so that the compilation works when run on a system with a different encoding. diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml index c833d441a2..4510f71038 100644 --- a/SCons/Tool/ninja/ninja.xml +++ b/SCons/Tool/ninja/ninja.xml @@ -350,7 +350,7 @@ python -m pip install ninja Internal value used to specify the function to call with argument env to generate the list of files - which if changed would require the &ninja; build file to be regenerated. + which -- if changed -- would require the &ninja; build file to be regenerated. diff --git a/SCons/Tool/packaging/packaging.xml b/SCons/Tool/packaging/packaging.xml index 0c42462e39..6f1a8d8b5c 100644 --- a/SCons/Tool/packaging/packaging.xml +++ b/SCons/Tool/packaging/packaging.xml @@ -262,7 +262,7 @@ placed if applicable. The default value is &cv-NAME;-&cv-VERSION; Selects the package type to build when using the &b-link-Package; -builder. May be a string or list of strings. See the docuentation +builder. It may be a string or list of strings. See the documentation for the builder for the currently supported types. diff --git a/SCons/Tool/qt3.xml b/SCons/Tool/qt3.xml index a2762f7ca9..4c75a88b74 100644 --- a/SCons/Tool/qt3.xml +++ b/SCons/Tool/qt3.xml @@ -74,7 +74,7 @@ The &t-qt3; tool supports the following operations: Automatic moc file generation from header files. You do not have to specify moc files explicitly, the tool does it for you. However, there are a few preconditions to do so: Your header file must have -the same filebase as your implementation file and must stay in the same +the same basename as your implementation file and must stay in the same directory. It must have one of the suffixes .h, .hpp, diff --git a/SCons/Tool/tlib.xml b/SCons/Tool/tlib.xml index f186a177b3..1fb34ef38c 100644 --- a/SCons/Tool/tlib.xml +++ b/SCons/Tool/tlib.xml @@ -28,7 +28,7 @@ This file is processed by the bin/SConsDoc.py module. Sets construction variables for the Borland -tib library archiver. +tlib library archiver. diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 656d34c0b3..d6461851c9 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -2368,7 +2368,7 @@ is run with the &Python; option or from optimized &Python; (.pyo) modules. Note the "no-" prefix is part of the name of this warning. -Add an additional "-no" to disable. +Add another "-no" to disable. @@ -2383,7 +2383,7 @@ option is used. These warnings are enabled by default. Note the "no-" prefix is part of the name of this warning. -Add an additional "-no" to disable. +Add another "-no" to disable. @@ -2667,7 +2667,7 @@ one of the pre-defined platforms posix, sunos or win32), -or it may be a callable platform object +or a callable platform object returned by a call to &f-link-Platform; selecting a pre-defined platform, or it may be a user-supplied callable, diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml index 61dc1d8147..9278fa5caa 100644 --- a/doc/user/build-install.xml +++ b/doc/user/build-install.xml @@ -298,7 +298,7 @@ $ python /path/to/unpacked/scripts/scons.p of &Python; software). There is a setup.py file, but it is only tested and used for the automated procedure which prepares an &SCons; bundle for making a release on PyPI, - and even that is not guaranteed to work in future. + and even that is not guaranteed to work in the future.
diff --git a/doc/user/command-line.xml b/doc/user/command-line.xml index c0a3c0c55e..5245edf9b3 100644 --- a/doc/user/command-line.xml +++ b/doc/user/command-line.xml @@ -795,7 +795,7 @@ foo.in You may want to control various aspects of your build by allowing variable=value - values to be specified on the command line. + pairs to be specified on the command line. For example, suppose you want to be able to build a debug version of a program by running &SCons; as follows: diff --git a/doc/user/external.xml b/doc/user/external.xml index 4e021f6c3c..e462ef04f3 100644 --- a/doc/user/external.xml +++ b/doc/user/external.xml @@ -34,7 +34,7 @@ This file is processed by the bin/SConsDoc.py module. Sometimes a project needs to interact with other projects in various ways. For example, many open source projects make use of components from other open source projects, - and want to use those in their released form, not recode + and want to use those in their released form, not rewrite their builds into &SCons;. As another example, sometimes the flexibility and power of &SCons; is useful for managing the overall project, but developers might like faster incremental diff --git a/doc/user/hierarchy.xml b/doc/user/hierarchy.xml index 8c6b4ca6b3..9bce052e18 100644 --- a/doc/user/hierarchy.xml +++ b/doc/user/hierarchy.xml @@ -370,7 +370,7 @@ x in one pass, interpreting each in its local context, building up a tree of information, before starting to execute the needed builds in a second pass. - This is quite different than some other build tools + This is quite different from some other build tools which implement a hierarchical build by recursing. diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml index 4c1b14d545..19dab6f13c 100644 --- a/doc/user/less-simple.xml +++ b/doc/user/less-simple.xml @@ -301,7 +301,7 @@ Program('hello', ['hello.c']) Although &SCons; functions are forgiving about whether or not you use a string vs. a list for a single file name, - &Python; itself is more strict about + &Python; itself is stricter about treating lists and strings differently. So where &SCons; allows either a string or list: diff --git a/doc/user/sideeffect.xml b/doc/user/sideeffect.xml index cff8985b59..c1e2433863 100644 --- a/doc/user/sideeffect.xml +++ b/doc/user/sideeffect.xml @@ -47,7 +47,7 @@ This file is processed by the bin/SConsDoc.py module. will also put data into log, which is used as a source for the command to generate file2, but log is unknown to &SCons; on a clean - build: it neither exists, nor is it a target output by any builder. + build: it neither exists nor is it a target output by any builder. The SConscript uses &SideEffect; to inform &SCons; about the additional output file. From e431ee86b40f9a2c9bf1e088eb7cf782dba58842 Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Wed, 13 Nov 2024 10:44:15 -0600 Subject: [PATCH 185/386] Ruff/Mypy: Update excludes --- CHANGES.txt | 3 +++ RELEASE.txt | 2 ++ pyproject.toml | 31 ++++++++++++++++++++----------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7d6ef004d9..478b67d6d0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -76,6 +76,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Change the attempted conversion of a define expansion from using int() to a constant expression evaluation. + From Thaddeus Crews: + - Ruff/Mypy: Excluded items now synced. + From Alex James: - On Darwin, PermissionErrors are now handled while trying to access /etc/paths.d. This may occur if SCons is invoked in a sandboxed diff --git a/RELEASE.txt b/RELEASE.txt index e71f535ef3..ae1b90be55 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -177,6 +177,8 @@ DEVELOPMENT - List visible changes in the way SCons is developed +- Ruff/Mypy: Excluded items now synced. + Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text diff --git a/pyproject.toml b/pyproject.toml index cb824b7ba9..1e00257be9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,17 +73,15 @@ dist-dir = "build/dist" target-version = "py37" # Lowest python version supported extend-include = ["SConstruct", "SConscript"] extend-exclude = [ - "bench", - "bin", - "doc", - "src", - "template", - "test", - "testing", - "timings", - "SCons/Tool/docbook/docbook-xsl-1.76.1", - "bootstrap.py", - "runtest.py", + "bench/", + "bin/", + "doc/", + "src/", + "template/", + "test/", + "testing/", + "timings/", + "SCons/Tool/docbook/docbook-xsl-1.76.1/", ] [tool.ruff.format] @@ -99,3 +97,14 @@ quote-style = "preserve" # Equivalent to black's "skip-string-normalization" [tool.mypy] python_version = "3.8" +exclude = [ + "^bench/", + "^bin/", + "^doc/", + "^src/", + "^template/", + "^test/", + "^testing/", + "^timings/", + "^SCons/Tool/docbook/docbook-xsl-1.76.1/", +] From 37530ad7d18bbe192da134808194cdca1941dd74 Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Wed, 13 Nov 2024 11:05:42 -0600 Subject: [PATCH 186/386] Remove Python 3.6 support --- CHANGES.txt | 1 + RELEASE.txt | 2 ++ ReleaseConfig | 2 +- SCons/Action.py | 9 --------- SCons/ActionTests.py | 32 ++++---------------------------- SCons/Script/Main.py | 8 +------- doc/man/scons.xml | 6 +++++- pyproject.toml | 3 +-- runtest.py | 2 +- scripts/scons-configure-cache.py | 4 ++-- scripts/scons.py | 4 ++-- scripts/sconsign.py | 4 ++-- testing/framework/TestSCons.py | 2 +- 13 files changed, 23 insertions(+), 56 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 478b67d6d0..be8c900b9d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -77,6 +77,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER a constant expression evaluation. From Thaddeus Crews: + - Removed Python 3.6 support. - Ruff/Mypy: Excluded items now synced. From Alex James: diff --git a/RELEASE.txt b/RELEASE.txt index ae1b90be55..fc85751b68 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -29,6 +29,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - List modifications to existing features, where the previous behavior wouldn't actually be considered a bug +- Removed Python 3.6 support. + - Override environments, created when giving construction environment keyword arguments to Builder calls (or manually, through the undocumented Override method), were modified not to "leak" on item deletion. diff --git a/ReleaseConfig b/ReleaseConfig index f124879a04..3ec9df0973 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -37,7 +37,7 @@ version_tuple = (4, 8, 2, 'a', 0) # when that version is used. Python versions prior to deprecate_python_version # cause a warning to be issued (assuming it's not disabled). These values are # mandatory and must be present in the configuration file. -unsupported_python_version = (3, 6, 0) +unsupported_python_version = (3, 7, 0) deprecated_python_version = (3, 7, 0) # If release_date is (yyyy, mm, dd, hh, mm, ss), that is used as the release diff --git a/SCons/Action.py b/SCons/Action.py index 567f66cef4..3866d937bd 100644 --- a/SCons/Action.py +++ b/SCons/Action.py @@ -891,15 +891,6 @@ def scons_subproc_run(scons_env, *args, **kwargs) -> subprocess.CompletedProcess del kwargs['error'] kwargs['check'] = check - # TODO: Python version-compat stuff: remap/remove too-new args if needed - if 'text' in kwargs and sys.version_info < (3, 7): - kwargs['universal_newlines'] = kwargs.pop('text') - - if 'capture_output' in kwargs and sys.version_info < (3, 7): - capture_output = kwargs.pop('capture_output') - if capture_output: - kwargs['stdout'] = kwargs['stderr'] = PIPE - # Most SCons tools/tests expect not to fail on things like missing files. # check=True (or error="raise") means we're okay to take an exception; # else we catch the likely exception and construct a dummy diff --git a/SCons/ActionTests.py b/SCons/ActionTests.py index 39798809e9..c257d9f7f0 100644 --- a/SCons/ActionTests.py +++ b/SCons/ActionTests.py @@ -1541,8 +1541,6 @@ def LocalFunc() -> None: # Since the python bytecode has per version differences, we need different expected results per version func_matches = { - (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), - (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 9): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), @@ -1723,8 +1721,6 @@ def LocalFunc() -> None: pass func_matches = { - (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), - (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 9): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), @@ -1736,8 +1732,6 @@ def LocalFunc() -> None: } meth_matches = { - (3, 5): bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'), - (3, 6): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 7): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 8): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 9): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), @@ -1978,8 +1972,6 @@ def LocalFunc() -> None: pass func_matches = { - (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), - (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 9): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), @@ -2042,8 +2034,6 @@ def LocalFunc() -> None: pass matches = { - (3, 5): b'd\x00\x00S', - (3, 6): b'd\x00S\x00', (3, 7): b'd\x00S\x00', (3, 8): b'd\x00S\x00', (3, 9): b'd\x00S\x00', @@ -2246,8 +2236,6 @@ def func1(a, b, c): # we need different expected results per version # Note unlike the others, this result is a tuple, use assertIn expected = { - (3, 5): (bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()'),), - (3, 6): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),), (3, 7): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),), (3, 8): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),), (3, 9): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),), @@ -2275,12 +2263,6 @@ def test_object_contents(self) -> None: # Since the python bytecode has per version differences, # we need different expected results per version expected = { - (3, 5): bytearray( - b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}" - ), - (3, 6): bytearray( - b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" - ), (3, 7): bytearray( b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" ), @@ -2314,12 +2296,6 @@ def test_code_contents(self) -> None: # Since the python bytecode has per version differences, we need different expected results per version expected = { - (3, 5): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)' - ), - (3, 6): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)' - ), (3, 7): bytearray( b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)' ), @@ -2387,17 +2363,17 @@ def test_scons_subproc_run(self): {"text": True, "check": False}, ) - # 3.6: - sys.version_info = (3, 6, 2) + # 3.7: + sys.version_info = (3, 7, 2) with self.subTest(): self.assertEqual( scons_subproc_run(env, capture_output=True), - {"check": False, "stdout": PIPE, "stderr": PIPE}, + {"capture_output": True, "check": False}, ) with self.subTest(): self.assertEqual( scons_subproc_run(env, text=True), - {"check": False, "universal_newlines": True}, + {"check": False, "text": True}, ) with self.subTest(): self.assertEqual( diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 04b420a3bf..c7198397f5 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -66,7 +66,7 @@ from SCons import __version__ as SConsVersion # these define the range of versions SCons supports -minimum_python_version = (3, 6, 0) +minimum_python_version = (3, 7, 0) deprecated_python_version = (3, 7, 0) # ordered list of SConstruct names to look for if there is no -f flag @@ -1356,12 +1356,6 @@ def order(dependencies): # various print_* settings, tree_printer list, etc. BuildTask.options = options - is_pypy = platform.python_implementation() == 'PyPy' - # As of 3.7, python removed support for threadless platforms. - # See https://www.python.org/dev/peps/pep-0011/ - is_37_or_later = sys.version_info >= (3, 7) - # python_has_threads = sysconfig.get_config_var('WITH_THREAD') or is_pypy or is_37_or_later - # As of python 3.4 threading has a dummy_threading module for use when there is no threading # it's get_ident() will allways return -1, while real threading modules get_ident() will # always return a positive integer diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 022f44c536..8c9e1b425d 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -87,7 +87,7 @@ and a database of information about previous builds so details do not have to be recalculated each run. -&scons; requires &Python; 3.6 or later to run; +&scons; requires &Python; 3.7 or later to run; there should be no other dependencies or requirements, unless the experimental Ninja tool is used (requires the ninja package). @@ -104,6 +104,10 @@ in a future &SCons; release. The CPython project retired 3.6 in Sept 2021: . + +Changed in version NEXT_RELEASE: +support for &Python; 3.6 is removed. + You set up an &SCons; build by writing a script diff --git a/pyproject.toml b/pyproject.toml index 1e00257be9..c5e2120cfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires = ["setuptools"] [project] name = "SCons" description = "Open Source next-generation build tool." -requires-python = ">=3.6" +requires-python = ">=3.7" license = { text = "MIT" } readme = { file = "README-package.rst", content-type = "text/x-rst" } authors = [{ name = "William Deegan", email = "bill@baddogconsulting.com" }] @@ -16,7 +16,6 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", diff --git a/runtest.py b/runtest.py index 220b490333..9dfa9ef1c2 100755 --- a/runtest.py +++ b/runtest.py @@ -784,7 +784,7 @@ def run_test(t, io_lock=None, run_async=True): command_args = [] if debug: command_args.extend(['-m', debug]) - if args.devmode and sys.version_info >= (3, 7, 0): + if args.devmode: command_args.append('-X dev') command_args.append(t.path) if args.runner and t.path in unittests: diff --git a/scripts/scons-configure-cache.py b/scripts/scons-configure-cache.py index 2ae6b14aa5..c30859db14 100644 --- a/scripts/scons-configure-cache.py +++ b/scripts/scons-configure-cache.py @@ -49,9 +49,9 @@ import sys # python compatibility check -if sys.version_info < (3, 6, 0): +if sys.version_info < (3, 7, 0): msg = "scons: *** SCons version %s does not run under Python version %s.\n\ -Python >= 3.6.0 is required.\n" +Python >= 3.7.0 is required.\n" sys.stderr.write(msg % (__version__, sys.version.split()[0])) sys.exit(1) diff --git a/scripts/scons.py b/scripts/scons.py index efafdef803..9c5be9d484 100755 --- a/scripts/scons.py +++ b/scripts/scons.py @@ -42,9 +42,9 @@ import sys # Python compatibility check -if sys.version_info < (3, 6, 0): +if sys.version_info < (3, 7, 0): msg = "scons: *** SCons version %s does not run under Python version %s.\n\ -Python >= 3.6.0 is required.\n" +Python >= 3.7.0 is required.\n" sys.stderr.write(msg % (__version__, sys.version.split()[0])) sys.exit(1) diff --git a/scripts/sconsign.py b/scripts/sconsign.py index 95a9b96e67..a467ebafb5 100644 --- a/scripts/sconsign.py +++ b/scripts/sconsign.py @@ -41,9 +41,9 @@ import sys # python compatibility check -if sys.version_info < (3, 6, 0): +if sys.version_info < (3, 7, 0): msg = "scons: *** SCons version %s does not run under Python version %s.\n\ -Python >= 3.6.0 is required.\n" +Python >= 3.7.0 is required.\n" sys.stderr.write(msg % (__version__, sys.version.split()[0])) sys.exit(1) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 243be753a4..b2102e9707 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -59,7 +59,7 @@ default_version = '4.8.2ayyyymmdd' # TODO: these need to be hand-edited when there are changes -python_version_unsupported = (3, 6, 0) +python_version_unsupported = (3, 7, 0) python_version_deprecated = (3, 7, 0) python_version_supported_str = "3.7.0" # str of lowest non-deprecated Python From c2a8c0c0760cc4ca4288f3fc117c7db63d129668 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 12:45:02 -0800 Subject: [PATCH 187/386] [ci skip] Add CHANGES/RELEASE Blurb. Minor edit in ninja.xml --- CHANGES.txt | 3 +++ RELEASE.txt | 3 +++ SCons/Tool/ninja/ninja.xml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7d6ef004d9..aac00bd17e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -87,6 +87,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Keith F Prussing: - Added support for tracking beamer themes in the LaTeX scanner. + From rico-chet: + - Many grammatical and spelling fixes in the documentation. + From Mats Wichmann: - PackageVariable now does what the documentation always said it does if the variable is used on the command line with one of the enabling diff --git a/RELEASE.txt b/RELEASE.txt index e71f535ef3..aee76a99dc 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -172,6 +172,9 @@ DOCUMENTATION - Clarify documentation of Repository() in manpage and user guide. +- Many grammatical and spelling fixes in the documentation. + + DEVELOPMENT ----------- diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml index 4510f71038..f89687ef9c 100644 --- a/SCons/Tool/ninja/ninja.xml +++ b/SCons/Tool/ninja/ninja.xml @@ -350,7 +350,7 @@ python -m pip install ninja Internal value used to specify the function to call with argument env to generate the list of files - which -- if changed -- would require the &ninja; build file to be regenerated. + which, if changed, would require the &ninja; build file to be regenerated. From eb2c287dd14b4f9e2d191ce295db371297172f14 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 12:55:30 -0800 Subject: [PATCH 188/386] change look-up -> look up --- SCons/Defaults.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml index ce24b3b986..71c214d7b1 100644 --- a/SCons/Defaults.xml +++ b/SCons/Defaults.xml @@ -285,7 +285,7 @@ env = Environment(CPPPATH='#/include') -The directory look-up can also be forced using the +The directory look up can also be forced using the &f-link-Dir; function: @@ -548,7 +548,7 @@ directory names in &cv-LIBPATH; will be looked-up relative to the directory of the SConscript file 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: @@ -557,7 +557,7 @@ env = Environment(LIBPATH='#/libs') -The directory look-up can also be forced using the +The directory look up can also be forced using the &f-link-Dir; function: From cd73fab906e5d153bf6345c5376d4f3e08a47392 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 12:56:59 -0800 Subject: [PATCH 189/386] change to lookup --- SCons/Defaults.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SCons/Defaults.xml b/SCons/Defaults.xml index 71c214d7b1..4385390ca1 100644 --- a/SCons/Defaults.xml +++ b/SCons/Defaults.xml @@ -276,7 +276,7 @@ directory names in &cv-CPPPATH; will be looked-up relative to the directory of the SConscript file 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 lookup a directory relative to the root of the source tree, use the # prefix: @@ -285,7 +285,7 @@ env = Environment(CPPPATH='#/include') -The directory look up can also be forced using the +The directory lookup can also be forced using the &f-link-Dir; function: @@ -548,7 +548,7 @@ directory names in &cv-LIBPATH; will be looked-up relative to the directory of the SConscript file 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 lookup a directory relative to the root of the source tree, use the # prefix: @@ -557,7 +557,7 @@ env = Environment(LIBPATH='#/libs') -The directory look up can also be forced using the +The directory lookup can also be forced using the &f-link-Dir; function: From 3829f4551edebee3e42864b19e6332b24af3eb93 Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Wed, 13 Nov 2024 13:10:12 -0600 Subject: [PATCH 190/386] Integrate `from __future__ import annotations` --- CHANGES.txt | 4 ++ RELEASE.txt | 6 +++ SCons/Action.py | 86 +++++++++++++++--------------- SCons/ActionTests.py | 12 +++-- SCons/Builder.py | 7 +-- SCons/BuilderTests.py | 11 ++-- SCons/Defaults.py | 10 ++-- SCons/Environment.py | 20 ++++--- SCons/Errors.py | 10 ++-- SCons/Executor.py | 10 ++-- SCons/Node/FS.py | 7 +-- SCons/Node/FSTests.py | 14 +++-- SCons/Node/NodeTests.py | 10 ++-- SCons/Node/__init__.py | 9 ++-- SCons/SConf.py | 5 +- SCons/Script/Main.py | 11 ++-- SCons/Script/SConsOptions.py | 5 +- SCons/Script/SConscript.py | 7 +-- SCons/Subst.py | 7 +-- SCons/Tool/FortranCommon.py | 11 ++-- SCons/Tool/JavaCommon.py | 7 +-- SCons/Tool/__init__.py | 5 +- SCons/Tool/jar.py | 5 +- SCons/Tool/lex.py | 7 +-- SCons/Tool/ninja/Methods.py | 12 +++-- SCons/Tool/ninja/Utils.py | 11 ++-- SCons/Tool/yacc.py | 11 ++-- SCons/Util/UtilTests.py | 19 +++---- SCons/Util/__init__.py | 22 ++++---- SCons/Util/envs.py | 18 ++++--- SCons/Util/filelock.py | 11 ++-- SCons/Util/hashes.py | 3 +- SCons/Util/sctypes.py | 24 +++++---- SCons/Util/sctyping.py | 33 ------------ SCons/Variables/BoolVariable.py | 8 +-- SCons/Variables/EnumVariable.py | 10 ++-- SCons/Variables/ListVariable.py | 16 +++--- SCons/Variables/PackageVariable.py | 10 ++-- SCons/Variables/PathVariable.py | 7 +-- SCons/Variables/__init__.py | 26 ++++----- SCons/Warnings.py | 6 ++- pyproject.toml | 9 ++++ runtest.py | 10 ++-- test/Variables/PackageVariable.py | 5 +- testing/framework/TestCmd.py | 24 +++++---- testing/framework/TestCommon.py | 44 +++++++-------- testing/framework/TestSCons.py | 7 +-- 47 files changed, 351 insertions(+), 281 deletions(-) delete mode 100644 SCons/Util/sctyping.py diff --git a/CHANGES.txt b/CHANGES.txt index 478b67d6d0..808e2b96c9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -78,6 +78,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Thaddeus Crews: - Ruff/Mypy: Excluded items now synced. + - Ruff: Linter includes new rules - `FA`, `UP006`, `UP007`, and `UP037` - to + detect and upgrade legacy type-hint syntax. + - Removed "SCons.Util.sctyping.py", as the functionality can now be substituted + via top-level `from __future__ import annotations`. From Alex James: - On Darwin, PermissionErrors are now handled while trying to access diff --git a/RELEASE.txt b/RELEASE.txt index ae1b90be55..8ca51871b7 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -179,6 +179,12 @@ DEVELOPMENT - Ruff/Mypy: Excluded items now synced. +- Ruff: Linter includes new rules - `FA`, `UP006`, `UP007`, and `UP037` - to + detect and upgrade legacy type-hint syntax. + +- Removed "SCons.Util.sctyping.py", as the functionality can now be substituted + via top-level `from __future__ import annotations`. + Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text diff --git a/SCons/Action.py b/SCons/Action.py index 567f66cef4..6022b19b38 100644 --- a/SCons/Action.py +++ b/SCons/Action.py @@ -100,6 +100,8 @@ """ +from __future__ import annotations + import inspect import os import pickle @@ -109,7 +111,7 @@ from abc import ABC, abstractmethod from collections import OrderedDict from subprocess import DEVNULL, PIPE -from typing import List, Optional, Tuple +from typing import TYPE_CHECKING import SCons.Debug import SCons.Errors @@ -120,7 +122,9 @@ from SCons.Debug import logInstanceCreation from SCons.Subst import SUBST_CMD, SUBST_RAW, SUBST_SIG from SCons.Util import is_String, is_List -from SCons.Util.sctyping import ExecutorType + +if TYPE_CHECKING: + from SCons.Executor import Executor class _null: pass @@ -481,9 +485,7 @@ def _do_create_action(act, kw): return None -# TODO: from __future__ import annotations once we get to Python 3.7 base, -# to avoid quoting the defined-later classname -def _do_create_list_action(act, kw) -> "ListAction": +def _do_create_list_action(act, kw) -> ListAction: """A factory for list actions. Convert the input list *act* into Actions and then wrap them in a @@ -529,7 +531,7 @@ def __call__( show=_null, execute=_null, chdir=_null, - executor: Optional[ExecutorType] = None, + executor: Executor | None = None, ): raise NotImplementedError @@ -541,15 +543,15 @@ def no_batch_key(self, env, target, source): batch_key = no_batch_key - def genstring(self, target, source, env, executor: Optional[ExecutorType] = None) -> str: + def genstring(self, target, source, env, executor: Executor | None = None) -> str: return str(self) @abstractmethod - def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_presig(self, target, source, env, executor: Executor | None = None): raise NotImplementedError @abstractmethod - def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_implicit_deps(self, target, source, env, executor: Executor | None = None): raise NotImplementedError def get_contents(self, target, source, env): @@ -601,10 +603,10 @@ def presub_lines(self, env): self.presub_env = None # don't need this any more return lines - def get_varlist(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_varlist(self, target, source, env, executor: Executor | None = None): return self.varlist - def get_targets(self, env, executor: Optional[ExecutorType]): + def get_targets(self, env, executor: Executor | None): """ Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used by this action. @@ -658,7 +660,7 @@ def __call__(self, target, source, env, show=_null, execute=_null, chdir=_null, - executor: Optional[ExecutorType] = None): + executor: Executor | None = None): if not is_List(target): target = [target] if not is_List(source): @@ -742,10 +744,10 @@ def __call__(self, target, source, env, # an ABC like parent ActionBase, but things reach in and use it. It's # not just unittests or we could fix it up with a concrete subclass there. - def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_presig(self, target, source, env, executor: Executor | None = None): raise NotImplementedError - def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_implicit_deps(self, target, source, env, executor: Executor | None = None): raise NotImplementedError @@ -1010,7 +1012,7 @@ def __str__(self) -> str: return str(self.cmd_list) - def process(self, target, source, env, executor=None, overrides: Optional[dict] = None) -> Tuple[List, bool, bool]: + def process(self, target, source, env, executor: Executor | None = None, overrides: dict | None = None) -> tuple[list, bool, bool]: if executor: result = env.subst_list(self.cmd_list, SUBST_CMD, executor=executor, overrides=overrides) else: @@ -1031,7 +1033,7 @@ def process(self, target, source, env, executor=None, overrides: Optional[dict] pass return result, ignore, silent - def strfunction(self, target, source, env, executor: Optional[ExecutorType] = None, overrides: Optional[dict] = None) -> str: + def strfunction(self, target, source, env, executor: Executor | None = None, overrides: dict | None = None) -> str: if self.cmdstr is None: return None if self.cmdstr is not _null: @@ -1046,7 +1048,7 @@ def strfunction(self, target, source, env, executor: Optional[ExecutorType] = No return '' return _string_from_cmd_list(cmd_list[0]) - def execute(self, target, source, env, executor: Optional[ExecutorType] = None): + def execute(self, target, source, env, executor: Executor | None = None): """Execute a command action. This will handle lists of commands as well as individual commands, @@ -1108,7 +1110,7 @@ def execute(self, target, source, env, executor: Optional[ExecutorType] = None): command=cmd_line) return 0 - def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_presig(self, target, source, env, executor: Executor | None = None): """Return the signature contents of this action's command line. This strips $(-$) and everything in between the string, @@ -1123,7 +1125,7 @@ def get_presig(self, target, source, env, executor: Optional[ExecutorType] = Non return env.subst_target_source(cmd, SUBST_SIG, executor=executor) return env.subst_target_source(cmd, SUBST_SIG, target, source) - def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_implicit_deps(self, target, source, env, executor: Executor | None = None): """Return the implicit dependencies of this action's command line.""" icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) if is_String(icd) and icd[:1] == '$': @@ -1145,7 +1147,7 @@ def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType # lightweight dependency scanning. return self._get_implicit_deps_lightweight(target, source, env, executor) - def _get_implicit_deps_lightweight(self, target, source, env, executor: Optional[ExecutorType]): + def _get_implicit_deps_lightweight(self, target, source, env, executor: Executor | None): """ Lightweight dependency scanning involves only scanning the first entry in an action string, even if it contains &&. @@ -1166,7 +1168,7 @@ def _get_implicit_deps_lightweight(self, target, source, env, executor: Optional res.append(env.fs.File(d)) return res - def _get_implicit_deps_heavyweight(self, target, source, env, executor: Optional[ExecutorType], + def _get_implicit_deps_heavyweight(self, target, source, env, executor: Executor | None, icd_int): """ Heavyweight dependency scanning involves scanning more than just the @@ -1234,7 +1236,7 @@ def __init__(self, generator, kw) -> None: self.varlist = kw.get('varlist', ()) self.targets = kw.get('targets', '$TARGETS') - def _generate(self, target, source, env, for_signature, executor: Optional[ExecutorType] = None): + def _generate(self, target, source, env, for_signature, executor: Executor | None = None): # ensure that target is a list, to make it easier to write # generator functions: if not is_List(target): @@ -1265,11 +1267,11 @@ def __str__(self) -> str: def batch_key(self, env, target, source): return self._generate(target, source, env, 1).batch_key(env, target, source) - def genstring(self, target, source, env, executor: Optional[ExecutorType] = None) -> str: + def genstring(self, target, source, env, executor: Executor | None = None) -> str: return self._generate(target, source, env, 1, executor).genstring(target, source, env) def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, - show=_null, execute=_null, chdir=_null, executor: Optional[ExecutorType] = None): + show=_null, execute=_null, chdir=_null, executor: Executor | None = None): act = self._generate(target, source, env, 0, executor) if act is None: raise SCons.Errors.UserError( @@ -1281,7 +1283,7 @@ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, target, source, env, exitstatfunc, presub, show, execute, chdir, executor ) - def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_presig(self, target, source, env, executor: Executor | None = None): """Return the signature contents of this action's command line. This strips $(-$) and everything in between the string, @@ -1289,13 +1291,13 @@ def get_presig(self, target, source, env, executor: Optional[ExecutorType] = Non """ return self._generate(target, source, env, 1, executor).get_presig(target, source, env) - def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_implicit_deps(self, target, source, env, executor: Executor | None = None): return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env) - def get_varlist(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_varlist(self, target, source, env, executor: Executor | None = None): return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor) - def get_targets(self, env, executor: Optional[ExecutorType]): + def get_targets(self, env, executor: Executor | None): return self._generate(None, None, env, 1, executor).get_targets(env, executor) @@ -1341,22 +1343,22 @@ def _generate_cache(self, env): raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) return gen_cmd - def _generate(self, target, source, env, for_signature, executor: Optional[ExecutorType] = None): + def _generate(self, target, source, env, for_signature, executor: Executor | None = None): return self._generate_cache(env) def __call__(self, target, source, env, *args, **kw): c = self.get_parent_class(env) return c.__call__(self, target, source, env, *args, **kw) - def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_presig(self, target, source, env, executor: Executor | None = None): c = self.get_parent_class(env) return c.get_presig(self, target, source, env) - def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_implicit_deps(self, target, source, env, executor: Executor | None = None): c = self.get_parent_class(env) return c.get_implicit_deps(self, target, source, env) - def get_varlist(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_varlist(self, target, source, env, executor: Executor | None = None): c = self.get_parent_class(env) return c.get_varlist(self, target, source, env, executor) @@ -1389,7 +1391,7 @@ def function_name(self): except AttributeError: return "unknown_python_function" - def strfunction(self, target, source, env, executor: Optional[ExecutorType] = None): + def strfunction(self, target, source, env, executor: Executor | None = None): if self.cmdstr is None: return None if self.cmdstr is not _null: @@ -1430,7 +1432,7 @@ def __str__(self) -> str: return str(self.execfunction) return "%s(target, source, env)" % name - def execute(self, target, source, env, executor: Optional[ExecutorType] = None): + def execute(self, target, source, env, executor: Executor | None = None): exc_info = (None,None,None) try: if executor: @@ -1461,14 +1463,14 @@ def execute(self, target, source, env, executor: Optional[ExecutorType] = None): # more information about this issue. del exc_info - def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_presig(self, target, source, env, executor: Executor | None = None): """Return the signature contents of this callable action.""" try: return self.gc(target, source, env) except AttributeError: return self.funccontents - def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_implicit_deps(self, target, source, env, executor: Executor | None = None): return [] class ListAction(ActionBase): @@ -1485,7 +1487,7 @@ def list_of_actions(x): self.varlist = () self.targets = '$TARGETS' - def genstring(self, target, source, env, executor: Optional[ExecutorType] = None) -> str: + def genstring(self, target, source, env, executor: Executor | None = None) -> str: return '\n'.join([a.genstring(target, source, env) for a in self.list]) def __str__(self) -> str: @@ -1495,7 +1497,7 @@ def presub_lines(self, env): return SCons.Util.flatten_sequence( [a.presub_lines(env) for a in self.list]) - def get_presig(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_presig(self, target, source, env, executor: Executor | None = None): """Return the signature contents of this action list. Simple concatenation of the signatures of the elements. @@ -1503,7 +1505,7 @@ def get_presig(self, target, source, env, executor: Optional[ExecutorType] = Non return b"".join([bytes(x.get_contents(target, source, env)) for x in self.list]) def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, - show=_null, execute=_null, chdir=_null, executor: Optional[ExecutorType] = None): + show=_null, execute=_null, chdir=_null, executor: Executor | None = None): if executor: target = executor.get_all_targets() source = executor.get_all_sources() @@ -1514,13 +1516,13 @@ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, return stat return 0 - def get_implicit_deps(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_implicit_deps(self, target, source, env, executor: Executor | None = None): result = [] for act in self.list: result.extend(act.get_implicit_deps(target, source, env)) return result - def get_varlist(self, target, source, env, executor: Optional[ExecutorType] = None): + def get_varlist(self, target, source, env, executor: Executor | None = None): result = OrderedDict() for act in self.list: for var in act.get_varlist(target, source, env, executor): @@ -1586,7 +1588,7 @@ def subst_kw(self, target, source, env): kw[key] = self.subst(self.kw[key], target, source, env) return kw - def __call__(self, target, source, env, executor: Optional[ExecutorType] = None): + def __call__(self, target, source, env, executor: Executor | None = None): args = self.subst_args(target, source, env) kw = self.subst_kw(target, source, env) return self.parent.actfunc(*args, **kw) diff --git a/SCons/ActionTests.py b/SCons/ActionTests.py index 39798809e9..497de1869d 100644 --- a/SCons/ActionTests.py +++ b/SCons/ActionTests.py @@ -27,6 +27,8 @@ # contents, so try to minimize changes by defining them here, before we # even import anything. +from __future__ import annotations + def GlobalFunc() -> None: pass @@ -43,13 +45,15 @@ def __call__(self) -> None: import unittest from unittest import mock from subprocess import PIPE -from typing import Optional +from typing import TYPE_CHECKING import SCons.Action import SCons.Environment import SCons.Errors from SCons.Action import scons_subproc_run -from SCons.Util.sctyping import ExecutorType + +if TYPE_CHECKING: + from SCons.Executor import Executor import TestCmd @@ -1701,11 +1705,11 @@ def __call__(self, target, source, env) -> int: c = test.read(outfile, 'r') assert c == "class1b\n", c - def build_it(target, source, env, executor: Optional[ExecutorType] = None, self=self) -> int: + def build_it(target, source, env, executor: Executor | None = None, self=self) -> int: self.build_it = 1 return 0 - def string_it(target, source, env, executor: Optional[ExecutorType] = None, self=self): + def string_it(target, source, env, executor: Executor | None = None, self=self): self.string_it = 1 return None diff --git a/SCons/Builder.py b/SCons/Builder.py index 3efcc8271d..c81d104143 100644 --- a/SCons/Builder.py +++ b/SCons/Builder.py @@ -99,10 +99,11 @@ """ +from __future__ import annotations + import os from collections import UserDict, UserList from contextlib import suppress -from typing import Optional import SCons.Action import SCons.Debug @@ -112,7 +113,7 @@ import SCons.Warnings from SCons.Debug import logInstanceCreation from SCons.Errors import InternalError, UserError -from SCons.Util.sctyping import ExecutorType +from SCons.Executor import Executor class _Null: pass @@ -591,7 +592,7 @@ def _execute(self, env, target, source, overwarn={}, executor_kw={}): # build this particular list of targets from this particular list of # sources. - executor: Optional[ExecutorType] = None + executor: Executor | None = None key = None if self.multi: diff --git a/SCons/BuilderTests.py b/SCons/BuilderTests.py index b66f52439e..adfa648280 100644 --- a/SCons/BuilderTests.py +++ b/SCons/BuilderTests.py @@ -21,6 +21,8 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import annotations + import SCons.compat # Define a null function for use as a builder action. @@ -31,6 +33,7 @@ def Func() -> None: pass from collections import UserList +from typing import TYPE_CHECKING import io import os.path import re @@ -45,7 +48,9 @@ def Func() -> None: import SCons.Errors import SCons.Subst import SCons.Util -from SCons.Util.sctyping import ExecutorType + +if TYPE_CHECKING: + from SCons.Executor import Executor sys.stdout = io.StringIO() @@ -185,9 +190,9 @@ def generate_build_env(self, env): return env def get_build_env(self): return self.executor.get_build_env() - def set_executor(self, executor: ExecutorType) -> None: + def set_executor(self, executor: Executor) -> None: self.executor = executor - def get_executor(self, create: int=1) -> ExecutorType: + def get_executor(self, create: int=1) -> Executor: return self.executor class MyNode(MyNode_without_target_from_source): diff --git a/SCons/Defaults.py b/SCons/Defaults.py index a5d49fc76f..d971d060f6 100644 --- a/SCons/Defaults.py +++ b/SCons/Defaults.py @@ -31,12 +31,14 @@ from distutils.msvccompiler. """ +from __future__ import annotations + import os import shutil import stat import sys import time -from typing import List, Callable +from typing import Callable import SCons.Action import SCons.Builder @@ -467,8 +469,8 @@ def _stripixes( prefix: str, items, suffix: str, - stripprefixes: List[str], - stripsuffixes: List[str], + stripprefixes: list[str], + stripsuffixes: list[str], env, literal_prefix: str = "", c: Callable[[list], list] = None, @@ -547,7 +549,7 @@ def _stripixes( return c(prefix, stripped, suffix, env) -def processDefines(defs) -> List[str]: +def processDefines(defs) -> list[str]: """Return list of strings for preprocessor defines from *defs*. Resolves the different forms ``CPPDEFINES`` can be assembled in: diff --git a/SCons/Environment.py b/SCons/Environment.py index 0c14468406..62926f56a5 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -30,6 +30,8 @@ are construction variables used to initialize the Environment. """ +from __future__ import annotations + import copy import os import sys @@ -37,7 +39,7 @@ import shlex from collections import UserDict, UserList, deque from subprocess import PIPE, DEVNULL -from typing import Callable, Collection, Optional, Sequence, Union +from typing import TYPE_CHECKING, Callable, Collection, Sequence import SCons.Action import SCons.Builder @@ -76,7 +78,9 @@ to_String_for_subst, uniquer_hashables, ) -from SCons.Util.sctyping import ExecutorType + +if TYPE_CHECKING: + from SCons.Executor import Executor class _Null: pass @@ -698,7 +702,7 @@ def gvars(self): def lvars(self): return {} - def subst(self, string, raw: int=0, target=None, source=None, conv=None, executor: Optional[ExecutorType] = None, overrides: Optional[dict] = None): + def subst(self, string, raw: int=0, target=None, source=None, conv=None, executor: Executor | None = None, overrides: dict | None = None): """Recursively interpolates construction variables from the Environment into the specified string, returning the expanded result. Construction variables are specified by a $ prefix @@ -724,7 +728,7 @@ def subst_kw(self, kw, raw: int=0, target=None, source=None): nkw[k] = v return nkw - def subst_list(self, string, raw: int=0, target=None, source=None, conv=None, executor: Optional[ExecutorType] = None, overrides: Optional[dict] = None): + def subst_list(self, string, raw: int=0, target=None, source=None, conv=None, executor: Executor | None = None, overrides: dict | None = None): """Calls through to SCons.Subst.scons_subst_list(). See the documentation for that function. @@ -901,7 +905,7 @@ def ParseFlags(self, *flags) -> dict: 'RPATH' : [], } - def do_parse(arg: Union[str, Sequence]) -> None: + def do_parse(arg: str | Sequence) -> None: if not arg: return @@ -1798,7 +1802,7 @@ def default(self, obj): raise ValueError("Unsupported serialization format: %s." % fmt) - def FindIxes(self, paths: Sequence[str], prefix: str, suffix: str) -> Optional[str]: + def FindIxes(self, paths: Sequence[str], prefix: str, suffix: str) -> str | None: """Search *paths* for a path that has *prefix* and *suffix*. Returns on first match. @@ -2079,7 +2083,7 @@ def _find_toolpath_dir(self, tp): return self.fs.Dir(self.subst(tp)).srcnode().get_abspath() def Tool( - self, tool: Union[str, Callable], toolpath: Optional[Collection[str]] = None, **kwargs + self, tool: str | Callable, toolpath: Collection[str] | None = None, **kwargs ) -> Callable: """Find and run tool module *tool*. @@ -2615,7 +2619,7 @@ class OverrideEnvironment(Base): ``OverrideEnvironment``. """ - def __init__(self, subject, overrides: Optional[dict] = None) -> None: + def __init__(self, subject, overrides: dict | None = None) -> None: if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.OverrideEnvironment') overrides = {} if overrides is None else overrides # set these directly via __dict__ to avoid trapping by __setattr__ diff --git a/SCons/Errors.py b/SCons/Errors.py index a2efc97088..af77971032 100644 --- a/SCons/Errors.py +++ b/SCons/Errors.py @@ -26,11 +26,15 @@ Used to handle internal and user errors in SCons. """ +from __future__ import annotations + import shutil -from typing import Optional +from typing import TYPE_CHECKING from SCons.Util.sctypes import to_String, is_String -from SCons.Util.sctyping import ExecutorType + +if TYPE_CHECKING: + from SCons.Executor import Executor # Note that not all Errors are defined here, some are at the point of use @@ -75,7 +79,7 @@ class BuildError(Exception): def __init__(self, node=None, errstr: str="Unknown error", status: int=2, exitstatus: int=2, - filename=None, executor: Optional[ExecutorType] = None, action=None, command=None, + filename=None, executor: Executor | None = None, action=None, command=None, exc_info=(None, None, None)) -> None: # py3: errstr should be string and not bytes. diff --git a/SCons/Executor.py b/SCons/Executor.py index 1b054b4ba8..53eb5cbb2f 100644 --- a/SCons/Executor.py +++ b/SCons/Executor.py @@ -23,8 +23,9 @@ """Execute actions with specific lists of target and source Nodes.""" +from __future__ import annotations + import collections -from typing import Dict import SCons.Errors import SCons.Memoize @@ -32,7 +33,6 @@ from SCons.compat import NoSlotsPyPy import SCons.Debug from SCons.Debug import logInstanceCreation -from SCons.Util.sctyping import ExecutorType class Batch: """Remembers exact association between targets @@ -550,12 +550,12 @@ def get_implicit_deps(self): -_batch_executors: Dict[str, ExecutorType] = {} +_batch_executors: dict[str, Executor] = {} -def GetBatchExecutor(key: str) -> ExecutorType: +def GetBatchExecutor(key: str) -> Executor: return _batch_executors[key] -def AddBatchExecutor(key: str, executor: ExecutorType) -> None: +def AddBatchExecutor(key: str, executor: Executor) -> None: assert key not in _batch_executors _batch_executors[key] = executor diff --git a/SCons/Node/FS.py b/SCons/Node/FS.py index 3cd7720c0f..bdecffcfbd 100644 --- a/SCons/Node/FS.py +++ b/SCons/Node/FS.py @@ -30,6 +30,8 @@ that can be used by scripts or modules looking for the canonical default. """ +from __future__ import annotations + import codecs import fnmatch import importlib.util @@ -40,7 +42,6 @@ import sys import time from itertools import chain -from typing import Optional import SCons.Action import SCons.Debug @@ -1492,7 +1493,7 @@ def Repository(self, *dirs) -> None: d = self.Dir(d) self.Top.addRepository(d) - def PyPackageDir(self, modulename) -> Optional[Dir]: + def PyPackageDir(self, modulename) -> Dir | None: r"""Locate the directory of Python module *modulename*. For example 'SCons' might resolve to @@ -3190,7 +3191,7 @@ def exists(self): # SIGNATURE SUBSYSTEM # - def get_max_drift_csig(self) -> Optional[str]: + def get_max_drift_csig(self) -> str | None: """ Returns the content signature currently stored for this node if it's been unmodified longer than the max_drift value, or the diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py index 83ceef28c7..9ae8c03e10 100644 --- a/SCons/Node/FSTests.py +++ b/SCons/Node/FSTests.py @@ -21,6 +21,8 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import annotations + import SCons.compat import os import os.path @@ -29,7 +31,7 @@ import unittest import shutil import stat -from typing import Optional +from typing import TYPE_CHECKING from TestCmd import TestCmd, IS_WINDOWS, IS_ROOT @@ -38,7 +40,9 @@ import SCons.Util import SCons.Warnings import SCons.Environment -from SCons.Util.sctyping import ExecutorType + +if TYPE_CHECKING: + from SCons.Executor import Executor built_it = None @@ -320,7 +324,7 @@ class MkdirAction(Action): def __init__(self, dir_made) -> None: self.dir_made = dir_made - def __call__(self, target, source, env, executor: Optional[ExecutorType] = None) -> None: + def __call__(self, target, source, env, executor: Executor | None = None) -> None: if executor: target = executor.get_all_targets() source = executor.get_all_sources() @@ -2491,7 +2495,7 @@ def collect(self, args): result += a return result - def signature(self, executor: ExecutorType): + def signature(self, executor: Executor): return self.val + 222 self.module = M(val) @@ -3582,7 +3586,7 @@ class MkdirAction(Action): def __init__(self, dir_made) -> None: self.dir_made = dir_made - def __call__(self, target, source, env, executor: Optional[ExecutorType] = None) -> None: + def __call__(self, target, source, env, executor: Executor | None = None) -> None: if executor: target = executor.get_all_targets() source = executor.get_all_sources() diff --git a/SCons/Node/NodeTests.py b/SCons/Node/NodeTests.py index 70c8551b13..6c7437d600 100644 --- a/SCons/Node/NodeTests.py +++ b/SCons/Node/NodeTests.py @@ -21,17 +21,21 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import annotations + import SCons.compat import collections import re import unittest -from typing import Optional +from typing import TYPE_CHECKING import SCons.Errors import SCons.Node import SCons.Util -from SCons.Util.sctyping import ExecutorType + +if TYPE_CHECKING: + from SCons.Executor import Executor built_it = None @@ -64,7 +68,7 @@ class MyAction(MyActionBase): def __init__(self) -> None: self.order = 0 - def __call__(self, target, source, env, executor: Optional[ExecutorType] = None) -> int: + def __call__(self, target, source, env, executor: Executor | None = None) -> int: global built_it, built_target, built_source, built_args, built_order if executor: target = executor.get_all_targets() diff --git a/SCons/Node/__init__.py b/SCons/Node/__init__.py index 00bf4ac3b1..8ae991ecf7 100644 --- a/SCons/Node/__init__.py +++ b/SCons/Node/__init__.py @@ -40,18 +40,19 @@ """ +from __future__ import annotations + import collections import copy from itertools import chain, zip_longest -from typing import Optional import SCons.Debug import SCons.Executor import SCons.Memoize from SCons.compat import NoSlotsPyPy from SCons.Debug import logInstanceCreation, Trace +from SCons.Executor import Executor from SCons.Util import hash_signature, is_List, UniqueList, render_tree -from SCons.Util.sctyping import ExecutorType print_duplicate = 0 @@ -636,11 +637,11 @@ def get_build_scanner_path(self, scanner): """Fetch the appropriate scanner path for this node.""" return self.get_executor().get_build_scanner_path(scanner) - def set_executor(self, executor: ExecutorType) -> None: + def set_executor(self, executor: Executor) -> None: """Set the action executor for this node.""" self.executor = executor - def get_executor(self, create: int=1) -> ExecutorType: + def get_executor(self, create: int=1) -> Executor: """Fetch the action executor for this node. Create one if there isn't already one, and requested to do so.""" try: diff --git a/SCons/SConf.py b/SCons/SConf.py index d2e09be34e..c22355665c 100644 --- a/SCons/SConf.py +++ b/SCons/SConf.py @@ -31,6 +31,8 @@ libraries are installed, if some command line options are supported etc. """ +from __future__ import annotations + import SCons.compat import atexit @@ -39,7 +41,6 @@ import re import sys import traceback -from typing import Tuple import SCons.Action import SCons.Builder @@ -265,7 +266,7 @@ def failed(self): sys.excepthook(*self.exc_info()) return SCons.Taskmaster.Task.failed(self) - def collect_node_states(self) -> Tuple[bool, bool, bool]: + def collect_node_states(self) -> tuple[bool, bool, bool]: # returns (is_up_to_date, cached_error, cachable) # where is_up_to_date is True if the node(s) are up_to_date # cached_error is True if the node(s) are up_to_date, but the diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 04b420a3bf..7f04d00243 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -31,6 +31,8 @@ it goes here. """ +from __future__ import annotations + import SCons.compat import importlib.util @@ -42,7 +44,6 @@ import traceback import platform import threading -from typing import Optional, List, TYPE_CHECKING import SCons.CacheDir import SCons.Debug @@ -552,7 +553,7 @@ def SetOption(name: str, value): """Set the value of an option - Public API.""" return OptionsParser.values.set_option(name, value) -def DebugOptions(json: Optional[str] = None) -> None: +def DebugOptions(json: str | None = None) -> None: """Specify options to SCons debug logic - Public API. Currently only *json* is supported, which changes the JSON file @@ -681,8 +682,8 @@ def _scons_internal_error() -> None: sys.exit(2) def _SConstruct_exists( - dirname: str, repositories: List[str], filelist: List[str] -) -> Optional[str]: + dirname: str, repositories: list[str], filelist: list[str] +) -> str | None: """Check that an SConstruct file exists in a directory. Arguments: @@ -1424,7 +1425,7 @@ def _exec_main(parser, values) -> None: class SConsPdb(pdb.Pdb): """Specialization of Pdb to help find SConscript files.""" - def lookupmodule(self, filename: str) -> Optional[str]: + def lookupmodule(self, filename: str) -> str | None: """Helper function for break/clear parsing -- SCons version. Translates (possibly incomplete) file or module name diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index 08531814f5..ef27b70669 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -21,13 +21,14 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import annotations + import gettext import optparse import re import shutil import sys import textwrap -from typing import Optional import SCons.Node.FS import SCons.Platform.virtualenv @@ -318,7 +319,7 @@ class SConsBadOptionError(optparse.BadOptionError): """ # TODO why is 'parser' needed? Not called in current code base. - def __init__(self, opt_str: str, parser: Optional["SConsOptionParser"] = None) -> None: + def __init__(self, opt_str: str, parser: SConsOptionParser | None = None) -> None: self.opt_str = opt_str self.parser = parser diff --git a/SCons/Script/SConscript.py b/SCons/Script/SConscript.py index a2ef3b9d57..7cc9bea5af 100644 --- a/SCons/Script/SConscript.py +++ b/SCons/Script/SConscript.py @@ -23,6 +23,8 @@ """This module defines the Python API provided to SConscript files.""" +from __future__ import annotations + import SCons import SCons.Action import SCons.Builder @@ -45,7 +47,6 @@ import sys import traceback import time -from typing import Tuple class SConscriptReturn(Exception): pass @@ -386,7 +387,7 @@ class SConsEnvironment(SCons.Environment.Base): # Private methods of an SConsEnvironment. # @staticmethod - def _get_major_minor_revision(version_string: str) -> Tuple[int, int, int]: + def _get_major_minor_revision(version_string: str) -> tuple[int, int, int]: """Split a version string into major, minor and (optionally) revision parts. @@ -485,7 +486,7 @@ def Default(self, *targets) -> None: SCons.Script._Set_Default_Targets(self, targets) @staticmethod - def GetSConsVersion() -> Tuple[int, int, int]: + def GetSConsVersion() -> tuple[int, int, int]: """Return the current SCons version. .. versionadded:: 4.8.0 diff --git a/SCons/Subst.py b/SCons/Subst.py index b04ebe50cd..4d6b249c6c 100644 --- a/SCons/Subst.py +++ b/SCons/Subst.py @@ -23,10 +23,11 @@ """SCons string substitution.""" +from __future__ import annotations + import collections import re from inspect import signature, Parameter -from typing import Optional import SCons.Errors from SCons.Util import is_String, is_Sequence @@ -807,7 +808,7 @@ def _remove_list(list): _space_sep = re.compile(r'[\t ]+(?![^{]*})') -def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None, overrides: Optional[dict] = None): +def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None, overrides: dict | None = None): """Expand a string or list containing construction variable substitutions. @@ -889,7 +890,7 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={ return result -def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None, overrides: Optional[dict] = None): +def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None, overrides: dict | None = None): """Substitute construction variables in a string (or list or other object) and separate the arguments into a command list. diff --git a/SCons/Tool/FortranCommon.py b/SCons/Tool/FortranCommon.py index f221d15ec7..dc9dcddf6e 100644 --- a/SCons/Tool/FortranCommon.py +++ b/SCons/Tool/FortranCommon.py @@ -23,9 +23,10 @@ """Routines for setting up Fortran, common to all dialects.""" +from __future__ import annotations + import re import os.path -from typing import Tuple, List import SCons.Scanner.Fortran import SCons.Tool @@ -96,7 +97,7 @@ def ShFortranEmitter(target, source, env) -> Tuple: return SharedObjectEmitter(target, source, env) -def ComputeFortranSuffixes(suffixes: List[str], ppsuffixes: List[str]) -> None: +def ComputeFortranSuffixes(suffixes: list[str], ppsuffixes: list[str]) -> None: """Update the suffix lists to reflect the platform requirements. If upper-cased suffixes can be distinguished from lower, those are @@ -119,7 +120,7 @@ def ComputeFortranSuffixes(suffixes: List[str], ppsuffixes: List[str]) -> None: def CreateDialectActions( dialect: str, -) -> Tuple[CommandAction, CommandAction, CommandAction, CommandAction]: +) -> tuple[CommandAction, CommandAction, CommandAction, CommandAction]: """Create dialect specific actions.""" CompAction = Action(f'${dialect}COM ', cmdstr=f'${dialect}COMSTR') CompPPAction = Action(f'${dialect}PPCOM ', cmdstr=f'${dialect}PPCOMSTR') @@ -131,8 +132,8 @@ def CreateDialectActions( def DialectAddToEnv( env, dialect: str, - suffixes: List[str], - ppsuffixes: List[str], + suffixes: list[str], + ppsuffixes: list[str], support_mods: bool = False, ) -> None: """Add dialect specific construction variables. diff --git a/SCons/Tool/JavaCommon.py b/SCons/Tool/JavaCommon.py index c7e62b88ce..0bcb0eaa5e 100644 --- a/SCons/Tool/JavaCommon.py +++ b/SCons/Tool/JavaCommon.py @@ -23,11 +23,12 @@ """Common routines for processing Java. """ +from __future__ import annotations + import os import re import glob from pathlib import Path -from typing import List import SCons.Util @@ -491,7 +492,7 @@ def parse_java_file(fn, version=default_java_version): return os.path.split(fn) -def get_java_install_dirs(platform, version=None) -> List[str]: +def get_java_install_dirs(platform, version=None) -> list[str]: """ Find possible java jdk installation directories. Returns a list for use as `default_paths` when looking up actual @@ -540,7 +541,7 @@ def win32getvnum(java): return [] -def get_java_include_paths(env, javac, version) -> List[str]: +def get_java_include_paths(env, javac, version) -> list[str]: """Find java include paths for JNI building. Cannot be called in isolation - `javac` refers to an already detected diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py index faa92a78b2..a7bc927ebc 100644 --- a/SCons/Tool/__init__.py +++ b/SCons/Tool/__init__.py @@ -33,10 +33,11 @@ tool specifications. """ +from __future__ import annotations + import sys import os import importlib.util -from typing import Optional import SCons.Builder import SCons.Errors @@ -824,7 +825,7 @@ def tool_list(platform, env): return [x for x in tools if x] -def find_program_path(env, key_program, default_paths=None, add_path: bool=False) -> Optional[str]: +def find_program_path(env, key_program, default_paths=None, add_path: bool=False) -> str | None: """ Find the location of a tool using various means. diff --git a/SCons/Tool/jar.py b/SCons/Tool/jar.py index 1967294f08..13bdca0518 100644 --- a/SCons/Tool/jar.py +++ b/SCons/Tool/jar.py @@ -28,8 +28,9 @@ selection method. """ +from __future__ import annotations + import os -from typing import List import SCons.Node import SCons.Node.FS @@ -41,7 +42,7 @@ from SCons.Tool.JavaCommon import get_java_install_dirs -def jarSources(target, source, env, for_signature) -> List[str]: +def jarSources(target, source, env, for_signature) -> list[str]: """Only include sources that are not a manifest file.""" try: env['JARCHDIR'] diff --git a/SCons/Tool/lex.py b/SCons/Tool/lex.py index 527f91c294..5e77ea692d 100644 --- a/SCons/Tool/lex.py +++ b/SCons/Tool/lex.py @@ -31,9 +31,10 @@ selection method. """ +from __future__ import annotations + import os.path import sys -from typing import Optional import SCons.Action import SCons.Tool @@ -95,7 +96,7 @@ def lexEmitter(target, source, env) -> tuple: return target, source -def get_lex_path(env, append_paths: bool=False) -> Optional[str]: +def get_lex_path(env, append_paths: bool=False) -> str | None: """ Returns the path to the lex tool, searching several possible names. @@ -162,7 +163,7 @@ def generate(env) -> None: env['_LEX_TABLES'] = '${LEX_TABLES_FILE and "--tables-file=" + str(LEX_TABLES_FILE)}' -def exists(env) -> Optional[str]: +def exists(env) -> str | None: if sys.platform == 'win32': return get_lex_path(env) else: diff --git a/SCons/Tool/ninja/Methods.py b/SCons/Tool/ninja/Methods.py index 5c56e49085..ff006c072e 100644 --- a/SCons/Tool/ninja/Methods.py +++ b/SCons/Tool/ninja/Methods.py @@ -21,10 +21,12 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import annotations + import os import shlex import textwrap -from typing import Optional +from typing import TYPE_CHECKING import SCons from SCons.Subst import SUBST_CMD @@ -32,7 +34,9 @@ from SCons.Tool.ninja.Globals import __NINJA_RULE_MAPPING from SCons.Tool.ninja.Utils import get_targets_sources, get_dependencies, get_order_only, get_outputs, get_inputs, \ get_rule, get_path, generate_command, get_command_env, get_comstr -from SCons.Util.sctyping import ExecutorType + +if TYPE_CHECKING: + from SCons.Executor import Executor def register_custom_handler(env, name, handler) -> None: @@ -78,7 +82,7 @@ def set_build_node_callback(env, node, callback) -> None: node.attributes.ninja_build_callback = callback -def get_generic_shell_command(env, node, action, targets, sources, executor: Optional[ExecutorType] = None): +def get_generic_shell_command(env, node, action, targets, sources, executor: Executor | None = None): return ( "GENERATED_CMD", { @@ -231,7 +235,7 @@ def gen_get_response_file_command(env, rule, tool, tool_is_dynamic: bool=False, if "$" in tool: tool_is_dynamic = True - def get_response_file_command(env, node, action, targets, sources, executor: Optional[ExecutorType] = None): + def get_response_file_command(env, node, action, targets, sources, executor: Executor | None = None): if hasattr(action, "process"): cmd_list, _, _ = action.process(targets, sources, env, executor=executor) cmd_list = [str(c).replace("$", "$$") for c in cmd_list[0]] diff --git a/SCons/Tool/ninja/Utils.py b/SCons/Tool/ninja/Utils.py index cdce92895c..24d439ef5e 100644 --- a/SCons/Tool/ninja/Utils.py +++ b/SCons/Tool/ninja/Utils.py @@ -20,17 +20,22 @@ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from __future__ import annotations + import os import shutil from os.path import join as joinpath from collections import OrderedDict -from typing import Optional +from typing import TYPE_CHECKING import SCons from SCons.Action import get_default_ENV, _string_from_cmd_list from SCons.Script import AddOption from SCons.Util import is_List, flatten_sequence -from SCons.Util.sctyping import ExecutorType + +if TYPE_CHECKING: + from SCons.Executor import Executor class NinjaExperimentalWarning(SCons.Warnings.WarningOnByDefault): pass @@ -349,7 +354,7 @@ def get_comstr(env, action, targets, sources): return action.genstring(targets, sources, env) -def generate_command(env, node, action, targets, sources, executor: Optional[ExecutorType] = None): +def generate_command(env, node, action, targets, sources, executor: Executor | None = None): # Actions like CommandAction have a method called process that is # used by SCons to generate the cmd_line they need to run. So # check if it's a thing like CommandAction and call it if we can. diff --git a/SCons/Tool/yacc.py b/SCons/Tool/yacc.py index 7a4ddfc54e..bfd82f6053 100644 --- a/SCons/Tool/yacc.py +++ b/SCons/Tool/yacc.py @@ -34,9 +34,10 @@ selection method. """ +from __future__ import annotations + import os.path import sys -from typing import Optional import SCons.Defaults import SCons.Tool @@ -68,7 +69,7 @@ def _yaccEmitter(target, source, env, ysuf, hsuf) -> tuple: # If -d is specified on the command line, yacc will emit a .h # or .hpp file with the same base name as the .c or .cpp output file. - # if '-d' in flags: + # if '-d' in flags: # or bison options -H, --header, --defines (obsolete) if "-d" in flags or "-H" in flags or "--header" in flags or "--defines" in flags: target.append(targetBase + env.subst(hsuf, target=target, source=source)) @@ -76,7 +77,7 @@ def _yaccEmitter(target, source, env, ysuf, hsuf) -> tuple: # If -g is specified on the command line, yacc will emit a graph # file with the same base name as the .c or .cpp output file. # TODO: should this be handled like -v? i.e. a side effect, not target - # if "-g" in flags: + # if "-g" in flags: # or bison option --graph if "-g" in flags or "--graph" in flags: target.append(targetBase + env.subst("$YACC_GRAPH_FILE_SUFFIX")) @@ -134,7 +135,7 @@ def yyEmitter(target, source, env) -> tuple: return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX') -def get_yacc_path(env, append_paths: bool=False) -> Optional[str]: +def get_yacc_path(env, append_paths: bool=False) -> str | None: """ Returns the path to the yacc tool, searching several possible names. @@ -200,7 +201,7 @@ def generate(env) -> None: env['_YACC_GRAPH'] = '${YACC_GRAPH_FILE and "--graph=" + str(YACC_GRAPH_FILE)}' -def exists(env) -> Optional[str]: +def exists(env) -> str | None: if 'YACC' in env: return env.Detect(env['YACC']) diff --git a/SCons/Util/UtilTests.py b/SCons/Util/UtilTests.py index b1c01086e4..0f457c3ce0 100644 --- a/SCons/Util/UtilTests.py +++ b/SCons/Util/UtilTests.py @@ -21,6 +21,8 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +from __future__ import annotations + import functools import hashlib import io @@ -30,7 +32,6 @@ import unittest.mock import warnings from collections import UserDict, UserList, UserString, namedtuple -from typing import Union import TestCmd @@ -533,8 +534,8 @@ def test_get_native_path(self) -> None: def test_PrependPath(self) -> None: """Test prepending to a path""" - p1: Union[list, str] = r'C:\dir\num\one;C:\dir\num\two' - p2: Union[list, str] = r'C:\mydir\num\one;C:\mydir\num\two' + p1: list | str = r'C:\dir\num\one;C:\dir\num\two' + p2: list | str = r'C:\mydir\num\one;C:\mydir\num\two' # have to include the pathsep here so that the test will work on UNIX too. p1 = PrependPath(p1, r'C:\dir\num\two', sep=';') p1 = PrependPath(p1, r'C:\dir\num\three', sep=';') @@ -545,14 +546,14 @@ def test_PrependPath(self) -> None: assert p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two', p2 # check (only) first one is kept if there are dupes in new - p3: Union[list, str] = r'C:\dir\num\one' + p3: list | str = r'C:\dir\num\one' p3 = PrependPath(p3, r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\two', sep=';') assert p3 == r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\one', p3 def test_AppendPath(self) -> None: """Test appending to a path.""" - p1: Union[list, str] = r'C:\dir\num\one;C:\dir\num\two' - p2: Union[list, str] = r'C:\mydir\num\one;C:\mydir\num\two' + p1: list | str = r'C:\dir\num\one;C:\dir\num\two' + p2: list | str = r'C:\mydir\num\one;C:\mydir\num\two' # have to include the pathsep here so that the test will work on UNIX too. p1 = AppendPath(p1, r'C:\dir\num\two', sep=';') p1 = AppendPath(p1, r'C:\dir\num\three', sep=';') @@ -563,13 +564,13 @@ def test_AppendPath(self) -> None: assert p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one', p2 # check (only) last one is kept if there are dupes in new - p3: Union[list, str] = r'C:\dir\num\one' + p3: list | str = r'C:\dir\num\one' p3 = AppendPath(p3, r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\two', sep=';') assert p3 == r'C:\dir\num\one;C:\dir\num\three;C:\dir\num\two', p3 def test_PrependPathPreserveOld(self) -> None: """Test prepending to a path while preserving old paths""" - p1: Union[list, str] = r'C:\dir\num\one;C:\dir\num\two' + p1: list | str = r'C:\dir\num\one;C:\dir\num\two' # have to include the pathsep here so that the test will work on UNIX too. p1 = PrependPath(p1, r'C:\dir\num\two', sep=';', delete_existing=False) p1 = PrependPath(p1, r'C:\dir\num\three', sep=';') @@ -577,7 +578,7 @@ def test_PrependPathPreserveOld(self) -> None: def test_AppendPathPreserveOld(self) -> None: """Test appending to a path while preserving old paths""" - p1: Union[list, str] = r'C:\dir\num\one;C:\dir\num\two' + p1: list | str = r'C:\dir\num\one;C:\dir\num\two' # have to include the pathsep here so that the test will work on UNIX too. p1 = AppendPath(p1, r'C:\dir\num\one', sep=';', delete_existing=False) p1 = AppendPath(p1, r'C:\dir\num\three', sep=';') diff --git a/SCons/Util/__init__.py b/SCons/Util/__init__.py index 28565da797..0398c1fad7 100644 --- a/SCons/Util/__init__.py +++ b/SCons/Util/__init__.py @@ -49,6 +49,8 @@ # ) # (issue filed on this upstream, for now just be aware) +from __future__ import annotations + import copy import hashlib import logging @@ -59,7 +61,7 @@ from collections import UserDict, UserList, deque from contextlib import suppress from types import MethodType, FunctionType -from typing import Optional, Union, Any, List +from typing import Any from logging import Formatter # Util split into a package. Make sure things that used to work @@ -203,11 +205,11 @@ def __str__(self) -> str: def __iter__(self): return iter(self.data) - def __call__(self, *args, **kwargs) -> 'NodeList': + def __call__(self, *args, **kwargs) -> NodeList: result = [x(*args, **kwargs) for x in self.data] return self.__class__(result) - def __getattr__(self, name) -> 'NodeList': + def __getattr__(self, name) -> NodeList: """Returns a NodeList of `name` from each member.""" result = [getattr(x, name) for x in self.data] return self.__class__(result) @@ -254,8 +256,8 @@ def render_tree( root, child_func, prune: bool = False, - margin: List[bool] = [False], - visited: Optional[dict] = None, + margin: list[bool] = [False], + visited: dict | None = None, ) -> str: """Render a tree of nodes into an ASCII tree view. @@ -323,8 +325,8 @@ def print_tree( child_func, prune: bool = False, showtags: int = 0, - margin: List[bool] = [False], - visited: Optional[dict] = None, + margin: list[bool] = [False], + visited: dict | None = None, lastChild: bool = False, singleLineDraw: bool = False, ) -> None: @@ -694,7 +696,7 @@ def RegOpenKeyEx(root, key): if sys.platform == 'win32': - def WhereIs(file, path=None, pathext=None, reject=None) -> Optional[str]: + def WhereIs(file, path=None, pathext=None, reject=None) -> str | None: if path is None: try: path = os.environ['PATH'] @@ -731,7 +733,7 @@ def WhereIs(file, path=None, pathext=None, reject=None) -> Optional[str]: elif os.name == 'os2': - def WhereIs(file, path=None, pathext=None, reject=None) -> Optional[str]: + def WhereIs(file, path=None, pathext=None, reject=None) -> str | None: if path is None: try: path = os.environ['PATH'] @@ -763,7 +765,7 @@ def WhereIs(file, path=None, pathext=None, reject=None) -> Optional[str]: else: - def WhereIs(file, path=None, pathext=None, reject=None) -> Optional[str]: + def WhereIs(file, path=None, pathext=None, reject=None) -> str | None: import stat # pylint: disable=import-outside-toplevel if path is None: diff --git a/SCons/Util/envs.py b/SCons/Util/envs.py index 2640ef5c19..9c97da6e9e 100644 --- a/SCons/Util/envs.py +++ b/SCons/Util/envs.py @@ -9,10 +9,12 @@ that don't need the specifics of the Environment class. """ +from __future__ import annotations + import re import os from types import MethodType, FunctionType -from typing import Union, Callable, Optional, Any +from typing import Callable, Any from .sctypes import is_List, is_Tuple, is_String @@ -22,8 +24,8 @@ def PrependPath( newpath, sep=os.pathsep, delete_existing: bool = True, - canonicalize: Optional[Callable] = None, -) -> Union[list, str]: + canonicalize: Callable | None = None, +) -> list | str: """Prepend *newpath* path elements to *oldpath*. Will only add any particular path once (leaving the first one it @@ -112,8 +114,8 @@ def AppendPath( newpath, sep=os.pathsep, delete_existing: bool = True, - canonicalize: Optional[Callable] = None, -) -> Union[list, str]: + canonicalize: Callable | None = None, +) -> list | str: """Append *newpath* path elements to *oldpath*. Will only add any particular path once (leaving the last one it @@ -239,7 +241,7 @@ class MethodWrapper: a new underlying object being copied (without which we wouldn't need to save that info). """ - def __init__(self, obj: Any, method: Callable, name: Optional[str] = None) -> None: + def __init__(self, obj: Any, method: Callable, name: str | None = None) -> None: if name is None: name = method.__name__ self.object = obj @@ -275,7 +277,7 @@ def clone(self, new_object): # is not needed, the remaining bit is now used inline in AddMethod. -def AddMethod(obj, function: Callable, name: Optional[str] = None) -> None: +def AddMethod(obj, function: Callable, name: str | None = None) -> None: """Add a method to an object. Adds *function* to *obj* if *obj* is a class object. @@ -314,7 +316,7 @@ def AddMethod(obj, function: Callable, name: Optional[str] = None) -> None: function.__code__, function.__globals__, name, function.__defaults__ ) - method: Union[MethodType, MethodWrapper, Callable] + method: MethodType | MethodWrapper | Callable if hasattr(obj, '__class__') and obj.__class__ is not type: # obj is an instance, so it gets a bound method. diff --git a/SCons/Util/filelock.py b/SCons/Util/filelock.py index 8ebf3889fe..730f48607b 100644 --- a/SCons/Util/filelock.py +++ b/SCons/Util/filelock.py @@ -30,9 +30,10 @@ # The lock attributes could probably be made opaque. Showed one visible # in the example above, but not sure the benefit of that. +from __future__ import annotations + import os import time -from typing import Optional class SConsLockFailure(Exception): @@ -75,8 +76,8 @@ class FileLock: def __init__( self, file: str, - timeout: Optional[int] = None, - delay: Optional[float] = 0.05, + timeout: int | None = None, + delay: float | None = 0.05, writer: bool = False, ) -> None: if timeout is not None and delay is None: @@ -90,7 +91,7 @@ def __init__( # Our simple first guess is just put it where the file is. self.file = file self.lockfile = f"{file}.lock" - self.lock: Optional[int] = None + self.lock: int | None = None self.timeout = 999999 if timeout == 0 else timeout self.delay = 0.0 if delay is None else delay self.writer = writer @@ -128,7 +129,7 @@ def release_lock(self) -> None: os.unlink(self.lockfile) self.lock = None - def __enter__(self) -> "FileLock": + def __enter__(self) -> FileLock: """Context manager entry: acquire lock if not holding.""" if not self.lock: self.acquire_lock() diff --git a/SCons/Util/hashes.py b/SCons/Util/hashes.py index 566897abbe..016354c6e5 100644 --- a/SCons/Util/hashes.py +++ b/SCons/Util/hashes.py @@ -8,10 +8,11 @@ Routines for working with content and signature hashes. """ +from __future__ import annotations + import functools import hashlib import sys -from typing import Optional, Union from .sctypes import to_bytes diff --git a/SCons/Util/sctypes.py b/SCons/Util/sctypes.py index 765458e130..b95e395c80 100644 --- a/SCons/Util/sctypes.py +++ b/SCons/Util/sctypes.py @@ -6,13 +6,13 @@ Routines which check types and do type conversions. """ +from __future__ import annotations import codecs import os import pprint import re import sys -from typing import Optional, Union from collections import UserDict, UserList, UserString, deque from collections.abc import MappingView, Iterable @@ -56,20 +56,24 @@ if sys.version_info >= (3, 13): from typing import TypeAlias, TypeIs - DictTypeRet: TypeAlias = TypeIs[Union[dict, UserDict]] - ListTypeRet: TypeAlias = TypeIs[Union[list, UserList, deque]] - SequenceTypeRet: TypeAlias = TypeIs[Union[list, tuple, deque, UserList, MappingView]] + DictTypeRet: TypeAlias = TypeIs[dict | UserDict] + ListTypeRet: TypeAlias = TypeIs[list | UserList | deque] + SequenceTypeRet: TypeAlias = TypeIs[list | tuple | deque | UserList | MappingView] TupleTypeRet: TypeAlias = TypeIs[tuple] - StringTypeRet: TypeAlias = TypeIs[Union[str, UserString]] + StringTypeRet: TypeAlias = TypeIs[str | UserString] elif sys.version_info >= (3, 10): from typing import TypeAlias, TypeGuard - DictTypeRet: TypeAlias = TypeGuard[Union[dict, UserDict]] - ListTypeRet: TypeAlias = TypeGuard[Union[list, UserList, deque]] - SequenceTypeRet: TypeAlias = TypeGuard[Union[list, tuple, deque, UserList, MappingView]] + DictTypeRet: TypeAlias = TypeGuard[dict | UserDict] + ListTypeRet: TypeAlias = TypeGuard[list | UserList | deque] + SequenceTypeRet: TypeAlias = TypeGuard[list | tuple | deque | UserList | MappingView] TupleTypeRet: TypeAlias = TypeGuard[tuple] - StringTypeRet: TypeAlias = TypeGuard[Union[str, UserString]] + StringTypeRet: TypeAlias = TypeGuard[str | UserString] else: + # Because we have neither `TypeAlias` class nor `type` keyword pre-3.10, + # the boolean fallback type has to be wrapped in the legacy `Union` class. + from typing import Union + DictTypeRet = Union[bool, bool] ListTypeRet = Union[bool, bool] SequenceTypeRet = Union[bool, bool] @@ -354,7 +358,7 @@ def get_os_env_bool(name: str, default: bool=False) -> bool: _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') -def get_environment_var(varstr) -> Optional[str]: +def get_environment_var(varstr) -> str | None: """Return undecorated construction variable string. Determine if *varstr* looks like a reference diff --git a/SCons/Util/sctyping.py b/SCons/Util/sctyping.py deleted file mode 100644 index 5da5eb9fe7..0000000000 --- a/SCons/Util/sctyping.py +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: MIT -# -# Copyright The SCons Foundation - -"""Various SCons type aliases. - -For representing complex types across the entire repo without risking -circular dependencies, we take advantage of TYPE_CHECKING to import -modules in an tool-only environment. This allows us to introduce -hinting that resolves as expected in IDEs without clashing at runtime. - -For consistency, it's recommended to ALWAYS use these aliases in a -type-hinting context, even if the type is actually expected to be -resolved in a given file. -""" - -from typing import Union, TYPE_CHECKING - -if TYPE_CHECKING: - import SCons.Executor - - -# Because we don't have access to TypeAlias until 3.10, we have to utilize -# 'Union' for all aliases. As it expects at least two entries, anything that -# is only represented with a single type needs to list itself twice. -ExecutorType = Union["SCons.Executor.Executor", "SCons.Executor.Executor"] - - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/SCons/Variables/BoolVariable.py b/SCons/Variables/BoolVariable.py index 815a4b7865..573a0244e7 100644 --- a/SCons/Variables/BoolVariable.py +++ b/SCons/Variables/BoolVariable.py @@ -32,7 +32,9 @@ ... """ -from typing import Callable, Tuple, Union +from __future__ import annotations + +from typing import Callable import SCons.Errors @@ -42,7 +44,7 @@ FALSE_STRINGS = ('n', 'no', 'false', 'f', '0', 'off', 'none') -def _text2bool(val: Union[str, bool]) -> bool: +def _text2bool(val: str | bool) -> bool: """Convert boolean-like string to boolean. If *val* looks like it expresses a bool-like value, based on @@ -83,7 +85,7 @@ def _validator(key: str, val, env) -> None: raise SCons.Errors.UserError(msg) from None # lint: W0622: Redefining built-in 'help' (redefined-builtin) -def BoolVariable(key, help: str, default) -> Tuple[str, str, str, Callable, Callable]: +def BoolVariable(key, help: str, default) -> tuple[str, str, str, Callable, Callable]: """Return a tuple describing a boolean SCons Variable. The input parameters describe a boolean variable, using a string diff --git a/SCons/Variables/EnumVariable.py b/SCons/Variables/EnumVariable.py index 3698e470dc..f154a133b7 100644 --- a/SCons/Variables/EnumVariable.py +++ b/SCons/Variables/EnumVariable.py @@ -43,7 +43,9 @@ ... """ -from typing import Callable, List, Optional, Tuple +from __future__ import annotations + +from typing import Callable import SCons.Errors @@ -69,10 +71,10 @@ def EnumVariable( key, help: str, default: str, - allowed_values: List[str], - map: Optional[dict] = None, + allowed_values: list[str], + map: dict | None = None, ignorecase: int = 0, -) -> Tuple[str, str, str, Callable, Callable]: +) -> tuple[str, str, str, Callable, Callable]: """Return a tuple describing an enumaration SCons Variable. The input parameters describe a variable with only predefined values diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index 2c79ee7f15..4ea7dc304c 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -53,9 +53,11 @@ # Known Bug: This should behave like a Set-Type, but does not really, # since elements can occur twice. +from __future__ import annotations + import collections import functools -from typing import Callable, List, Optional, Tuple, Union +from typing import Callable import SCons.Util @@ -75,7 +77,7 @@ class _ListVariable(collections.UserList): """ def __init__( - self, initlist: Optional[list] = None, allowedElems: Optional[list] = None + self, initlist: list | None = None, allowedElems: list | None = None ) -> None: if initlist is None: initlist = [] @@ -179,11 +181,11 @@ def _validator(key, val, env) -> None: def ListVariable( key, help: str, - default: Union[str, List[str]], - names: List[str], - map: Optional[dict] = None, - validator: Optional[Callable] = None, -) -> Tuple[str, str, str, None, Callable]: + default: str | list[str], + names: list[str], + map: dict | None = None, + validator: Callable | None = None, +) -> tuple[str, str, str, None, Callable]: """Return a tuple describing a list variable. The input parameters describe a list variable, where the values diff --git a/SCons/Variables/PackageVariable.py b/SCons/Variables/PackageVariable.py index 7271cfbf8f..2ecedfe77a 100644 --- a/SCons/Variables/PackageVariable.py +++ b/SCons/Variables/PackageVariable.py @@ -50,9 +50,11 @@ ... # build with x11 ... """ +from __future__ import annotations + import os import functools -from typing import Callable, Optional, Tuple, Union +from typing import Callable import SCons.Errors @@ -61,7 +63,7 @@ ENABLE_STRINGS = ('1', 'yes', 'true', 'on', 'enable', 'search') DISABLE_STRINGS = ('0', 'no', 'false', 'off', 'disable') -def _converter(val: Union[str, bool], default: str) -> Union[str, bool]: +def _converter(val: str | bool, default: str) -> str | bool: """Convert a package variable. Returns *val* if it looks like a path string, and ``False`` if it @@ -108,8 +110,8 @@ def _validator(key: str, val, env, searchfunc) -> None: # lint: W0622: Redefining built-in 'help' (redefined-builtin) def PackageVariable( - key: str, help: str, default, searchfunc: Optional[Callable] = None -) -> Tuple[str, str, str, Callable, Callable]: + key: str, help: str, default, searchfunc: Callable | None = None +) -> tuple[str, str, str, Callable, Callable]: """Return a tuple describing a package list SCons Variable. The input parameters describe a 'package list' variable. Returns diff --git a/SCons/Variables/PathVariable.py b/SCons/Variables/PathVariable.py index 43904e62c7..f840a95d26 100644 --- a/SCons/Variables/PathVariable.py +++ b/SCons/Variables/PathVariable.py @@ -71,10 +71,11 @@ ) """ +from __future__ import annotations import os import os.path -from typing import Callable, Optional, Tuple +from typing import Callable import SCons.Errors import SCons.Util @@ -141,8 +142,8 @@ def PathExists(key: str, val, env) -> None: # lint: W0622: Redefining built-in 'help' (redefined-builtin) def __call__( - self, key: str, help: str, default, validator: Optional[Callable] = None - ) -> Tuple[str, str, str, Callable, None]: + self, key: str, help: str, default, validator: Callable | None = None + ) -> tuple[str, str, str, Callable, None]: """Return a tuple describing a path list SCons Variable. The input parameters describe a 'path list' variable. Returns diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 28325266fb..2d160072a5 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -23,10 +23,12 @@ """Adds user-friendly customizable variables to an SCons build.""" +from __future__ import annotations + import os.path import sys from functools import cmp_to_key -from typing import Callable, Dict, List, Optional, Sequence, Union +from typing import Callable, Sequence import SCons.Errors import SCons.Util @@ -90,16 +92,16 @@ class Variables: def __init__( self, - files: Optional[Union[str, Sequence[str]]] = None, - args: Optional[dict] = None, + files: str | Sequence[str | None] = None, + args: dict | None = None, is_global: bool = False, ) -> None: - self.options: List[Variable] = [] + self.options: list[Variable] = [] self.args = args if args is not None else {} if not SCons.Util.is_Sequence(files): files = [files] if files else [] self.files: Sequence[str] = files - self.unknown: Dict[str, str] = {} + self.unknown: dict[str, str] = {} def __str__(self) -> str: """Provide a way to "print" a Variables object.""" @@ -113,11 +115,11 @@ def __str__(self) -> str: # lint: W0622: Redefining built-in 'help' def _do_add( self, - key: Union[str, List[str]], + key: str | list[str], help: str = "", default=None, - validator: Optional[Callable] = None, - converter: Optional[Callable] = None, + validator: Callable | None = None, + converter: Callable | None = None, **kwargs, ) -> None: """Create a Variable and add it to the list. @@ -162,7 +164,7 @@ def keys(self) -> list: yield option.key def Add( - self, key: Union[str, Sequence], *args, **kwargs, + self, key: str | Sequence, *args, **kwargs, ) -> None: """Add a Build Variable. @@ -218,7 +220,7 @@ def AddVariables(self, *optlist) -> None: for opt in optlist: self._do_add(*opt) - def Update(self, env, args: Optional[dict] = None) -> None: + def Update(self, env, args: dict | None = None) -> None: """Update an environment with the Build Variables. Args: @@ -362,7 +364,7 @@ def Save(self, filename, env) -> None: msg = f'Error writing options to file: {filename}\n{exc}' raise SCons.Errors.UserError(msg) from exc - def GenerateHelpText(self, env, sort: Union[bool, Callable] = False) -> str: + def GenerateHelpText(self, env, sort: bool | Callable = False) -> str: """Generate the help text for the Variables object. Args: @@ -403,7 +405,7 @@ def FormatVariableHelpText( help: str, default, actual, - aliases: Optional[List[str]] = None, + aliases: list[str | None] = None, ) -> str: """Format the help text for a single variable. diff --git a/SCons/Warnings.py b/SCons/Warnings.py index d604659c40..b9f9cc16f4 100644 --- a/SCons/Warnings.py +++ b/SCons/Warnings.py @@ -61,8 +61,10 @@ framework and it will behave like an ordinary exception. """ +from __future__ import annotations + import sys -from typing import Callable, Sequence, Optional +from typing import Callable, Sequence import SCons.Errors @@ -75,7 +77,7 @@ # Function to emit the warning. Initialized by SCons/Main.py for regular use; # the unit test will set to a capturing version for testing. -_warningOut: Optional[Callable] = None +_warningOut: Callable | None = None class SConsWarning(SCons.Errors.UserError): diff --git a/pyproject.toml b/pyproject.toml index 1e00257be9..5f3a49e04f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,6 +84,15 @@ extend-exclude = [ "SCons/Tool/docbook/docbook-xsl-1.76.1/", ] +[tool.ruff.lint] +extend-select = [ + "FA", # Future annotations + "UP006", # Use {to} instead of {from} for type annotation + "UP007", # Use `X | Y` for type annotations + "UP037", # Remove quotes from type annotation +] +extend-safe-fixes = ["FA", "UP006", "UP007"] + [tool.ruff.format] quote-style = "preserve" # Equivalent to black's "skip-string-normalization" diff --git a/runtest.py b/runtest.py index 220b490333..3408d68681 100755 --- a/runtest.py +++ b/runtest.py @@ -15,6 +15,8 @@ performs test discovery and processes tests according to options. """ +from __future__ import annotations + import argparse import itertools import os @@ -27,11 +29,11 @@ from io import StringIO from pathlib import Path, PurePath, PureWindowsPath from queue import Queue -from typing import List, TextIO, Optional +from typing import TextIO cwd = os.getcwd() -debug: Optional[str] = None -scons: Optional[str] = None +debug: str | None = None +scons: str | None = None catch_output: bool = False suppress_output: bool = False script = PurePath(sys.argv[0]).name @@ -43,7 +45,7 @@ """ # this is currently expected to be global, maybe refactor later? -unittests: List[str] +unittests: list[str] parser = argparse.ArgumentParser( usage=usagestr, diff --git a/test/Variables/PackageVariable.py b/test/Variables/PackageVariable.py index bc447dd58e..77c04e6088 100644 --- a/test/Variables/PackageVariable.py +++ b/test/Variables/PackageVariable.py @@ -27,8 +27,9 @@ Test the PackageVariable canned Variable type. """ +from __future__ import annotations + import os -from typing import List import TestSCons @@ -36,7 +37,7 @@ SConstruct_path = test.workpath('SConstruct') -def check(expect: List[str]) -> None: +def check(expect: list[str]) -> None: result = test.stdout().split('\n') # skip first line and any lines beyond the length of expect assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect) diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 243745d020..7307078c5e 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -295,6 +295,8 @@ TestCmd.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') """ +from __future__ import annotations + __author__ = "Steven Knight " __revision__ = "TestCmd.py 1.3.D001 2010/06/03 12:58:27 knight" __version__ = "1.3" @@ -323,7 +325,7 @@ from collections import UserList, UserString from pathlib import Path from subprocess import PIPE, STDOUT -from typing import Callable, Dict, Optional, Union +from typing import Callable IS_WINDOWS = sys.platform == 'win32' IS_MACOS = sys.platform == 'darwin' @@ -437,7 +439,7 @@ def clean_up_ninja_daemon(self, result_type) -> None: def fail_test( self=None, condition: bool = True, - function: Optional[Callable] = None, + function: Callable | None = None, skip: int = 0, message: str = "", ) -> None: @@ -1044,8 +1046,8 @@ def __init__( diff_stdout=None, diff_stderr=None, combine: bool = False, - universal_newlines: Optional[bool] = True, - timeout: Optional[float] = None, + universal_newlines: bool | None = True, + timeout: float | None = None, ) -> None: self.external = os.environ.get('SCONS_EXTERNAL_TEST', 0) self._cwd = os.getcwd() @@ -1060,7 +1062,7 @@ def __init__( self.verbose_set(verbose) self.combine = combine self.universal_newlines = universal_newlines - self.process: Optional[Popen] = None + self.process: Popen | None = None # Two layers of timeout: one at the test class instance level, # one set on an individual start() call (usually via a run() call) self.timeout = timeout @@ -1068,7 +1070,7 @@ def __init__( self.set_match_function(match, match_stdout, match_stderr) self.set_diff_function(diff, diff_stdout, diff_stderr) self._dirlist = [] - self._preserve: Dict[str, Union[str, bool]] = { + self._preserve: dict[str, str | bool] = { 'pass_test': False, 'fail_test': False, 'no_result': False, @@ -1084,9 +1086,9 @@ def __init__( self._preserve['no_result'] = os.environ.get('PRESERVE_NO_RESULT', False) self._stdout = [] self._stderr = [] - self.status: Optional[int] = None + self.status: int | None = None self.condition = 'no_result' - self.workdir: Optional[str] + self.workdir: str | None self.workdir_set(workdir) self.subdir(subdir) @@ -1254,7 +1256,7 @@ def diff_stdout(self, a, b, *args, **kw): def fail_test( self, condition: bool = True, - function: Optional[Callable] = None, + function: Callable | None = None, skip: int = 0, message: str = "", )-> None: @@ -1738,7 +1740,7 @@ def sleep(self, seconds=default_sleep_seconds) -> None: """ time.sleep(seconds) - def stderr(self, run=None) -> Optional[str]: + def stderr(self, run=None) -> str | None: """Returns the stored standard error output from a given run. Args: @@ -1760,7 +1762,7 @@ def stderr(self, run=None) -> Optional[str]: except IndexError: return None - def stdout(self, run=None) -> Optional[str]: + def stdout(self, run=None) -> str | None: """Returns the stored standard output from a given run. Args: diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index f5bb084e68..470dbcb208 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -110,6 +110,8 @@ """ +from __future__ import annotations + __author__ = "Steven Knight " __revision__ = "TestCommon.py 1.3.D001 2010/06/03 12:58:27 knight" __version__ = "1.3" @@ -121,7 +123,7 @@ import sysconfig from collections import UserList -from typing import Callable, List, Optional, Union +from typing import Callable from TestCmd import * from TestCmd import __all__ @@ -226,14 +228,14 @@ def separate_files(flist): missing.append(f) return existing, missing -def contains(seq, subseq, find: Optional[Callable] = None) -> bool: +def contains(seq, subseq, find: Callable | None = None) -> bool: if find is None: return subseq in seq else: f = find(seq, subseq) return f not in (None, -1) and f is not False -def find_index(seq, subseq, find: Optional[Callable] = None) -> Optional[int]: +def find_index(seq, subseq, find: Callable | None = None) -> int | None: # Returns either an index of the subseq within the seq, or None. # Accepts a function find(seq, subseq), which returns an integer on success # and either: None, False, or -1, on failure. @@ -280,8 +282,8 @@ def __init__(self, **kw) -> None: def options_arguments( self, - options: Union[str, List[str]], - arguments: Union[str, List[str]], + options: str | list[str], + arguments: str | list[str], ): """Merges the "options" keyword argument with the arguments.""" # TODO: this *doesn't* split unless both are non-empty strings. @@ -323,7 +325,7 @@ def must_contain( file: str, required: str, mode: str = 'rb', - find: Optional[Callable] = None, + find: Callable | None = None, ) -> None: """Ensures specified file contains the required text. @@ -353,7 +355,7 @@ def must_contain( print(file_contents) self.fail_test() - def must_contain_all(self, output, input, title: str = "", find: Optional[Callable] = None)-> None: + def must_contain_all(self, output, input, title: str = "", find: Callable | None = None)-> None: """Ensures that the specified output string (first argument) contains all of the specified input as a block (second argument). @@ -376,7 +378,7 @@ def must_contain_all(self, output, input, title: str = "", find: Optional[Callab print(output) self.fail_test() - def must_contain_all_lines(self, output, lines, title: str = "", find: Optional[Callable] = None) -> None: + def must_contain_all_lines(self, output, lines, title: str = "", find: Callable | None = None) -> None: """Ensures that the specified output string (first argument) contains all of the specified lines (second argument). @@ -427,7 +429,7 @@ def must_contain_single_instance_of(self, output, lines, title: str = "") -> Non sys.stdout.write(output) self.fail_test() - def must_contain_any_line(self, output, lines, title: str = "", find: Optional[Callable] = None) -> None: + def must_contain_any_line(self, output, lines, title: str = "", find: Callable | None = None) -> None: """Ensures that the specified output string (first argument) contains at least one of the specified lines (second argument). @@ -451,7 +453,7 @@ def must_contain_any_line(self, output, lines, title: str = "", find: Optional[C sys.stdout.write(output) self.fail_test() - def must_contain_exactly_lines(self, output, expect, title: str = "", find: Optional[Callable] = None) -> None: + def must_contain_exactly_lines(self, output, expect, title: str = "", find: Callable | None = None) -> None: """Ensures that the specified output string (first argument) contains all of the lines in the expected string (second argument) with none left over. @@ -499,7 +501,7 @@ def must_contain_exactly_lines(self, output, expect, title: str = "", find: Opti sys.stdout.flush() self.fail_test() - def must_contain_lines(self, lines, output, title: str = "", find: Optional[Callable] = None) -> None: + def must_contain_lines(self, lines, output, title: str = "", find: Callable | None = None) -> None: # Deprecated; retain for backwards compatibility. self.must_contain_all_lines(output, lines, title, find) @@ -540,7 +542,7 @@ def must_match( file, expect, mode: str = 'rb', - match: Optional[Callable] = None, + match: Callable | None = None, message: str = "", newline=None, ): @@ -569,7 +571,7 @@ def must_match_file( file, golden_file, mode: str = 'rb', - match: Optional[Callable] = None, + match: Callable | None = None, message: str = "", newline=None, ) -> None: @@ -609,7 +611,7 @@ def must_not_contain(self, file, banned, mode: str = 'rb', find = None) -> None: print(file_contents) self.fail_test() - def must_not_contain_any_line(self, output, lines, title: str = "", find: Optional[Callable] = None) -> None: + def must_not_contain_any_line(self, output, lines, title: str = "", find: Callable | None = None) -> None: """Ensures that the specified output string (first argument) does not contain any of the specified lines (second argument). @@ -635,7 +637,7 @@ def must_not_contain_any_line(self, output, lines, title: str = "", find: Option sys.stdout.write(output) self.fail_test() - def must_not_contain_lines(self, lines, output, title: str = "", find: Optional[Callable] = None) -> None: + def must_not_contain_lines(self, lines, output, title: str = "", find: Callable | None = None) -> None: self.must_not_contain_any_line(output, lines, title, find) def must_not_exist(self, *files) -> None: @@ -768,9 +770,9 @@ def start(self, program = None, def finish( self, popen, - stdout: Optional[str] = None, - stderr: Optional[str] = '', - status: Optional[int] = 0, + stdout: str | None = None, + stderr: str | None = '', + status: int | None = 0, **kw, ) -> None: """Finish and wait for the process being run. @@ -800,9 +802,9 @@ def run( self, options=None, arguments=None, - stdout: Optional[str] = None, - stderr: Optional[str] = '', - status: Optional[int] = 0, + stdout: str | None = None, + stderr: str | None = '', + status: int | None = 0, **kw, ) -> None: """Runs the program under test, checking that the test succeeded. diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 243be753a4..806b596be0 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -34,6 +34,8 @@ attributes defined in this subclass. """ +from __future__ import annotations + import os import re import shutil @@ -42,7 +44,6 @@ import subprocess as sp import zipfile from collections import namedtuple -from typing import Optional, Tuple from TestCommon import * from TestCommon import __all__, _python_ @@ -865,7 +866,7 @@ def java_where_includes(self, version=None): result.append(os.path.join(d, 'linux')) return result - def java_where_java_home(self, version=None) -> Optional[str]: + def java_where_java_home(self, version=None) -> str | None: """ Find path to what would be JAVA_HOME. SCons does not read JAVA_HOME from the environment, so deduce it. @@ -980,7 +981,7 @@ def java_where_java(self, version=None) -> str: return where_java - def java_where_javac(self, version=None) -> Tuple[str, str]: + def java_where_javac(self, version=None) -> tuple[str, str]: """ Find java compiler. Args: From 6c64fb9fe0d1cc51b2414ffc903dc36bf6828268 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 13:17:13 -0800 Subject: [PATCH 191/386] change look-up and look up -> lookup --- SCons/Tool/f03.xml | 4 ++-- SCons/Tool/f08.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SCons/Tool/f03.xml b/SCons/Tool/f03.xml index eaa115d165..3a0a8a2182 100644 --- a/SCons/Tool/f03.xml +++ b/SCons/Tool/f03.xml @@ -154,7 +154,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F03PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F03PATH; if you need to define a specific include path for Fortran 03 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -168,7 +168,7 @@ env = Environment(F03PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: diff --git a/SCons/Tool/f08.xml b/SCons/Tool/f08.xml index dcf1b49898..8fd017679e 100644 --- a/SCons/Tool/f08.xml +++ b/SCons/Tool/f08.xml @@ -154,7 +154,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F08PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F08PATH; if you need to define a specific include path for Fortran 08 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -168,7 +168,7 @@ env = Environment(F08PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: From 0921270c14a7e13c01cda527650671b1c751806b Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 13:18:00 -0800 Subject: [PATCH 192/386] change look-up and look up -> lookup --- SCons/Tool/f90.xml | 4 ++-- SCons/Tool/f95.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SCons/Tool/f90.xml b/SCons/Tool/f90.xml index 4310ed8952..89c5ddced9 100644 --- a/SCons/Tool/f90.xml +++ b/SCons/Tool/f90.xml @@ -154,7 +154,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F90PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F90PATH; if you need to define a specific include path for Fortran 90 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -168,7 +168,7 @@ env = Environment(F90PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: diff --git a/SCons/Tool/f95.xml b/SCons/Tool/f95.xml index 91e5a48a2a..5dd55d943f 100644 --- a/SCons/Tool/f95.xml +++ b/SCons/Tool/f95.xml @@ -154,7 +154,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F95PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F95PATH; if you need to define a specific include path for Fortran 95 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -168,7 +168,7 @@ env = Environment(F95PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: From d4bc7a03c4b05a75e0ef83a9b653fa80dfac9daf Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 13:20:19 -0800 Subject: [PATCH 193/386] [ci skip] change look-up and look up -> lookup --- SCons/Tool/f77.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/f77.xml b/SCons/Tool/f77.xml index 5420c62603..e6992986f5 100644 --- a/SCons/Tool/f77.xml +++ b/SCons/Tool/f77.xml @@ -169,7 +169,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F77PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F77PATH; if you need to define a specific include path for Fortran 77 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -183,7 +183,7 @@ env = Environment(F77PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: From 06ae4b4a0d6c8dbd1c6a1bf7c4d76d4e97b39873 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 13:24:16 -0800 Subject: [PATCH 194/386] [ci skip] change look-up and look up -> lookup, also use the Tool entity instead of the word tool --- SCons/Tool/fortran.xml | 2 +- SCons/Tool/gs.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/fortran.xml b/SCons/Tool/fortran.xml index d9a84a574c..2b62f05005 100644 --- a/SCons/Tool/fortran.xml +++ b/SCons/Tool/fortran.xml @@ -239,7 +239,7 @@ non-portable and the directories will not be searched by the dependency scanner. Note: directory names in FORTRANPATH will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: diff --git a/SCons/Tool/gs.xml b/SCons/Tool/gs.xml index 2e0f46e03b..0feec0b383 100644 --- a/SCons/Tool/gs.xml +++ b/SCons/Tool/gs.xml @@ -27,7 +27,7 @@ This file is processed by the bin/SConsDoc.py module. -This tool sets the required construction variables for working with +This &f-Tool; sets the required construction variables for working with the Ghostscript software. It also registers an appropriate Action with the &b-link-PDF; Builder, such that the conversion from PS/EPS to PDF happens automatically for the TeX/LaTeX toolchain. From a9bcd84d538e3e0621b1d2e32820ce6ccfdd4e77 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 14:02:01 -0800 Subject: [PATCH 195/386] [ci skip] change look-up and look up -> lookup. --- SCons/Tool/swig.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/swig.xml b/SCons/Tool/swig.xml index ea5fabf4eb..4c0ce8ea13 100644 --- a/SCons/Tool/swig.xml +++ b/SCons/Tool/swig.xml @@ -217,7 +217,7 @@ env = Environment(SWIGPATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: From 3a6dd27b522013d2d550eb58654b8cc3b4b933d1 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 14:10:55 -0800 Subject: [PATCH 196/386] [ci skip] minor edits --- doc/man/scons.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/man/scons.xml b/doc/man/scons.xml index d6461851c9..0193924e20 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -3638,10 +3638,10 @@ the command line. If there are command line targets, this list has the same contents as &BUILD_TARGETS;. If there are no targets specified on the command line, -the list is empty. The elements of this list are strings. +this list is empty. The elements of this list are strings. This can be used, for example, to take specific actions only -when a certain target is explicitly requested for building. +when a certain target(s) are explicitly requested for building. Example: From 09b4ceabc0fd2bf1039ba3da12b3237100017a64 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 14:13:38 -0800 Subject: [PATCH 197/386] [ci skip] multi-user should be multiuser --- doc/user/caching.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/caching.xml b/doc/user/caching.xml index 413c72da92..36dfc78af3 100644 --- a/doc/user/caching.xml +++ b/doc/user/caching.xml @@ -86,7 +86,7 @@ CacheDir('/usr/local/build_cache') this directory would typically be on a shared or NFS-mounted file system. While &SCons; will create the specified cache directory as needed, - in this multi-user scenario it is usually best + in this multiuser scenario it is usually best to create it ahead of time, so the access rights can be set up correctly. From 60a960993f5fedbf1ac86cb858539741110e8cc4 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 14:14:43 -0800 Subject: [PATCH 198/386] [ci skip] change look-up and look up -> lookup. --- SCons/Tool/swig.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/swig.xml b/SCons/Tool/swig.xml index 4c0ce8ea13..14919ef26a 100644 --- a/SCons/Tool/swig.xml +++ b/SCons/Tool/swig.xml @@ -208,7 +208,7 @@ will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use a top-relative path (#): From 28d3e3bfd5cb9e1e175f0dd2106215b81698ab83 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 14:20:52 -0800 Subject: [PATCH 199/386] [ci skip] minor edits --- doc/user/separate.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/user/separate.xml b/doc/user/separate.xml index effc5aa2c5..394bf6b4a0 100644 --- a/doc/user/separate.xml +++ b/doc/user/separate.xml @@ -224,11 +224,11 @@ int main() { printf("Hello, world!\n"); } As noted in the previous chapter, all builds actually happen from the top level directory, but as an aid to understanding how &SCons; operates, think - of it as build in-place in the variant directory, - not build in-source but send build artifacts + of it as build in place in the variant directory, + not build in source but send build artifacts to the variant directory. - It turns out in-place builds are easier to get right than out-of-tree - builds - so by default &SCons; simulates an in-place build + It turns out in place builds are easier to get right than out-of-tree + builds - so by default &SCons; simulates an in place build by making the variant directory look just like the source directory. The most straightforward way to do that is by making copies of the files needed for the build. From 33077115417396603cb38d74d813332dadceb7ee Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 14:22:02 -0800 Subject: [PATCH 200/386] [ci skip] minor edits --- doc/user/simple.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/simple.xml b/doc/user/simple.xml index 88e3d8c7ff..9f57d33311 100644 --- a/doc/user/simple.xml +++ b/doc/user/simple.xml @@ -308,7 +308,7 @@ public class Example1 in exactly the same way as you control what gets built. If you build the C example above and then invoke scons -c - afterward, the output on POSIX looks like: + afterwards, the output on POSIX looks like: From e73b7d350d1e0aa005bd72b44dc5b067e30dc58e Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 16 Nov 2024 14:26:21 -0800 Subject: [PATCH 201/386] [ci skip] update generated files --- doc/generated/builders.gen | 601 ++++++++++-------- .../examples/caching_ex-random_1.xml | 6 +- .../commandline_PackageVariable_1.xml | 2 +- .../examples/troubleshoot_explain1_3.xml | 2 +- .../troubleshoot_taskmastertrace_1.xml | 50 +- doc/generated/functions.gen | 238 +++---- doc/generated/tools.gen | 75 ++- doc/generated/variables.gen | 140 ++-- 8 files changed, 600 insertions(+), 514 deletions(-) diff --git a/doc/generated/builders.gen b/doc/generated/builders.gen index d0ee3dde19..cfcb361768 100644 --- a/doc/generated/builders.gen +++ b/doc/generated/builders.gen @@ -47,7 +47,7 @@ This is useful for "one-off" builds where a full Builder is not needed. Since the anonymous Builder is never hooked into the standard Builder framework, -an Action must always be specfied. +an Action must always be specified. See the &f-link-Command; function description for the calling syntax and details. @@ -474,7 +474,7 @@ in this case. If the command line option is given, the target directory will be prefixed by the directory path specified. -This is useful to test installs without installing to +This is useful to test installation behavior without installing to a "live" location in the system. @@ -642,8 +642,8 @@ env.Java(target='classes', source=['File1.java', 'File2.java']) In this case, the user must specify the LANG environment variable to tell the compiler what encoding is used. - For portibility, it's best if the encoding is hard-coded - so that the compile will work if it is done on a system + For portability, it's best if the encoding is hard-coded, + so that the compilation works when run on a system with a different encoding. @@ -787,8 +787,12 @@ env.Moc('foo.cpp') # generates foo.moc MOFiles() env.MOFiles() -This builder belongs to &t-link-msgfmt; tool. The builder compiles +This builder is set up by the &t-link-msgfmt; tool. +The builder compiles PO files to MO files. +&b-MOFiles; is a single-source builder. +The source parameter +can also be omitted if &cv-link-LINGUAS_FILE; is set. @@ -796,19 +800,17 @@ This builder belongs to &t-link-msgfmt; tool. The builder compiles Create pl.mo and en.mo by compiling pl.po and en.po: - - # ... - env.MOFiles(['pl', 'en']) - + +env.MOFiles(['pl', 'en']) + Example 2. Compile files for languages defined in LINGUAS file: - - # ... - env.MOFiles(LINGUAS_FILE = 1) - + +env.MOFiles(LINGUAS_FILE=True) + Example 3. @@ -816,21 +818,19 @@ Create pl.mo and en.mo by compiling pl.po and en.po plus files for languages defined in LINGUAS file: - - # ... - env.MOFiles(['pl', 'en'], LINGUAS_FILE = 1) - + +env.MOFiles(['pl', 'en'], LINGUAS_FILE=True) + Example 4. Compile files for languages defined in LINGUAS file (another version): - - # ... - env['LINGUAS_FILE'] = 1 - env.MOFiles() - + +env['LINGUAS_FILE'] = True +env.MOFiles() + @@ -1075,7 +1075,7 @@ Compile files for languages defined in LINGUAS file "build" from the Visual Studio interface) you lose the direct control of target selection and command-line options you would have if launching the build directly from &SCons;, - because these will be hardcoded in the project file to the + because these will be hard-coded in the project file to the values specified in the &b-MSVSProject; call. You can regain some of this control by defining multiple variants, using multiple &b-MSVSProject; calls to arrange different build @@ -1088,7 +1088,7 @@ Compile files for languages defined in LINGUAS file it is important not to set Visual Studio to do parallel builds, as it will then launch the separate project builds in parallel, and &SCons; does not work well if called that way. - Instead you can set up the &SCons; build for parallel building - + Instead, you can set up the &SCons; build for parallel building - see the &f-link-SetOption; function for how to do this with num_jobs. @@ -1234,7 +1234,7 @@ V10DebugSettings = { } # -# 3. Select the dictionary you want depending on the version of visual Studio +# 3. Select the dictionary you want depending on the version of Visual Studio # Files you want to generate. # if not env.GetOption('userfile'): @@ -1337,6 +1337,74 @@ env.MSVSProject( + + In addition to the mandatory arguments above, the following optional + values may be specified as keyword arguments: + + + + auto_filter_projects + + + Under certain circumstances, solution file names or + solution file nodes may be present in the + projects argument list. + When solution file names or nodes are present in the + projects argument list, the generated + solution file may contain erroneous Project records resulting + in VS IDE error messages when opening the generated solution + file. + By default, an exception is raised when a solution file + name or solution file node is detected in the + projects argument list. + + + The accepted values for auto_filter_projects + are: + + + + None + + + An exception is raised when a solution file name or solution + file node is detected in the projects + argument list. + + + None is the default value. + + + + + True or evaluates True + + + Automatically remove solution file names and solution file + nodes from the projects argument list. + + + + + False or evaluates False + + + Leave the solution file names and solution file nodes + in the projects argument list. + An exception is not raised. + + + When opening the generated solution file with the VS IDE, + the VS IDE will likely report that there are erroneous + Project records that are not supported or that need to be + modified. + + + + + + + Example Usage: env.MSVSSolution( @@ -1442,7 +1510,7 @@ env = Environment(tools=['default', 'packaging']) &SCons; can build packages in a number of well known packaging formats. The target package type may be selected with the -the &cv-link-PACKAGETYPE; construction variable +&cv-link-PACKAGETYPE; construction variable or the command line option. The package type may be a list, in which case &SCons; will attempt to build packages for each type in the list. Example: @@ -1460,7 +1528,7 @@ The currently supported packagers are: msiMicrosoft Installer package -rpmRPM Package Manger package +rpmRPM Package Manager package ipkgItsy Package Management package tarbz2bzip2-compressed tar file targzgzip-compressed tar file @@ -1606,41 +1674,48 @@ or The suffix specified by the &cv-link-PDFSUFFIX; construction variable (.pdf by default) is added automatically to the target -if it is not already present. Example: +if it is not already present. +&b-PDF; is a single-source builder. +Example: - + # builds from aaa.tex env.PDF(target = 'aaa.pdf', source = 'aaa.tex') # builds bbb.pdf from bbb.dvi env.PDF(target = 'bbb', source = 'bbb.dvi') - + POInit() env.POInit() -This builder belongs to &t-link-msginit; tool. The builder initializes missing -PO file(s) if &cv-link-POAUTOINIT; is set. If -&cv-link-POAUTOINIT; is not set (default), &b-POInit; prints instruction for -user (that is supposed to be a translator), telling how the -PO file should be initialized. In normal projects +This builder is set up by the &t-link-msginit; tool. +The builder initializes missing +PO file(s) if &cv-link-POAUTOINIT; is set. +If &cv-link-POAUTOINIT; is not set (the default), +&b-POInit; prints instruction for the user (such as a translator), +telling how the PO file should be initialized. +In normal projects you should not use &b-POInit; and use &b-link-POUpdate; instead. &b-link-POUpdate; chooses intelligently between msgmerge(1) and msginit(1). &b-POInit; always uses msginit(1) and should be regarded as builder for special purposes or for temporary use (e.g. for quick, one time initialization of a bunch of PO files) or for tests. +&b-POInit; is a single-source builder. +The source parameter +can also be omitted if &cv-link-LINGUAS_FILE; is set. Target nodes defined through &b-POInit; are not built by default (they're Ignored from '.' node) but are added to -special Alias ('po-create' by default). +special &f-link-Alias; ('po-create' by default). The alias name may be changed through the &cv-link-POCREATE_ALIAS; -construction variable. All PO files defined through -&b-POInit; may be easily initialized by scons po-create. +&consvar;. All PO files defined through +&b-POInit; may be easily initialized by scons po-create. @@ -1648,31 +1723,27 @@ construction variable. All PO files defined through Initialize en.po and pl.po from messages.pot: - - # ... - env.POInit(['en', 'pl']) # messages.pot --> [en.po, pl.po] - + +env.POInit(['en', 'pl']) # messages.pot --> [en.po, pl.po] + Example 2. Initialize en.po and pl.po from foo.pot: - - # ... - env.POInit(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.po] - + +env.POInit(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.po] + Example 3. Initialize en.po and pl.po from -foo.pot but using &cv-link-POTDOMAIN; construction -variable: +foo.pot but using the &cv-link-POTDOMAIN; &consvar;: - - # ... - env.POInit(['en', 'pl'], POTDOMAIN='foo') # foo.pot --> [en.po, pl.po] - + +env.POInit(['en', 'pl'], POTDOMAIN='foo') # foo.pot --> [en.po, pl.po] + Example 4. @@ -1680,10 +1751,9 @@ Initialize PO files for languages defined in LINGUAS file. The files will be initialized from template messages.pot: - - # ... - env.POInit(LINGUAS_FILE = 1) # needs 'LINGUAS' file - + +env.POInit(LINGUAS_FILE=True) # needs 'LINGUAS' file + Example 5. @@ -1692,30 +1762,27 @@ Initialize en.po and pl.pl LINGUAS file. The files will be initialized from template messages.pot: - - # ... - env.POInit(['en', 'pl'], LINGUAS_FILE = 1) - + +env.POInit(['en', 'pl'], LINGUAS_FILE=True) + Example 6. You may preconfigure your environment first, and then initialize PO files: - - # ... - env['POAUTOINIT'] = 1 - env['LINGUAS_FILE'] = 1 - env['POTDOMAIN'] = 'foo' - env.POInit() - + +env['POAUTOINIT'] = True +env['LINGUAS_FILE'] = True +env['POTDOMAIN'] = 'foo' +env.POInit() + which has same efect as: - - # ... - env.POInit(POAUTOINIT = 1, LINGUAS_FILE = 1, POTDOMAIN = 'foo') - + +env.POInit(POAUTOINIT=True, LINGUAS_FILE=True, POTDOMAIN='foo') + @@ -1731,30 +1798,35 @@ or The suffix specified by the &cv-link-PSSUFFIX; construction variable (.ps by default) is added automatically to the target -if it is not already present. Example: +if it is not already present. +&b-PostScript; is a single-source builder. +Example: - + # builds from aaa.tex env.PostScript(target = 'aaa.ps', source = 'aaa.tex') # builds bbb.ps from bbb.dvi env.PostScript(target = 'bbb', source = 'bbb.dvi') - + POTUpdate() env.POTUpdate() -The builder belongs to &t-link-xgettext; tool. The builder updates target -POT file if exists or creates one if it doesn't. The node is -not built by default (i.e. it is Ignored from -'.'), but only on demand (i.e. when given -POT file is required or when special alias is invoked). This -builder adds its targe node (messages.pot, say) to a +The builder is set up by the &t-link-xgettext; tool, +part of the &t-link-gettext; toolset. +The builder updates the target +POT file if exists or creates it if it doesn't. +The target node is not +selected for building by default (e.g. scons .), +but only on demand (i.e. when the given +POT file is required or when special alias is invoked). +This builder adds its target node (messages.pot, say) to a special alias (pot-update by default, see &cv-link-POTUPDATE_ALIAS;) so you can update/create them easily with -scons pot-update. The file is not written until there is no +scons pot-update. The file is not written until there is no real change in internationalized messages (or in comments that enter POT file). @@ -1774,86 +1846,87 @@ not. Let's create po/ directory and place following SConstruct script there: - - # SConstruct in 'po/' subdir - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(['foo'], ['../a.cpp', '../b.cpp']) - env.POTUpdate(['bar'], ['../c.cpp', '../d.cpp']) - + +# SConstruct in 'po/' subdir +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(['foo'], ['../a.cpp', '../b.cpp']) +env.POTUpdate(['bar'], ['../c.cpp', '../d.cpp']) + Then invoke scons few times: - - user@host:$ scons # Does not create foo.pot nor bar.pot - user@host:$ scons foo.pot # Updates or creates foo.pot - user@host:$ scons pot-update # Updates or creates foo.pot and bar.pot - user@host:$ scons -c # Does not clean foo.pot nor bar.pot. - + +$ scons # Does not create foo.pot nor bar.pot +$ scons foo.pot # Updates or creates foo.pot +$ scons pot-update # Updates or creates foo.pot and bar.pot +$ scons -c # Does not clean foo.pot nor bar.pot. + the results shall be as the comments above say. Example 2. -The &b-POTUpdate; builder may be used with no target specified, in which -case default target messages.pot will be used. The -default target may also be overridden by setting &cv-link-POTDOMAIN; construction -variable or providing it as an override to &b-POTUpdate; builder: +The target argument can be omitted, in which +case the default target name messages.pot is used. +The target may also be overridden by setting the &cv-link-POTDOMAIN; +&consvar; or providing it as an override to the &b-POTUpdate; builder: - - # SConstruct script - env = Environment( tools = ['default', 'xgettext'] ) - env['POTDOMAIN'] = "foo" - env.POTUpdate(source = ["a.cpp", "b.cpp"]) # Creates foo.pot ... - env.POTUpdate(POTDOMAIN = "bar", source = ["c.cpp", "d.cpp"]) # and bar.pot - + +# SConstruct script +env = Environment(tools=['default', 'xgettext']) +env['POTDOMAIN'] = "foo" +env.POTUpdate(source=["a.cpp", "b.cpp"]) # Creates foo.pot ... +env.POTUpdate(POTDOMAIN="bar", source=["c.cpp", "d.cpp"]) # and bar.pot + Example 3. -The sources may be specified within separate file, for example +The source parameter may also be omitted, +if it is specified in a separate file, for example POTFILES.in: - - # POTFILES.in in 'po/' subdirectory - ../a.cpp - ../b.cpp - # end of file - + +# POTFILES.in in 'po/' subdirectory +../a.cpp +../b.cpp +# end of file + The name of the file (POTFILES.in) containing the list of sources is provided via &cv-link-XGETTEXTFROM;: - - # SConstruct file in 'po/' subdirectory - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(XGETTEXTFROM = 'POTFILES.in') - + +# SConstruct file in 'po/' subdirectory +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(XGETTEXTFROM='POTFILES.in') + Example 4. -You may use &cv-link-XGETTEXTPATH; to define source search path. Assume, for -example, that you have files a.cpp, +You can use &cv-link-XGETTEXTPATH; to define the source search path. +Assume, for example, that you have files a.cpp, b.cpp, po/SConstruct, -po/POTFILES.in. Then your POT-related -files could look as below: +po/POTFILES.in. +Then your POT-related files could look like this: - - # POTFILES.in in 'po/' subdirectory - a.cpp - b.cpp - # end of file - + +# POTFILES.in in 'po/' subdirectory +a.cpp +b.cpp +# end of file + - - # SConstruct file in 'po/' subdirectory - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH='../') - + +# SConstruct file in 'po/' subdirectory +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(XGETTEXTFROM='POTFILES.in', XGETTEXTPATH='../') + Example 5. -Multiple search directories may be defined within a list, i.e. -XGETTEXTPATH = ['dir1', 'dir2', ...]. The order in the list +Multiple search directories may be defined as a list, i.e. +XGETTEXTPATH=['dir1', 'dir2', ...]. The order in the list determines the search order of source files. The path to the first file found is used. @@ -1861,44 +1934,44 @@ is used. Let's create 0/1/po/SConstruct script: - - # SConstruct file in '0/1/po/' subdirectory - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../', '../../']) - + +# SConstruct file in '0/1/po/' subdirectory +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(XGETTEXTFROM='POTFILES.in', XGETTEXTPATH=['../', '../../']) + and 0/1/po/POTFILES.in: - - # POTFILES.in in '0/1/po/' subdirectory - a.cpp - # end of file - + +# POTFILES.in in '0/1/po/' subdirectory +a.cpp +# end of file + Write two *.cpp files, the first one is 0/a.cpp: - - /* 0/a.cpp */ - gettext("Hello from ../../a.cpp") - + +/* 0/a.cpp */ +gettext("Hello from ../../a.cpp") + and the second is 0/1/a.cpp: - - /* 0/1/a.cpp */ - gettext("Hello from ../a.cpp") - + +/* 0/1/a.cpp */ +gettext("Hello from ../a.cpp") + then run scons. You'll obtain 0/1/po/messages.pot with the message "Hello from ../a.cpp". When you reverse order in $XGETTEXTFOM, i.e. when you write SConscript as - - # SConstruct file in '0/1/po/' subdirectory - env = Environment( tools = ['default', 'xgettext'] ) - env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../../', '../']) - + +# SConstruct file in '0/1/po/' subdirectory +env = Environment(tools=['default', 'xgettext']) +env.POTUpdate(XGETTEXTFROM='POTFILES.in', XGETTEXTPATH=['../../', '../']) + then the messages.pot will contain msgid "Hello from ../../a.cpp" line and not @@ -1911,23 +1984,29 @@ then the messages.pot will contain POUpdate() env.POUpdate() -The builder belongs to &t-link-msgmerge; tool. The builder updates +The builder is set up by the &t-link-msgmerge; tool. +part of the &t-link-gettext; toolset. +The builder updates PO files with msgmerge(1), or initializes -missing PO files as described in documentation of -&t-link-msginit; tool and &b-link-POInit; builder (see also -&cv-link-POAUTOINIT;). Note, that &b-POUpdate; does not add its -targets to po-create alias as &b-link-POInit; -does. +missing PO files as described in the documentation of the +&t-link-msginit; tool and the &b-link-POInit; builder (see also +&cv-link-POAUTOINIT;). +&b-POUpdate; is a single-source builder. +The source parameter +can also be omitted if &cv-link-LINGUAS_FILE; is set. -Target nodes defined through &b-POUpdate; are not built by default -(they're Ignored from '.' node). Instead, -they are added automatically to special Alias +The target nodes are not +selected for building by default (e.g. scons .). +Instead, they are added automatically to special &f-link-Alias; ('po-update' by default). The alias name may be changed -through the &cv-link-POUPDATE_ALIAS; construction variable. You can easily -update PO files in your project by scons -po-update. +through the &cv-link-POUPDATE_ALIAS; &consvar;. You can easily +update PO files in your project by +scons po-update. +Note that &b-POUpdate; does not add its +targets to the po-create alias as &b-link-POInit; +does. @@ -1937,49 +2016,44 @@ Update en.po and pl.po from assuming that the later one exists or there is rule to build it (see &b-link-POTUpdate;): - - # ... - env.POUpdate(['en','pl']) # messages.pot --> [en.po, pl.po] - + +env.POUpdate(['en','pl']) # messages.pot --> [en.po, pl.po] + Example 2. Update en.po and pl.po from foo.pot template: - - # ... - env.POUpdate(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.pl] - + +env.POUpdate(['en', 'pl'], ['foo']) # foo.pot --> [en.po, pl.pl] + Example 3. Update en.po and pl.po from foo.pot (another version): - - # ... - env.POUpdate(['en', 'pl'], POTDOMAIN='foo') # foo.pot -- > [en.po, pl.pl] - + +env.POUpdate(['en', 'pl'], POTDOMAIN='foo') # foo.pot -- > [en.po, pl.pl] + Example 4. Update files for languages defined in LINGUAS file. The files are updated from messages.pot template: - - # ... - env.POUpdate(LINGUAS_FILE = 1) # needs 'LINGUAS' file - + +env.POUpdate(LINGUAS_FILE=True) # needs 'LINGUAS' file + Example 5. Same as above, but update from foo.pot template: - - # ... - env.POUpdate(LINGUAS_FILE = 1, source = ['foo']) - + +env.POUpdate(LINGUAS_FILE=True, source=['foo']) + Example 6. @@ -1987,20 +2061,19 @@ Update en.po and pl.po plus files for languages defined in LINGUAS file. The files are updated from messages.pot template: - - # produce 'en.po', 'pl.po' + files defined in 'LINGUAS': - env.POUpdate(['en', 'pl' ], LINGUAS_FILE = 1) - + +# produce 'en.po', 'pl.po' + files defined in 'LINGUAS': +env.POUpdate(['en', 'pl' ], LINGUAS_FILE=True) + Example 7. Use &cv-link-POAUTOINIT; to automatically initialize PO file if it doesn't exist: - - # ... - env.POUpdate(LINGUAS_FILE = 1, POAUTOINIT = 1) - + +env.POUpdate(LINGUAS_FILE=True, POAUTOINIT=True) + Example 8. @@ -2009,13 +2082,12 @@ Update PO files for languages defined in foo.pot template. All necessary settings are pre-configured via environment. - - # ... - env['POAUTOINIT'] = 1 - env['LINGUAS_FILE'] = 1 - env['POTDOMAIN'] = 'foo' - env.POUpdate() - + +env['POAUTOINIT'] = True +env['LINGUAS_FILE'] = True +env['POTDOMAIN'] = 'foo' +env.POUpdate() + @@ -2508,7 +2580,7 @@ are flattened. See also &b-link-Textfile;. -By default the target file encoding is "utf-8" and can be changed by &cv-link-FILE_ENCODING; +By default, the target file encoding is "utf-8" and can be changed by &cv-link-FILE_ENCODING; Examples: @@ -2531,7 +2603,7 @@ it may be either a Python dictionary or a sequence of If it is a dictionary it is converted into a list of tuples with unspecified order, so if one key is a prefix of another key -or if one substitution could be further expanded by another subsitition, +or if one substitution could be further expanded by another substitution, it is unpredictable whether the expansion will occur. @@ -2562,7 +2634,7 @@ env.Substfile('foo.in', SUBST_DICT=bad_foo) good_foo = [('$foobar', '$foobar'), ('$foo', '$foo')] env.Substfile('foo.in', SUBST_DICT=good_foo) -# UNPREDICTABLE - one substitution could be futher expanded +# UNPREDICTABLE - one substitution could be further expanded bad_bar = {'@bar@': '@soap@', '@soap@': 'lye'} env.Substfile('bar.in', SUBST_DICT=bad_bar) @@ -2654,7 +2726,7 @@ and &cv-link-TEXTFILESUFFIX; &consvars; are automatically added to the target if they are not already present. -By default the target file encoding is "utf-8" and can be changed by &cv-link-FILE_ENCODING; +By default, the target file encoding is "utf-8" and can be changed by &cv-link-FILE_ENCODING; Examples: @@ -2668,7 +2740,7 @@ env.Textfile(target='bar', source=['lalala', 'tanteratei'], LINESEPARATOR='|*') # nested lists are flattened automatically env.Textfile(target='blob', source=['lalala', ['Goethe', 42, 'Schiller'], 'tanteratei']) -# files may be used as input by wraping them in File() +# files may be used as input by wrapping them in File() env.Textfile( target='concat', # concatenate files with a marker between source=[File('concat1'), File('concat2')], @@ -2705,54 +2777,56 @@ env.Textfile( Translate() env.Translate() -This pseudo-builder belongs to &t-link-gettext; toolset. The builder extracts -internationalized messages from source files, updates POT -template (if necessary) and then updates PO translations (if -necessary). If &cv-link-POAUTOINIT; is set, missing PO files +This pseudo-Builder is part of the &t-link-gettext; toolset. +The builder extracts internationalized messages from source files, +updates the POT template (if necessary) +and then updates PO translations (if necessary). +If &cv-link-POAUTOINIT; is set, missing PO files will be automatically created (i.e. without translator person intervention). The variables &cv-link-LINGUAS_FILE; and &cv-link-POTDOMAIN; are taken into -acount too. All other construction variables used by &b-link-POTUpdate;, and +account too. All other construction variables used by &b-link-POTUpdate;, and &b-link-POUpdate; work here too. Example 1. The simplest way is to specify input files and output languages inline in -a SCons script when invoking &b-Translate; +a SCons script when invoking &b-Translate;: - + # SConscript in 'po/' directory -env = Environment( tools = ["default", "gettext"] ) -env['POAUTOINIT'] = 1 -env.Translate(['en','pl'], ['../a.cpp','../b.cpp']) - +env = Environment(tools=["default", "gettext"]) +env['POAUTOINIT'] = True +env.Translate(['en', 'pl'], ['../a.cpp', '../b.cpp']) + Example 2. -If you wish, you may also stick to conventional style known from +If you wish, you may also stick to the conventional style known from autotools, i.e. using POTFILES.in and LINGUAS files +to specify the targets and sources: - + # LINGUAS en pl -#end - +# end + - + # POTFILES.in a.cpp b.cpp # end - + - + # SConscript -env = Environment( tools = ["default", "gettext"] ) -env['POAUTOINIT'] = 1 +env = Environment(tools=["default", "gettext"]) +env['POAUTOINIT'] = True env['XGETTEXTPATH'] = ['../'] -env.Translate(LINGUAS_FILE = 1, XGETTEXTFROM = 'POTFILES.in') - +env.Translate(LINGUAS_FILE=True, XGETTEXTFROM='POTFILES.in') + The last approach is perhaps the recommended one. It allows easily split @@ -2765,7 +2839,7 @@ factor" synchronizing these two scripts is then the content of LINGUAS file. Note, that the updated POT and PO files are usually going to be committed back to the repository, so they must be updated within the source -directory (and not in variant directories). Additionaly, the file listing of +directory (and not in variant directories). Additionally, the file listing of po/ directory contains LINGUAS file, so the source tree looks familiar to translators, and they may work with the project in their usual way. @@ -2775,7 +2849,7 @@ project in their usual way. Example 3. Let's prepare a development tree as below - + project/ + SConstruct + build/ @@ -2785,52 +2859,55 @@ Let's prepare a development tree as below + SConscript.i18n + POTFILES.in + LINGUAS - + -with build being variant directory. Write the top-level +with build being the variant directory. +Write the top-level SConstruct script as follows - - # SConstruct - env = Environment( tools = ["default", "gettext"] ) - VariantDir('build', 'src', duplicate = 0) - env['POAUTOINIT'] = 1 - SConscript('src/po/SConscript.i18n', exports = 'env') - SConscript('build/po/SConscript', exports = 'env') - + +# SConstruct +env = Environment(tools=["default", "gettext"]) +VariantDir('build', 'src', duplicate=False) +env['POAUTOINIT'] = True +SConscript('src/po/SConscript.i18n', exports='env') +SConscript('build/po/SConscript', exports='env') + the src/po/SConscript.i18n as - - # src/po/SConscript.i18n - Import('env') - env.Translate(LINGUAS_FILE=1, XGETTEXTFROM='POTFILES.in', XGETTEXTPATH=['../']) - + +# src/po/SConscript.i18n +Import('env') +env.Translate(LINGUAS_FILE=True, XGETTEXTFROM='POTFILES.in', XGETTEXTPATH=['../']) + and the src/po/SConscript - - # src/po/SConscript - Import('env') - env.MOFiles(LINGUAS_FILE = 1) - + +# src/po/SConscript +Import('env') +env.MOFiles(LINGUAS_FILE=True) + -Such setup produces POT and PO files -under source tree in src/po/ and binary -MO files under variant tree in +Such a setup produces POT and PO files +under the source tree in src/po/ and binary +MO files under the variant tree in build/po/. This way the POT and PO files are separated from other output files, which must not be committed back to source repositories (e.g. MO files). - -In above example, the PO files are not updated, -nor created automatically when you issue scons '.' command. -The files must be updated (created) by hand via scons -po-update and then MO files can be compiled by -running scons '.'. - +In the above example, +the PO files are not updated, +nor created automatically when you issue the command +scons .. +The files must be updated (created) by hand via +scons po-update +and then MO files can be compiled by +running scons .. + diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml index 0f0cfe1415..d7e5ecca36 100644 --- a/doc/generated/examples/caching_ex-random_1.xml +++ b/doc/generated/examples/caching_ex-random_1.xml @@ -1,8 +1,8 @@ % scons -Q -cc -o f1.o -c f1.c -cc -o f4.o -c f4.c -cc -o f5.o -c f5.c cc -o f2.o -c f2.c +cc -o f4.o -c f4.c cc -o f3.o -c f3.c +cc -o f1.o -c f1.c +cc -o f5.o -c f5.c cc -o prog f1.o f2.o f3.o f4.o f5.o diff --git a/doc/generated/examples/commandline_PackageVariable_1.xml b/doc/generated/examples/commandline_PackageVariable_1.xml index 367638f371..49c6c85c7e 100644 --- a/doc/generated/examples/commandline_PackageVariable_1.xml +++ b/doc/generated/examples/commandline_PackageVariable_1.xml @@ -3,7 +3,7 @@ cc -o foo.o -c -DPACKAGE="/opt/location" foo.c % scons -Q PACKAGE=/usr/local/location foo.o cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c % scons -Q PACKAGE=yes foo.o -cc -o foo.o -c -DPACKAGE="True" foo.c +cc -o foo.o -c -DPACKAGE="/opt/location" foo.c % scons -Q PACKAGE=no foo.o cc -o foo.o -c -DPACKAGE="False" foo.c diff --git a/doc/generated/examples/troubleshoot_explain1_3.xml b/doc/generated/examples/troubleshoot_explain1_3.xml index e658d89fd2..d301ff3229 100644 --- a/doc/generated/examples/troubleshoot_explain1_3.xml +++ b/doc/generated/examples/troubleshoot_explain1_3.xml @@ -2,5 +2,5 @@ cp file.in file.oout scons: warning: Cannot find target file.out after building -File "/Users/bdbaddog/devel/scons/git/as_scons/scripts/scons.py", line 97, in <module> +File "/Users/bdbaddog/devel/scons/git/users/prs/scripts/scons.py", line 97, in <module> diff --git a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml index 5d10fea3a1..fdc755d32c 100644 --- a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml +++ b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml @@ -1,8 +1,8 @@ % scons -Q --taskmastertrace=- prog -Job.NewParallel._work(): [Thread:8682049344] Gained exclusive access -Job.NewParallel._work(): [Thread:8682049344] Starting search -Job.NewParallel._work(): [Thread:8682049344] Found 0 completed tasks to process -Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks +Job.NewParallel._work(): [Thread:8445988672] Gained exclusive access +Job.NewParallel._work(): [Thread:8445988672] Starting search +Job.NewParallel._work(): [Thread:8445988672] Found 0 completed tasks to process +Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'prog'> and its children: @@ -18,12 +18,12 @@ Taskmaster: Evaluating <pending 0 'prog.c'> Task.make_ready_current(): node <pending 0 'prog.c'> Task.prepare(): node <up_to_date 0 'prog.c'> -Job.NewParallel._work(): [Thread:8682049344] Found internal task +Job.NewParallel._work(): [Thread:8445988672] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'prog.c'> Task.postprocess(): node <up_to_date 0 'prog.c'> Task.postprocess(): removing <up_to_date 0 'prog.c'> Task.postprocess(): adjusted parent ref count <pending 1 'prog.o'> -Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks +Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'inc.h'> and its children: @@ -31,12 +31,12 @@ Taskmaster: Evaluating <pending 0 'inc.h'> Task.make_ready_current(): node <pending 0 'inc.h'> Task.prepare(): node <up_to_date 0 'inc.h'> -Job.NewParallel._work(): [Thread:8682049344] Found internal task +Job.NewParallel._work(): [Thread:8445988672] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'inc.h'> Task.postprocess(): node <up_to_date 0 'inc.h'> Task.postprocess(): removing <up_to_date 0 'inc.h'> Task.postprocess(): adjusted parent ref count <pending 0 'prog.o'> -Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks +Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog.o'> and its children: @@ -46,19 +46,19 @@ Taskmaster: Evaluating <pending 0 'prog.o'> Task.make_ready_current(): node <pending 0 'prog.o'> Task.prepare(): node <executing 0 'prog.o'> -Job.NewParallel._work(): [Thread:8682049344] Found task requiring execution -Job.NewParallel._work(): [Thread:8682049344] Executing task +Job.NewParallel._work(): [Thread:8445988672] Found task requiring execution +Job.NewParallel._work(): [Thread:8445988672] Executing task Task.execute(): node <executing 0 'prog.o'> cc -o prog.o -c -I. prog.c -Job.NewParallel._work(): [Thread:8682049344] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8682049344] Gained exclusive access -Job.NewParallel._work(): [Thread:8682049344] Starting search -Job.NewParallel._work(): [Thread:8682049344] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8445988672] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8445988672] Gained exclusive access +Job.NewParallel._work(): [Thread:8445988672] Starting search +Job.NewParallel._work(): [Thread:8445988672] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog.o'> Task.postprocess(): node <executed 0 'prog.o'> Task.postprocess(): removing <executed 0 'prog.o'> Task.postprocess(): adjusted parent ref count <pending 0 'prog'> -Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks +Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog'> and its children: @@ -67,21 +67,21 @@ Taskmaster: Evaluating <pending 0 'prog'> Task.make_ready_current(): node <pending 0 'prog'> Task.prepare(): node <executing 0 'prog'> -Job.NewParallel._work(): [Thread:8682049344] Found task requiring execution -Job.NewParallel._work(): [Thread:8682049344] Executing task +Job.NewParallel._work(): [Thread:8445988672] Found task requiring execution +Job.NewParallel._work(): [Thread:8445988672] Executing task Task.execute(): node <executing 0 'prog'> cc -o prog prog.o -Job.NewParallel._work(): [Thread:8682049344] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8682049344] Gained exclusive access -Job.NewParallel._work(): [Thread:8682049344] Starting search -Job.NewParallel._work(): [Thread:8682049344] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8445988672] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8445988672] Gained exclusive access +Job.NewParallel._work(): [Thread:8445988672] Starting search +Job.NewParallel._work(): [Thread:8445988672] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog'> Task.postprocess(): node <executed 0 'prog'> -Job.NewParallel._work(): [Thread:8682049344] Searching for new tasks +Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. -Job.NewParallel._work(): [Thread:8682049344] Found no task requiring execution, and have no jobs: marking complete -Job.NewParallel._work(): [Thread:8682049344] Gained exclusive access -Job.NewParallel._work(): [Thread:8682049344] Completion detected, breaking from main loop +Job.NewParallel._work(): [Thread:8445988672] Found no task requiring execution, and have no jobs: marking complete +Job.NewParallel._work(): [Thread:8445988672] Gained exclusive access +Job.NewParallel._work(): [Thread:8445988672] Completion detected, breaking from main loop diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index 8563d66acb..9668af818f 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -26,13 +26,12 @@ for a complete explanation of the arguments and behavior. Note that the &f-env-Action; form of the invocation will expand -construction variables in any argument strings, +&consvars; in any argument strings, including the action argument, at the time it is called -using the construction variables in the -env -construction environment through which +using the &consvars; in the +&consenv; through which &f-env-Action; was called. The &f-Action; global function form delays all variable expansion @@ -98,7 +97,7 @@ but with a few additional capabilities noted below. See the optparse documentation -for a thorough discussion of its option-processing capabities. +for a thorough discussion of its option-processing capabilities. All options added through &f-AddOption; are placed in a special "Local Options" option group. @@ -440,7 +439,7 @@ Example: AllowSubstExceptions() # Also allow a string containing a zero-division expansion -# like '${1 / 0}' to evalute to ''. +# like '${1 / 0}' to evaluate to ''. AllowSubstExceptions(IndexError, NameError, ZeroDivisionError) @@ -451,7 +450,7 @@ AllowSubstExceptions(IndexError, NameError, ZeroDivisionError) Marks each given target -so that it is always assumed to be out of date, +so that it is always assumed to be out-of-date, and will always be rebuilt if needed. Note, however, that &f-AlwaysBuild; @@ -506,15 +505,15 @@ named key, then key is simply stored with a value of val. Otherwise, val is -combinined with the existing value, +combined 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 +however, the contents of &cv-link-CPPDEFINES; +will be post-processed 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, @@ -630,7 +629,7 @@ scons: `.' is up to date. Changed in version 4.5: -clarifined the use of tuples vs. other types, +clarified the use of tuples vs. other types, handling is now consistent across the four functions. @@ -657,7 +656,7 @@ See &cv-link-CPPDEFINES; for more details. Appending a string val -to a dictonary-typed &consvar; enters +to a dictionary-typed &consvar; enters val as the key in the dictionary, and None as its value. Using a tuple type to supply a key-value pair @@ -784,14 +783,14 @@ for a complete explanation of the arguments and behavior. Note that the env.Builder() form of the invocation will expand -construction variables in any arguments strings, +&consvars; in any arguments strings, including the action argument, at the time it is called -using the construction variables in the +using the &consvars; in the env -construction environment through which +&consenv; through which &f-env-Builder; was called. The &f-Builder; @@ -822,12 +821,12 @@ disables derived file caching. Calling the environment method &f-link-env-CacheDir; limits the effect to targets built -through the specified construction environment. +through the specified &consenv;. Calling the global function &f-link-CacheDir; sets a global default that will be used by all targets built -through construction environments +through &consenvs; that do not set up environment-specific caching by calling &f-env-CacheDir;. @@ -950,7 +949,7 @@ either as separate arguments to the &f-Clean; method, or as a list. &f-Clean; -will also accept the return value of any of the construction environment +will also accept the return value of the &consenv; Builder methods. Examples: @@ -982,7 +981,7 @@ Clean(['foo', 'bar'], 'something_else_to_clean') In this example, installing the project creates a subdirectory for the documentation. This statement causes the subdirectory to be removed -if the project is deinstalled. +if the project is uninstalled. Clean(docdir, os.path.join(docdir, projectname)) @@ -1207,8 +1206,8 @@ for a complete explanation of the arguments and behavior. DebugOptions([json]) -Allows setting options for SCons debug options. Currently the only supported value is - json which sets the path to the json file created when +Allows setting options for SCons debug options. Currently, the only supported value is + json which sets the path to the JSON file created when --debug=json is set. @@ -1222,13 +1221,11 @@ DebugOptions(json='#/build/output/scons_stats.json') env.Decider(function) Specifies that all up-to-date decisions for -targets built through this construction environment -will be handled by the specified -function. +targets built through this &consenv; +will be handled by function. function can be the name of a function or one of the following strings -that specify the predefined decision function -that will be applied: +that specify a predefined decider function: @@ -1237,7 +1234,7 @@ that will be applied: "content" -Specifies that a target shall be considered out of date and rebuilt +Specifies that a target shall be considered out-of-date and rebuilt if the dependency's content has changed since the last time the target was built, as determined by performing a checksum @@ -1259,7 +1256,7 @@ can still be used as a synonym, but is deprecated. "content-timestamp" -Specifies that a target shall be considered out of date and rebuilt +Specifies that a target shall be considered out-of-date and rebuilt if the dependency's content has changed since the last time the target was built, except that dependencies with a timestamp that matches @@ -1296,7 +1293,7 @@ can still be used as a synonym, but is deprecated. "timestamp-newer" -Specifies that a target shall be considered out of date and rebuilt +Specifies that a target shall be considered out-of-date and rebuilt if the dependency's timestamp is newer than the target file's timestamp. This is the behavior of the classic Make utility, and @@ -1310,7 +1307,7 @@ can be used a synonym for "timestamp-match" -Specifies that a target shall be considered out of date and rebuilt +Specifies that a target shall be considered out-of-date and rebuilt if the dependency's timestamp is different than the timestamp recorded the last time the target was built. This provides behavior very similar to the classic Make utility @@ -1335,7 +1332,7 @@ Examples: Decider('timestamp-match') # Use hash content signatures for any targets built -# with the attached construction environment. +# with the attached &consenv;. env.Decider('content') @@ -1356,7 +1353,7 @@ The Node (file) which should cause the target to be rebuilt -if it has "changed" since the last tme +if it has "changed" since the last time target was built. @@ -1431,7 +1428,7 @@ otherwise not be rebuilt). Note that the decision can be made -using whatever criteria are appopriate. +using whatever criteria are appropriate. Ignoring some or all of the function arguments is perfectly normal. @@ -1538,7 +1535,7 @@ build phase begins, if has not already been done. However, calling it explicitly provides the opportunity to affect and examine its contents. Instantiation occurs even if nothing in the build system -appars to use it, due to internal uses. +appears to use it, due to internal uses. @@ -1617,14 +1614,20 @@ but will not include any such extension in the return value. - env.Dictionary([vars]) + env.Dictionary([var, ...], [as_dict=]) -Returns a dictionary object -containing the &consvars; in the &consenv;. -If there are any arguments specified, -the values of the specified &consvars; -are returned as a string (if one -argument) or as a list of strings. +Return an object containing &consvars; from +env. +If var is omitted, +all the &consvars; with their values +are returned in a dict. +If var is specified, +and as_dict is true, +the specified &consvars; are returned in a dict; +otherwise (the default, for backwards compatibility), +values only are returned, +as a scalar if one var is given, +or as a list if multiples. @@ -1639,8 +1642,8 @@ cc_values = env.Dictionary('CC', 'CCFLAGS', 'CCCOM') The object returned by &f-link-env-Dictionary; should be treated as a read-only view into the &consvars;. -Some &consvars; have special internal handling, -and making changes through the &f-env-Dictionary; object can bypass +Some &consvars; require special internal handling, +and modifying them through the &f-env-Dictionary; object can bypass that handling and cause data inconsistencies. The primary use of &f-env-Dictionary; is for diagnostic purposes - it is used widely by test cases specifically because @@ -1648,6 +1651,11 @@ it bypasses the special handling so that behavior can be verified. + +Changed in NEXT_RELEASE: +as_dict added. + + @@ -1688,22 +1696,28 @@ for more information. - env.Dump([key, ...], [format=]) + env.Dump([var, ...], [format=TYPE]) -Serializes &consvars; from the current &consenv; -to a string. -The method supports the following formats specified by -format, -which must be used a a keyword argument: +Serialize &consvars; from env to a string. +If var is omitted, +all the &consvars; are serialized. +If one or more var values are supplied, +only those variables and their values are serialized. + + + +The optional format string +selects the serialization format: pretty -Returns a pretty-printed representation of the variables +Returns a pretty-printed representation +of the &consvars; - the result will look like a +&Python; dict (this is the default). -The variables will be presented in &Python; dict form. @@ -1711,31 +1725,25 @@ The variables will be presented in &Python; dict form. json -Returns a JSON-formatted string representation of the variables. +Returns a JSON-formatted representation of the variables. The variables will be presented as a JSON object literal, -the JSON equivalent of a &Python; dict. +the JSON equivalent of a &Python; dict.. -If no key is supplied, -all the &consvars; are serialized. -If one or more keys are supplied, -only those keys and their values are serialized. - - - -Changed in NEXT_VERSION: +Changed in NEXT_RELEASE: More than one key can be specified. -The returned string always looks like a dict (or JSON equivalent); +The returned string always looks like a dict +(or equivalent in other formats); previously a single key serialized only the value, not the key with the value. -This SConstruct: +Examples: this &SConstruct; @@ -1757,7 +1765,7 @@ will print something like: -While this SConstruct: +While this &SConstruct;: @@ -2184,7 +2192,7 @@ FindSourceFiles('src') -As you can see build support files (SConstruct in the above example) +As you can see, build support files (&SConstruct; in the above example) will also be returned by this function. @@ -2656,8 +2664,8 @@ or (most commonly) relative to the directory of the current &f-Glob; matches both files stored on disk and Nodes which &SCons; already knows about, even if any corresponding file is not currently stored on disk. -The evironment method form (&f-env-Glob;) -performs string substition on +The environment method form (&f-env-Glob;) +performs string substitution on pattern and returns whatever matches the resulting expanded pattern. The results are sorted, unlike for the similar &Python; @@ -2748,7 +2756,7 @@ If the optional source argument evaluates true, and the local directory is a variant directory, -then &f-Glob; returnes Nodes from +then &f-Glob; returns Nodes from the corresponding source directory, rather than the local directory. @@ -2782,7 +2790,7 @@ directory.) The optional exclude argument may be set to a pattern or a list of patterns -descibing files or directories +describing files or directories to filter out of the match list. Elements matching a least one specified pattern will be excluded. These patterns use the same syntax as for @@ -2927,7 +2935,7 @@ Import("*") The specified string will be preserved as-is -and not have construction variables expanded. +and not have &consvars; expanded. @@ -2976,7 +2984,7 @@ In case of duplication, any &consvar; names that end in PATH keep the left-most value so the -path searcb order is not altered. +path search order is not altered. All other &consvars; keep the right-most value. If unique is false, @@ -3026,7 +3034,7 @@ either as separate arguments to the &f-NoCache; method, or as a list. &f-NoCache; -will also accept the return value of any of the construction environment +will also accept the return value of any of the &consenv; Builder methods. @@ -3074,7 +3082,7 @@ either as separate arguments to the &f-NoClean; method, or as a list. &f-NoClean; -will also accept the return value of any of the construction environment +will also accept the return value of any of the &consenv; Builder methods. @@ -3115,7 +3123,7 @@ is omitted or None, &f-link-env-MergeFlags; is used. By default, duplicate values are not -added to any construction variables; +added to any &consvars;; you can specify unique=False to allow duplicate values to be added. @@ -3138,11 +3146,11 @@ the output produced by command in order to distribute it to appropriate &consvars;. &f-env-MergeFlags; uses a separate function to do that processing - -see &f-link-env-ParseFlags; for the details, including a -a table of options and corresponding construction variables. +see &f-link-env-ParseFlags; for the details, including +a table of options and corresponding &consvars;. To provide alternative processing of the output of command, -you can suppply a custom +you can supply a custom function, which must accept three arguments: the &consenv; to modify, @@ -3215,12 +3223,12 @@ function. Parses one or more strings containing typical command-line flags for GCC-style tool chains and returns a dictionary with the flag values -separated into the appropriate SCons construction variables. +separated into the appropriate SCons &consvars;. Intended as a companion to the &f-link-env-MergeFlags; method, but allows for the values in the returned dictionary to be modified, if necessary, -before merging them into the construction environment. +before merging them into the &consenv;. (Note that &f-env-MergeFlags; will call this method if its argument is not a dictionary, @@ -3249,7 +3257,7 @@ See &f-link-ParseConfig; for more details. Flag values are translated according to the prefix found, -and added to the following construction variables: +and added to the following &consvars;: @@ -3289,8 +3297,7 @@ and added to the following construction variables: Any other strings not associated with options are assumed to be the names of libraries and added to the -&cv-LIBS; -construction variable. +&cv-LIBS; &consvar;. @@ -3315,7 +3322,7 @@ selected by plat (defaults to the detected platform for the current system) that can be used to initialize -a construction environment by passing it as the +a &consenv; by passing it as the platform keyword argument to the &f-link-Environment; function. @@ -3361,7 +3368,7 @@ Returns a list of the affected target nodes. env.Prepend(key=val, [...]) Prepend values to &consvars; in the current &consenv;, -Works like &f-link-env-Append; (see for details), +works like &f-link-env-Append; (see for details), except that values are added to the front, rather than the end, of any existing value of the &consvar; @@ -3566,7 +3573,7 @@ If the string contains the verbatim substring it will be replaced with the Node. Note that, for performance reasons, this is not -a regular SCons variable substition, +a regular SCons variable substitution, so you can not use other variables or use curly braces. The following example will print the name of @@ -3666,7 +3673,7 @@ env = Environment( env.Replace(key=val, [...]) -Replaces construction variables in the Environment +Replaces &consvars; in the Environment with the specified keyword arguments. @@ -3683,50 +3690,43 @@ env.Replace(CCFLAGS='-g', FOO='foo.xxx') Repository(directory) env.Repository(directory) -Specifies that +Sets directory -is a repository to be searched for files. +as a repository to be searched for files contributing to the build. Multiple calls to &f-Repository; -are legal, -and each one adds to the list of -repositories that will be searched. +are allowed, +with repositories searched in the given order. +Repositories specified via command-line option +have higher priority. -To +In &scons;, -a repository is a copy of the source tree, -from the top-level directory on down, -which may contain -both source files and derived files +a repository is partial or complete copy of the source tree, +from the top-level directory down, +containing source files that can be used to build targets in -the local source tree. -The canonical example would be an -official source tree maintained by an integrator. -If the repository contains derived files, -then the derived files should have been built using -&scons;, -so that the repository contains the necessary -signature information to allow -&scons; -to figure out when it is appropriate to -use the repository copy of a derived file, -instead of building one locally. +the current worktree. +Repositories can also contain derived files. +An example might be an official source tree maintained by an integrator. +If a repository contains derived files, +they should be the result of building with &SCons;, +so a signature database (sconsign) is present +in the repository, +allowing better decisions on whether they are +up-to-date or not. Note that if an up-to-date derived file already exists in a repository, -&scons; -will +&scons; will not make a copy in the local directory tree. -In order to guarantee that a local copy -will be made, -use the -&f-link-Local; -method. +If you need a local copy to be made, +use the &f-link-Local; method. @@ -3769,7 +3769,7 @@ env.Requires('foo', 'file-that-must-be-built-before-foo') Return to the calling SConscript, optionally returning the values of variables named in vars. -Multiple strings contaning variable names may be passed to +Multiple strings containing variable names may be passed to &f-Return;. A string containing white space is split into individual variable names. Returns the value if one variable is specified, @@ -4174,7 +4174,7 @@ in a separate .sconsign file in each directory, not in a single combined database file. -This is a backwards-compatibility meaure to support +This is a backwards-compatibility measure to support what was the default behavior prior to &SCons; 0.97 (i.e. before 2008). Use of this mode is discouraged and may be @@ -4212,7 +4212,7 @@ SConsignFile(dbm_module=dbm.gnu) env.SetDefault(key=val, [...]) -Sets construction variables to default values specified with the keyword +Sets &consvars; to default values specified with the keyword arguments if (and only if) the variables are not already set. The following statements are equivalent: @@ -4836,7 +4836,7 @@ Returns a Node object representing the specified &Python; Value Nodes can be used as dependencies of targets. If the string representation of the Value Node changes between &SCons; runs, it is considered -out of date and any targets depending it will be rebuilt. +out-of-date and any targets depending on it will be rebuilt. Since Value Nodes have no filesystem representation, timestamps are not used; the timestamp deciders perform the same content-based up to date check. diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen index b2b15831bc..60fdf4bf07 100644 --- a/doc/generated/tools.gen +++ b/doc/generated/tools.gen @@ -465,33 +465,33 @@ Sets construction variables for the D language compiler GDC. gettext -This is actually a toolset, which supports internationalization and -localization of software being constructed with SCons. The toolset loads -following tools: +A toolset supporting internationalization and +localization of software being constructed with &SCons;. +The toolset loads the following tools: - &t-link-xgettext; - to extract internationalized messages from source code to - POT file(s), + &t-link-xgettext; - extract internationalized messages from source code to + POT file(s). - &t-link-msginit; - may be optionally used to initialize PO - files, + &t-link-msginit; - initialize PO + files during initial translation of a project. - &t-link-msgmerge; - to update PO files, that already contain + &t-link-msgmerge; - update PO files that already contain translated messages, - &t-link-msgfmt; - to compile textual PO file to binary - installable MO file. + &t-link-msgfmt; - compile textual PO files to binary + installable MO files. -When you enable &t-gettext;, it internally loads all abovementioned tools, +When you enable &t-gettext;, it internally loads all the above-mentioned tools, so you're encouraged to see their individual documentation. @@ -503,12 +503,12 @@ may be however interested in top-level -To use &t-gettext; tools add 'gettext' tool to your -environment: +To use the &t-gettext; tools, add the 'gettext' tool to your +&consenv;: - - env = Environment( tools = ['default', 'gettext'] ) - + +env = Environment(tools=['default', 'gettext']) + @@ -529,7 +529,7 @@ Set construction variables for GNU linker/loader. gs -This Tool sets the required construction variables for working with +This &f-Tool; sets the required construction variables for working with the Ghostscript software. It also registers an appropriate Action with the &b-link-PDF; Builder, such that the conversion from PS/EPS to PDF happens automatically for the TeX/LaTeX toolchain. @@ -665,7 +665,7 @@ Sets construction variables for the D language compiler LDC2. lex -Sets construction variables for the &lex; lexical analyser. +Sets construction variables for the &lex; lexical analyzer. Sets: &cv-link-LEX;, &cv-link-LEXCOM;, &cv-link-LEXFLAGS;, &cv-link-LEXUNISTD;.Uses: &cv-link-LEXCOMSTR;, &cv-link-LEXFLAGS;, &cv-link-LEX_HEADER_FILE;, &cv-link-LEX_TABLES_FILE;. @@ -718,28 +718,35 @@ Sets construction variables for MinGW (Minimal Gnu on Windows). msgfmt -This scons tool is a part of scons &t-link-gettext; toolset. It provides scons -interface to msgfmt(1) command, which generates binary -message catalog (MO) from a textual translation description -(PO). +This tool is a part of the &t-link-gettext; toolset. +It provides &SCons; +an interface to the msgfmt(1) command +by setting up the &b-link-MOFiles; builder, +which generates binary message catalog (MO) files +from a textual translation description +(PO files). Sets: &cv-link-MOSUFFIX;, &cv-link-MSGFMT;, &cv-link-MSGFMTCOM;, &cv-link-MSGFMTCOMSTR;, &cv-link-MSGFMTFLAGS;, &cv-link-POSUFFIX;.Uses: &cv-link-LINGUAS_FILE;. msginit -This scons tool is a part of scons &t-link-gettext; toolset. It provides -scons interface to msginit(1) program, which creates new +This tool is a part of scons &t-link-gettext; toolset. It provides +&SCons; an interface to the msginit(1) program, +by setting up the &b-link-POInit; builder, +which creates a new PO file, initializing the meta information with values from -user's environment (or options). +the &consenv; (or options). Sets: &cv-link-MSGINIT;, &cv-link-MSGINITCOM;, &cv-link-MSGINITCOMSTR;, &cv-link-MSGINITFLAGS;, &cv-link-POAUTOINIT;, &cv-link-POCREATE_ALIAS;, &cv-link-POSUFFIX;, &cv-link-POTSUFFIX;, &cv-link-_MSGINITLOCALE;.Uses: &cv-link-LINGUAS_FILE;, &cv-link-POAUTOINIT;, &cv-link-POTDOMAIN;. msgmerge -This scons tool is a part of scons &t-link-gettext; toolset. It provides -scons interface to msgmerge(1) command, which merges two +This tool is a part of scons &t-link-gettext; toolset. It provides +&SCons; an interface to the msgmerge(1) command, +by setting up the &b-link-POUpdate; builder, +which merges two Uniform style .po files together. Sets: &cv-link-MSGMERGE;, &cv-link-MSGMERGECOM;, &cv-link-MSGMERGECOMSTR;, &cv-link-MSGMERGEFLAGS;, &cv-link-POSUFFIX;, &cv-link-POTSUFFIX;, &cv-link-POUPDATE_ALIAS;.Uses: &cv-link-LINGUAS_FILE;, &cv-link-POAUTOINIT;, &cv-link-POTDOMAIN;. @@ -921,7 +928,7 @@ The &t-qt3; tool supports the following operations: Automatic moc file generation from header files. You do not have to specify moc files explicitly, the tool does it for you. However, there are a few preconditions to do so: Your header file must have -the same filebase as your implementation file and must stay in the same +the same basename as your implementation file and must stay in the same directory. It must have one of the suffixes .h, .hpp, @@ -1085,18 +1092,18 @@ Set &consvars; for the &b-link-Textfile; and &b-link-Substfile; builders. tlib -Sets construction variables for the Borlan -tib library archiver. +Sets construction variables for the Borland +tlib library archiver. Sets: &cv-link-AR;, &cv-link-ARCOM;, &cv-link-ARFLAGS;, &cv-link-LIBPREFIX;, &cv-link-LIBSUFFIX;.Uses: &cv-link-ARCOMSTR;. xgettext -This scons tool is a part of scons &t-link-gettext; toolset. It provides -scons interface to xgettext(1) -program, which extracts internationalized messages from source code. The tool -provides &b-POTUpdate; builder to make PO +This tool is a part of the &t-link-gettext; toolset. It provides +&SCons; an interface to the xgettext(1) +program, which extracts internationalized messages from source code. +The tool sets up the &b-POTUpdate; builder to make PO Template files. Sets: &cv-link-POTSUFFIX;, &cv-link-POTUPDATE_ALIAS;, &cv-link-XGETTEXTCOM;, &cv-link-XGETTEXTCOMSTR;, &cv-link-XGETTEXTFLAGS;, &cv-link-XGETTEXTFROM;, &cv-link-XGETTEXTFROMPREFIX;, &cv-link-XGETTEXTFROMSUFFIX;, &cv-link-XGETTEXTPATH;, &cv-link-XGETTEXTPATHPREFIX;, &cv-link-XGETTEXTPATHSUFFIX;, &cv-link-_XGETTEXTDOMAIN;, &cv-link-_XGETTEXTFROMFLAGS;, &cv-link-_XGETTEXTPATHFLAGS;.Uses: &cv-link-POTDOMAIN;. diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 26371cff20..8383f2c5e6 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -18,7 +18,7 @@ This construction variable automatically introduces &cv-link-_LDMODULEVERSIONFLAGS; -if &cv-link-LDMODULEVERSION; is set. Othervise it evaluates to an empty string. +if &cv-link-LDMODULEVERSION; is set. Otherwise, it evaluates to an empty string. @@ -28,7 +28,7 @@ if &cv-link-LDMODULEVERSION; is set. Othervise it evaluates to an empty string. This construction variable automatically introduces &cv-link-_SHLIBVERSIONFLAGS; -if &cv-link-SHLIBVERSION; is set. Othervise it evaluates to an empty string. +if &cv-link-SHLIBVERSION; is set. Otherwise, it evaluates to an empty string. @@ -560,7 +560,7 @@ after the SCons template for the file has been written. A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -572,7 +572,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -763,7 +763,7 @@ and the second is the macro definition. If the definition is not omitted or None, the name and definition are combined into a single name=definition item -before the preending/appending. +before the prepending/appending. @@ -912,7 +912,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. -In general it's not advised to put include directory directives +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 and the directories will not be searched by the dependency scanner. @@ -927,7 +927,7 @@ directory names in &cv-CPPPATH; will be looked-up relative to the directory of the SConscript file 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 lookup a directory relative to the root of the source tree, use the # prefix: @@ -936,7 +936,7 @@ env = Environment(CPPPATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &f-link-Dir; function: @@ -990,7 +990,7 @@ The default list is: The C++ compiler. -See also &cv-link-SHCXX; for compiling to shared objects.. +See also &cv-link-SHCXX; for compiling to shared objects. @@ -1003,7 +1003,7 @@ The command line used to compile a C++ source file to an object file. Any options specified in the &cv-link-CXXFLAGS; and &cv-link-CPPFLAGS; construction variables are included on this command line. -See also &cv-link-SHCXXCOM; for compiling to shared objects.. +See also &cv-link-SHCXXCOM; for compiling to shared objects. @@ -1015,7 +1015,7 @@ See also &cv-link-SHCXXCOM; for compiling to shared objects.. If set, the string displayed when a C++ source file is compiled to a (static) object file. If not set, then &cv-link-CXXCOM; (the command line) is displayed. -See also &cv-link-SHCXXCOMSTR; for compiling to shared objects.. +See also &cv-link-SHCXXCOMSTR; for compiling to shared objects. @@ -1042,7 +1042,7 @@ and as C++ files, and files with .mm -suffixes as Objective C++ files. +suffixes as Objective-C++ files. On case-sensitive systems (Linux, UNIX, and other POSIX-alikes), SCons also treats .C @@ -1061,7 +1061,7 @@ By default, this includes the value of &cv-link-CCFLAGS;, so that setting &cv-CCFLAGS; affects both C and C++ compilation. If you want to add C++-specific flags, you must set or override the value of &cv-link-CXXFLAGS;. -See also &cv-link-SHCXXFLAGS; for compiling to shared objects.. +See also &cv-link-SHCXXFLAGS; for compiling to shared objects. @@ -1509,7 +1509,7 @@ The string displayed when a renderer like fop or DOCBOOK_FOPFLAGS -Additonal command-line flags for the +Additional command-line flags for the PDF renderer fop or xep. @@ -1551,7 +1551,7 @@ XIncludes for a given XML file. DOCBOOK_XMLLINTFLAGS -Additonal command-line flags for the external executable +Additional command-line flags for the external executable xmllint. @@ -1595,7 +1595,7 @@ an XML file via a given XSLT stylesheet. DOCBOOK_XSLTPROCFLAGS -Additonal command-line flags for the external executable +Additional command-line flags for the external executable xsltproc (or saxon, xalan). @@ -1606,7 +1606,7 @@ Additonal command-line flags for the external executable DOCBOOK_XSLTPROCPARAMS -Additonal parameters that are not intended for the XSLT processor executable, but +Additional parameters that are not intended for the XSLT processor executable, but the XSL processing itself. By default, they get appended at the end of the command line for saxon and saxon-xslt, respectively. @@ -1907,7 +1907,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F03PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F03PATH; if you need to define a specific include path for Fortran 03 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -1921,7 +1921,7 @@ env = Environment(F03PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: @@ -2095,7 +2095,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F08PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F08PATH; if you need to define a specific include path for Fortran 08 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -2109,7 +2109,7 @@ env = Environment(F08PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: @@ -2283,7 +2283,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F77PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F77PATH; if you need to define a specific include path for Fortran 77 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -2297,7 +2297,7 @@ env = Environment(F77PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: @@ -2471,7 +2471,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F90PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F90PATH; if you need to define a specific include path for Fortran 90 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -2485,7 +2485,7 @@ env = Environment(F90PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: @@ -2658,7 +2658,7 @@ and the directories will not be searched by the dependency scanner. Note: directory names in &cv-link-F95PATH; will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: You only need to set &cv-link-F95PATH; if you need to define a specific include path for Fortran 95 files. You should normally set the &cv-link-FORTRANPATH; variable, @@ -2672,7 +2672,7 @@ env = Environment(F95PATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: @@ -2962,7 +2962,7 @@ non-portable and the directories will not be searched by the dependency scanner. Note: directory names in FORTRANPATH will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use #: @@ -3365,9 +3365,9 @@ greater than one, as that has a different meaning). Action strings can be segmented by the use of an AND operator, &&. -In a segemented string, each segment is a separate +In a segmented string, each segment is a separate command line, these are run -sequentially until one fails or the entire +sequentially until one fails, or the entire sequence has been executed. If an action string is segmented, then the selected behavior of &cv-IMPLICIT_COMMAND_DEPENDENCIES; @@ -3559,7 +3559,7 @@ env = Environment(JARCOMSTR="JARchiving $SOURCES into $TARGET") General options passed to the Java archive tool. -By default this is set to +By default, this is set to to create the necessary jar @@ -3881,7 +3881,7 @@ for Java classes. that &SCons; expects will be generated by the &javac; compiler. Setting &cv-JAVAVERSION; to a version greater than 1.4 makes &SCons; realize that a build - with such a compiler is actually up to date. + with such a compiler is actually up-to-date. The default is 1.4. @@ -4196,7 +4196,7 @@ by &SCons; in all situations. Consider using LEXUNISTD -Used only on windows environments to set a lex flag to prevent 'unistd.h' from being included. The default value is '--nounistd'. +Used only in Windows environments to set a lex flag to prevent 'unistd.h' from being included. The default value is '--nounistd'. @@ -4362,7 +4362,7 @@ directory names in &cv-LIBPATH; will be looked-up relative to the directory of the SConscript file 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 lookup a directory relative to the root of the source tree, use the # prefix: @@ -4371,7 +4371,7 @@ env = Environment(LIBPATH='#/libs') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &f-link-Dir; function: @@ -4461,7 +4461,7 @@ If &cv-link-LIBLITERALPREFIX; is set to a non-empty string, then a string-valued &cv-LIBS; entry that starts with &cv-link-LIBLITERALPREFIX; will cause the rest of the entry -to be searched for for unmodified, +to be searched for unmodified, but respecting normal library search paths (this is an exception to the guideline above about leaving off the prefix/suffix from the library name). @@ -4492,12 +4492,12 @@ env.Append(LIBS=File('/tmp/mylib.so')) For each &Builder; call that causes linking with libraries, &SCons; will add the libraries in the setting of &cv-LIBS; -in effect at that moment to the dependecy graph +in effect at that moment to the dependency graph as dependencies of the target being generated. -The library list will transformed to command line +The library list will be transformed to command-line arguments through the automatically-generated &cv-link-_LIBFLAGS; &consvar; which is constructed by @@ -4581,10 +4581,10 @@ It defaults to the current system line separator. The &cv-LINGUAS_FILE; defines file(s) containing list of additional linguas to be processed by &b-link-POInit;, &b-link-POUpdate; or &b-link-MOFiles; builders. It also affects &b-link-Translate; builder. If the variable contains -a string, it defines name of the list file. The &cv-LINGUAS_FILE; may be a +a string, it defines the name of the list file. The &cv-LINGUAS_FILE; may be a list of file names as well. If &cv-LINGUAS_FILE; is set to -True (or non-zero numeric value), the list will be read from -default file named +a non-string truthy value, the list will be read from +the file named LINGUAS. @@ -4852,7 +4852,7 @@ See &t-link-msgfmt; tool and &b-link-MOFiles; builder. Path to msginit(1) program (found via -Detect()). +&f-link-Detect;). See &t-link-msginit; tool and &b-link-POInit; builder. @@ -4872,8 +4872,9 @@ See &t-link-msginit; tool and &b-link-POInit; builder. MSGINITCOMSTR -String to display when msginit(1) is invoked -(default: '', which means ``print &cv-link-MSGINITCOM;''). +String to display when msginit(1) is invoked. +The default is an empty string, +which will print the command line (&cv-link-MSGINITCOM;). See &t-link-msginit; tool and &b-link-POInit; builder. @@ -4928,8 +4929,9 @@ See &t-link-msgmerge; tool and &b-link-POUpdate; builder. MSGMERGECOMSTR -String to be displayed when msgmerge(1) is invoked -(default: '', which means ``print &cv-link-MSGMERGECOM;''). +String to be displayed when msgmerge(1) is invoked. +The default is an empty string, +which will print the command line (&cv-link-MSGMERGECOM;). See &t-link-msgmerge; tool and &b-link-POUpdate; builder. @@ -6206,7 +6208,7 @@ Visual Studio It is only necessary to specify the Exp suffix to select the express edition when both express and non-express editions of the same product are installed - simulaneously. The Exp suffix is unnecessary, + simultaneously. The Exp suffix is unnecessary, but accepted, when only the express edition is installed. @@ -6368,7 +6370,7 @@ and &cv-link-MSVC_USE_SETTINGS;. SccProjectFilePathRelativizedFromConnection[i] (where [i] ranges from 0 to the number of projects in the solution) attributes of the GlobalSection(SourceCodeControl) - section of the Microsoft Visual Studio solution file. Similarly + section of the Microsoft Visual Studio solution file. Similarly, the relative solution file path is placed as the values of the SccLocalPath[i] (where [i] ranges from 0 to the number of projects in the solution) attributes of the @@ -6433,7 +6435,7 @@ and &cv-link-MSVC_USE_SETTINGS;. and &cv-MSVC_VERSION; is not, &cv-MSVC_VERSION; will be initialized to the value of &cv-MSVS_VERSION;. - An error is raised if If both are set and have different values, + An error is raised if both are set and have different values. @@ -6759,7 +6761,7 @@ scons NINJA_CMD_ARGS="-v -j 3" NINJA_FORCE_SCONS_BUILD - If true, causes the build nodes to callback to scons instead of using + If true, causes the build nodes to call back to scons instead of using &ninja; to build them. This is intended to be passed to the environment on the builder invocation. It is useful if you have a build node which does something which is not easily translated into &ninja;. @@ -6829,7 +6831,7 @@ scons NINJA_CMD_ARGS="-v -j 3" Internal value used to specify the function to call with argument env to generate the list of files - which if changed would require the &ninja; build file to be regenerated. + which, if changed, would require the &ninja; build file to be regenerated. @@ -6838,7 +6840,7 @@ scons NINJA_CMD_ARGS="-v -j 3" NINJA_SCONS_DAEMON_KEEP_ALIVE - The number of seconds for the SCons deamon launched by ninja to stay alive. + The number of seconds for the SCons daemon launched by ninja to stay alive. (Default: 180000) @@ -6916,7 +6918,7 @@ placed if applicable. The default value is &cv-NAME;-&cv-VERSION; Selects the package type to build when using the &b-link-Package; -builder. May be a string or list of strings. See the docuentation +builder. It may be a string or list of strings. See the documentation for the builder for the currently supported types. @@ -7000,7 +7002,7 @@ only if the &cv-link-PDB; &consvar; is set. This variable specifies how much of a source file is precompiled. This variable is ignored by tools other than &MSVC;, or when -the PCH variable is not being used. When this variable is define it +the PCH variable is not being used. When this variable is defined, it must be a string that is the name of the header that is included at the end of the precompiled portion of the source files, or the empty string if the "#pragma hrdstop" construct is being used: @@ -8294,7 +8296,7 @@ See also &cv-link-CXXFLAGS; for compiling to static objects. The name of the compiler to use when compiling D source -destined to be in a shared objects. +destined to be in a shared object. See also &cv-link-DC; for compiling to static objects. @@ -9006,7 +9008,7 @@ When this &consvar; is defined, a versioned shared library is created by the &b-link-SharedLibrary; builder. This activates the &cv-link-_SHLIBVERSIONFLAGS; and thus modifies the &cv-link-SHLINKCOM; as required, adds the version number to the library name, and creates the symlinks -that are needed. &cv-link-SHLIBVERSION; versions should exist as alpha-numeric, +that are needed. &cv-link-SHLIBVERSION; versions should exist as alphanumeric, decimal-delimited values as defined by the regular expression "\w+[\.\w+]*". Example &cv-link-SHLIBVERSION; values include '1', '1.2.3', and '1.2.gitaa412c8b'. @@ -9146,7 +9148,7 @@ The variable is used, for example, by &t-link-gnulink; linker tool. A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -9173,7 +9175,7 @@ field in the controlling information for Ipkg and RPM packages. A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -9458,7 +9460,7 @@ will be looked-up relative to the SConscript directory 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 lookup a directory relative to the root of the source tree, use a top-relative path (#): @@ -9467,7 +9469,7 @@ env = Environment(SWIGPATH='#/include') -The directory look-up can also be forced using the +The directory lookup can also be forced using the &Dir;() function: @@ -9555,7 +9557,7 @@ General options passed to the tar archiver. A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -9631,7 +9633,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -9885,7 +9887,7 @@ The value is informative and is not guaranteed to be complete. A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -9897,7 +9899,7 @@ for more information). A reserved variable name -that may not be set or used in a construction environment. +that may not be set or used in a &consenv;. (See the manpage section "Variable Substitution" for more information). @@ -10362,7 +10364,7 @@ field in the RPM X_RPM_EXTRADEFS -A list used to supply extra defintions or flags +A list used to supply extra definitions or flags to be added to the RPM .spec file. Each item is added as-is with a carriage return appended. This is useful if some specific RPM feature not otherwise @@ -10643,7 +10645,7 @@ See &t-link-xgettext; tool and &b-link-POTUpdate; builder. _XGETTEXTFROMFLAGS -Internal "macro". Genrates list of -D<dir> flags +Internal "macro". Generates list of -D<dir> flags from the &cv-link-XGETTEXTPATH; list. @@ -10654,7 +10656,7 @@ from the &cv-link-XGETTEXTPATH; list. This flag is used to add single &cv-link-XGETTEXTFROM; file to -xgettext(1)'s commandline (default: +xgettext(1)'s command line (default: '-f'). @@ -10698,7 +10700,7 @@ from &cv-link-XGETTEXTFROM;. This flag is used to add single search path to -xgettext(1)'s commandline (default: +xgettext(1)'s command line (default: '-D'). @@ -11010,7 +11012,7 @@ General options passed to the zip utility. An optional zip root directory (default empty). The filenames stored in the zip file will be relative to this directory, if given. -Otherwise the filenames are relative to the current directory of the +Otherwise, the filenames are relative to the current directory of the command. For instance: From 488707f4a9d1f1c7c7cd2b28a0bcbaf4e9bbb0dc Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Sat, 16 Nov 2024 22:24:09 -0600 Subject: [PATCH 202/386] Add type hints to main Node script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Adjust NodeTests to account for tweaks in hinting, primarily accounting for truthiness instead of pure types --- CHANGES.txt | 1 + RELEASE.txt | 2 + SCons/Node/NodeTests.py | 30 ++--- SCons/Node/__init__.py | 251 +++++++++++++++++++++------------------- 4 files changed, 147 insertions(+), 137 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 227a56f90b..73ffba2a52 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -83,6 +83,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER detect and upgrade legacy type-hint syntax. - Removed "SCons.Util.sctyping.py", as the functionality can now be substituted via top-level `from __future__ import annotations`. + - Implemented type hints for Nodes. From Alex James: - On Darwin, PermissionErrors are now handled while trying to access diff --git a/RELEASE.txt b/RELEASE.txt index 72ec73c9c7..da566bd328 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -190,6 +190,8 @@ DEVELOPMENT - Removed "SCons.Util.sctyping.py", as the functionality can now be substituted via top-level `from __future__ import annotations`. +- Implemented type hints for Nodes. + Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text diff --git a/SCons/Node/NodeTests.py b/SCons/Node/NodeTests.py index 6c7437d600..d8288f4c9a 100644 --- a/SCons/Node/NodeTests.py +++ b/SCons/Node/NodeTests.py @@ -461,13 +461,13 @@ def test_push_to_cache(self) -> None: """Test the base push_to_cache() method""" n = SCons.Node.Node() r = n.push_to_cache() - assert r is None, r + assert not r, r def test_retrieve_from_cache(self) -> None: """Test the base retrieve_from_cache() method""" n = SCons.Node.Node() r = n.retrieve_from_cache() - assert r == 0, r + assert not r, r def test_visited(self) -> None: """Test the base visited() method @@ -711,21 +711,21 @@ def test_set_always_build(self) -> None: node = SCons.Node.Node() node.set_always_build() assert node.always_build - node.set_always_build(3) - assert node.always_build == 3 + node.set_always_build(3) # type: ignore[arg-type] + assert node.always_build def test_set_noclean(self) -> None: """Test setting a Node's noclean value """ node = SCons.Node.Node() node.set_noclean() - assert node.noclean == 1, node.noclean - node.set_noclean(7) - assert node.noclean == 1, node.noclean - node.set_noclean(0) - assert node.noclean == 0, node.noclean - node.set_noclean(None) - assert node.noclean == 0, node.noclean + assert node.noclean, node.noclean + node.set_noclean(7) # type: ignore[arg-type] + assert node.noclean, node.noclean + node.set_noclean(0) # type: ignore[arg-type] + assert not node.noclean, node.noclean + node.set_noclean(None) # type: ignore[arg-type] + assert not node.noclean, node.noclean def test_set_precious(self) -> None: """Test setting a Node's precious value @@ -733,8 +733,8 @@ def test_set_precious(self) -> None: node = SCons.Node.Node() node.set_precious() assert node.precious - node.set_precious(7) - assert node.precious == 7 + node.set_precious(7) # type: ignore[arg-type] + assert node.precious def test_set_pseudo(self) -> None: """Test setting a Node's pseudo value @@ -750,14 +750,14 @@ def test_exists(self) -> None: """ node = SCons.Node.Node() e = node.exists() - assert e == 1, e + assert e, e def test_exists_repo(self) -> None: """Test evaluating whether a Node exists locally or in a repository. """ node = SCons.Node.Node() e = node.rexists() - assert e == 1, e + assert e, e class MyNode(SCons.Node.Node): def exists(self) -> str: diff --git a/SCons/Node/__init__.py b/SCons/Node/__init__.py index 8ae991ecf7..055c3090cb 100644 --- a/SCons/Node/__init__.py +++ b/SCons/Node/__init__.py @@ -45,6 +45,7 @@ import collections import copy from itertools import chain, zip_longest +from typing import Any, Callable, TYPE_CHECKING import SCons.Debug import SCons.Executor @@ -54,6 +55,12 @@ from SCons.Executor import Executor from SCons.Util import hash_signature, is_List, UniqueList, render_tree +if TYPE_CHECKING: + from SCons.Builder import BuilderBase + from SCons.Environment import Base as Environment + from SCons.Scanner import ScannerBase + from SCons.SConsign import SConsignEntry + print_duplicate = 0 def classname(obj): @@ -102,7 +109,7 @@ def do_nothing_node(node) -> None: pass Annotate = do_nothing_node # global set for recording all processed SContruct/SConscript nodes -SConscriptNodes = set() +SConscriptNodes: set[Node] = set() # Gets set to 'True' if we're running in interactive mode. Is # currently used to release parts of a target's info during @@ -189,7 +196,7 @@ def get_contents_entry(node): """Fetch the contents of the entry. Returns the exact binary contents of the file.""" try: - node = node.disambiguate(must_exist=1) + node = node.disambiguate(must_exist=True) except SCons.Errors.UserError: # There was nothing on disk with which to disambiguate # this entry. Leave it as an Entry, but return a null @@ -356,7 +363,7 @@ class NodeInfoBase: __slots__ = ('__weakref__',) current_version_id = 2 - def update(self, node) -> None: + def update(self, node: Node) -> None: try: field_list = self.field_list except AttributeError: @@ -376,7 +383,7 @@ def update(self, node) -> None: def convert(self, node, val) -> None: pass - def merge(self, other) -> None: + def merge(self, other: NodeInfoBase) -> None: """ Merge the fields of another object into this object. Already existing information is overwritten by the other instance's data. @@ -386,7 +393,7 @@ def merge(self, other) -> None: state = other.__getstate__() self.__setstate__(state) - def format(self, field_list=None, names: int=0): + def format(self, field_list: list[str] | None = None, names: bool = False): if field_list is None: try: field_list = self.field_list @@ -409,7 +416,7 @@ def format(self, field_list=None, names: int=0): fields.append(f) return fields - def __getstate__(self): + def __getstate__(self) -> dict[str, Any]: """ Return all fields that shall be pickled. Walk the slots in the class hierarchy and add those to the state dictionary. If a '__dict__' slot is @@ -429,7 +436,7 @@ def __getstate__(self): pass return state - def __setstate__(self, state) -> None: + def __setstate__(self, state: dict[str, Any]) -> None: """ Restore the attributes from a pickled state. The version is discarded. """ @@ -458,12 +465,12 @@ class BuildInfoBase: def __init__(self) -> None: # Create an object attribute from the class attribute so it ends up # in the pickled data in the .sconsign file. - self.bsourcesigs = [] - self.bdependsigs = [] - self.bimplicitsigs = [] - self.bactsig = None + self.bsourcesigs: list[BuildInfoBase] = [] + self.bdependsigs: list[BuildInfoBase] = [] + self.bimplicitsigs: list[BuildInfoBase] = [] + self.bactsig: str | None = None - def merge(self, other) -> None: + def merge(self, other: BuildInfoBase) -> None: """ Merge the fields of another object into this object. Already existing information is overwritten by the other instance's data. @@ -473,7 +480,7 @@ def merge(self, other) -> None: state = other.__getstate__() self.__setstate__(state) - def __getstate__(self): + def __getstate__(self) -> dict[str, Any]: """ Return all fields that shall be pickled. Walk the slots in the class hierarchy and add those to the state dictionary. If a '__dict__' slot is @@ -493,7 +500,7 @@ def __getstate__(self): pass return state - def __setstate__(self, state) -> None: + def __setstate__(self, state: dict[str, Any]) -> None: """ Restore the attributes from a pickled state. """ @@ -571,42 +578,42 @@ def __init__(self) -> None: # this way, instead of wrapping up each list+dictionary pair in # a class. (Of course, we could always still do that in the # future if we had a good reason to...). - self.sources = [] # source files used to build node - self.sources_set = set() + self.sources: list[Node] = [] # source files used to build node + self.sources_set: set[Node] = set() self._specific_sources = False - self.depends = [] # explicit dependencies (from Depends) - self.depends_set = set() - self.ignore = [] # dependencies to ignore - self.ignore_set = set() - self.prerequisites = None - self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) - self.waiting_parents = set() - self.waiting_s_e = set() + self.depends: list[Node] = [] # explicit dependencies (from Depends) + self.depends_set: set[Node] = set() + self.ignore: list[Node] = [] # dependencies to ignore + self.ignore_set: set[Node] = set() + self.prerequisites: UniqueList | None = None + self.implicit: list[Node] | None = None # implicit (scanned) dependencies (None means not scanned yet) + self.waiting_parents: set[Node] = set() + self.waiting_s_e: set[Node] = set() self.ref_count = 0 - self.wkids = None # Kids yet to walk, when it's an array + self.wkids: list[Node] | None = None # Kids yet to walk, when it's an array - self.env = None + self.env: Environment | None = None self.state = no_state - self.precious = None + self.precious = False self.pseudo = False - self.noclean = 0 - self.nocache = 0 - self.cached = 0 # is this node pulled from cache? - self.always_build = None - self.includes = None + self.noclean = False + self.nocache = False + self.cached = False # is this node pulled from cache? + self.always_build = False + self.includes: list[str] | None = None self.attributes = self.Attrs() # Generic place to stick information about the Node. - self.side_effect = 0 # true iff this node is a side effect - self.side_effects = [] # the side effects of building this target - self.linked = 0 # is this node linked to the variant directory? - self.changed_since_last_build = 0 - self.store_info = 0 - self._tags = None - self._func_is_derived = 1 - self._func_exists = 1 - self._func_rexists = 1 - self._func_get_contents = 0 - self._func_target_from_source = 0 - self.ninfo = None + self.side_effect = False # true iff this node is a side effect + self.side_effects: list[Node] = [] # the side effects of building this target + self.linked = False # is this node linked to the variant directory? + self.changed_since_last_build = 0 # Index for "_decider_map". + self.store_info = 0 # Index for "store_info_map". + self._tags: dict[str, Any] | None = None + self._func_is_derived = 1 # Index for "_is_derived_map". + self._func_exists = 1 # Index for "_exists_map" + self._func_rexists = 1 # Index for "_rexists_map" + self._func_get_contents = 0 # Index for "_get_contents_map" + self._func_target_from_source = 0 # Index for "_target_from_source_map" + self.ninfo: NodeInfoBase | None = None self.clear_memoized_values() @@ -615,14 +622,14 @@ def __init__(self) -> None: # what line in what file created the node, for example). Annotate(self) - def disambiguate(self, must_exist=None): + def disambiguate(self, must_exist: bool = False): return self def get_suffix(self) -> str: return '' @SCons.Memoize.CountMethodCall - def get_build_env(self): + def get_build_env(self) -> Environment: """Fetch the appropriate Environment to build this node. """ try: @@ -633,7 +640,7 @@ def get_build_env(self): self._memo['get_build_env'] = result return result - def get_build_scanner_path(self, scanner): + def get_build_scanner_path(self, scanner: ScannerBase): """Fetch the appropriate scanner path for this node.""" return self.get_executor().get_build_scanner_path(scanner) @@ -641,7 +648,7 @@ def set_executor(self, executor: Executor) -> None: """Set the action executor for this node.""" self.executor = executor - def get_executor(self, create: int=1) -> Executor: + def get_executor(self, create: bool = True) -> Executor: """Fetch the action executor for this node. Create one if there isn't already one, and requested to do so.""" try: @@ -652,7 +659,7 @@ def get_executor(self, create: int=1) -> Executor: try: act = self.builder.action except AttributeError: - executor = SCons.Executor.Null(targets=[self]) # type: ignore + executor = SCons.Executor.Null(targets=[self]) # type: ignore[assignment] else: executor = SCons.Executor.Executor(act, self.env or self.builder.env, @@ -665,7 +672,7 @@ def get_executor(self, create: int=1) -> Executor: def executor_cleanup(self) -> None: """Let the executor clean up any cached information.""" try: - executor = self.get_executor(create=None) + executor = self.get_executor(create=False) except AttributeError: pass else: @@ -682,7 +689,7 @@ def reset_executor(self) -> None: def push_to_cache(self) -> bool: """Try to push a node into a cache """ - pass + return False def retrieve_from_cache(self) -> bool: """Try to retrieve the node's content from a cache @@ -709,7 +716,7 @@ def make_ready(self) -> None: """ pass - def prepare(self): + def prepare(self) -> None: """Prepare for this Node to be built. This is called after the Taskmaster has decided that the Node @@ -742,7 +749,7 @@ def prepare(self): raise SCons.Errors.StopError(msg % (i, self)) self.binfo = self.get_binfo() - def build(self, **kw): + def build(self, **kw) -> None: """Actually build the node. This is called by the Taskmaster after it's decided that the @@ -827,10 +834,10 @@ def release_target_info(self) -> None: """ pass - def add_to_waiting_s_e(self, node) -> None: + def add_to_waiting_s_e(self, node: Node) -> None: self.waiting_s_e.add(node) - def add_to_waiting_parents(self, node) -> int: + def add_to_waiting_parents(self, node: Node) -> int: """ Returns the number of nodes added to our waiting parents list: 1 if we add a unique waiting parent, 0 if not. (Note that the @@ -867,13 +874,13 @@ def clear(self) -> None: delattr(self, attr) except AttributeError: pass - self.cached = 0 + self.cached = False self.includes = None def clear_memoized_values(self) -> None: self._memo = {} - def builder_set(self, builder) -> None: + def builder_set(self, builder: BuilderBase | None) -> None: self.builder = builder try: del self.executor @@ -899,7 +906,7 @@ def has_builder(self) -> bool: b = self.builder = None return b is not None - def set_explicit(self, is_explicit) -> None: + def set_explicit(self, is_explicit: bool) -> None: self.is_explicit = is_explicit def has_explicit_builder(self) -> bool: @@ -915,7 +922,7 @@ def has_explicit_builder(self) -> bool: self.is_explicit = False return False - def get_builder(self, default_builder=None): + def get_builder(self, default_builder: BuilderBase | None = None) -> BuilderBase | None: """Return the set builder, or a specified default value""" try: return self.builder @@ -948,7 +955,7 @@ def is_conftest(self) -> bool: return False return True - def check_attributes(self, name): + def check_attributes(self, name: str) -> Any | None: """ Simple API to check if the node.attributes for name has been set""" return getattr(getattr(self, "attributes", None), name, None) @@ -958,7 +965,7 @@ def alter_targets(self): """ return [], None - def get_found_includes(self, env, scanner, path): + def get_found_includes(self, env: Environment, scanner: ScannerBase | None, path) -> list[Node]: """Return the scanned include lines (implicit dependencies) found in this node. @@ -968,7 +975,7 @@ def get_found_includes(self, env, scanner, path): """ return [] - def get_implicit_deps(self, env, initial_scanner, path_func, kw = {}): + def get_implicit_deps(self, env: Environment, initial_scanner: ScannerBase | None, path_func, kw = {}) -> list[Node]: """Return a list of implicit dependencies for this node. This method exists to handle recursive invocation of the scanner @@ -1003,7 +1010,7 @@ def get_implicit_deps(self, env, initial_scanner, path_func, kw = {}): return dependencies - def _get_scanner(self, env, initial_scanner, root_node_scanner, kw): + def _get_scanner(self, env: Environment, initial_scanner: ScannerBase | None, root_node_scanner: ScannerBase | None, kw: dict[str, Any] | None) -> ScannerBase | None: if initial_scanner: # handle explicit scanner case scanner = initial_scanner.select(self) @@ -1020,13 +1027,13 @@ def _get_scanner(self, env, initial_scanner, root_node_scanner, kw): return scanner - def get_env_scanner(self, env, kw={}): + def get_env_scanner(self, env: Environment, kw: dict[str, Any] | None = {}) -> ScannerBase | None: return env.get_scanner(self.scanner_key()) - def get_target_scanner(self): + def get_target_scanner(self) -> ScannerBase | None: return self.builder.target_scanner - def get_source_scanner(self, node): + def get_source_scanner(self, node: Node) -> ScannerBase | None: """Fetch the source scanner for the specified node NOTE: "self" is the target being built, "node" is @@ -1052,10 +1059,10 @@ def get_source_scanner(self, node): scanner = scanner.select(node) return scanner - def add_to_implicit(self, deps) -> None: + def add_to_implicit(self, deps: list[Node]) -> None: if not hasattr(self, 'implicit') or self.implicit is None: self.implicit = [] - self.implicit_set = set() + self.implicit_set: set[Node] = set() self._children_reset() self._add_child(self.implicit, self.implicit_set, deps) @@ -1114,10 +1121,10 @@ def print_nodelist(n): if scanner: executor.scan_targets(scanner) - def scanner_key(self): + def scanner_key(self) -> str | None: return None - def select_scanner(self, scanner): + def select_scanner(self, scanner: ScannerBase) -> ScannerBase | None: """Selects a scanner for this Node. This is a separate method so it can be overridden by Node @@ -1127,7 +1134,7 @@ def select_scanner(self, scanner): """ return scanner.select(self) - def env_set(self, env, safe: bool=False) -> None: + def env_set(self, env: Environment, safe: bool = False) -> None: if safe and self.env: return self.env = env @@ -1139,21 +1146,21 @@ def env_set(self, env, safe: bool=False) -> None: NodeInfo = NodeInfoBase BuildInfo = BuildInfoBase - def new_ninfo(self): + def new_ninfo(self) -> NodeInfoBase: ninfo = self.NodeInfo() return ninfo - def get_ninfo(self): + def get_ninfo(self) -> NodeInfoBase: if self.ninfo is not None: return self.ninfo self.ninfo = self.new_ninfo() return self.ninfo - def new_binfo(self): + def new_binfo(self) -> BuildInfoBase: binfo = self.BuildInfo() return binfo - def get_binfo(self): + def get_binfo(self) -> BuildInfoBase: """ Fetch a node's build information. @@ -1212,7 +1219,7 @@ def del_binfo(self) -> None: except AttributeError: pass - def get_csig(self): + def get_csig(self) -> str: try: return self.ninfo.csig except AttributeError: @@ -1220,13 +1227,13 @@ def get_csig(self): ninfo.csig = hash_signature(self.get_contents()) return self.ninfo.csig - def get_cachedir_csig(self): + def get_cachedir_csig(self) -> str: return self.get_csig() - def get_stored_info(self): + def get_stored_info(self) -> SConsignEntry | None: return None - def get_stored_implicit(self): + def get_stored_implicit(self) -> list[Node] | None: """Fetch the stored implicit dependencies""" return None @@ -1234,7 +1241,7 @@ def get_stored_implicit(self): # # - def set_precious(self, precious: int = 1) -> None: + def set_precious(self, precious: bool = True) -> None: """Set the Node's precious value.""" self.precious = precious @@ -1242,19 +1249,15 @@ def set_pseudo(self, pseudo: bool = True) -> None: """Set the Node's pseudo value.""" self.pseudo = pseudo - def set_noclean(self, noclean: int = 1) -> None: + def set_noclean(self, noclean: bool = True) -> None: """Set the Node's noclean value.""" - # Make sure noclean is an integer so the --debug=stree - # output in Util.py can use it as an index. - self.noclean = noclean and 1 or 0 + self.noclean = noclean - def set_nocache(self, nocache: int = 1) -> None: + def set_nocache(self, nocache: bool = True) -> None: """Set the Node's nocache value.""" - # Make sure nocache is an integer so the --debug=stree - # output in Util.py can use it as an index. - self.nocache = nocache and 1 or 0 + self.nocache = nocache - def set_always_build(self, always_build: int = 1) -> None: + def set_always_build(self, always_build: bool = True) -> None: """Set the Node's always_build value.""" self.always_build = always_build @@ -1262,12 +1265,12 @@ def exists(self) -> bool: """Reports whether node exists.""" return _exists_map[self._func_exists](self) - def rexists(self): + def rexists(self) -> bool: """Does this node exist locally or in a repository?""" # There are no repositories by default: return _rexists_map[self._func_rexists](self) - def get_contents(self): + def get_contents(self) -> bytes | str: """Fetch the contents of the entry.""" return _get_contents_map[self._func_get_contents](self) @@ -1276,11 +1279,11 @@ def missing(self) -> bool: not self.linked and \ not self.rexists() - def remove(self): + def remove(self) -> None: """Remove this Node: no-op by default.""" return None - def add_dependency(self, depend): + def add_dependency(self, depend: list[Node]) -> None: """Adds dependencies.""" try: self._add_child(self.depends, self.depends_set, depend) @@ -1292,14 +1295,14 @@ def add_dependency(self, depend): s = str(e) raise SCons.Errors.UserError("attempted to add a non-Node dependency to %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) - def add_prerequisite(self, prerequisite) -> None: + def add_prerequisite(self, prerequisite: list[Node]) -> None: """Adds prerequisites""" if self.prerequisites is None: self.prerequisites = UniqueList() self.prerequisites.extend(prerequisite) self._children_reset() - def add_ignore(self, depend): + def add_ignore(self, depend: list[Node]) -> None: """Adds dependencies to ignore.""" try: self._add_child(self.ignore, self.ignore_set, depend) @@ -1311,7 +1314,7 @@ def add_ignore(self, depend): s = str(e) raise SCons.Errors.UserError("attempted to ignore a non-Node dependency of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) - def add_source(self, source): + def add_source(self, source: list[Node]) -> None: """Adds sources.""" if self._specific_sources: return @@ -1325,7 +1328,7 @@ def add_source(self, source): s = str(e) raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) - def _add_child(self, collection, set, child) -> None: + def _add_child(self, collection: list[Node], set: set[Node], child: list[Node]) -> None: """Adds 'child' to 'collection', first checking 'set' to see if it's already present.""" added = None @@ -1337,11 +1340,11 @@ def _add_child(self, collection, set, child) -> None: if added: self._children_reset() - def set_specific_source(self, source) -> None: + def set_specific_source(self, source: list[Node]) -> None: self.add_source(source) self._specific_sources = True - def add_wkid(self, wkid) -> None: + def add_wkid(self, wkid: Node) -> None: """Add a node to the list of kids waiting to be evaluated""" if self.wkids is not None: self.wkids.append(wkid) @@ -1353,7 +1356,7 @@ def _children_reset(self) -> None: self.executor_cleanup() @SCons.Memoize.CountMethodCall - def _children_get(self): + def _children_get(self) -> list[Node]: try: return self._memo['_children_get'] except KeyError: @@ -1384,12 +1387,12 @@ def _children_get(self): if i not in self.ignore_set: children.append(i) else: - children = self.all_children(scan=0) + children = self.all_children(scan=False) self._memo['_children_get'] = children return children - def all_children(self, scan: int=1): + def all_children(self, scan: bool = True) -> list[Node]: """Return a list of all the node's direct children.""" if scan: self.scan() @@ -1413,27 +1416,27 @@ def all_children(self, scan: int=1): # internally anyway...) return list(chain.from_iterable([_f for _f in [self.sources, self.depends, self.implicit] if _f])) - def children(self, scan: int=1): + def children(self, scan: bool = True) -> list[Node]: """Return a list of the node's direct children, minus those that are ignored by this node.""" if scan: self.scan() return self._children_get() - def set_state(self, state) -> None: + def set_state(self, state: int) -> None: self.state = state - def get_state(self): + def get_state(self) -> int: return self.state - def get_env(self): + def get_env(self) -> Environment: env = self.env if not env: import SCons.Defaults env = SCons.Defaults.DefaultEnvironment() return env - def Decider(self, function) -> None: + def Decider(self, function: Callable[[Node, Node, NodeInfoBase, Node | None], bool]) -> None: foundkey = None for k, v in _decider_map.items(): if v == function: @@ -1444,19 +1447,19 @@ def Decider(self, function) -> None: _decider_map[foundkey] = function self.changed_since_last_build = foundkey - def Tag(self, key, value) -> None: + def Tag(self, key: str, value: Any | None) -> None: """ Add a user-defined tag. """ if not self._tags: self._tags = {} self._tags[key] = value - def GetTag(self, key): + def GetTag(self, key: str) -> Any | None: """ Return a user-defined tag. """ if not self._tags: return None return self._tags.get(key, None) - def changed(self, node=None, allowcache: bool=False): + def changed(self, node: Node | None = None, allowcache: bool = False) -> bool: """ Returns if the node is up-to-date with respect to the BuildInfo stored last time it was built. The default behavior is to compare @@ -1535,7 +1538,7 @@ def children_are_up_to_date(self) -> bool: if self.always_build: return False state = 0 - for kid in self.children(None): + for kid in self.children(False): s = kid.get_state() if s and (not state or s > state): state = s @@ -1560,13 +1563,13 @@ def render_include_tree(self): path = self.get_build_scanner_path(scanner) else: path = None - def f(node, env=env, scanner=scanner, path=path): + def f(node: Node, env: Environment = env, scanner: ScannerBase = scanner, path=path): return node.get_found_includes(env, scanner, path) return render_tree(s, f, 1) else: return None - def get_abspath(self): + def get_abspath(self) -> str: """ Return an absolute path to the Node. This will return simply str(Node) by default, but for Node types that have a concept of @@ -1574,7 +1577,7 @@ def get_abspath(self): """ return str(self) - def for_signature(self): + def for_signature(self) -> str: """ Return a string representation of the Node that will always be the same for this particular Node, no matter what. This @@ -1589,7 +1592,7 @@ def for_signature(self): """ return str(self) - def get_string(self, for_signature): + def get_string(self, for_signature: bool) -> str: """This is a convenience function designed primarily to be used in command generators (i.e., CommandGeneratorActions or Environment variables that are callable), which are called @@ -1720,9 +1723,9 @@ class NodeList(collections.UserList): def __str__(self) -> str: return str(list(map(str, self.data))) -def get_children(node, parent): return node.children() -def ignore_cycle(node, stack) -> None: pass -def do_nothing(node, parent) -> None: pass +def get_children(node: Node, parent: Node | None) -> list[Node]: return node.children() +def ignore_cycle(node: Node, stack: list[Node]) -> None: pass +def do_nothing(node: Node, parent: Node | None) -> None: pass class Walker: """An iterator for walking a Node tree. @@ -1737,15 +1740,19 @@ class Walker: This class does not get caught in node cycles caused, for example, by C header file include loops. """ - def __init__(self, node, kids_func=get_children, - cycle_func=ignore_cycle, - eval_func=do_nothing) -> None: + def __init__( + self, + node: Node, + kids_func: Callable[[Node, Node | None], list[Node]] = get_children, + cycle_func: Callable[[Node, list[Node]], None] = ignore_cycle, + eval_func: Callable[[Node, Node | None], None] = do_nothing, + ) -> None: self.kids_func = kids_func self.cycle_func = cycle_func self.eval_func = eval_func node.wkids = copy.copy(kids_func(node, None)) self.stack = [node] - self.history = {} # used to efficiently detect and avoid cycles + self.history: dict[Node, Any | None] = {} # used to efficiently detect and avoid cycles self.history[node] = None def get_next(self): From b8b2edecc492962f40cd94e7d9565ca7d1c8813c Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 15 Nov 2024 07:30:29 -0700 Subject: [PATCH 203/386] Tweak Clean and NoClean manpage entries [skip appveyor] Motivated by an issue that pointed out the argument names differ between doc and implementation, adjusted the wording a bit. Clean() used to suggest you could use a whole bunch of args in one call ("Multiple files or directories should be specified either as separate arguments to the Clean method, or as a list") but as it's written to take exactly two positionial arguments that wasn't correct - only the list approach works. Fixes #4638 Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 1 + SCons/Environment.py | 14 ++++- SCons/Environment.xml | 100 +++++++++++++--------------------- doc/generated/functions.gen | 104 ++++++++++++++---------------------- 5 files changed, 92 insertions(+), 128 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 227a56f90b..8ffb07d69b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -149,6 +149,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER always returns a dict. The default remains to return different types depending on whether zero, one, or multiple construction variable names are given. + - Update Clean and NoClean documentation. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 72ec73c9c7..77afdef03f 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -176,6 +176,7 @@ DOCUMENTATION - Many grammatical and spelling fixes in the documentation. +- Update Clean and NoClean documentation. DEVELOPMENT ----------- diff --git a/SCons/Environment.py b/SCons/Environment.py index 62926f56a5..468da237df 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -2248,6 +2248,16 @@ def CacheDir(self, path, custom_class=None) -> None: self.get_CacheDir() def Clean(self, targets, files) -> None: + """Mark additional files for cleaning. + + *files* will be removed if any of *targets* are selected + for cleaning - that is, the combination of target selection + and -c clean mode. + + Args: + targets (files or nodes): targets to associate *files* with. + files (files or nodes): items to remove if *targets* are selected. + """ global CleanTargets tlist = self.arg2nodes(targets, self.fs.Entry) flist = self.arg2nodes(files, self.fs.Entry) @@ -2334,8 +2344,8 @@ def PyPackageDir(self, modulename): return result return self.fs.PyPackageDir(s) - def NoClean(self, *targets): - """Tag target(s) so that it will not be cleaned by -c.""" + def NoClean(self, *targets) -> list: + """Tag *targets* to not be removed in clean mode.""" tlist = [] for t in targets: tlist.extend(self.arg2nodes(t, self.fs.Entry)) diff --git a/SCons/Environment.xml b/SCons/Environment.xml index dafc6491e3..f24c2af3ea 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -1010,45 +1010,34 @@ to arrange for cache pruning, expiry, access control, etc. if needed. -(targets, files_or_dirs) +(targets, files) -This specifies a list of files or directories which should be removed -whenever the targets are specified with the - -command line option. -The specified targets may be a list -or an individual target. -Multiple calls to -&f-Clean; -are legal, -and create new targets or add files and directories to the -clean list for the specified targets. - - - -Multiple files or directories should be specified -either as separate arguments to the -&f-Clean; -method, or as a list. -&f-Clean; -will also accept the return value of the &consenv; -Builder methods. -Examples: +Set additional files +for removal when any of +targets are selected +for cleaning +( +command line option). +targets and +files +can each be a single filename or node, +or a list of filenames or nodes. +These can refer to files or directories. +Calling this method repeatedly +has an additive effect. The related &f-link-NoClean; -function overrides calling -&f-Clean; -for the same target, -and any targets passed to both functions will -not -be removed by the - -option. +method has higher priority: +any target specified to +&f-NoClean; +will not be cleaned even if also given as +a files +parameter to &f-Clean;. @@ -1062,9 +1051,12 @@ Clean(['foo', 'bar'], 'something_else_to_clean') +&SCons; does not directly track directories as targets - +they are created if needed and not normally removed +in clean mode. In this example, installing the project creates a subdirectory for the documentation. -This statement causes the subdirectory to be removed +The &f-Clean; call ensures that the subdirectory is removed if the project is uninstalled. @@ -2525,35 +2517,21 @@ NoCache(env.Program('hello', 'hello.c')) -(target, ...) +(targets, ...) -Specifies a list of files or directories which should -not -be removed whenever the targets (or their dependencies) -are specified with the - -command line option. -The specified targets may be a list -or an individual target. -Multiple calls to -&f-NoClean; -are legal, -and prevent each specified target -from being removed by calls to the - -option. - - - -Multiple files or directories should be specified -either as separate arguments to the -&f-NoClean; -method, or as a list. -&f-NoClean; -will also accept the return value of any of the &consenv; -Builder methods. +Specifies files or directories which should not +be removed whenever a specified +target (or its dependencies) +is selected and clean mode is active +( +command line option). +targets +may be one or more file or directory names +or nodes, +and/or lists of names or nodes. +&f-NoClean; can be called multiple times. @@ -2562,11 +2540,9 @@ Calling for a target overrides calling &f-link-Clean; for the same target, -and any targets passed to both functions will +so any targets passed to both functions will not -be removed by the - -option. +be removed in clean mode. diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index 9668af818f..b5fcfcc7ac 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -927,44 +927,33 @@ to arrange for cache pruning, expiry, access control, etc. if needed. - Clean(targets, files_or_dirs) - env.Clean(targets, files_or_dirs) + Clean(targets, files) + env.Clean(targets, files) -This specifies a list of files or directories which should be removed -whenever the targets are specified with the - -command line option. -The specified targets may be a list -or an individual target. -Multiple calls to -&f-Clean; -are legal, -and create new targets or add files and directories to the -clean list for the specified targets. - - - -Multiple files or directories should be specified -either as separate arguments to the -&f-Clean; -method, or as a list. -&f-Clean; -will also accept the return value of the &consenv; -Builder methods. -Examples: +Set additional files +for removal when any of +targets are selected +for cleaning +( +command line option). +targets and +files +can each be a single filename or node, +or a list of filenames or nodes. +These can refer to files or directories. +Calling this method repeatedly +has an additive effect. The related &f-link-NoClean; -function overrides calling -&f-Clean; -for the same target, -and any targets passed to both functions will -not -be removed by the - -option. +method has higher priority: +any target specified to +&f-NoClean; +will not be cleaned even if also given as +a files +parameter to &f-Clean;. @@ -978,9 +967,12 @@ Clean(['foo', 'bar'], 'something_else_to_clean') +&SCons; does not directly track directories as targets - +they are created if needed and not normally removed +in clean mode. In this example, installing the project creates a subdirectory for the documentation. -This statement causes the subdirectory to be removed +The &f-Clean; call ensures that the subdirectory is removed if the project is uninstalled. @@ -3056,34 +3048,20 @@ NoCache(env.Program('hello', 'hello.c')) - NoClean(target, ...) - env.NoClean(target, ...) + NoClean(targets, ...) + env.NoClean(targets, ...) -Specifies a list of files or directories which should -not -be removed whenever the targets (or their dependencies) -are specified with the - -command line option. -The specified targets may be a list -or an individual target. -Multiple calls to -&f-NoClean; -are legal, -and prevent each specified target -from being removed by calls to the - -option. - - - -Multiple files or directories should be specified -either as separate arguments to the -&f-NoClean; -method, or as a list. -&f-NoClean; -will also accept the return value of any of the &consenv; -Builder methods. +Specifies files or directories which should not +be removed whenever a specified +target (or its dependencies) +is selected and clean mode is active +( +command line option). +targets +may be one or more file or directory names +or nodes, +and/or lists of names or nodes. +&f-NoClean; can be called multiple times. @@ -3092,11 +3070,9 @@ Calling for a target overrides calling &f-link-Clean; for the same target, -and any targets passed to both functions will +so any targets passed to both functions will not -be removed by the - -option. +be removed in clean mode. From a8ef7dd990df10a0011bbe1e2d86262133094320 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 18 Nov 2024 11:48:51 -0700 Subject: [PATCH 204/386] Unknown variables from Variables file are now reported Previously Update added everything from the file(s) to the local values dict by doing "exec", and then unknown ones ended up silently dropped since they weren't declared as options. Now the result of the exec call is reprocessed so unknowns can be collected. Fixes #4645 Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ RELEASE.txt | 3 +++ SCons/Variables/VariablesTests.py | 29 +++++++++++++++-------------- SCons/Variables/__init__.py | 27 +++++++++++++++++++-------- 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8ffb07d69b..3364b95ce7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -150,6 +150,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER types depending on whether zero, one, or multiple construction variable names are given. - Update Clean and NoClean documentation. + - Make sure unknown variables from a Variables file are recognized + as such (issue #4645) RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 77afdef03f..a73e654b31 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -137,6 +137,9 @@ FIXES - Skip running a few validation tests if the user is root and the test is not designed to work for the root user. +- Make sure unknown variables from a Variables file are recognized + as such (issue #4645) + IMPROVEMENTS ------------ diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index 0791ea2556..87b7c60aa3 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -654,41 +654,42 @@ def test_unknown(self) -> None: self.assertEqual('answer', env['ANSWER']) def test_AddOptionUpdatesUnknown(self) -> None: - """Test updating of the 'unknown' dict""" - opts = SCons.Variables.Variables() - - opts.Add('A', - 'A test variable', - "1") + """Test updating of the 'unknown' dict. + Get one unknown from args, one from a variables file. Add one + of those later, make sure it gets removed from 'unknown' when added. + """ + test = TestSCons.TestSCons() + var_file = test.workpath('vars.py') + test.write('vars.py', 'FROMFILE="notadded"') + opts = SCons.Variables.Variables(files=var_file) + opts.Add('A', 'A test variable', "1") args = { 'A' : 'a', 'ADDEDLATER' : 'notaddedyet', } - env = Environment() opts.Update(env,args) r = opts.UnknownVariables() with self.subTest(): - self.assertEqual({'ADDEDLATER': 'notaddedyet'}, r) + self.assertEqual('notaddedyet', r['ADDEDLATER']) + self.assertEqual('notadded', r['FROMFILE']) self.assertEqual('a', env['A']) - opts.Add('ADDEDLATER', - 'An option not present initially', - "1") - + opts.Add('ADDEDLATER', 'An option not present initially', "1") args = { 'A' : 'a', 'ADDEDLATER' : 'added', } - opts.Update(env, args) r = opts.UnknownVariables() with self.subTest(): - self.assertEqual(0, len(r)) + self.assertEqual(1, len(r)) # should still have FROMFILE + self.assertNotIn('ADDEDLATER', r) self.assertEqual('added', env['ADDEDLATER']) + self.assertIn('FROMFILE', r) def test_AddOptionWithAliasUpdatesUnknown(self) -> None: """Test updating of the 'unknown' dict (with aliases)""" diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 2d160072a5..1826c64445 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -235,19 +235,30 @@ def Update(self, env, args: dict | None = None) -> None: for filename in self.files: # TODO: issue #816 use Node to access saved-variables file? if os.path.exists(filename): - # lint: W0622: Redefining built-in 'dir' - dir = os.path.split(os.path.abspath(filename))[0] - if dir: - sys.path.insert(0, dir) + # issue #4645: don't exec directly into values, + # so we can iterate through for unknown variables. + temp_values = {} + dirname = os.path.split(os.path.abspath(filename))[0] + if dirname: + sys.path.insert(0, dirname) try: - values['__name__'] = filename + temp_values['__name__'] = filename with open(filename) as f: contents = f.read() - exec(contents, {}, values) + exec(contents, {}, temp_values) finally: - if dir: + if dirname: del sys.path[0] - del values['__name__'] + del temp_values['__name__'] + + for arg, value in temp_values.items(): + added = False + for option in self.options: + if arg in option.aliases + [option.key,]: + values[option.key] = value + added = True + if not added: + self.unknown[arg] = value # set the values specified on the command line if args is None: From 53c252242ced51eb4c29047dad9de1cca22773f1 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 20 Nov 2024 08:11:21 -0700 Subject: [PATCH 205/386] Update unittest for unkown variables Signed-off-by: Mats Wichmann --- SCons/Variables/VariablesTests.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index 87b7c60aa3..80c4d11397 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -656,12 +656,13 @@ def test_unknown(self) -> None: def test_AddOptionUpdatesUnknown(self) -> None: """Test updating of the 'unknown' dict. - Get one unknown from args, one from a variables file. Add one - of those later, make sure it gets removed from 'unknown' when added. + Get one unknown from args and one from a variables file. + Add these later, making sure they no longer appear in unknowns + after the subsequent Update(). """ test = TestSCons.TestSCons() var_file = test.workpath('vars.py') - test.write('vars.py', 'FROMFILE="notadded"') + test.write('vars.py', 'FROMFILE="added"') opts = SCons.Variables.Variables(files=var_file) opts.Add('A', 'A test variable', "1") args = { @@ -674,10 +675,11 @@ def test_AddOptionUpdatesUnknown(self) -> None: r = opts.UnknownVariables() with self.subTest(): self.assertEqual('notaddedyet', r['ADDEDLATER']) - self.assertEqual('notadded', r['FROMFILE']) + self.assertEqual('added', r['FROMFILE']) self.assertEqual('a', env['A']) opts.Add('ADDEDLATER', 'An option not present initially', "1") + opts.Add('FROMFILE', 'An option from a file also absent', "1") args = { 'A' : 'a', 'ADDEDLATER' : 'added', @@ -686,10 +688,11 @@ def test_AddOptionUpdatesUnknown(self) -> None: r = opts.UnknownVariables() with self.subTest(): - self.assertEqual(1, len(r)) # should still have FROMFILE + self.assertEqual(0, len(r)) self.assertNotIn('ADDEDLATER', r) self.assertEqual('added', env['ADDEDLATER']) - self.assertIn('FROMFILE', r) + self.assertNotIn('FROMFILE', r) + self.assertEqual('added', env['FROMFILE']) def test_AddOptionWithAliasUpdatesUnknown(self) -> None: """Test updating of the 'unknown' dict (with aliases)""" From ab05d8ee364de3afc0b523c3dcd29a8ae2d7c9fc Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 20 Nov 2024 10:54:00 -0800 Subject: [PATCH 206/386] [ci skip] Fix contributors name in CHANGES.txt --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8ffb07d69b..cf7e383168 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -95,7 +95,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Keith F Prussing: - Added support for tracking beamer themes in the LaTeX scanner. - From rico-chet: + From Alex Thiessen: - Many grammatical and spelling fixes in the documentation. From Mats Wichmann: From 8999666f07311ba025ee91e801c04dd2fd5adb96 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 24 Nov 2024 20:47:03 -0800 Subject: [PATCH 207/386] Fixed ninja binary location logic to use ninja.BIN_DIR. Previous logic no longer works starting with python ninja package version 1.11.1.2 --- CHANGES.txt | 6 + RELEASE.txt | 7 + SCons/Tool/ninja/NinjaState.py | 19 ++- test/ninja/build_libraries.py | 9 +- test/ninja/command_line_targets.py | 7 +- test/ninja/copy_function_command.py | 7 +- test/ninja/default_targets.py | 4 +- test/ninja/force_scons_callback.py | 5 +- test/ninja/generate_and_build.py | 7 +- test/ninja/generate_and_build_cxx.py | 7 +- test/ninja/generate_source.py | 7 +- test/ninja/generated_sources_alias.py | 5 +- test/ninja/iterative_speedup.py | 7 +- test/ninja/mingw_command_generator_action.py | 4 +- test/ninja/mingw_depfile_format.py | 4 +- test/ninja/mkdir_function_command.py | 7 +- test/ninja/multi_env.py | 7 +- test/ninja/ninja_command_line.py | 7 +- test/ninja/ninja_conftest.py | 7 +- test/ninja/ninja_file_deterministic.py | 4 +- test/ninja/ninja_handle_control_c_rebuild.py | 6 +- test/ninja/no_for_sig_subst.py | 7 +- test/ninja/response_file.py | 7 +- test/ninja/shell_command.py | 7 +- test/ninja/shutdown_scons_daemon.py | 4 +- testing/framework/TestSCons.py | 146 +++++++++++-------- 26 files changed, 134 insertions(+), 180 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e449bbab56..964296614d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -85,6 +85,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER via top-level `from __future__ import annotations`. - Implemented type hints for Nodes. + From William Deegan: + - Update ninja tool to use ninja.BIN_DIR to find pypi packaged ninja binary. + python ninja package version 1.11.1.2 changed the location and previous + logic no longer worked. + - Added ninja_binary() method to TestSCons to centralize logic to find ninja binary + From Alex James: - On Darwin, PermissionErrors are now handled while trying to access /etc/paths.d. This may occur if SCons is invoked in a sandboxed diff --git a/RELEASE.txt b/RELEASE.txt index 8e88a0bbde..4aa9697d39 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -140,6 +140,11 @@ FIXES - Make sure unknown variables from a Variables file are recognized as such (issue #4645) +- Update ninja tool to use ninja.BIN_DIR to find pypi packaged ninja binary. + python ninja package version 1.11.1.2 changed the location and previous + logic no longer worked. + + IMPROVEMENTS ------------ @@ -196,6 +201,8 @@ DEVELOPMENT - Implemented type hints for Nodes. +- Added ninja_binary() method to TestSCons to centralize logic to find ninja binary + Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py index 549af7855e..da7aa28f12 100644 --- a/SCons/Tool/ninja/NinjaState.py +++ b/SCons/Tool/ninja/NinjaState.py @@ -60,12 +60,11 @@ def __init__(self, env, ninja_file, ninja_syntax) -> None: if not self.ninja_bin_path: # default to using ninja installed with python module ninja_bin = 'ninja.exe' if env["PLATFORM"] == "win32" else 'ninja' + self.ninja_bin_path = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - ninja_bin)) + ninja.BIN_DIR, ninja_bin + )) + if not os.path.exists(self.ninja_bin_path): # couldn't find it, just give the bin name and hope # its in the path later @@ -398,7 +397,7 @@ def generate(self): self.rules.update({key: non_rsp_rule}) else: self.rules.update({key: rule}) - + self.pools.update(self.env.get(NINJA_POOLS, {})) content = io.StringIO() @@ -435,7 +434,7 @@ def generate(self): generated_source_files = sorted( [] if not generated_sources_build else generated_sources_build['implicit'] ) - + def check_generated_source_deps(build): return ( build != generated_sources_build @@ -464,7 +463,7 @@ def check_generated_source_deps(build): rule="phony", implicit=generated_source_files ) - + def check_generated_source_deps(build): return ( not build["rule"] == "INSTALL" @@ -661,7 +660,7 @@ def check_generated_source_deps(build): all_targets = [str(node) for node in NINJA_DEFAULT_TARGETS] else: all_targets = list(all_targets) - + if len(all_targets) == 0: all_targets = ["phony_default"] ninja_sorted_build( @@ -669,7 +668,7 @@ def check_generated_source_deps(build): outputs=all_targets, rule="phony", ) - + ninja.default([self.ninja_syntax.escape_path(path) for path in sorted(all_targets)]) with NamedTemporaryFile(delete=False, mode='w') as temp_ninja_file: diff --git a/test/ninja/build_libraries.py b/test/ninja/build_libraries.py index 0a1941ab5a..0561f6f544 100644 --- a/test/ninja/build_libraries.py +++ b/test/ninja/build_libraries.py @@ -23,8 +23,6 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -import os - import TestSCons from TestCmd import IS_WINDOWS, IS_MACOS from TestSCons import _exe, _lib, lib_, _dll, dll_ @@ -36,12 +34,7 @@ except ImportError: test.skip_test("Could not find ninja module. Skipping test.\n") -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/command_line_targets.py b/test/ninja/command_line_targets.py index 9fa77eae47..b043f62bca 100644 --- a/test/ninja/command_line_targets.py +++ b/test/ninja/command_line_targets.py @@ -36,12 +36,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/copy_function_command.py b/test/ninja/copy_function_command.py index 7e999b33e5..13036ee3a0 100644 --- a/test/ninja/copy_function_command.py +++ b/test/ninja/copy_function_command.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/default_targets.py b/test/ninja/default_targets.py index 1b2f2b9f4b..7752d5ab7e 100644 --- a/test/ninja/default_targets.py +++ b/test/ninja/default_targets.py @@ -36,9 +36,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath( - os.path.join(ninja.__file__, os.pardir, 'data', 'bin', 'ninja' + _exe) -) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/force_scons_callback.py b/test/ninja/force_scons_callback.py index e0da0ddd31..b668f92c98 100644 --- a/test/ninja/force_scons_callback.py +++ b/test/ninja/force_scons_callback.py @@ -37,9 +37,8 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath( - os.path.join(ninja.__file__, os.pardir, "data", "bin", "ninja" + _exe) -) +ninja_bin = test.ninja_binary() + test.dir_fixture("ninja-fixture") diff --git a/test/ninja/generate_and_build.py b/test/ninja/generate_and_build.py index e1c26d4bb0..c14af70452 100644 --- a/test/ninja/generate_and_build.py +++ b/test/ninja/generate_and_build.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/generate_and_build_cxx.py b/test/ninja/generate_and_build_cxx.py index 074a5cb9af..51f68dca00 100644 --- a/test/ninja/generate_and_build_cxx.py +++ b/test/ninja/generate_and_build_cxx.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/generate_source.py b/test/ninja/generate_source.py index 8300176c2e..f4bd0c0aef 100644 --- a/test/ninja/generate_source.py +++ b/test/ninja/generate_source.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/generated_sources_alias.py b/test/ninja/generated_sources_alias.py index 2c4ed36ae0..3e7f9d16b1 100644 --- a/test/ninja/generated_sources_alias.py +++ b/test/ninja/generated_sources_alias.py @@ -37,9 +37,8 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.BIN_DIR, - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() + test.dir_fixture('ninja-fixture') diff --git a/test/ninja/iterative_speedup.py b/test/ninja/iterative_speedup.py index e5673b0b42..8190175326 100644 --- a/test/ninja/iterative_speedup.py +++ b/test/ninja/iterative_speedup.py @@ -39,12 +39,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/mingw_command_generator_action.py b/test/ninja/mingw_command_generator_action.py index 58c5106427..8fc08a816a 100644 --- a/test/ninja/mingw_command_generator_action.py +++ b/test/ninja/mingw_command_generator_action.py @@ -52,9 +52,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.BIN_DIR, - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/mingw_depfile_format.py b/test/ninja/mingw_depfile_format.py index 5de7b5fbca..e9c89a0f63 100644 --- a/test/ninja/mingw_depfile_format.py +++ b/test/ninja/mingw_depfile_format.py @@ -36,9 +36,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.BIN_DIR, - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/mkdir_function_command.py b/test/ninja/mkdir_function_command.py index 8a17623e7f..c01cb982f7 100644 --- a/test/ninja/mkdir_function_command.py +++ b/test/ninja/mkdir_function_command.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.write('SConstruct', """ SetOption('experimental','ninja') diff --git a/test/ninja/multi_env.py b/test/ninja/multi_env.py index e5da6cf885..6aaeccd584 100644 --- a/test/ninja/multi_env.py +++ b/test/ninja/multi_env.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test = TestSCons.TestSCons() diff --git a/test/ninja/ninja_command_line.py b/test/ninja/ninja_command_line.py index d8e3c08107..d6744aa713 100644 --- a/test/ninja/ninja_command_line.py +++ b/test/ninja/ninja_command_line.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture', 'src') diff --git a/test/ninja/ninja_conftest.py b/test/ninja/ninja_conftest.py index 91d2e03b9b..a92ecd9935 100644 --- a/test/ninja/ninja_conftest.py +++ b/test/ninja/ninja_conftest.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/ninja_file_deterministic.py b/test/ninja/ninja_file_deterministic.py index 9832f226ac..232e7abba5 100644 --- a/test/ninja/ninja_file_deterministic.py +++ b/test/ninja/ninja_file_deterministic.py @@ -39,9 +39,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.BIN_DIR, - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/ninja_handle_control_c_rebuild.py b/test/ninja/ninja_handle_control_c_rebuild.py index 9f6b41366c..5635367395 100644 --- a/test/ninja/ninja_handle_control_c_rebuild.py +++ b/test/ninja/ninja_handle_control_c_rebuild.py @@ -23,7 +23,7 @@ # """ This test ensures if ninja gets a control-c (or other interrupting signal) while -regenerating the build.ninja, it doesn't remove the build.ninja leaving it +regenerating the build.ninja, it doesn't remove the build.ninja leaving it in an unworkable state. """ import os @@ -41,9 +41,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath( - os.path.join(ninja.__file__, os.pardir, "data", "bin", "ninja" + _exe) -) +ninja_bin = test.ninja_binary() test.dir_fixture("ninja-fixture") diff --git a/test/ninja/no_for_sig_subst.py b/test/ninja/no_for_sig_subst.py index a0292ae029..da33f8d1c0 100644 --- a/test/ninja/no_for_sig_subst.py +++ b/test/ninja/no_for_sig_subst.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/response_file.py b/test/ninja/response_file.py index 3d23c2b2ae..e9f778ae8c 100644 --- a/test/ninja/response_file.py +++ b/test/ninja/response_file.py @@ -42,12 +42,7 @@ _exe = TestSCons._exe _obj = TestSCons._obj -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/shell_command.py b/test/ninja/shell_command.py index a6c48c4288..0ed8f2a0d0 100644 --- a/test/ninja/shell_command.py +++ b/test/ninja/shell_command.py @@ -37,12 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath(os.path.join( - ninja.__file__, - os.pardir, - 'data', - 'bin', - 'ninja' + _exe)) +ninja_bin = test.ninja_binary() test.dir_fixture('ninja-fixture') diff --git a/test/ninja/shutdown_scons_daemon.py b/test/ninja/shutdown_scons_daemon.py index 64ec2c717a..25823d742e 100644 --- a/test/ninja/shutdown_scons_daemon.py +++ b/test/ninja/shutdown_scons_daemon.py @@ -44,9 +44,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = os.path.abspath( - os.path.join(ninja.__file__, os.pardir, "data", "bin", "ninja" + _exe) -) +ninja_bin = test.ninja_binary() test.dir_fixture("ninja-fixture") diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 51aa7cf38a..d6c51251e8 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -40,17 +40,14 @@ import re import shutil import sys -import time import subprocess as sp +import time import zipfile from collections import namedtuple +from SCons.Util import get_hash_format, get_current_hash_algorithm_used from TestCommon import * from TestCommon import __all__, _python_ -from SCons.Util import get_hash_format, get_current_hash_algorithm_used - -from TestCmd import Popen -from TestCmd import PIPE # Some tests which verify that SCons has been packaged properly need to # look for specific version file names. Replicating the version number @@ -224,6 +221,8 @@ def restore_sconsflags(sconsflags) -> None: # Helpers for Configure()'s config.log processing ConfigCheckInfo = namedtuple('ConfigCheckInfo', ['check_string', 'result', 'cached', 'temp_filename']) + + # check_string: the string output to for this checker # results : The expected results for each check # cached : If the corresponding check is expected to be cached @@ -234,6 +233,7 @@ class NoMatch(Exception): """ Exception for matchPart to indicate there was no match found in the passed logfile """ + def __init__(self, p) -> None: self.pos = p @@ -418,7 +418,8 @@ def where_is(self, prog, path=None, pathext=None): return None - def wrap_stdout(self, build_str: str="", read_str: str="", error: int=0, cleaning: int=0) -> str: + def wrap_stdout(self, build_str: str = "", read_str: str = "", error: int = 0, + cleaning: int = 0) -> str: """Wraps "expect" strings in SCons boilerplate. Given strings of expected output specific to a test, @@ -444,11 +445,11 @@ def wrap_stdout(self, build_str: str="", read_str: str="", error: int=0, cleanin term = f"scons: done {lc}ing targets.\n" return "scons: Reading SConscript files ...\n" + \ - read_str + \ - "scons: done reading SConscript files.\n" + \ - f"scons: {cap}ing targets ...\n" + \ - build_str + \ - term + read_str + \ + "scons: done reading SConscript files.\n" + \ + f"scons: {cap}ing targets ...\n" + \ + build_str + \ + term def run(self, *args, **kw) -> None: """ @@ -484,7 +485,7 @@ def run(self, *args, **kw) -> None: # kw['options'] = ' '.join(options) # TestCommon.run(self, *args, **kw) - def up_to_date(self, arguments: str='.', read_str: str="", **kw) -> None: + def up_to_date(self, arguments: str = '.', read_str: str = "", **kw) -> None: """Asserts that all of the targets listed in arguments is up to date, but does not make any assumptions on other targets. This function is most useful in conjunction with the -n option. @@ -500,7 +501,7 @@ def up_to_date(self, arguments: str='.', read_str: str="", **kw) -> None: kw['match'] = self.match_re_dotall self.run(**kw) - def not_up_to_date(self, arguments: str='.', read_str: str="", **kw) -> None: + def not_up_to_date(self, arguments: str = '.', read_str: str = "", **kw) -> None: """Asserts that none of the targets listed in arguments is up to date, but does not make any assumptions on other targets. This function is most useful in conjunction with the -n option. @@ -510,7 +511,8 @@ def not_up_to_date(self, arguments: str='.', read_str: str="", **kw) -> None: s = f"{s}(?!scons: `{re.escape(arg)}' is up to date.)" s = f"({s}[^\n]*\n)*" kw['arguments'] = arguments - stdout = re.escape(self.wrap_stdout(read_str=read_str, build_str='ARGUMENTSGOHERE')) + stdout = re.escape( + self.wrap_stdout(read_str=read_str, build_str='ARGUMENTSGOHERE')) kw['stdout'] = stdout.replace('ARGUMENTSGOHERE', s) kw['match'] = self.match_re_dotall self.run(**kw) @@ -619,15 +621,15 @@ def RunPair(option, expected) -> None: return warning - def diff_substr(self, expect, actual, prelen: int=20, postlen: int=40) -> str: + def diff_substr(self, expect, actual, prelen: int = 20, postlen: int = 40) -> str: i = 0 for x, y in zip(expect, actual): if x != y: return "Actual did not match expect at char %d:\n" \ " Expect: %s\n" \ " Actual: %s\n" \ - % (i, repr(expect[i - prelen:i + postlen]), - repr(actual[i - prelen:i + postlen])) + % (i, repr(expect[i - prelen:i + postlen]), + repr(actual[i - prelen:i + postlen])) i = i + 1 return "Actual matched the expected output???" @@ -671,7 +673,7 @@ def normalize_ps(self, s): return s @staticmethod - def to_bytes_re_sub(pattern, repl, string, count: int=0, flags: int=0): + def to_bytes_re_sub(pattern, repl, string, count: int = 0, flags: int = 0): """ Wrapper around re.sub to change pattern and repl to bytes to work with both python 2 & 3 @@ -713,8 +715,9 @@ def normalize_pdf(self, s): d = zlib.decompress(s[b:e]) d = self.to_bytes_re_sub(r'%%CreationDate: [^\n]*\n', r'%%CreationDate: 1970 Jan 01 00:00:00\n', d) - d = self.to_bytes_re_sub(r'%DVIPSSource: TeX output \d\d\d\d\.\d\d\.\d\d:\d\d\d\d', - r'%DVIPSSource: TeX output 1970.01.01:0000', d) + d = self.to_bytes_re_sub( + r'%DVIPSSource: TeX output \d\d\d\d\.\d\d\.\d\d:\d\d\d\d', + r'%DVIPSSource: TeX output 1970.01.01:0000', d) d = self.to_bytes_re_sub(r'/(BaseFont|FontName) /[A-Z]{6}', r'/\1 /XXXXXX', d) r.append(d) @@ -754,10 +757,9 @@ def get_sconsignname(self) -> str: if hash_format is None and current_hash_algorithm == 'md5': return ".sconsign" else: - database_prefix=f".sconsign_{current_hash_algorithm}" + database_prefix = f".sconsign_{current_hash_algorithm}" return database_prefix - def unlink_sconsignfile(self, name: str = '.sconsign.dblite') -> None: """Delete the sconsign file. @@ -850,7 +852,8 @@ def java_where_includes(self, version=None): '/usr/lib/jvm/default-java/include/jni.h', '/usr/lib/jvm/java-*-oracle/include/jni.h'] else: - jni_dirs = [f'/System/Library/Frameworks/JavaVM.framework/Versions/{version}*/Headers/jni.h'] + jni_dirs = [ + f'/System/Library/Frameworks/JavaVM.framework/Versions/{version}*/Headers/jni.h'] jni_dirs.extend([f'/usr/lib/jvm/java-*-sun-{version}*/include/jni.h', f'/usr/lib/jvm/java-{version}*-openjdk*/include/jni.h', f'/usr/java/jdk{version}*/include/jni.h']) @@ -975,7 +978,8 @@ def java_where_java(self, version=None) -> str: where_java = self.where_is('java', ENV['PATH']) if not where_java: - self.skip_test("Could not find Java java, skipping test(s).\n", from_fw=True) + self.skip_test("Could not find Java java, skipping test(s).\n", + from_fw=True) elif sys.platform == "darwin": self.java_mac_check(where_java, 'java') @@ -996,7 +1000,8 @@ def java_where_javac(self, version=None) -> tuple[str, str]: else: where_javac = self.where_is('javac', ENV['PATH']) if not where_javac: - self.skip_test("Could not find Java javac, skipping test(s).\n", from_fw=True) + self.skip_test("Could not find Java javac, skipping test(s).\n", + from_fw=True) elif sys.platform == "darwin": self.java_mac_check(where_javac, 'javac') @@ -1050,7 +1055,8 @@ def java_where_javah(self, version=None) -> str: else: where_javah = self.where_is('javah', ENV['PATH']) if not where_javah: - self.skip_test("Could not find Java javah, skipping test(s).\n", from_fw=True) + self.skip_test("Could not find Java javah, skipping test(s).\n", + from_fw=True) return where_javah def java_where_rmic(self, version=None) -> str: @@ -1068,7 +1074,9 @@ def java_where_rmic(self, version=None) -> str: else: where_rmic = self.where_is('rmic', ENV['PATH']) if not where_rmic: - self.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n", from_fw=True) + self.skip_test( + "Could not find Java rmic, skipping non-simulated test(s).\n", + from_fw=True) return where_rmic def java_get_class_files(self, dir): @@ -1079,7 +1087,16 @@ def java_get_class_files(self, dir): result.append(os.path.join(dirpath, fname)) return sorted(result) - def Qt_dummy_installation(self, dir: str='qt') -> None: + def ninja_binary(self): + try: + import ninja + except ImportError: + return False + + return os.path.abspath(os.path.join(ninja.BIN_DIR, 'ninja' + _exe)) + + + def Qt_dummy_installation(self, dir: str = 'qt') -> None: # create a dummy qt installation self.subdir(dir, [dir, 'bin'], [dir, 'include'], [dir, 'lib']) @@ -1185,11 +1202,11 @@ def Qt_dummy_installation(self, dir: str='qt') -> None: self.QT_UIC = f"{_python_} {self.workpath(dir, 'bin', 'myuic.py')}" self.QT_LIB_DIR = self.workpath(dir, 'lib') - def Qt_create_SConstruct(self, place, qt_tool: str='qt3') -> None: + def Qt_create_SConstruct(self, place, qt_tool: str = 'qt3') -> None: if isinstance(place, list): place = self.workpath(*place) - var_prefix=qt_tool.upper() + var_prefix = qt_tool.upper() self.write(place, f"""\ if ARGUMENTS.get('noqtdir', 0): {var_prefix}DIR = None @@ -1239,7 +1256,7 @@ def coverage_run(self) -> bool: """ return 'COVERAGE_PROCESS_START' in os.environ or 'COVERAGE_FILE' in os.environ - def skip_if_not_msvc(self, check_platform: bool=True) -> None: + def skip_if_not_msvc(self, check_platform: bool = True) -> None: """ Skip test if MSVC is not available. Check whether we are on a Windows platform and skip the test if @@ -1264,10 +1281,10 @@ def skip_if_not_msvc(self, check_platform: bool=True) -> None: pass def checkConfigureLogAndStdout(self, checks, - logfile: str='config.log', - sconf_dir: str='.sconf_temp', - sconstruct: str="SConstruct", - doCheckLog: bool=True, doCheckStdout: bool=True): + logfile: str = 'config.log', + sconf_dir: str = '.sconf_temp', + sconstruct: str = "SConstruct", + doCheckLog: bool = True, doCheckStdout: bool = True): """ Verify expected output from Configure. Used to verify the expected output from using Configure() @@ -1298,7 +1315,8 @@ def checkConfigureLogAndStdout(self, checks, # sys.stderr.write("LOGFILE[%s]:%s"%(type(logfile),logfile)) if (doCheckLog and - logfile.find("scons: warning: The stored build information has an unexpected class.") >= 0): + logfile.find( + "scons: warning: The stored build information has an unexpected class.") >= 0): self.fail_test() log = r'file \S*%s\,line \d+:' % re.escape(sconstruct) + ls @@ -1321,7 +1339,7 @@ def checkConfigureLogAndStdout(self, checks, result_cached = 1 for bld_desc in check_info.cached: # each TryXXX for ext, flag in bld_desc: # each file in TryBuild - conf_filename = re.escape(check_info.temp_filename%ext) + conf_filename = re.escape(check_info.temp_filename % ext) if flag == self.NCR: # NCR = Non Cached Rebuild @@ -1339,8 +1357,9 @@ def checkConfigureLogAndStdout(self, checks, re.escape("scons: Configure: \"") + \ conf_filename + \ re.escape("\" is up to date.") + ls - log = log + re.escape("scons: Configure: The original builder " - "output was:") + ls + log = log + re.escape( + "scons: Configure: The original builder " + "output was:") + ls log = f"{log}( \\|.*{ls})+" if flag == self.NCF: # non-cached rebuild failure @@ -1351,8 +1370,10 @@ def checkConfigureLogAndStdout(self, checks, log = log + \ re.escape("scons: Configure: Building \"") + \ conf_filename + \ - re.escape("\" failed in a previous run and all its sources are up to date.") + ls - log = log + re.escape("scons: Configure: The original builder output was:") + ls + re.escape( + "\" failed in a previous run and all its sources are up to date.") + ls + log = log + re.escape( + "scons: Configure: The original builder output was:") + ls log = f"{log}( \\|.*{ls})+" if result_cached: result = f"(cached) {check_info.result}" @@ -1396,11 +1417,9 @@ def checkConfigureLogAndStdout(self, checks, print("-----------------------------------------------------") self.fail_test() - - def checkLogAndStdout(self, checks, results, cached, logfile, sconf_dir, sconstruct, - doCheckLog: bool=True, doCheckStdout: bool=True): + doCheckLog: bool = True, doCheckStdout: bool = True): """ Verify expected output from Configure. Used to verify the expected output from using Configure() @@ -1434,7 +1453,8 @@ def checkLogAndStdout(self, checks, results, cached, # sys.stderr.write("LOGFILE[%s]:%s"%(type(logfile),logfile)) if (doCheckLog and - logfile.find("scons: warning: The stored build information has an unexpected class.") >= 0): + logfile.find( + "scons: warning: The stored build information has an unexpected class.") >= 0): self.fail_test() sconf_dir = sconf_dir @@ -1462,10 +1482,12 @@ def checkLogAndStdout(self, checks, results, cached, for bld_desc in cache_desc: # each TryXXX for ext, flag in bld_desc: # each file in TryBuild if ext in ['.c', '.cpp']: - conf_filename = re.escape(os.path.join(sconf_dir, "conftest")) +\ + conf_filename = re.escape( + os.path.join(sconf_dir, "conftest")) + \ r'_[a-z0-9]{32,64}_\d+%s' % re.escape(ext) elif ext == '': - conf_filename = re.escape(os.path.join(sconf_dir, "conftest")) +\ + conf_filename = re.escape( + os.path.join(sconf_dir, "conftest")) + \ r'_[a-z0-9]{32,64}(_\d+_[a-z0-9]{32,64})?' else: @@ -1477,8 +1499,10 @@ def checkLogAndStdout(self, checks, results, cached, # this shortcut should be sufficient. # TODO: perhaps revisit and/or fix file naming for intermediate files in # Configure context logic - conf_filename = re.escape(os.path.join(sconf_dir, "conftest")) +\ - r'_[a-z0-9]{32,64}_\d+(_[a-z0-9]{32,64})?%s' % re.escape(ext) + conf_filename = re.escape( + os.path.join(sconf_dir, "conftest")) + \ + r'_[a-z0-9]{32,64}_\d+(_[a-z0-9]{32,64})?%s' % re.escape( + ext) if flag == self.NCR: # NCR = Non Cached Rebuild @@ -1496,8 +1520,9 @@ def checkLogAndStdout(self, checks, results, cached, re.escape("scons: Configure: \"") + \ conf_filename + \ re.escape("\" is up to date.") + ls - log = log + re.escape("scons: Configure: The original builder " - "output was:") + ls + log = log + re.escape( + "scons: Configure: The original builder " + "output was:") + ls log = f"{log}( \\|.*{ls})+" if flag == self.NCF: # non-cached rebuild failure @@ -1508,8 +1533,10 @@ def checkLogAndStdout(self, checks, results, cached, log = log + \ re.escape("scons: Configure: Building \"") + \ conf_filename + \ - re.escape("\" failed in a previous run and all its sources are up to date.") + ls - log = log + re.escape("scons: Configure: The original builder output was:") + ls + re.escape( + "\" failed in a previous run and all its sources are up to date.") + ls + log = log + re.escape( + "scons: Configure: The original builder output was:") + ls log = f"{log}( \\|.*{ls})+" # cnt = cnt + 1 if result_cached: @@ -1562,7 +1589,7 @@ def get_python_version(self) -> str: # see also sys.prefix documentation return python_minor_version_string() - def get_platform_python_info(self, python_h_required: bool=False): + def get_platform_python_info(self, python_h_required: bool = False): """Return information about Python. Returns a path to a Python executable suitable for testing on @@ -1577,7 +1604,8 @@ def get_platform_python_info(self, python_h_required: bool=False): """ python = os.environ.get('python_executable', self.where_is('python')) if not python: - self.skip_test('Can not find installed "python", skipping test.\n', from_fw=True) + self.skip_test('Can not find installed "python", skipping test.\n', + from_fw=True) # construct a program to run in the intended environment # in order to fetch the characteristics of that Python. @@ -1635,7 +1663,8 @@ def venv_path(): stdout = self.stdout() or "" incpath, libpath, libname, python_h = stdout.strip().split('\n') if python_h == "False" and python_h_required: - self.skip_test('Can not find required "Python.h", skipping test.\n', from_fw=True) + self.skip_test('Can not find required "Python.h", skipping test.\n', + from_fw=True) return (python, incpath, libpath, libname + _lib) @@ -1656,7 +1685,7 @@ def start(self, *args, **kw): restore_sconsflags(sconsflags) return p - def wait_for(self, fname, timeout: float=20.0, popen=None) -> None: + def wait_for(self, fname, timeout: float = 20.0, popen=None) -> None: """ Waits for the specified file name to exist. """ @@ -2004,7 +2033,7 @@ def copy_timing_configuration(self, source_dir, dest_dir) -> None: destination = source.replace(source_dir, dest_dir) shutil.copy2(source, destination) - def up_to_date(self, arguments: str='.', read_str: str="", **kw) -> None: + def up_to_date(self, arguments: str = '.', read_str: str = "", **kw) -> None: """Asserts that all of the targets listed in arguments is up to date, but does not make any assumptions on other targets. This function is most useful in conjunction with the -n option. @@ -2025,7 +2054,6 @@ def up_to_date(self, arguments: str='.', read_str: str="", **kw) -> None: self.run(**kw) - # In some environments, $AR will generate a warning message to stderr # if the library doesn't previously exist and is being created. One # way to fix this is to tell AR to be quiet (sometimes the 'c' flag), From 0b5ab8b6e4ab4fab4206220c593d9e7d88b0ebf9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 25 Nov 2024 12:43:54 -0800 Subject: [PATCH 208/386] updated so TestSCons.NINJA_BINARY is set and used by all such tests --- test/ninja/build_libraries.py | 2 +- test/ninja/command_line_targets.py | 2 +- test/ninja/copy_function_command.py | 2 +- test/ninja/default_targets.py | 2 +- test/ninja/force_scons_callback.py | 3 +-- test/ninja/generate_and_build.py | 2 +- test/ninja/generate_and_build_cxx.py | 2 +- test/ninja/generate_source.py | 2 +- test/ninja/generated_sources_alias.py | 3 +-- test/ninja/iterative_speedup.py | 2 +- test/ninja/mingw_command_generator_action.py | 2 +- test/ninja/mingw_depfile_format.py | 2 +- test/ninja/mkdir_function_command.py | 2 +- test/ninja/multi_env.py | 2 +- test/ninja/ninja_command_line.py | 2 +- test/ninja/ninja_conftest.py | 2 +- test/ninja/ninja_file_deterministic.py | 2 +- test/ninja/ninja_handle_control_c_rebuild.py | 3 +-- test/ninja/no_for_sig_subst.py | 2 +- test/ninja/response_file.py | 2 +- test/ninja/shell_command.py | 2 +- test/ninja/shutdown_scons_daemon.py | 2 +- testing/framework/TestCmd.py | 3 +++ testing/framework/TestSCons.py | 18 ++++++++---------- 24 files changed, 33 insertions(+), 35 deletions(-) diff --git a/test/ninja/build_libraries.py b/test/ninja/build_libraries.py index 0561f6f544..9436053c7b 100644 --- a/test/ninja/build_libraries.py +++ b/test/ninja/build_libraries.py @@ -34,7 +34,7 @@ except ImportError: test.skip_test("Could not find ninja module. Skipping test.\n") -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/command_line_targets.py b/test/ninja/command_line_targets.py index b043f62bca..1a6033b9e0 100644 --- a/test/ninja/command_line_targets.py +++ b/test/ninja/command_line_targets.py @@ -36,7 +36,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/copy_function_command.py b/test/ninja/copy_function_command.py index 13036ee3a0..e8721a19d9 100644 --- a/test/ninja/copy_function_command.py +++ b/test/ninja/copy_function_command.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/default_targets.py b/test/ninja/default_targets.py index 7752d5ab7e..e809d75379 100644 --- a/test/ninja/default_targets.py +++ b/test/ninja/default_targets.py @@ -36,7 +36,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/force_scons_callback.py b/test/ninja/force_scons_callback.py index b668f92c98..3ae5a5a616 100644 --- a/test/ninja/force_scons_callback.py +++ b/test/ninja/force_scons_callback.py @@ -37,8 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() - +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture("ninja-fixture") diff --git a/test/ninja/generate_and_build.py b/test/ninja/generate_and_build.py index c14af70452..9d056836fa 100644 --- a/test/ninja/generate_and_build.py +++ b/test/ninja/generate_and_build.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/generate_and_build_cxx.py b/test/ninja/generate_and_build_cxx.py index 51f68dca00..80faa4bbe1 100644 --- a/test/ninja/generate_and_build_cxx.py +++ b/test/ninja/generate_and_build_cxx.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/generate_source.py b/test/ninja/generate_source.py index f4bd0c0aef..b848b17c14 100644 --- a/test/ninja/generate_source.py +++ b/test/ninja/generate_source.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/generated_sources_alias.py b/test/ninja/generated_sources_alias.py index 3e7f9d16b1..22ca734b33 100644 --- a/test/ninja/generated_sources_alias.py +++ b/test/ninja/generated_sources_alias.py @@ -37,8 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() - +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/iterative_speedup.py b/test/ninja/iterative_speedup.py index 8190175326..cbd4b6ac99 100644 --- a/test/ninja/iterative_speedup.py +++ b/test/ninja/iterative_speedup.py @@ -39,7 +39,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/mingw_command_generator_action.py b/test/ninja/mingw_command_generator_action.py index 8fc08a816a..ad7878469a 100644 --- a/test/ninja/mingw_command_generator_action.py +++ b/test/ninja/mingw_command_generator_action.py @@ -52,7 +52,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/mingw_depfile_format.py b/test/ninja/mingw_depfile_format.py index e9c89a0f63..7792440d0e 100644 --- a/test/ninja/mingw_depfile_format.py +++ b/test/ninja/mingw_depfile_format.py @@ -36,7 +36,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/mkdir_function_command.py b/test/ninja/mkdir_function_command.py index c01cb982f7..3fd678a3f8 100644 --- a/test/ninja/mkdir_function_command.py +++ b/test/ninja/mkdir_function_command.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.write('SConstruct', """ SetOption('experimental','ninja') diff --git a/test/ninja/multi_env.py b/test/ninja/multi_env.py index 6aaeccd584..7b113ed22b 100644 --- a/test/ninja/multi_env.py +++ b/test/ninja/multi_env.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test = TestSCons.TestSCons() diff --git a/test/ninja/ninja_command_line.py b/test/ninja/ninja_command_line.py index d6744aa713..8fc232e428 100644 --- a/test/ninja/ninja_command_line.py +++ b/test/ninja/ninja_command_line.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture', 'src') diff --git a/test/ninja/ninja_conftest.py b/test/ninja/ninja_conftest.py index a92ecd9935..60c4b033ee 100644 --- a/test/ninja/ninja_conftest.py +++ b/test/ninja/ninja_conftest.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/ninja_file_deterministic.py b/test/ninja/ninja_file_deterministic.py index 232e7abba5..3d226ca4e3 100644 --- a/test/ninja/ninja_file_deterministic.py +++ b/test/ninja/ninja_file_deterministic.py @@ -39,7 +39,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/ninja_handle_control_c_rebuild.py b/test/ninja/ninja_handle_control_c_rebuild.py index 5635367395..d187c5f437 100644 --- a/test/ninja/ninja_handle_control_c_rebuild.py +++ b/test/ninja/ninja_handle_control_c_rebuild.py @@ -40,8 +40,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe - -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture("ninja-fixture") diff --git a/test/ninja/no_for_sig_subst.py b/test/ninja/no_for_sig_subst.py index da33f8d1c0..ef2c795991 100644 --- a/test/ninja/no_for_sig_subst.py +++ b/test/ninja/no_for_sig_subst.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/response_file.py b/test/ninja/response_file.py index e9f778ae8c..f2561bbfa9 100644 --- a/test/ninja/response_file.py +++ b/test/ninja/response_file.py @@ -42,7 +42,7 @@ _exe = TestSCons._exe _obj = TestSCons._obj -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/shell_command.py b/test/ninja/shell_command.py index 0ed8f2a0d0..8b3d134298 100644 --- a/test/ninja/shell_command.py +++ b/test/ninja/shell_command.py @@ -37,7 +37,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture('ninja-fixture') diff --git a/test/ninja/shutdown_scons_daemon.py b/test/ninja/shutdown_scons_daemon.py index 25823d742e..c646f970f8 100644 --- a/test/ninja/shutdown_scons_daemon.py +++ b/test/ninja/shutdown_scons_daemon.py @@ -44,7 +44,7 @@ _python_ = TestSCons._python_ _exe = TestSCons._exe -ninja_bin = test.ninja_binary() +ninja_bin = TestSCons.NINJA_BINARY test.dir_fixture("ninja-fixture") diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 7307078c5e..56c282c990 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -337,6 +337,9 @@ IS_ROOT = False NEED_HELPER = os.environ.get('SCONS_NO_DIRECT_SCRIPT') + + + # sentinel for cases where None won't do _Null = object() diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index d6c51251e8..5c95c24236 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -74,7 +74,8 @@ 'lib_', '_lib', 'dll_', - '_dll' + '_dll', + 'NINJA_BINARY' ]) machine_map = { @@ -103,6 +104,12 @@ _dll = dll_suffix dll_ = dll_prefix +try: + import ninja + NINJA_BINARY = os.path.abspath(os.path.join(ninja.BIN_DIR, 'ninja' + _exe)) +except ImportError: + NINJA_BINARY = None + if sys.platform == 'cygwin': # On Cygwin, os.path.normcase() lies, so just report back the # fact that the underlying Win32 OS is case-insensitive. @@ -1087,15 +1094,6 @@ def java_get_class_files(self, dir): result.append(os.path.join(dirpath, fname)) return sorted(result) - def ninja_binary(self): - try: - import ninja - except ImportError: - return False - - return os.path.abspath(os.path.join(ninja.BIN_DIR, 'ninja' + _exe)) - - def Qt_dummy_installation(self, dir: str = 'qt') -> None: # create a dummy qt installation From 6d70e82c31788fdbc95cc9dff0b116f632be5d62 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 25 Nov 2024 13:43:46 -0800 Subject: [PATCH 209/386] renamed SCons.Tool.ninja -> SCons.Tool.ninja_tool and added alias in tool loading logic. This fixes changes in this PR breaking JavaCommonTests because pypi's ninja module and SCons.Tool.ninja had the same name which python couldn't differentiate --- SCons/Tool/__init__.py | 1 + SCons/Tool/{ninja => ninja_tool}/Globals.py | 0 SCons/Tool/{ninja => ninja_tool}/Methods.py | 8 ++++---- SCons/Tool/{ninja => ninja_tool}/NinjaState.py | 2 +- SCons/Tool/{ninja => ninja_tool}/Overrides.py | 0 SCons/Tool/{ninja => ninja_tool}/Rules.py | 0 SCons/Tool/{ninja => ninja_tool}/Utils.py | 8 ++++---- SCons/Tool/{ninja => ninja_tool}/__init__.py | 10 +++++----- SCons/Tool/{ninja => ninja_tool}/ninja.xml | 0 SCons/Tool/{ninja => ninja_tool}/ninja_daemon_build.py | 0 SCons/Tool/{ninja => ninja_tool}/ninja_run_daemon.py | 0 SCons/Tool/{ninja => ninja_tool}/ninja_scons_daemon.py | 0 12 files changed, 15 insertions(+), 14 deletions(-) rename SCons/Tool/{ninja => ninja_tool}/Globals.py (100%) rename SCons/Tool/{ninja => ninja_tool}/Methods.py (97%) rename SCons/Tool/{ninja => ninja_tool}/NinjaState.py (99%) rename SCons/Tool/{ninja => ninja_tool}/Overrides.py (100%) rename SCons/Tool/{ninja => ninja_tool}/Rules.py (100%) rename SCons/Tool/{ninja => ninja_tool}/Utils.py (98%) rename SCons/Tool/{ninja => ninja_tool}/__init__.py (99%) rename SCons/Tool/{ninja => ninja_tool}/ninja.xml (100%) rename SCons/Tool/{ninja => ninja_tool}/ninja_daemon_build.py (100%) rename SCons/Tool/{ninja => ninja_tool}/ninja_run_daemon.py (100%) rename SCons/Tool/{ninja => ninja_tool}/ninja_scons_daemon.py (100%) diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py index a7bc927ebc..23b7eeba2c 100644 --- a/SCons/Tool/__init__.py +++ b/SCons/Tool/__init__.py @@ -102,6 +102,7 @@ 'gettext': 'gettext_tool', 'clang++': 'clangxx', 'as': 'asm', + 'ninja' : 'ninja_tool' } diff --git a/SCons/Tool/ninja/Globals.py b/SCons/Tool/ninja_tool/Globals.py similarity index 100% rename from SCons/Tool/ninja/Globals.py rename to SCons/Tool/ninja_tool/Globals.py diff --git a/SCons/Tool/ninja/Methods.py b/SCons/Tool/ninja_tool/Methods.py similarity index 97% rename from SCons/Tool/ninja/Methods.py rename to SCons/Tool/ninja_tool/Methods.py index ff006c072e..01866e5f3f 100644 --- a/SCons/Tool/ninja/Methods.py +++ b/SCons/Tool/ninja_tool/Methods.py @@ -30,9 +30,9 @@ import SCons from SCons.Subst import SUBST_CMD -from SCons.Tool.ninja import NINJA_CUSTOM_HANDLERS, NINJA_RULES, NINJA_POOLS -from SCons.Tool.ninja.Globals import __NINJA_RULE_MAPPING -from SCons.Tool.ninja.Utils import get_targets_sources, get_dependencies, get_order_only, get_outputs, get_inputs, \ +from SCons.Tool.ninja_tool import NINJA_CUSTOM_HANDLERS, NINJA_RULES, NINJA_POOLS +from SCons.Tool.ninja_tool.Globals import __NINJA_RULE_MAPPING +from SCons.Tool.ninja_tool.Utils import get_targets_sources, get_dependencies, get_order_only, get_outputs, get_inputs, \ get_rule, get_path, generate_command, get_command_env, get_comstr if TYPE_CHECKING: @@ -46,7 +46,7 @@ def register_custom_handler(env, name, handler) -> None: def register_custom_rule_mapping(env, pre_subst_string, rule) -> None: """Register a function to call for a given rule.""" - SCons.Tool.ninja.Globals.__NINJA_RULE_MAPPING[pre_subst_string] = rule + __NINJA_RULE_MAPPING[pre_subst_string] = rule def register_custom_rule(env, rule, command, description: str="", deps=None, pool=None, use_depfile: bool=False, use_response_file: bool=False, response_file_content: str="$rspc") -> None: diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja_tool/NinjaState.py similarity index 99% rename from SCons/Tool/ninja/NinjaState.py rename to SCons/Tool/ninja_tool/NinjaState.py index da7aa28f12..274331e0de 100644 --- a/SCons/Tool/ninja/NinjaState.py +++ b/SCons/Tool/ninja_tool/NinjaState.py @@ -753,7 +753,7 @@ def action_to_ninja_build(self, node, action=None): # Ninja builders out of being sources of ninja builders but I # can't fix every DAG problem so we just skip ninja_builders # if we find one - if SCons.Tool.ninja.NINJA_STATE.ninja_file == str(node): + if SCons.Tool.ninja_tool.NINJA_STATE.ninja_file == str(node): build = None elif isinstance(action, SCons.Action.FunctionAction): build = self.handle_func_action(node, action) diff --git a/SCons/Tool/ninja/Overrides.py b/SCons/Tool/ninja_tool/Overrides.py similarity index 100% rename from SCons/Tool/ninja/Overrides.py rename to SCons/Tool/ninja_tool/Overrides.py diff --git a/SCons/Tool/ninja/Rules.py b/SCons/Tool/ninja_tool/Rules.py similarity index 100% rename from SCons/Tool/ninja/Rules.py rename to SCons/Tool/ninja_tool/Rules.py diff --git a/SCons/Tool/ninja/Utils.py b/SCons/Tool/ninja_tool/Utils.py similarity index 98% rename from SCons/Tool/ninja/Utils.py rename to SCons/Tool/ninja_tool/Utils.py index 24d439ef5e..7782687791 100644 --- a/SCons/Tool/ninja/Utils.py +++ b/SCons/Tool/ninja_tool/Utils.py @@ -413,14 +413,14 @@ def ninja_stat(_self, path): """ try: - return SCons.Tool.ninja.Globals.NINJA_STAT_MEMO[path] + return SCons.Tool.ninja_tool.Globals.NINJA_STAT_MEMO[path] except KeyError: try: result = os.stat(path) except os.error: result = None - SCons.Tool.ninja.Globals.NINJA_STAT_MEMO[path] = result + SCons.Tool.ninja_tool.Globals.NINJA_STAT_MEMO[path] = result return result @@ -430,7 +430,7 @@ def ninja_whereis(thing, *_args, **_kwargs): # Optimize for success, this gets called significantly more often # when the value is already memoized than when it's not. try: - return SCons.Tool.ninja.Globals.NINJA_WHEREIS_MEMO[thing] + return SCons.Tool.ninja_tool.Globals.NINJA_WHEREIS_MEMO[thing] except KeyError: # TODO: Fix this to respect env['ENV']['PATH']... WPD # We do not honor any env['ENV'] or env[*] variables in the @@ -443,7 +443,7 @@ def ninja_whereis(thing, *_args, **_kwargs): # with shell quoting is nigh impossible. So I've decided to # cross that bridge when it's absolutely required. path = shutil.which(thing) - SCons.Tool.ninja.Globals.NINJA_WHEREIS_MEMO[thing] = path + SCons.Tool.ninja_tool.Globals.NINJA_WHEREIS_MEMO[thing] = path return path diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja_tool/__init__.py similarity index 99% rename from SCons/Tool/ninja/__init__.py rename to SCons/Tool/ninja_tool/__init__.py index 7320d03852..d86c2c9d4d 100644 --- a/SCons/Tool/ninja/__init__.py +++ b/SCons/Tool/ninja_tool/__init__.py @@ -32,7 +32,7 @@ import SCons import SCons.Script -import SCons.Tool.ninja.Globals +from SCons.Tool.ninja_tool.Globals import ninja_builder_initialized from SCons.Script import GetOption from SCons.Util import sanitize_shell_env @@ -187,13 +187,13 @@ def ninja_emitter(target, source, env): def generate(env): """Generate the NINJA builders.""" - global NINJA_STATE, NINJA_CMDLINE_TARGETS + global NINJA_STATE, NINJA_CMDLINE_TARGETS, ninja_builder_initialized if 'ninja' not in GetOption('experimental'): return - if not SCons.Tool.ninja.Globals.ninja_builder_initialized: - SCons.Tool.ninja.Globals.ninja_builder_initialized = True + if not ninja_builder_initialized: + ninja_builder_initialized = True ninja_add_command_line_options() @@ -255,7 +255,7 @@ def ninja_generate_deps(env): pass else: env.Append(CCFLAGS='$CCDEPFLAGS') - + env.AddMethod(CheckNinjaCompdbExpand, "CheckNinjaCompdbExpand") # Provide a way for custom rule authors to easily access command diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja_tool/ninja.xml similarity index 100% rename from SCons/Tool/ninja/ninja.xml rename to SCons/Tool/ninja_tool/ninja.xml diff --git a/SCons/Tool/ninja/ninja_daemon_build.py b/SCons/Tool/ninja_tool/ninja_daemon_build.py similarity index 100% rename from SCons/Tool/ninja/ninja_daemon_build.py rename to SCons/Tool/ninja_tool/ninja_daemon_build.py diff --git a/SCons/Tool/ninja/ninja_run_daemon.py b/SCons/Tool/ninja_tool/ninja_run_daemon.py similarity index 100% rename from SCons/Tool/ninja/ninja_run_daemon.py rename to SCons/Tool/ninja_tool/ninja_run_daemon.py diff --git a/SCons/Tool/ninja/ninja_scons_daemon.py b/SCons/Tool/ninja_tool/ninja_scons_daemon.py similarity index 100% rename from SCons/Tool/ninja/ninja_scons_daemon.py rename to SCons/Tool/ninja_tool/ninja_scons_daemon.py From eeb5dfd941c580ac329ab2d8c97f3dfae4e5f0a9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 25 Nov 2024 14:08:52 -0800 Subject: [PATCH 210/386] [ci skip] Update CHANGES/RELEASE with newer changes --- CHANGES.txt | 4 +++- RELEASE.txt | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 964296614d..54def0ad48 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -89,7 +89,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Update ninja tool to use ninja.BIN_DIR to find pypi packaged ninja binary. python ninja package version 1.11.1.2 changed the location and previous logic no longer worked. - - Added ninja_binary() method to TestSCons to centralize logic to find ninja binary + - Added TestSCons.NINJA_BINARY to TestSCons to centralize logic to find ninja binary + - Refactored SCons.Tool.ninja -> SCons.Tool.ninja_tool, and added alias so + env.Tool('ninja') will still work. This avoids conflicting with the pypi module ninja. From Alex James: - On Darwin, PermissionErrors are now handled while trying to access diff --git a/RELEASE.txt b/RELEASE.txt index 4aa9697d39..af5fb2fd2f 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -201,7 +201,10 @@ DEVELOPMENT - Implemented type hints for Nodes. -- Added ninja_binary() method to TestSCons to centralize logic to find ninja binary +- Added TestSCons.NINJA_BINARY to TestSCons to centralize logic to find ninja binary + +- Refactored SCons.Tool.ninja -> SCons.Tool.ninja_tool, and added alias so env.Tool('ninja') + will still work. This avoids conflicting with the pypi module ninja. Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== From eeb025f1591b0838f549b310883ab6a28eb42381 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 25 Nov 2024 09:42:40 -0700 Subject: [PATCH 211/386] Add Variables.defualted attribute Also document the Variables.unknown attribute as being the same thing as the ruturn from the UnknownVariables method. Signed-off-by: Mats Wichmann --- CHANGES.txt | 8 +- RELEASE.txt | 6 ++ SCons/Variables/VariablesTests.py | 9 +- SCons/Variables/__init__.py | 67 ++++++++++----- doc/man/scons.xml | 131 ++++++++++++++++++------------ 5 files changed, 146 insertions(+), 75 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 54def0ad48..aa29180f5a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -160,7 +160,13 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER variable names are given. - Update Clean and NoClean documentation. - Make sure unknown variables from a Variables file are recognized - as such (issue #4645) + as such. Previously only unknowns from the command line were + recognized (issue #4645). + - A Variables object now makes available a "defaulted" attribute, + a list of variable names that were set in the environment with + their values taken from the default in the variable description + (if a variable was set to the same value as the default in one + of the input sources, it is not included in this list). RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index af5fb2fd2f..aa3e3c15db 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -76,6 +76,12 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY always returns a dict. The default remains to return different types depending on whether zero, one, or multiple construction +- A Variables object now makes available a "defaulted" attribute, + a list of variable names that were set in the environment with + their values taken from the default in the variable description + (if a variable was set to the same value as the default in one + of the input sources, it is not included in this list). + FIXES ----- diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index 80c4d11397..bc981e06fe 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -659,24 +659,28 @@ def test_AddOptionUpdatesUnknown(self) -> None: Get one unknown from args and one from a variables file. Add these later, making sure they no longer appear in unknowns after the subsequent Update(). + + While we're here, test the *defaulted* attribute. """ test = TestSCons.TestSCons() var_file = test.workpath('vars.py') test.write('vars.py', 'FROMFILE="added"') opts = SCons.Variables.Variables(files=var_file) - opts.Add('A', 'A test variable', "1") + opts.Add('A', 'A test variable', default="1") + opts.Add('B', 'Test variable B', default="1") args = { 'A' : 'a', 'ADDEDLATER' : 'notaddedyet', } env = Environment() - opts.Update(env,args) + opts.Update(env, args) r = opts.UnknownVariables() with self.subTest(): self.assertEqual('notaddedyet', r['ADDEDLATER']) self.assertEqual('added', r['FROMFILE']) self.assertEqual('a', env['A']) + self.assertEqual(['B'], opts.defaulted) opts.Add('ADDEDLATER', 'An option not present initially', "1") opts.Add('FROMFILE', 'An option from a file also absent', "1") @@ -693,6 +697,7 @@ def test_AddOptionUpdatesUnknown(self) -> None: self.assertEqual('added', env['ADDEDLATER']) self.assertNotIn('FROMFILE', r) self.assertEqual('added', env['FROMFILE']) + self.assertEqual(['B'], opts.defaulted) def test_AddOptionWithAliasUpdatesUnknown(self) -> None: """Test updating of the 'unknown' dict (with aliases)""" diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 1826c64445..de26e7b65f 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -27,6 +27,7 @@ import os.path import sys +from contextlib import suppress from functools import cmp_to_key from typing import Callable, Sequence @@ -58,22 +59,30 @@ class Variable: __slots__ = ('key', 'aliases', 'help', 'default', 'validator', 'converter', 'do_subst') def __lt__(self, other): - """Comparison fuction so Variable instances sort.""" + """Comparison fuction so :class:`Variable` instances sort.""" return self.key < other.key def __str__(self) -> str: - """Provide a way to "print" a Variable object.""" + """Provide a way to "print" a :class:`Variable` object.""" return ( - f"({self.key!r}, {self.aliases}, {self.help!r}, {self.default!r}, " + f"({self.key!r}, {self.aliases}, " + f"help={self.help!r}, default={self.default!r}, " f"validator={self.validator}, converter={self.converter})" ) class Variables: - """A container for multiple Build Variables. + """A container for Build Variables. - Includes methods to updates the environment with the variables, - and to render the help text. + Includes a method to populate the variables with values into a + construction envirionment, and methods to render the help text. + + Note that the pubic API for creating a ``Variables`` object is + :func:`SCons.Script.Variables`, a kind of factory function, which + defaults to supplying the contents of :attr:`~SCons.Script.ARGUMENTS` + as the *args* parameter if it was not otherwise given. That is the + behavior documented in the manpage for ``Variables`` - and different + from the default if you instantiate this directly. Arguments: files: string or list of strings naming variable config scripts @@ -83,11 +92,15 @@ class Variables: instead of a fresh instance. Currently inoperable (default ``False``) .. versionchanged:: 4.8.0 - The default for *is_global* changed to ``False`` (previously - ``True`` but it had no effect due to an implementation error). + The default for *is_global* changed to ``False`` (the previous + default ``True`` had no effect due to an implementation error). .. deprecated:: 4.8.0 *is_global* is deprecated. + + .. versionadded:: NEXT_RELEASE + The :attr:`defaulted` attribute now lists those variables which + were filled in from default values. """ def __init__( @@ -102,15 +115,18 @@ def __init__( files = [files] if files else [] self.files: Sequence[str] = files self.unknown: dict[str, str] = {} + self.defaulted: list[str] = [] def __str__(self) -> str: - """Provide a way to "print" a Variables object.""" - s = "Variables(\n options=[\n" - for option in self.options: - s += f" {str(option)},\n" - s += " ],\n" - s += f" args={self.args},\n files={self.files},\n unknown={self.unknown},\n)" - return s + """Provide a way to "print" a :class:`Variables` object.""" + opts = ',\n'.join((f" {option!s}" for option in self.options)) + return ( + f"Variables(\n options=[\n{opts}\n ],\n" + f" args={self.args},\n" + f" files={self.files},\n" + f" unknown={self.unknown},\n" + f" defaulted={self.defaulted},\n)" + ) # lint: W0622: Redefining built-in 'help' def _do_add( @@ -122,7 +138,7 @@ def _do_add( converter: Callable | None = None, **kwargs, ) -> None: - """Create a Variable and add it to the list. + """Create a :class:`Variable` and add it to the list. This is the internal implementation for :meth:`Add` and :meth:`AddVariables`. Not part of the public API. @@ -203,9 +219,9 @@ def Add( return self._do_add(key, *args, **kwargs) def AddVariables(self, *optlist) -> None: - """Add a list of Build Variables. + """Add Build Variables. - Each list element is a tuple/list of arguments to be passed on + Each *optlist* element is a sequence of arguments to be passed on to the underlying method for adding variables. Example:: @@ -223,13 +239,22 @@ def AddVariables(self, *optlist) -> None: def Update(self, env, args: dict | None = None) -> None: """Update an environment with the Build Variables. + Collects variables from the input sources which do not match + a variable description in this object. These are ignored for + purposes of adding to *env*, but can be retrieved using the + :meth:`UnknownVariables` method. Also collects variables which + are set in *env* from the default in a variable description and + not from the input sources. These are available in the + :attr:`defaulted` attribute. + Args: env: the environment to update. args: a dictionary of keys and values to update in *env*. If omitted, uses the saved :attr:`args` """ - # first pull in the defaults + # first pull in the defaults, except any which are None. values = {opt.key: opt.default for opt in self.options if opt.default is not None} + self.defaulted = list(values) # next set the values specified in any options script(s) for filename in self.files: @@ -256,6 +281,8 @@ def Update(self, env, args: dict | None = None) -> None: for option in self.options: if arg in option.aliases + [option.key,]: values[option.key] = value + with suppress(ValueError): + self.defaulted.remove(option.key) added = True if not added: self.unknown[arg] = value @@ -269,6 +296,8 @@ def Update(self, env, args: dict | None = None) -> None: for option in self.options: if arg in option.aliases + [option.key,]: values[option.key] = value + with suppress(ValueError): + self.defaulted.remove(option.key) added = True if not added: self.unknown[arg] = value diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 8366bbd92e..1b7e886c69 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -4704,47 +4704,52 @@ env = conf.Finish() Command-Line Construction Variables -Often when building software, -specialized information needs to be conveyed at build time -to override the defaults in the build scripts. -Command-line arguments (like --implcit-cache) -and giving names of build targets are two ways to do that. -Another is to provide variable-assignment arguments -on the command line. -For the particular case where you want to specify new + +&SCons; depends on information stored in &consvars; to +control how targets are built. +It is often necessary to pass +specialized information at build time +to override the variables in the build scripts. +This can be done through variable-assignment arguments +on the command line and/or in stored variable files. + + + +For the case where you want to specify new values for &consvars;, &SCons; provides a &Variables; object to simplify collecting those and updating a &consenv; with the values. -The typical calling style looks like: +This helps processing commands lines like this: -scons VARIABLE=foo +scons VARIABLE=foo OTHERVAR=bar -Variables specified in the above way -can be manually processed by accessing the +Variables supplied on the command line +can always be manually processed by iterating the &ARGUMENTS; dictionary -(or &ARGLIST; list), -but using a &Variables; object allows you to describe +or the &ARGLIST; list, +However, using a &Variables; object allows you to describe anticipated variables, -convert them to a suitable type if necessary, -validate the values are within defined constraints, -and define defaults, help messages and aliases. -This is conceptually similar to the structure of options +perform necessary type conversion, +validate that values meet defined constraints, +and specify default values, help messages and aliases. +This provides a somewhat similar interface to option handling (see &f-link-AddOption;). -It also allows obtaining values from a saved variables file, +A &Variables; object also allows +obtaining values from a saved variables file, or from a custom dictionary in an &SConscript; file. The processed variables can then be applied to the desired &consenv;. -Roughly speaking, arguments are used to convey information to the -&SCons; program about how it should behave; -variables are used to convey information to the build -(although &SCons; does not enforce any such constraint). +Conceptually, command-line targets control what to build, +command-line variables (and variable files) control how to build, +and command-line options control how &SCons; operates +(although &SCons; does not enforce that separation). To obtain an object for manipulating variables, @@ -4755,23 +4760,23 @@ call the &Variables; factory function: Variables([files, [args]]) If files is a filename or list of filenames, -they are considered to be &Python; scripts which will -be executed to set variables when the +they are executed as &Python; scripts +to set saved variables when the Update -method is called - -this allows the use of &Python; syntax in the assignments. -A file can be the result of an earlier call to the +method is called. +This allows the use of &Python; syntax in the assignments. +A variables file can be the result of an previous call to the &Save; method. If files is not specified, or the files argument is None, -then no files will be read. +then no files will be processed. Supplying None is required if there are no files but you want to specify args as a positional argument; -this can be omitted if using the keyword argument style. +or you can use keyword arguments to avoid that. If any of files is missing, it is silently skipped. @@ -4822,25 +4827,42 @@ vars = Variables(files=None, args=ARGUMENTS) -A &Variables; object serves as a container for -descriptions of variables, -which are added by calling methods of the object. -Each variable consists of a name (which will -become a &consvar;), aliases for the name, +A &Variables; object is a container for variable descriptions, +added by calling the +Add or +AddVariables +methods. +Each variable description consists of a name (which will +be used as the &consvar; name), aliases for the name, a help message, a default value, and functions to validate and convert values. -Once the object is asked to process variables, -it matches up data from the input -sources it was given with the definitions, -and generates key-value pairs which are added -to the specified &consenv;, -except that if any variable was described -to have a default of None, -it is not added to -the construction environment unless it -appears in the input sources. -Otherwise, a variable not in the -input sources is added using its default value. +Processing of input sources +is deferred until the +Update +method is called, +at which time the variables are added to the +specified &consenv;. +Variables from the input sources which do not match any +names or aliases from the variable descriptions in this object are skipped, +except that a dictionary of their names and values are made available +in the .unknown attribute of the &Variables; object. +This list can also be obtained via the +UnknownVariables +method. +If a variable description has a default value +other than None and does not +appear in the input sources, +it is added to the &consenv; with its default value. +A list of variables set from their defaults and +not supplied a value in the input sources is +available as the .defaulted attribute +of the &Variables; object. +The unknown variables and defaulted information is +not available until the &Update; method has run. + + +New in NEXT_RELEASE: +the defaulted attribute. @@ -4848,7 +4870,8 @@ Note that since the variables are eventually added as &consvars;, you should choose variable names which do not unintentionally change pre-defined &consvars; that your project will make use of (see for a reference), -since variables obtained have values overridden, not merged. +since the specified values are assigned, not merged, +to the respective &consvars;. @@ -5037,7 +5060,9 @@ variables that were specified in the files and/or args parameters when &Variables; -was called, but the object was not actually configured for. +was called, but which were not configured in the object. +The same dictionary is also available as the +unknown attribute of the object. This information is not available until the Update method has run. @@ -5148,7 +5173,7 @@ vars.FormatVariableHelpText = my_format &SCons; provides five pre-defined variable types, accessible through factory functions that generate a tuple appropriate for directly passing to the -Add +Add or AddVariables methods. @@ -5191,7 +5216,7 @@ as false. Set up a variable named key -whose value may only be from +whose value will be a choice from a specified list ("enumeration") of values. The variable will have a default value of default @@ -5234,8 +5259,8 @@ converted to lower case. Set up a variable named key -whose value may be one or more -from a specified list of values. +whose value will be one or more +choices from a specified list of values. The variable will have a default value of default, and help From 677633581e642708879eea7d7127fda6814509a0 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 25 Nov 2024 13:23:09 -0700 Subject: [PATCH 212/386] Turn SCons.Variables.Variable into a dataclass Also correct one long-standing minor inconsistency: if a list of names for a variable is given, it is split into name and aliases, with the name not duplicated into the aliases. If only one name is given, it is both the name and the only entry in the aliases. All of the usages of aliases combine them back together anyway, like: if arg in option.aliases + [option.key,]: So there's no need for the behavior of the one-name form including the name in aliases. One test did need to change for this - the test of a custom help formatter function was wired to accept the old form, but only because of the way the custom formatter in the test was written, so this is a self-contained problem. Signed-off-by: Mats Wichmann --- CHANGES.txt | 7 ++++ RELEASE.txt | 5 +++ SCons/Variables/VariablesTests.py | 14 ++++---- SCons/Variables/__init__.py | 56 ++++++++++++------------------- 4 files changed, 40 insertions(+), 42 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index aa29180f5a..155ab6b567 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -167,6 +167,13 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER their values taken from the default in the variable description (if a variable was set to the same value as the default in one of the input sources, it is not included in this list). + - If a build Variable is created with no alises, the name of the + Variable is no longer listed in its aliases. Internally, the name + and alises are considered together anyway so this should not have + any effect except for being visible to custom help text formatters. + - A build Variable is now a dataclass, with initialization moving to + the automatically provided method; the Variables class no longer + writes directly to a Variable (makes static checkers happier). RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index aa3e3c15db..94ffa0c106 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -82,6 +82,11 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY (if a variable was set to the same value as the default in one of the input sources, it is not included in this list). +- If a build Variable is created with no alises, the name of the + Variable is no longer listed in its aliases. Internally, the name + and alises are considered together anyway so this should not have + any effect except for being visible to custom help text formatters. + FIXES ----- diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index bc981e06fe..c409db4022 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -550,7 +550,7 @@ def my_format(env, opt, help, default, actual, aliases) -> str: check, lambda x: int(x) + 12) - opts.Add('B', + opts.Add(['B', 'BOPTION'], 'b - alpha test', "42", check, @@ -566,9 +566,9 @@ def my_format(env, opt, help, default, actual, aliases) -> str: opts.Update(env, {}) expect = """\ -ANSWER 42 54 THE answer to THE question ['ANSWER'] -B 42 54 b - alpha test ['B'] -A 42 54 a - alpha test ['A'] +ANSWER 42 54 THE answer to THE question [] +B 42 54 b - alpha test ['BOPTION'] +A 42 54 a - alpha test [] """ text = opts.GenerateHelpText(env) @@ -576,9 +576,9 @@ def my_format(env, opt, help, default, actual, aliases) -> str: self.assertEqual(expect, text) expectAlpha = """\ -A 42 54 a - alpha test ['A'] -ANSWER 42 54 THE answer to THE question ['ANSWER'] -B 42 54 b - alpha test ['B'] +A 42 54 a - alpha test [] +ANSWER 42 54 THE answer to THE question [] +B 42 54 b - alpha test ['BOPTION'] """ text = opts.GenerateHelpText(env, sort=cmp) with self.subTest(): diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index de26e7b65f..521380468d 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -28,8 +28,9 @@ import os.path import sys from contextlib import suppress +from dataclasses import dataclass from functools import cmp_to_key -from typing import Callable, Sequence +from typing import Any, Callable, Sequence import SCons.Errors import SCons.Util @@ -53,22 +54,18 @@ "PathVariable", ] + +@dataclass(order=True) class Variable: """A Build Variable.""" - __slots__ = ('key', 'aliases', 'help', 'default', 'validator', 'converter', 'do_subst') - - def __lt__(self, other): - """Comparison fuction so :class:`Variable` instances sort.""" - return self.key < other.key - - def __str__(self) -> str: - """Provide a way to "print" a :class:`Variable` object.""" - return ( - f"({self.key!r}, {self.aliases}, " - f"help={self.help!r}, default={self.default!r}, " - f"validator={self.validator}, converter={self.converter})" - ) + key: str + aliases: list[str] + help: str + default: Any + validator: Callable | None + converter: Callable | None + do_subst: bool class Variables: @@ -131,7 +128,7 @@ def __str__(self) -> str: # lint: W0622: Redefining built-in 'help' def _do_add( self, - key: str | list[str], + key: str | Sequence[str], help: str = "", default=None, validator: Callable | None = None, @@ -146,30 +143,19 @@ def _do_add( .. versionadded:: 4.8.0 *subst* keyword argument is now recognized. """ - option = Variable() - - # If we get a list or a tuple, we take the first element as the - # option key and store the remaining in aliases. + # aliases needs to be a list for later concatenation operations if SCons.Util.is_Sequence(key): - option.key = key[0] - option.aliases = list(key[1:]) + name, aliases = key[0], list(key[1:]) else: - option.key = key - # TODO: normalize to not include key in aliases. Currently breaks tests. - option.aliases = [key,] - if not option.key.isidentifier(): - raise SCons.Errors.UserError(f"Illegal Variables key {option.key!r}") - option.help = help - option.default = default - option.validator = validator - option.converter = converter - option.do_subst = kwargs.pop("subst", True) - # TODO should any remaining kwargs be saved in the Variable? - + name, aliases = key, [] + if not name.isidentifier(): + raise SCons.Errors.UserError(f"Illegal Variables key {name!r}") + do_subst = kwargs.pop("subst", True) + option = Variable(name, aliases, help, default, validator, converter, do_subst) self.options.append(option) - # options might be added after the 'unknown' dict has been set up, - # so we remove the key and all its aliases from that dict + # options might be added after the 'unknown' dict has been set up: + # look for and remove the key and all its aliases from that dict for alias in option.aliases + [option.key,]: if alias in self.unknown: del self.unknown[alias] From 8558c8f9528fad24a86996d7ec97750b817cd5ee Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 11 Dec 2024 18:29:42 -0800 Subject: [PATCH 213/386] [ci skip] fix typo in CHANGES/RELEASE --- CHANGES.txt | 4 ++-- RELEASE.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 155ab6b567..d73da372e3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -167,9 +167,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER their values taken from the default in the variable description (if a variable was set to the same value as the default in one of the input sources, it is not included in this list). - - If a build Variable is created with no alises, the name of the + - If a build Variable is created with no aliases, the name of the Variable is no longer listed in its aliases. Internally, the name - and alises are considered together anyway so this should not have + and aliases are considered together anyway so this should not have any effect except for being visible to custom help text formatters. - A build Variable is now a dataclass, with initialization moving to the automatically provided method; the Variables class no longer diff --git a/RELEASE.txt b/RELEASE.txt index 94ffa0c106..0838c69730 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -82,9 +82,9 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY (if a variable was set to the same value as the default in one of the input sources, it is not included in this list). -- If a build Variable is created with no alises, the name of the +- If a build Variable is created with no aliases, the name of the Variable is no longer listed in its aliases. Internally, the name - and alises are considered together anyway so this should not have + and aliases are considered together anyway so this should not have any effect except for being visible to custom help text formatters. FIXES From 38261a8202c848d309c0ff0a6b53383e4cce7d2f Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 7 Nov 2024 07:16:55 -0700 Subject: [PATCH 214/386] Improve C scanner conditional inclusion Simplistic macro replacement is now done on the contents of CPPDEFINES, to improve accuracy of conditional inclusion as compared to the real preprocessor (ref: issue #4623). Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 ++ RELEASE.txt | 3 ++ SCons/Scanner/C.py | 69 ++++++++++++++++++++++++++++++++--------- SCons/Scanner/CTests.py | 12 +++++++ 4 files changed, 73 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index aa29180f5a..6411fcb56b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -167,6 +167,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER their values taken from the default in the variable description (if a variable was set to the same value as the default in one of the input sources, it is not included in this list). + - The C scanner now does (limited) macro replacement on the values in + CPPDEFINES, to improve results of conditional source file inclusion + (issue #4523). RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index aa3e3c15db..93e137f4d8 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -150,6 +150,9 @@ FIXES python ninja package version 1.11.1.2 changed the location and previous logic no longer worked. +- The C scanner now does (limited) macro replacement on the values in + CPPDEFINES, to improve results of conditional source file inclusion + (issue #4523). IMPROVEMENTS ------------ diff --git a/SCons/Scanner/C.py b/SCons/Scanner/C.py index aafe0d9a56..ad332b8ad6 100644 --- a/SCons/Scanner/C.py +++ b/SCons/Scanner/C.py @@ -28,6 +28,7 @@ add_scanner() for each affected suffix. """ +from typing import Dict import SCons.Node.FS import SCons.cpp import SCons.Util @@ -66,31 +67,69 @@ def read_file(self, file) -> str: return '' def dictify_CPPDEFINES(env) -> dict: - """Returns CPPDEFINES converted to a dict. - - This should be similar to :func:`~SCons.Defaults.processDefines`. - Unfortunately, we can't do the simple thing of calling that routine and - passing the result to the dict() constructor, because it turns the defines - into a list of "name=value" pairs, which the dict constructor won't - consume correctly. Also cannot just call dict on CPPDEFINES itself - it's - fine if it's stored in the converted form (currently deque of tuples), but - CPPDEFINES could be in other formats too. - - So we have to do all the work here - keep concepts in sync with - ``processDefines``. + """Return CPPDEFINES converted to a dict for preprocessor emulation. + + The concept is similar to :func:`~SCons.Defaults.processDefines`: + turn the values stored in an internal form in ``env['CPPDEFINES']`` + into one usable in a specific context - in this case the cpp-like + work the C/C++ scanner will do. We can't reuse ``processDefines`` + output as that's a list of strings for the command line. We also can't + pass the ``CPPDEFINES`` variable directly to the ``dict`` constructor, + as SCons allows it to be stored in several different ways - it's only + after ``Append`` and relatives has been called we know for sure it will + be a deque of tuples. + + Since the result here won't pass through a real preprocessor, simulate + some of the macro replacement that would take place if it did, or some + conditional inclusions might come out wrong. A bit of an edge case, but + does happen (GH #4623). See 6.10.5 in the C standard and 15.6 in the + C++ standard). + + .. versionchanged:: NEXT_RELEASE + Simple macro replacement added. """ + def _replace(mapping: Dict) -> Dict: + """Simplistic macro replacer for dictify_CPPDEFINES. + + *mapping* is scanned for any value that is the same as a key in + the dict, and is replaced by the value of that key; the process + is repeated. This is a cheap approximation of the C preprocessor's + macro replacement rules with no smarts - it doesn't "look inside" + the values, so only triggers on object-like macros, not on + function-like macros, and will not work on complex values, + e.g. a value like ``(1UL << PR_MTE_TCF_SHIFT)`` would not have + ``PR_MTE_TCF_SHIFT`` replaced if it was also in ``CPPDEFINES``, + but rather left as-is for the scanner to do comparisons against. + + Args: + mapping: a dictionary representing macro names and replacements. + + Returns: + a dictionary with substitutions made. + """ + old_ns = mapping + ns = {} + while True: + ns = {k: old_ns[v] if v in old_ns else v for k, v in old_ns.items()} + if old_ns == ns: + break + old_ns = ns + return ns + cppdefines = env.get('CPPDEFINES', {}) result = {} if cppdefines is None: return result if SCons.Util.is_Tuple(cppdefines): + # single macro defined in a tuple try: return {cppdefines[0]: cppdefines[1]} except IndexError: return {cppdefines[0]: None} if SCons.Util.is_Sequence(cppdefines): + # multiple (presumably) macro defines in a deque, list, etc. for c in cppdefines: if SCons.Util.is_Sequence(c): try: @@ -107,9 +146,10 @@ def dictify_CPPDEFINES(env) -> dict: else: # don't really know what to do here result[c] = None - return result + return _replace(result) if SCons.Util.is_String(cppdefines): + # single macro define in a string try: name, value = cppdefines.split('=') return {name: value} @@ -117,7 +157,8 @@ def dictify_CPPDEFINES(env) -> dict: return {cppdefines: None} if SCons.Util.is_Dict(cppdefines): - return cppdefines + # already in the desired form + return _replace(result) return {cppdefines: None} diff --git a/SCons/Scanner/CTests.py b/SCons/Scanner/CTests.py index 6860a10cef..8355937671 100644 --- a/SCons/Scanner/CTests.py +++ b/SCons/Scanner/CTests.py @@ -572,6 +572,18 @@ def runTest(self) -> None: expect = {"STRING": "VALUE", "UNVALUED": None} self.assertEqual(d, expect) + with self.subTest("CPPDEFINES with macro replacement"): + env = DummyEnvironment( + CPPDEFINES=[ + ("STRING", "VALUE"), + ("REPLACEABLE", "RVALUE"), + ("RVALUE", "AVALUE"), + ] + ) + d = SCons.Scanner.C.dictify_CPPDEFINES(env) + expect = {"STRING": "VALUE", "REPLACEABLE": "AVALUE", "RVALUE": "AVALUE"} + self.assertEqual(d, expect) + if __name__ == "__main__": unittest.main() From 0cb87375ad87f3d6cc77e1fdcf0e819bad400b54 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 27 Nov 2024 10:39:12 -0700 Subject: [PATCH 215/386] Further tweak CPPDEFINES repleacement in scanner Replacement is now limited to five passes, to avoid going into an endless loop in pathlogical cases where there are three or more macros that circularly refer to each other. No error is reported in this case. Replacement is now only done for the otional C Conditional scanner, not for the classical C scanner. Signed-off-by: Mats Wichmann --- CHANGES.txt | 7 +++++ RELEASE.txt | 13 ++++++--- SCons/Scanner/C.py | 60 ++++++++++++++++++++++++----------------- SCons/Scanner/CTests.py | 2 +- 4 files changed, 53 insertions(+), 29 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6411fcb56b..2609355a97 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -170,6 +170,13 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - The C scanner now does (limited) macro replacement on the values in CPPDEFINES, to improve results of conditional source file inclusion (issue #4523). + - The (optional) C Conditional Scanner now does limited macro + replacement on the contents of CPPDEFINES, to improve finding deps + that are conditionally included. Previously replacement was only + done on macro definitions found in the file being scanned. + Only object-like macros are replaced (not function-like), and + only on a whole-word basis; recursion is limited to five levels + and does not error out if that limit is reached (issue #4523). RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 93e137f4d8..da8f74d3e8 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -144,15 +144,20 @@ FIXES not designed to work for the root user. - Make sure unknown variables from a Variables file are recognized - as such (issue #4645) + as such. Previously only unknowns from the command line were + recognized (issue #4645). - Update ninja tool to use ninja.BIN_DIR to find pypi packaged ninja binary. python ninja package version 1.11.1.2 changed the location and previous logic no longer worked. -- The C scanner now does (limited) macro replacement on the values in - CPPDEFINES, to improve results of conditional source file inclusion - (issue #4523). +- The (optional) C Conditional Scanner now does limited macro + replacement on the contents of CPPDEFINES, to improve finding deps + that are conditionally included. Previously replacement was only + done on macro definitions found in the file being scanned. + Only object-like macros are replaced (not function-like), and + only on a whole-word basis; recursion is limited to five levels + and does not error out if that limit is reached (issue #4523). IMPROVEMENTS ------------ diff --git a/SCons/Scanner/C.py b/SCons/Scanner/C.py index ad332b8ad6..e111778c97 100644 --- a/SCons/Scanner/C.py +++ b/SCons/Scanner/C.py @@ -29,6 +29,7 @@ """ from typing import Dict + import SCons.Node.FS import SCons.cpp import SCons.Util @@ -66,12 +67,12 @@ def read_file(self, file) -> str: self.missing.append((file, self.current_file)) return '' -def dictify_CPPDEFINES(env) -> dict: +def dictify_CPPDEFINES(env, replace: bool = False) -> dict: """Return CPPDEFINES converted to a dict for preprocessor emulation. The concept is similar to :func:`~SCons.Defaults.processDefines`: turn the values stored in an internal form in ``env['CPPDEFINES']`` - into one usable in a specific context - in this case the cpp-like + into one needed for a specific context - in this case the cpp-like work the C/C++ scanner will do. We can't reuse ``processDefines`` output as that's a list of strings for the command line. We also can't pass the ``CPPDEFINES`` variable directly to the ``dict`` constructor, @@ -79,47 +80,50 @@ def dictify_CPPDEFINES(env) -> dict: after ``Append`` and relatives has been called we know for sure it will be a deque of tuples. - Since the result here won't pass through a real preprocessor, simulate - some of the macro replacement that would take place if it did, or some - conditional inclusions might come out wrong. A bit of an edge case, but - does happen (GH #4623). See 6.10.5 in the C standard and 15.6 in the - C++ standard). + If requested (*replace* is true), simulate some of the macro + replacement that would take place if an actual preprocessor ran, + to avoid some conditional inclusions comeing out wrong. A bit + of an edge case, but does happen (GH #4623). See 6.10.5 in the C + standard and 15.6 in the C++ standard). + + Args: + replace: if true, simulate macro replacement .. versionchanged:: NEXT_RELEASE - Simple macro replacement added. + Simple macro replacement added, and *replace* arg to enable it. """ def _replace(mapping: Dict) -> Dict: """Simplistic macro replacer for dictify_CPPDEFINES. - *mapping* is scanned for any value that is the same as a key in - the dict, and is replaced by the value of that key; the process - is repeated. This is a cheap approximation of the C preprocessor's + Scan *mapping* for a value that is the same as a key in the dict, + and replace with the value of that key; the process is repeated a few + times, but not forever in case someone left a case that can't be + fully resolved. This is a cheap approximation of the preprocessor's macro replacement rules with no smarts - it doesn't "look inside" the values, so only triggers on object-like macros, not on - function-like macros, and will not work on complex values, - e.g. a value like ``(1UL << PR_MTE_TCF_SHIFT)`` would not have - ``PR_MTE_TCF_SHIFT`` replaced if it was also in ``CPPDEFINES``, - but rather left as-is for the scanner to do comparisons against. + function-like macros, and will not work on complex values, e.g. + a value like ``(1UL << PR_MTE_TCF_SHIFT)`` would not have + ``PR_MTE_TCF_SHIFT`` replaced if that was also a key in ``CPPDEFINES``. Args: mapping: a dictionary representing macro names and replacements. Returns: - a dictionary with substitutions made. + a dictionary with replacements made. """ old_ns = mapping - ns = {} - while True: + loops = 0 + while loops < 5: # don't recurse forever in case there's circular data ns = {k: old_ns[v] if v in old_ns else v for k, v in old_ns.items()} if old_ns == ns: break old_ns = ns + loops += 1 return ns cppdefines = env.get('CPPDEFINES', {}) - result = {} - if cppdefines is None: - return result + if not cppdefines: + return {} if SCons.Util.is_Tuple(cppdefines): # single macro defined in a tuple @@ -130,6 +134,7 @@ def _replace(mapping: Dict) -> Dict: if SCons.Util.is_Sequence(cppdefines): # multiple (presumably) macro defines in a deque, list, etc. + result = {} for c in cppdefines: if SCons.Util.is_Sequence(c): try: @@ -146,7 +151,9 @@ def _replace(mapping: Dict) -> Dict: else: # don't really know what to do here result[c] = None - return _replace(result) + if replace: + return _replace(result) + return(result) if SCons.Util.is_String(cppdefines): # single macro define in a string @@ -158,7 +165,9 @@ def _replace(mapping: Dict) -> Dict: if SCons.Util.is_Dict(cppdefines): # already in the desired form - return _replace(result) + if replace: + return _replace(cppdefines) + return cppdefines return {cppdefines: None} @@ -177,7 +186,9 @@ def __init__(self, name, variable) -> None: def __call__(self, node, env, path=()): cpp = SConsCPPScanner( - current=node.get_dir(), cpppath=path, dict=dictify_CPPDEFINES(env) + current=node.get_dir(), + cpppath=path, + dict=dictify_CPPDEFINES(env, replace=True), ) result = cpp(node) for included, includer in cpp.missing: @@ -190,6 +201,7 @@ def __call__(self, node, env, path=()): def recurse_nodes(self, nodes): return nodes + def select(self, node): return self diff --git a/SCons/Scanner/CTests.py b/SCons/Scanner/CTests.py index 8355937671..b0fdb566e2 100644 --- a/SCons/Scanner/CTests.py +++ b/SCons/Scanner/CTests.py @@ -580,7 +580,7 @@ def runTest(self) -> None: ("RVALUE", "AVALUE"), ] ) - d = SCons.Scanner.C.dictify_CPPDEFINES(env) + d = SCons.Scanner.C.dictify_CPPDEFINES(env, replace=True) expect = {"STRING": "VALUE", "REPLACEABLE": "AVALUE", "RVALUE": "AVALUE"} self.assertEqual(d, expect) From 3cda270b4fa252743e615711c0f91de1a4f66e32 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 13 Dec 2024 15:01:39 -0700 Subject: [PATCH 216/386] Switch scanner CPPDEFINES replacement algorithm Now using the "modified" approach from the PR discussion: unroll the dict comprehension and as we process replacements keep track if changes were made, rather than doing the relatively more expensive dict-vs-dict comparison at the end of each loop. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 --- SCons/Scanner/C.py | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 2609355a97..9bf0edfc53 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -167,9 +167,6 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER their values taken from the default in the variable description (if a variable was set to the same value as the default in one of the input sources, it is not included in this list). - - The C scanner now does (limited) macro replacement on the values in - CPPDEFINES, to improve results of conditional source file inclusion - (issue #4523). - The (optional) C Conditional Scanner now does limited macro replacement on the contents of CPPDEFINES, to improve finding deps that are conditionally included. Previously replacement was only diff --git a/SCons/Scanner/C.py b/SCons/Scanner/C.py index e111778c97..1d7e101e4e 100644 --- a/SCons/Scanner/C.py +++ b/SCons/Scanner/C.py @@ -114,8 +114,19 @@ def _replace(mapping: Dict) -> Dict: old_ns = mapping loops = 0 while loops < 5: # don't recurse forever in case there's circular data - ns = {k: old_ns[v] if v in old_ns else v for k, v in old_ns.items()} - if old_ns == ns: + # this was originally written as a dict comprehension, but unrolling + # lets us add a finer-grained check for whether another loop is + # needed, rather than comparing two dicts to see if one changed. + again = False + ns = {} + for k, v in old_ns.items(): + if v in old_ns: + ns[k] = old_ns[v] + if not again and ns[k] != v: + again = True + else: + ns[k] = v + if not again: break old_ns = ns loops += 1 From f1a4abc20898b570cd84d6b64f731b3a3d771d49 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 7 Dec 2024 07:17:16 -0700 Subject: [PATCH 217/386] Tweak Variables docs Adjusts some doctrings and comments, and one error in typing. Manpage has the introdctory Variables material updated a bit, and the methods sorted, to match everywhere else in the manpage. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 2 + SCons/Tool/install.xml | 2 +- SCons/Variables/EnumVariable.py | 11 +- SCons/Variables/ListVariable.py | 23 +-- SCons/Variables/__init__.py | 37 ++-- doc/man/scons.xml | 333 +++++++++++++++++++------------- 7 files changed, 244 insertions(+), 165 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f074da86e7..01aac59a9c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -174,6 +174,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - A build Variable is now a dataclass, with initialization moving to the automatically provided method; the Variables class no longer writes directly to a Variable (makes static checkers happier). + - Improved Variables documentation. - The (optional) C Conditional Scanner now does limited macro replacement on the contents of CPPDEFINES, to improve finding deps that are conditionally included. Previously replacement was only diff --git a/RELEASE.txt b/RELEASE.txt index b35ece2657..b98c5e6522 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -205,6 +205,8 @@ DOCUMENTATION - Update Clean and NoClean documentation. +- Improved Variables documentation. + DEVELOPMENT ----------- diff --git a/SCons/Tool/install.xml b/SCons/Tool/install.xml index cdb044b695..3cbb0f4f6a 100644 --- a/SCons/Tool/install.xml +++ b/SCons/Tool/install.xml @@ -77,7 +77,7 @@ a "live" location in the system. -See also &FindInstalledFiles;. +See also &f-link-FindInstalledFiles;. For more thoughts on installation, see the User Guide (particularly the section on Command-Line Targets and the chapters on Installing Files and on Alias Targets). diff --git a/SCons/Variables/EnumVariable.py b/SCons/Variables/EnumVariable.py index f154a133b7..a576af513e 100644 --- a/SCons/Variables/EnumVariable.py +++ b/SCons/Variables/EnumVariable.py @@ -77,19 +77,20 @@ def EnumVariable( ) -> tuple[str, str, str, Callable, Callable]: """Return a tuple describing an enumaration SCons Variable. - The input parameters describe a variable with only predefined values - allowed. The value of *ignorecase* defines the behavior of the + An Enum Variable is an abstraction that allows choosing one + value from a provided list of possibilities (*allowed_values*). + The value of *ignorecase* defines the behavior of the validator and converter: if ``0``, the validator/converter are case-sensitive; if ``1``, the validator/converter are case-insensitive; if ``2``, the validator/converter are case-insensitive and the converted value will always be lower-case. Arguments: - key: variable name, passed directly through to the return tuple. - default: default values, passed directly through to the return tuple. + key: the name of the variable. + default: default value, passed directly through to the return tuple. help: descriptive part of the help text, will have the allowed values automatically appended. - allowed_values: list of the allowed values for this variable. + allowed_values: the values for the choice. map: optional dictionary which may be used for converting the input value into canonical values (e.g. for aliases). ignorecase: defines the behavior of the validator and converter. diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index 4ea7dc304c..880496f706 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -185,23 +185,24 @@ def ListVariable( names: list[str], map: dict | None = None, validator: Callable | None = None, -) -> tuple[str, str, str, None, Callable]: +) -> tuple[str, str, str, Callable, Callable]: """Return a tuple describing a list variable. - The input parameters describe a list variable, where the values - can be one or more from *names* plus the special values ``all`` - and ``none``. + A List Variable is an abstraction that allows choosing one or more + values from a provided list of possibilities (*names). The special terms + ``all`` and ``none`` are also provided to help make the selection. Arguments: key: the name of the list variable. help: the basic help message. Will have text appended indicating - the allowable values (not including any extra names from *map*). - default: the default value(s) for the list variable. Can be - given as string (possibly comma-separated), or as a list of strings. - ``all`` or ``none`` are allowed as *default*. You can also simulate - a must-specify ListVariable by giving a *default* that is not part - of *names*, it will fail validation if not supplied. - names: the allowable values. Must be a list of strings. + the allowed values (not including any extra names from *map*). + default: the default value(s) for the list variable. Can be given + as string (use commas to -separated multiple values), or as a list + of strings. ``all`` or ``none`` are allowed as *default*. + A must-specify ListVariable can be simulated by giving a value + that is not part of *names*, which will cause validation to fail + if the variable is not given in the input sources. + names: the values to choose from. Must be a list of strings. map: optional dictionary to map alternative names to the ones in *names*, providing a form of alias. The converter will make the replacement, names from *map* are not stored and will diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 521380468d..2d151e0c9a 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -225,13 +225,17 @@ def AddVariables(self, *optlist) -> None: def Update(self, env, args: dict | None = None) -> None: """Update an environment with the Build Variables. - Collects variables from the input sources which do not match - a variable description in this object. These are ignored for - purposes of adding to *env*, but can be retrieved using the - :meth:`UnknownVariables` method. Also collects variables which - are set in *env* from the default in a variable description and - not from the input sources. These are available in the - :attr:`defaulted` attribute. + This is where the work of adding variables to the environment + happens, The input sources saved at init time are scanned for + variables to add, though if *args* is passed, then it is used + instead of the saved one. If any variable description set up + a callback for a validator and/or converter, those are called. + Variables from the input sources which do not match a variable + description in this object are ignored for purposes of adding + to *env*, but are saved in the :attr:`unknown` dict attribute. + Variables which are set in *env* from the default in a variable + description and not from the input sources are saved in the + :attr:`defaulted` list attribute. Args: env: the environment to update. @@ -242,7 +246,7 @@ def Update(self, env, args: dict | None = None) -> None: values = {opt.key: opt.default for opt in self.options if opt.default is not None} self.defaulted = list(values) - # next set the values specified in any options script(s) + # next set the values specified in any saved-variables script(s) for filename in self.files: # TODO: issue #816 use Node to access saved-variables file? if os.path.exists(filename): @@ -288,8 +292,16 @@ def Update(self, env, args: dict | None = None) -> None: if not added: self.unknown[arg] = value - # put the variables in the environment: + # put the variables in the environment # (don't copy over variables that are not declared as options) + # + # Nitpicking: in OO terms, this method increases coupling as its + # main work is to update a different object (env), rather than + # the object it's bound to (although it does update self, too). + # It's tricky to decouple because the algorithm counts on directly + # setting a var in *env* first so it can call env.subst() on it + # to transform it. + for option in self.options: try: env[option.key] = values[option.key] @@ -400,9 +412,10 @@ def GenerateHelpText(self, env, sort: bool | Callable = False) -> str: (must take two arguments and return ``-1``, ``0`` or ``1``) or a boolean to indicate if it should be sorted. """ - # TODO the 'sort' argument matched the old way Python's sorted() - # worked, taking a comparison function argument. That has been - # removed so now we have to convert to a key. + # TODO this interface was designed when Pythin sorted() took an + # optional comparison function (pre-3.0). Since it no longer does, + # we use functools.cmp_to_key() since can't really change the + # documented meaning of the "sort" argument. Maybe someday? if callable(sort): options = sorted(self.options, key=cmp_to_key(lambda x, y: sort(x.key, y.key))) elif sort is True: diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 1b7e886c69..764134140d 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -4832,20 +4832,22 @@ added by calling the Add or AddVariables methods. -Each variable description consists of a name (which will -be used as the &consvar; name), aliases for the name, +A variable description consists of a name, +a list of aliases for the name, a help message, a default value, and functions to validate and convert values. -Processing of input sources -is deferred until the +Processing of input sources is deferred until the Update method is called, at which time the variables are added to the -specified &consenv;. +specified &consenv;, +using the name as the &consvar; name; +any aliases are not added. Variables from the input sources which do not match any names or aliases from the variable descriptions in this object are skipped, -except that a dictionary of their names and values are made available -in the .unknown attribute of the &Variables; object. +except that a dictionary of their names and values are made available in the +unknown +attribute of the &Variables; object. This list can also be obtained via the UnknownVariables method. @@ -4854,19 +4856,15 @@ other than None and does not appear in the input sources, it is added to the &consenv; with its default value. A list of variables set from their defaults and -not supplied a value in the input sources is -available as the .defaulted attribute -of the &Variables; object. +not from the input sources is available as the +defaulted +attribute of the &Variables; object. The unknown variables and defaulted information is not available until the &Update; method has run. -New in NEXT_RELEASE: -the defaulted attribute. - - -Note that since the variables are eventually added as &consvars;, +Since the variables are eventually added as &consvars;, you should choose variable names which do not unintentionally change pre-defined &consvars; that your project will make use of (see for a reference), @@ -4875,16 +4873,16 @@ to the respective &consvars;. -Also note there is currently no way to use the &Variables; -mechanism to define a variable which the user is -required to supply; -if necessary this can be implemented by accessing -&ARGUMENTS; directly, -although that only applies to the command line, -not to any stored-values files. +The &Variables; subsystem does not directly support a way +to define a variable the user must supply, +but this can be simulated by using a validator function, +and specifying a default value which the validator will reject, +resulting in an invalid value error message +(the convenience methods &EnumVariable; and +&ListVariable; make this relatively straightforward). -A Variables object has the following methods: +A &Variables; object has the following methods: @@ -4897,17 +4895,24 @@ or a sequence of strings, in which case the first item in the sequence is taken as the variable name, and any remaining values are considered aliases for the variable. key is mandatory, -there is no default. +the other fields are optional. help is the help text for the variable (defaults to an empty string). default is the default value of the variable (defaults to None). +The variable will be set to the value of +default if it does +not appear in the input sources, +except if default +is None, +in which case it is not added to the &consenv; +unless it has been set in the input sources. -If the optional validator argument is supplied, +If the validator argument is supplied, it is a callback function to validate the value of the variable when the variables are processed (that is, when the &Update; @@ -4922,7 +4927,7 @@ No return value is expected from the validator. -If the optional converter argument is supplied, +If the converter argument is supplied, it is a callback function to convert the value into one suitable for adding to the &consenv;. A converter function must accept the @@ -4944,7 +4949,7 @@ it can raise a ValueError. Substitution will be performed on the variable value before the converter and validator are called, unless the optional subst parameter -is false (default True). +is false (the default is True). Suppressing substitution may be useful if the variable value looks like a &consvar; reference (e.g. $VAR) and the validator and/or converter should see it unexpanded. @@ -5018,60 +5023,97 @@ opt.AddVariables( - - vars.Update(env, [args]) + + vars.FormatVariableHelpText(env, opt, help, default, actual, aliases) -Process the input sources recorded -when the &Variables; object was initialized -and update +Returns a formatted string +containing the printable help text +for the single variable opt. +All of the arguments must be supplied +except aliases, which is optional. env -with the customized &consvars;. -The names of any variables in the input sources that are not -configured in the &Variables; object -are recorded and may be retrieved using the -&UnknownVariables; -method. +is the &consenv; containing the variable values, +(env is not used by the standard +implementation of FormatVariableHelpText); +var +is the name of the variable; +help +is the text of the initial help message when the variable was +added to the &Variables; object; +default +is the default value assigned when the variable was added +to the &Variables; object; +actual +is the value as assigned in env +(which may be the same as default, +if none of the input sources assign to the variable); +and aliases +are any alias names for the variable, +if omitted defaults to an empty list. + -If the optional -args -argument is provided, it is a dictionary of variables -to use in place of the one saved when -&Variables; -was called. +FormatVariableHelpText +is normally not called directy, but by +&GenerateHelpText;, which does the work of +obtaining the necessary values. +You can patch in your own +function that takes the same function signature +in order to customize the appearance of variable help messages. +Example: -Normally, &Update; is not called directly, -but rather invoked indirectly by passing the &Variables; object to -the &f-link-Environment; function: - -env = Environment(..., variables=vars) +def my_format(env, var, help, default, actual): + fmt = "\n%s: default=%s actual=%s (%s)\n" + return fmt % (var, default, actual, help) + +vars.FormatVariableHelpText = my_format + +Note that &GenerateHelpText; +will not put any blank lines or extra +characters between the entries, +so you must add those characters to the returned +string if you want the entries separated. + - - vars.UnknownVariables() + + vars.GenerateHelpText(env, [sort]) -Returns a dictionary containing any -variables that were specified in the -files and/or -args parameters -when &Variables; -was called, but which were not configured in the object. -The same dictionary is also available as the -unknown attribute of the object. -This information is not available until the -Update -method has run. + +Return a formatted string with the help text collected +from all the variables configured in this &Variables; object. +This string is suitable for passing in to the &f-link-Help; function. +The generated string include an indication of the +actual value in the environment given by env. + + + +If the optional +sort parameter is set to +a callable value, it is used as a comparison function to +determine how to sort the added variables. +This function must accept two arguments, compare them, +and return a negative integer if the first is +less-than the second, zero if equal, or a positive integer +if greater-than. +If sort is not callable, +but evaluates true, +an alphabetical sort is performed. +The default is False (unsorted). -env = Environment(variables=vars) -for key, value in vars.UnknownVariables(): - print("unknown variable: %s=%s" % (key, value)) +Help(vars.GenerateHelpText(env)) + +def cmp(a, b): + return (a > b) - (a < b) + +Help(vars.GenerateHelpText(env, sort=cmp)) @@ -5101,73 +5143,99 @@ vars.Save('variables.cache', env) - - vars.GenerateHelpText(env, [sort]) + + vars.UnknownVariables() - -Return a formatted string with the help text collected -from all the variables configured in this &Variables; object. -This string is suitable for passing in to the &f-link-Help; function. -The generated string include an indication of the -actual value in the environment given by env. +Returns a dictionary containing any +variables that were specified in the +files and/or +args parameters +when &Variables; +was called, but the object was not actually configured for. +This information is not available until the +Update +method has run. + +env = Environment(variables=vars) +for key, value in vars.UnknownVariables(): + print("unknown variable: %s=%s" % (key, value)) + + + + + + + vars.Update(env, [args]) + +Process the input sources recorded +when the &Variables; object was initialized +and update +env +with the customized &consvars;. +The names of any variables in the input sources that are not +configured in the &Variables; object +are recorded and may be retrieved using the +&UnknownVariables; +method. + If the optional -sort parameter is set to -a callable value, it is used as a comparison function to -determine how to sort the added variables. -This function must accept two arguments, compare them, -and return a negative integer if the first is -less-than the second, zero if equal, or a positive integer -if greater-than. -If sort is not callable, -but is set to True, -an alphabetical sort is performed. -The default is False (unsorted). +args +argument is provided, it must be a dictionary of variables, +which will be used in place of the one saved when the +&Variables; object +was created. - -Help(vars.GenerateHelpText(env)) - -def cmp(a, b): - return (a > b) - (a < b) +Normally, &Update; is not called directly, +but rather invoked indirectly by passing the &Variables; object to +the &f-link-Environment; function: -Help(vars.GenerateHelpText(env, sort=cmp)) + +env = Environment(..., variables=vars) + - - vars.FormatVariableHelpText(env, opt, help, default, actual) - -Returns a formatted string -containing the printable help text -for the single option opt. -It is normally not called directly, -but is called by the &GenerateHelpText; -method to create the returned help text. -It may be overridden with your own -function that takes the arguments specified above -and returns a string of help text formatted to your liking. -Note that &GenerateHelpText; -will not put any blank lines or extra -characters in between the entries, -so you must add those characters to the returned -string if you want the entries separated. + +A &Variables; object also makes available two data attributes +that can be read for further information. These only have +values if Update +has previously run. + - -def my_format(env, opt, help, default, actual): - fmt = "\n%s: default=%s actual=%s (%s)\n" - return fmt % (opt, default, actual, help) + + + vars.defaulted + + +A list of variable names that were set in the &consenv; +from the default values in the variable descriptions - +that is, variables that have a default value and were +not defined in the input sources. + + + -vars.FormatVariableHelpText = my_format - + + vars.unknown + + +A dictionary of variables that were specified in the input sources, +but do not have matching variable definitions. +This is the same information that is returned by the +&UnknownVariables; method. + - +Added in NEXT_RELEASE: +the defaulted attribute. + &SCons; provides five pre-defined variable types, @@ -5216,7 +5284,7 @@ as false. Set up a variable named key -whose value will be a choice from +whose value may only be chosen from a specified list ("enumeration") of values. The variable will have a default value of default @@ -5231,23 +5299,14 @@ argument is a dictionary that can be used to map additional names into a particular name in the allowed_values list. -If the value of optional -ignore_case -is -0 -(the default), -then the values are case-sensitive. -If the value of -ignore_case -is -1, -then values will be matched +If the optional +ignorecase is 0 (the default), +the values are considered case-sensitive. +If ignorecase is 1, +values will be matched case-insensitively. -If the value of -ignore_case -is -2, -then values will be matched +If ignorecase is 2, +values will be matched case-insensitively, and all input values will be converted to lower case. @@ -5259,8 +5318,8 @@ converted to lower case. Set up a variable named key -whose value will be one or more -choices from a specified list of values. +whose value may be one or more choices +from a specified list of values. The variable will have a default value of default, and help @@ -5293,8 +5352,9 @@ can be used to specify a custom validator callback function, as described for Add. The default is to use an internal validator routine. -New in 4.8.0: validator. - +Added in 4.8.0: +the validator parameter. + @@ -5466,7 +5526,8 @@ vars.AddVariables( PathVariable( "qtdir", help="where the root of Qt is installed", - default=qtdir), + default=qtdir + ), PathVariable( "foopath", help="where the foo library is installed", From 406fb6e1cfb5e5684fe3311c7f5393e4e8531bf4 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 15 Dec 2024 09:54:23 -0700 Subject: [PATCH 218/386] Change update-release-info test for Python changes Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ test/update-release-info/update-release-info.py | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f074da86e7..f7f20896c4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -181,6 +181,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Only object-like macros are replaced (not function-like), and only on a whole-word basis; recursion is limited to five levels and does not error out if that limit is reached (issue #4523). + - The update-release-info test is adapted to accept changed help output + introduced in Python 3.12.8/3.13.1. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/test/update-release-info/update-release-info.py b/test/update-release-info/update-release-info.py index 2de4713b0c..bebd8a9875 100644 --- a/test/update-release-info/update-release-info.py +++ b/test/update-release-info/update-release-info.py @@ -53,11 +53,24 @@ if not os.path.exists(test.program): test.skip_test("update-release-info.py is not distributed in this package\n") -expected_stderr = """usage: update-release-info.py [-h] [--verbose] [--timestamp TIMESTAMP] +expected_stderr = """\ +usage: update-release-info.py [-h] [--verbose] [--timestamp TIMESTAMP] [{develop,release,post}] update-release-info.py: error: argument mode: invalid choice: 'bad' (choose from 'develop', 'release', 'post') """ -test.run(arguments='bad', stderr=expected_stderr, status=2) +# The way the choices are rendered in help by argparse changed with +# Python 3.12.8, # 3.13.1, 3.14.0a2. Change the test to accept either. +expected_stderr_new = """\ +usage: update-release-info.py [-h] [--verbose] [--timestamp TIMESTAMP] + [{develop,release,post}] +update-release-info.py: error: argument mode: invalid choice: 'bad' (choose from develop, release, post) +""" +test.run(arguments='bad', stderr=None, status=2) +fail_strings = [ + expected_stderr, + expected_stderr_new, +] +test.must_contain_any_line(test.stderr(), fail_strings) # Strings to go in ReleaseConfig combo_strings = [ From 9d5e76a42341596583924470feebcc637fcb8ac8 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 8 Nov 2024 08:13:26 -0700 Subject: [PATCH 219/386] Modernize stat usage Since Python 2.2, the object returned by an os.stat() call presents attributes matching the 10-tuple of stat values. Use these instead of indexing into the tuple. As usual for non-removed tests, minor tweaks made if needed - copyright header and DefautlEnvironment() call for performance. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 + RELEASE.txt | 4 + SCons/CacheDir.py | 2 +- SCons/Node/FS.py | 2 +- SCons/Node/FSTests.py | 4 +- SCons/Tool/install.py | 4 +- site_scons/Utilities.py | 2 +- test/Actions/append.py | 2 +- test/Actions/pre-post.py | 4 +- test/Chmod.py | 77 ++++++++++--------- test/Decider/MD5-timestamp-Repository.py | 2 +- test/Decider/MD5-timestamp.py | 2 +- test/Decider/timestamp.py | 4 +- test/Removed/BuildDir/Old/BuildDir.py | 6 +- .../BuildDir/Old/SConscript-build_dir.py | 22 +++--- test/VariantDir/SConscript-variant_dir.py | 13 ++-- test/VariantDir/VariantDir.py | 10 +-- test/VariantDir/errors.py | 13 ++-- test/ZIP/ZIP.py | 6 +- test/option/option--duplicate.py | 4 +- testing/framework/TestCmd.py | 14 ++-- testing/framework/TestCmdTests.py | 26 +++---- testing/framework/TestCommon.py | 2 +- testing/framework/TestCommonTests.py | 12 +-- 24 files changed, 123 insertions(+), 116 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f074da86e7..b86d8f5875 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -181,6 +181,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Only object-like macros are replaced (not function-like), and only on a whole-word basis; recursion is limited to five levels and does not error out if that limit is reached (issue #4523). + - Minor modernization: make use of stat object's st_mode, st_mtime + and other attributes rather than indexing into stat return. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index b35ece2657..1251be6cc0 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -164,6 +164,10 @@ FIXES only on a whole-word basis; recursion is limited to five levels and does not error out if that limit is reached (issue #4523). +- Minor modernization: make use of stat object's st_mode, st_mtime + and other attributes rather than indexing into stat return. + + IMPROVEMENTS ------------ diff --git a/SCons/CacheDir.py b/SCons/CacheDir.py index 7f8deb55e1..25e3f666f4 100644 --- a/SCons/CacheDir.py +++ b/SCons/CacheDir.py @@ -71,7 +71,7 @@ def CacheRetrieveFunc(target, source, env) -> int: except OSError: pass st = fs.stat(cachefile) - fs.chmod(t.get_internal_path(), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + fs.chmod(t.get_internal_path(), stat.S_IMODE(st.st_mode) | stat.S_IWRITE) return 0 def CacheRetrieveString(target, source, env) -> str: diff --git a/SCons/Node/FS.py b/SCons/Node/FS.py index bdecffcfbd..ec0a69a7b4 100644 --- a/SCons/Node/FS.py +++ b/SCons/Node/FS.py @@ -762,7 +762,7 @@ def getmtime(self): st = self.stat() if st: - return st[stat.ST_MTIME] + return st.st_mtime else: return None diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py index 9ae8c03e10..ae98bc5a59 100644 --- a/SCons/Node/FSTests.py +++ b/SCons/Node/FSTests.py @@ -774,7 +774,7 @@ def test_update(self) -> None: ni.update(fff) - mtime = st[stat.ST_MTIME] + mtime = st.st_mtime assert ni.timestamp == mtime, (ni.timestamp, mtime) size = st.st_size assert ni.size == size, (ni.size, size) @@ -786,7 +786,7 @@ def test_update(self) -> None: st = os.stat('fff') - mtime = st[stat.ST_MTIME] + mtime = st.st_mtime assert ni.timestamp != mtime, (ni.timestamp, mtime) size = st.st_size assert ni.size != size, (ni.size, size) diff --git a/SCons/Tool/install.py b/SCons/Tool/install.py index d553e31afe..fc20586ff0 100644 --- a/SCons/Tool/install.py +++ b/SCons/Tool/install.py @@ -176,7 +176,7 @@ def copyFunc(dest, source, env) -> int: else: copy2(source, dest) st = os.stat(source) - os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + os.chmod(dest, stat.S_IMODE(st.st_mode) | stat.S_IWRITE) return 0 @@ -204,7 +204,7 @@ def copyFuncVersionedLib(dest, source, env) -> int: pass copy2(source, dest) st = os.stat(source) - os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + os.chmod(dest, stat.S_IMODE(st.st_mode) | stat.S_IWRITE) installShlibLinks(dest, source, env) return 0 diff --git a/site_scons/Utilities.py b/site_scons/Utilities.py index 7ef45a33c6..620595d7ec 100644 --- a/site_scons/Utilities.py +++ b/site_scons/Utilities.py @@ -31,7 +31,7 @@ def whereis(filename): st = os.stat(f_ext) except: continue - if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: + if stat.S_IMODE(st.st_mode) & stat.S_IXUSR: return f_ext return None diff --git a/test/Actions/append.py b/test/Actions/append.py index 01a9f23219..8205e1beff 100644 --- a/test/Actions/append.py +++ b/test/Actions/append.py @@ -63,7 +63,7 @@ def after(env, target, source): test.run(arguments='.') test.must_match('before.txt', 'Bar\n') -os.chmod(after_exe, os.stat(after_exe)[stat.ST_MODE] | stat.S_IXUSR) +os.chmod(after_exe, os.stat(after_exe).st_mode | stat.S_IXUSR) test.run(program=after_exe, stdout="Foo\n") test.pass_test() diff --git a/test/Actions/pre-post.py b/test/Actions/pre-post.py index ac6a96fd12..0d9f36fce9 100644 --- a/test/Actions/pre-post.py +++ b/test/Actions/pre-post.py @@ -50,7 +50,7 @@ def before(env, target, source): a=str(target[0]) with open(a, "wb") as f: f.write(b"Foo\\n") - os.chmod(a, os.stat(a)[stat.ST_MODE] | stat.S_IXUSR) + os.chmod(a, os.stat(a).st_mode | stat.S_IXUSR) with open("before.txt", "ab") as f: f.write((os.path.splitext(str(target[0]))[0] + "\\n").encode()) @@ -59,7 +59,7 @@ def after(env, target, source): a = "after_" + t with open(t, "rb") as fin, open(a, "wb") as fout: fout.write(fin.read()) - os.chmod(a, os.stat(a)[stat.ST_MODE] | stat.S_IXUSR) + os.chmod(a, os.stat(a).st_mode | stat.S_IXUSR) foo = env.Program(source='foo.c', target='foo') AddPreAction(foo, before) diff --git a/test/Chmod.py b/test/Chmod.py index 7af95b4130..87e7b15acb 100644 --- a/test/Chmod.py +++ b/test/Chmod.py @@ -51,6 +51,7 @@ def cat(env, source, target): f.write(infp.read()) Cat = Action(cat) +DefaultEnvironment(tools=[]) # test speedup env = Environment() env.Command( 'bar.out', @@ -154,92 +155,92 @@ def cat(env, source, target): """) test.run(options = '-n', arguments = '.', stdout = expect) -s = stat.S_IMODE(os.stat(test.workpath('f1'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f1')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('f1-File'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f1-File')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('d2'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d2')).st_mode) test.fail_test(s != 0o555) -s = stat.S_IMODE(os.stat(test.workpath('d2-Dir'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d2-Dir')).st_mode) test.fail_test(s != 0o555) test.must_not_exist('bar.out') -s = stat.S_IMODE(os.stat(test.workpath('f3'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f3')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('d4'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d4')).st_mode) test.fail_test(s != 0o555) -s = stat.S_IMODE(os.stat(test.workpath('f5'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f5')).st_mode) test.fail_test(s != 0o444) test.must_not_exist('f6.out') test.must_not_exist('f7.out') -s = stat.S_IMODE(os.stat(test.workpath('Chmod-f7.in'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('Chmod-f7.in')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('f7.out-Chmod'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f7.out-Chmod')).st_mode) test.fail_test(s != 0o444) test.must_not_exist('f8.out') -s = stat.S_IMODE(os.stat(test.workpath('f9'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f9')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('f10'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f10')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('d11'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d11')).st_mode) test.fail_test(s != 0o555) -s = stat.S_IMODE(os.stat(test.workpath('d12'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d12')).st_mode) test.fail_test(s != 0o555) -s = stat.S_IMODE(os.stat(test.workpath('f13'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f13')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('f14'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f14')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('f15'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f15')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('d16'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d16')).st_mode) test.fail_test(s != 0o555) -s = stat.S_IMODE(os.stat(test.workpath('d17'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d17')).st_mode) test.fail_test(s != 0o555) -s = stat.S_IMODE(os.stat(test.workpath('d18'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d18')).st_mode) test.fail_test(s != 0o555) test.run() -s = stat.S_IMODE(os.stat(test.workpath('f1'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f1')).st_mode) test.fail_test(s != 0o666) -s = stat.S_IMODE(os.stat(test.workpath('f1-File'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f1-File')).st_mode) test.fail_test(s != 0o666) -s = stat.S_IMODE(os.stat(test.workpath('d2'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d2')).st_mode) test.fail_test(s != 0o777) -s = stat.S_IMODE(os.stat(test.workpath('d2-Dir'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d2-Dir')).st_mode) test.fail_test(s != 0o777) test.must_match('bar.out', "bar.in\n") -s = stat.S_IMODE(os.stat(test.workpath('f3'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f3')).st_mode) test.fail_test(s != 0o666) -s = stat.S_IMODE(os.stat(test.workpath('d4'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d4')).st_mode) test.fail_test(s != 0o777) -s = stat.S_IMODE(os.stat(test.workpath('f5'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f5')).st_mode) test.fail_test(s != 0o666) test.must_match('f6.out', "f6.in\n") test.must_match('f7.out', "f7.in\n") -s = stat.S_IMODE(os.stat(test.workpath('Chmod-f7.in'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('Chmod-f7.in')).st_mode) test.fail_test(s != 0o666) -s = stat.S_IMODE(os.stat(test.workpath('f7.out-Chmod'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f7.out-Chmod')).st_mode) test.fail_test(s != 0o666) test.must_match('f8.out', "f8.in\n") -s = stat.S_IMODE(os.stat(test.workpath('f9'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f9')).st_mode) test.fail_test(s != 0o666) -s = stat.S_IMODE(os.stat(test.workpath('f10'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f10')).st_mode) test.fail_test(s != 0o666) -s = stat.S_IMODE(os.stat(test.workpath('d11'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d11')).st_mode) test.fail_test(s != 0o777) -s = stat.S_IMODE(os.stat(test.workpath('d12'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d12')).st_mode) test.fail_test(s != 0o777) -s = stat.S_IMODE(os.stat(test.workpath('f13'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f13')).st_mode) test.fail_test(s != 0o444) -s = stat.S_IMODE(os.stat(test.workpath('f14'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f14')).st_mode) test.fail_test(s != 0o666) -s = stat.S_IMODE(os.stat(test.workpath('f15'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('f15')).st_mode) test.fail_test(s != 0o666) -s = stat.S_IMODE(os.stat(test.workpath('d16'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d16')).st_mode) test.fail_test(s != 0o777) -s = stat.S_IMODE(os.stat(test.workpath('d17'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d17')).st_mode) test.fail_test(s != 0o777) -s = stat.S_IMODE(os.stat(test.workpath('d18'))[stat.ST_MODE]) +s = stat.S_IMODE(os.stat(test.workpath('d18')).st_mode) test.fail_test(s != 0o777) test.pass_test() diff --git a/test/Decider/MD5-timestamp-Repository.py b/test/Decider/MD5-timestamp-Repository.py index 201bdfe496..35e24f4979 100644 --- a/test/Decider/MD5-timestamp-Repository.py +++ b/test/Decider/MD5-timestamp-Repository.py @@ -58,7 +58,7 @@ test.sleep() # delay for timestamps test.write(['Repository','content1.in'], "content1.in 2\n") test.touch(['Repository','content2.in']) -time_content = os.stat(os.path.join(repository,'content3.in'))[stat.ST_MTIME] +time_content = os.stat(os.path.join(repository,'content3.in')).st_mtime test.write(['Repository','content3.in'], "content3.in 2\n") test.touch(['Repository','content3.in'], time_content) diff --git a/test/Decider/MD5-timestamp.py b/test/Decider/MD5-timestamp.py index 3815639672..b8988402b0 100644 --- a/test/Decider/MD5-timestamp.py +++ b/test/Decider/MD5-timestamp.py @@ -54,7 +54,7 @@ test.write('content1.in', "content1.in 2\n") test.touch('content2.in') -time_content = os.stat('content3.in')[stat.ST_MTIME] +time_content = os.stat('content3.in').st_mtime test.write('content3.in', "content3.in 2\n") test.touch('content3.in', time_content) diff --git a/test/Decider/timestamp.py b/test/Decider/timestamp.py index d713a621dd..1fcb9e70d2 100644 --- a/test/Decider/timestamp.py +++ b/test/Decider/timestamp.py @@ -55,8 +55,8 @@ test.run(arguments = '.') test.up_to_date(arguments = '.') -time_match = os.stat('match2.out')[stat.ST_MTIME] -time_newer = os.stat('newer2.out')[stat.ST_MTIME] +time_match = os.stat('match2.out').st_mtime +time_newer = os.stat('newer2.out').st_mtime # Now make all the source files newer than (different timestamps from) # the last time the targets were built, and touch the target files diff --git a/test/Removed/BuildDir/Old/BuildDir.py b/test/Removed/BuildDir/Old/BuildDir.py index 1a1ba02f6e..7463f21eec 100644 --- a/test/Removed/BuildDir/Old/BuildDir.py +++ b/test/Removed/BuildDir/Old/BuildDir.py @@ -224,8 +224,8 @@ def filter_tempnam(err): def equal_stats(x,y): x = os.stat(x) y = os.stat(y) - return (stat.S_IMODE(x[stat.ST_MODE]) == stat.S_IMODE(y[stat.ST_MODE]) and - x[stat.ST_MTIME] == y[stat.ST_MTIME]) + return (stat.S_IMODE(x.st_mode) == stat.S_IMODE(y.st_mode) and + x.st_mtime == y.st_mtime) # Make sure we did duplicate the source files in build/var2, # and that their stats are the same: @@ -233,7 +233,7 @@ def equal_stats(x,y): test.must_exist(['work1', 'build', 'var2', 'f2.in']) test.fail_test(not equal_stats(test.workpath('work1', 'build', 'var2', 'f1.c'), test.workpath('work1', 'src', 'f1.c'))) test.fail_test(not equal_stats(test.workpath('work1', 'build', 'var2', 'f2.in'), test.workpath('work1', 'src', 'f2.in'))) - + # Make sure we didn't duplicate the source files in build/var3. test.must_not_exist(['work1', 'build', 'var3', 'f1.c']) test.must_not_exist(['work1', 'build', 'var3', 'f2.in']) diff --git a/test/Removed/BuildDir/Old/SConscript-build_dir.py b/test/Removed/BuildDir/Old/SConscript-build_dir.py index 0d1ba6abde..ed520370ee 100644 --- a/test/Removed/BuildDir/Old/SConscript-build_dir.py +++ b/test/Removed/BuildDir/Old/SConscript-build_dir.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 specifying a build_dir argument to SConscript still works. @@ -108,7 +107,7 @@ def cat(env, source, target): # VariantDir('build/var9', '.') # SConscript('build/var9/src/SConscript') SConscript('src/SConscript', build_dir='build/var9', src_dir='.') -""") +""") test.subdir(['test', 'src'], ['test', 'alt']) @@ -152,8 +151,8 @@ def cat(env, source, target): def equal_stats(x,y): x = os.stat(x) y = os.stat(y) - return (stat.S_IMODE(x[stat.ST_MODE]) == stat.S_IMODE(y[stat.ST_MODE]) and - x[stat.ST_MTIME] == y[stat.ST_MTIME]) + return (stat.S_IMODE(x.st_mode) == stat.S_IMODE(y.st_mode) and + x.st_mtime == y.st_mtime) # Make sure we did duplicate the source files in build/var1, # and that their stats are the same: @@ -168,12 +167,12 @@ def equal_stats(x,y): test.must_exist(test.workpath('test', 'build', 'var2', file)) test.fail_test(not equal_stats(test.workpath('test', 'build', 'var2', file), test.workpath('test', 'src', file))) - + # Make sure we didn't duplicate the source files in build/var3. test.must_not_exist(test.workpath('test', 'build', 'var3', 'aaa.in')) test.must_not_exist(test.workpath('test', 'build', 'var3', 'bbb.in')) test.must_not_exist(test.workpath('test', 'build', 'var3', 'ccc.in')) - + #XXX We can't support var4 and var5 yet, because our VariantDir linkage #XXX is to an entire source directory. We haven't yet generalized our #XXX infrastructure to be able to take the SConscript file from one source @@ -200,12 +199,12 @@ def equal_stats(x,y): test.must_exist(test.workpath('build', 'var6', file)) test.fail_test(not equal_stats(test.workpath('build', 'var6', file), test.workpath('test', 'src', file))) - + # Make sure we didn't duplicate the source files in build/var7. test.must_not_exist(test.workpath('build', 'var7', 'aaa.in')) test.must_not_exist(test.workpath('build', 'var7', 'bbb.in')) test.must_not_exist(test.workpath('build', 'var7', 'ccc.in')) - + # Make sure we didn't duplicate the source files in build/var8. test.must_not_exist(test.workpath('build', 'var8', 'aaa.in')) test.must_not_exist(test.workpath('build', 'var8', 'bbb.in')) @@ -219,6 +218,7 @@ def equal_stats(x,y): """) test.write(['test2', 'SConscript'], """\ +DefaultEnvironment(tools=[]) # test speedup env = Environment() foo_obj = env.Object('foo.c') env.Program('foo', [foo_obj, 'bar.c']) diff --git a/test/VariantDir/SConscript-variant_dir.py b/test/VariantDir/SConscript-variant_dir.py index 1e28c47a98..43c3638a1f 100644 --- a/test/VariantDir/SConscript-variant_dir.py +++ b/test/VariantDir/SConscript-variant_dir.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 specifying a variant_dir argument to SConscript works properly. @@ -64,6 +63,7 @@ def cat(env, source, target): with open(str(src), "rb") as ifp: ofp.write(ifp.read()) +DefaultEnvironment(tools=[]) # test speedup env = Environment(BUILDERS={'Cat':Builder(action=cat)}, BUILD='build') @@ -138,8 +138,8 @@ def cat(env, source, target): def equal_stats(x,y): x = os.stat(x) y = os.stat(y) - return (stat.S_IMODE(x[stat.ST_MODE]) == stat.S_IMODE(y[stat.ST_MODE]) and - x[stat.ST_MTIME] == y[stat.ST_MTIME]) + return (stat.S_IMODE(x.st_mode) == stat.S_IMODE(y.st_mode) and + x.st_mtime == y.st_mtime) # Make sure we did duplicate the source files in build/var1, # and that their stats are the same: @@ -205,6 +205,7 @@ def equal_stats(x,y): """) test.write(['test2', 'SConscript'], """\ +DefaultEnvironment(tools=[]) # test speedup env = Environment() foo_obj = env.Object('foo.c') env.Program('foo', [foo_obj, 'bar.c']) diff --git a/test/VariantDir/VariantDir.py b/test/VariantDir/VariantDir.py index bd329d8b1b..a173b86531 100644 --- a/test/VariantDir/VariantDir.py +++ b/test/VariantDir/VariantDir.py @@ -119,7 +119,7 @@ def buildIt(target, source, env): if fortran and env.Detect(fortran): if sys.platform =='win32': - env_prog = Environment(tools=['mingw'], + env_prog = Environment(tools=['mingw'], # BUILD = env['BUILD'], SRC = ENV['src'], CPPPATH=env['CPPPATH'], FORTRANPATH=env['FORTRANPATH'] ) else: @@ -276,8 +276,8 @@ def blank_output(err): def equal_stats(x,y): x = os.stat(x) y = os.stat(y) - return (stat.S_IMODE(x[stat.ST_MODE]) == stat.S_IMODE(y[stat.ST_MODE]) and - x[stat.ST_MTIME] == y[stat.ST_MTIME]) + return (stat.S_IMODE(x.st_mode) == stat.S_IMODE(y.st_mode) and + x.st_mtime == y.st_mtime) # Make sure we did duplicate the source files in build/var2, # and that their stats are the same: @@ -285,7 +285,7 @@ def equal_stats(x,y): test.must_exist(['work1', 'build', 'var2', 'f2.in']) test.fail_test(not equal_stats(test.workpath('work1', 'build', 'var2', 'f1.c'), test.workpath('work1', 'src', 'f1.c'))) test.fail_test(not equal_stats(test.workpath('work1', 'build', 'var2', 'f2.in'), test.workpath('work1', 'src', 'f2.in'))) - + # Make sure we didn't duplicate the source files in build/var3. test.must_not_exist(['work1', 'build', 'var3', 'f1.c']) test.must_not_exist(['work1', 'build', 'var3', 'f2.in']) @@ -381,7 +381,7 @@ def equal_stats(x,y): test.write( ['work3', 'SConstruct'], """\ SConscriptChdir(0) -VariantDir('build', '.', duplicate=1 ) +VariantDir('build', '.', duplicate=1 ) SConscript( 'build/SConscript' ) """) diff --git a/test/VariantDir/errors.py b/test/VariantDir/errors.py index 1ff3be3c4d..7400056e7e 100644 --- a/test/VariantDir/errors.py +++ b/test/VariantDir/errors.py @@ -65,6 +65,7 @@ def cat(env, source, target): with open(str(src), "r") as f2: f.write(f2.read()) +DefaultEnvironment(tools=[]) # test speedup env = Environment(BUILDERS={'Build':Builder(action=cat)}, SCANNERS=[Scanner(fake_scan, skeys = ['.in'])]) @@ -88,7 +89,7 @@ def cat(env, source, target): if sys.platform != 'win32': dir = os.path.join('ro-dir', 'build') test.subdir(dir) - os.chmod(dir, os.stat(dir)[stat.ST_MODE] & ~stat.S_IWUSR) + os.chmod(dir, os.stat(dir).st_mode & ~stat.S_IWUSR) test.run(chdir = 'ro-dir', arguments = ".", @@ -103,16 +104,16 @@ def cat(env, source, target): test.subdir(dir) SConscript = test.workpath(dir, 'SConscript') test.write(SConscript, '') -os.chmod(SConscript, os.stat(SConscript)[stat.ST_MODE] & ~stat.S_IWUSR) +os.chmod(SConscript, os.stat(SConscript).st_mode & ~stat.S_IWUSR) with open(SConscript, 'r'): - os.chmod(dir, os.stat(dir)[stat.ST_MODE] & ~stat.S_IWUSR) + os.chmod(dir, os.stat(dir).st_mode & ~stat.S_IWUSR) test.run(chdir = 'ro-SConscript', arguments = ".", status = 2, stderr = "scons: *** Cannot duplicate `%s' in `build': Permission denied. Stop.\n" % os.path.join('src', 'SConscript')) - os.chmod('ro-SConscript', os.stat('ro-SConscript')[stat.ST_MODE] | stat.S_IWUSR) + os.chmod('ro-SConscript', os.stat('ro-SConscript').st_mode | stat.S_IWUSR) test.run(chdir = 'ro-SConscript', arguments = ".", @@ -130,9 +131,9 @@ def cat(env, source, target): test.write([dir, 'SConscript'], '') file_in = test.workpath(dir, 'file.in') test.write(file_in, '') -os.chmod(file_in, os.stat(file_in)[stat.ST_MODE] & ~stat.S_IWUSR) +os.chmod(file_in, os.stat(file_in).st_mode & ~stat.S_IWUSR) with open(file_in, 'r'): - os.chmod(dir, os.stat(dir)[stat.ST_MODE] & ~stat.S_IWUSR) + os.chmod(dir, os.stat(dir).st_mode & ~stat.S_IWUSR) test.run(chdir = 'ro-src', arguments = ".", diff --git a/test/ZIP/ZIP.py b/test/ZIP/ZIP.py index 09115fea13..69f524e152 100644 --- a/test/ZIP/ZIP.py +++ b/test/ZIP/ZIP.py @@ -118,9 +118,9 @@ def marker(target, source, env): test.fail_test(test.zipfile_files("f3.xyzzy") != ['file16', 'file17', 'file18']) -f4_size = os.stat('f4.zip')[stat.ST_SIZE] -f4stored_size = os.stat('f4stored.zip')[stat.ST_SIZE] -f4deflated_size = os.stat('f4deflated.zip')[stat.ST_SIZE] +f4_size = os.stat('f4.zip').st_size +f4stored_size = os.stat('f4stored.zip').st_size +f4deflated_size = os.stat('f4deflated.zip').st_size test.fail_test(f4_size != f4deflated_size) test.fail_test(f4stored_size == f4deflated_size) diff --git a/test/option/option--duplicate.py b/test/option/option--duplicate.py index 74de64b646..927de86c93 100644 --- a/test/option/option--duplicate.py +++ b/test/option/option--duplicate.py @@ -28,8 +28,6 @@ SConscript settable option. """ -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - import os import sys import stat @@ -85,7 +83,7 @@ } def testLink(file, type): - nl = os.stat(file)[stat.ST_NLINK] + nl = os.stat(file).st_nlink islink = os.path.islink(file) assert criterion[type](nl, islink), \ "Expected %s to be %s (nl %d, islink %d)" \ diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 56c282c990..4863a13c82 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -811,7 +811,7 @@ def where_is(file, path=None, pathext=None): st = os.stat(f) except OSError: continue - if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: + if stat.S_IMODE(st.st_mode) & stat.S_IXUSR: return f return None @@ -1978,7 +1978,7 @@ def do_chmod(fname) -> None: pass else: os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] | stat.S_IREAD)) + st.st_mode | stat.S_IREAD)) else: def do_chmod(fname) -> None: try: @@ -1987,7 +1987,7 @@ def do_chmod(fname) -> None: pass else: os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~stat.S_IREAD)) + st.st_mode & ~stat.S_IREAD)) if os.path.isfile(top): # If it's a file, that's easy, just chmod it. @@ -2047,7 +2047,7 @@ def do_chmod(fname) -> None: except OSError: pass else: - os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE] | stat.S_IWRITE)) + os.chmod(fname, stat.S_IMODE(st.st_mode | stat.S_IWRITE)) else: def do_chmod(fname) -> None: try: @@ -2056,7 +2056,7 @@ def do_chmod(fname) -> None: pass else: os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~stat.S_IWRITE)) + st.st_mode & ~stat.S_IWRITE)) if os.path.isfile(top): do_chmod(top) @@ -2087,7 +2087,7 @@ def do_chmod(fname) -> None: pass else: os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] | stat.S_IEXEC)) + st.st_mode | stat.S_IEXEC)) else: def do_chmod(fname) -> None: try: @@ -2096,7 +2096,7 @@ def do_chmod(fname) -> None: pass else: os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~stat.S_IEXEC)) + st.st_mode & ~stat.S_IEXEC)) if os.path.isfile(top): # If it's a file, that's easy, just chmod it. diff --git a/testing/framework/TestCmdTests.py b/testing/framework/TestCmdTests.py index 61c0c5da97..f7a437b106 100644 --- a/testing/framework/TestCmdTests.py +++ b/testing/framework/TestCmdTests.py @@ -47,15 +47,15 @@ def _is_readable(path): # XXX this doesn't take into account UID, it assumes it's our file - return os.stat(path)[stat.ST_MODE] & stat.S_IREAD + return os.stat(path).st_mode & stat.S_IREAD def _is_writable(path): # XXX this doesn't take into account UID, it assumes it's our file - return os.stat(path)[stat.ST_MODE] & stat.S_IWRITE + return os.stat(path).st_mode & stat.S_IWRITE def _is_executable(path): # XXX this doesn't take into account UID, it assumes it's our file - return os.stat(path)[stat.ST_MODE] & stat.S_IEXEC + return os.stat(path).st_mode & stat.S_IEXEC def _clear_dict(dict, *keys) -> None: for key in keys: @@ -268,17 +268,17 @@ def test_chmod(self) -> None: test.chmod(wdir_file1, stat.S_IREAD) test.chmod(['sub', 'file2'], stat.S_IWRITE) - file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) + file1_mode = stat.S_IMODE(os.stat(wdir_file1).st_mode) assert file1_mode == 0o444, f'0{file1_mode:o}' - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) + file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2).st_mode) assert file2_mode == 0o666, f'0{file2_mode:o}' test.chmod('file1', stat.S_IWRITE) test.chmod(wdir_sub_file2, stat.S_IREAD) - file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) + file1_mode = stat.S_IMODE(os.stat(wdir_file1).st_mode) assert file1_mode == 0o666, f'0{file1_mode:o}' - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) + file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2).st_mode) assert file2_mode == 0o444, f'0{file2_mode:o}' else: @@ -286,17 +286,17 @@ def test_chmod(self) -> None: test.chmod(wdir_file1, 0o700) test.chmod(['sub', 'file2'], 0o760) - file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) + file1_mode = stat.S_IMODE(os.stat(wdir_file1).st_mode) assert file1_mode == 0o700, f'0{file1_mode:o}' - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) + file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2).st_mode) assert file2_mode == 0o760, f'0{file2_mode:o}' test.chmod('file1', 0o765) test.chmod(wdir_sub_file2, 0o567) - file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE]) + file1_mode = stat.S_IMODE(os.stat(wdir_file1).st_mode) assert file1_mode == 0o765, f'0{file1_mode:o}' - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) + file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2).st_mode) assert file2_mode == 0o567, f'0{file2_mode:o}' @@ -3315,11 +3315,11 @@ def test_executable(self) -> None: def make_executable(fname) -> None: st = os.stat(fname) - os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0o100)) + os.chmod(fname, stat.S_IMODE(st.st_mode|0o100)) def make_non_executable(fname) -> None: st = os.stat(fname) - os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0o100)) + os.chmod(fname, stat.S_IMODE(st.st_mode&~0o100)) test.executable(test.workdir, 0) # XXX skip these tests if euid == 0? diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index 470dbcb208..5038f1a665 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -215,7 +215,7 @@ def is_Sequence(e): hasattr(e, "__iter__")) def is_writable(f): - mode = os.stat(f)[stat.ST_MODE] + mode = os.stat(f).st_mode return mode & stat.S_IWUSR def separate_files(flist): diff --git a/testing/framework/TestCommonTests.py b/testing/framework/TestCommonTests.py index 3f7d3fd2e4..5ed2779c0a 100644 --- a/testing/framework/TestCommonTests.py +++ b/testing/framework/TestCommonTests.py @@ -222,7 +222,7 @@ def test_writable_file_exists(self) -> None: tc = TestCommon(workdir='') tc.write('file1', "file1\\n") f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] + mode = os.stat(f1).st_mode os.chmod(f1, mode | stat.S_IWUSR) tc.must_be_writable('file1') tc.pass_test() @@ -244,7 +244,7 @@ def test_non_writable_file_exists(self) -> None: tc = TestCommon(workdir='') tc.write('file1', "file1\\n") f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] + mode = os.stat(f1).st_mode os.chmod(f1, mode & ~stat.S_IWUSR) tc.must_be_writable('file1') tc.pass_test() @@ -267,7 +267,7 @@ def test_file_specified_as_list(self) -> None: tc.subdir('sub') tc.write(['sub', 'file1'], "sub/file1\\n") f1 = tc.workpath('sub', 'file1') - mode = os.stat(f1)[stat.ST_MODE] + mode = os.stat(f1).st_mode os.chmod(f1, mode | stat.S_IWUSR) tc.must_be_writable(['sub', 'file1']) tc.pass_test() @@ -1253,7 +1253,7 @@ def test_writable_file_exists(self) -> None: tc = TestCommon(workdir='') tc.write('file1', "file1\\n") f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] + mode = os.stat(f1).st_mode os.chmod(f1, mode | stat.S_IWUSR) tc.must_not_be_writable('file1') tc.pass_test() @@ -1275,7 +1275,7 @@ def test_non_writable_file_exists(self) -> None: tc = TestCommon(workdir='') tc.write('file1', "file1\\n") f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] + mode = os.stat(f1).st_mode os.chmod(f1, mode & ~stat.S_IWUSR) tc.must_not_be_writable('file1') tc.pass_test() @@ -1298,7 +1298,7 @@ def test_file_specified_as_list(self) -> None: tc.subdir('sub') tc.write(['sub', 'file1'], "sub/file1\\n") f1 = tc.workpath('sub', 'file1') - mode = os.stat(f1)[stat.ST_MODE] + mode = os.stat(f1).st_mode os.chmod(f1, mode & ~stat.S_IWUSR) tc.must_not_be_writable(['sub', 'file1']) tc.pass_test() From 70e8cf9245f069d4eba070b6c8aa8fbe218e19ec Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 8 Nov 2024 13:29:07 -0700 Subject: [PATCH 220/386] Adjust sconsign tests for float timestamps If a stat_result object's st_*time attributes are used, a float is returned instead of an int. Adjust sconsign test regexes to optionally accept a .digits tail of the number. Confirmed this works for both indexed attributes (which return an int) and named attributes (float). Signed-off-by: Mats Wichmann --- test/sconsign/script/Configure.py | 6 +- test/sconsign/script/SConsignFile.py | 208 ++++++++++++------------ test/sconsign/script/Signatures.py | 24 +-- test/sconsign/script/dblite.py | 24 +-- test/sconsign/script/no-SConsignFile.py | 124 +++++++------- 5 files changed, 193 insertions(+), 193 deletions(-) diff --git a/test/sconsign/script/Configure.py b/test/sconsign/script/Configure.py index 2e1e9c1455..6b064b1ee8 100644 --- a/test/sconsign/script/Configure.py +++ b/test/sconsign/script/Configure.py @@ -76,16 +76,16 @@ # Value node being printed actually begins with a newline. It would # probably be good to change that to a repr() of the contents. expect = r"""=== .: -SConstruct: None \d+ \d+ +SConstruct: None \d+(\.\d*)? \d+ === .sconf_temp: conftest_%(sig_re)s_0.c: - '.*': + '.*':\s #include "math.h" %(sig_re)s \[.*\] conftest_%(sig_re)s_0_%(sig_re)s%(_obj)s: - %(_sconf_temp_conftest_0_c)s: %(sig_re)s \d+ \d+ + %(_sconf_temp_conftest_0_c)s: %(sig_re)s \d+(\.\d*)? \d+ %(CC)s: %(sig_re)s None None %(sig_re)s \[.*\] === %(CC_dir)s: diff --git a/test/sconsign/script/SConsignFile.py b/test/sconsign/script/SConsignFile.py index 680eae5451..6361ff30e8 100644 --- a/test/sconsign/script/SConsignFile.py +++ b/test/sconsign/script/SConsignFile.py @@ -164,160 +164,160 @@ def process(infp, outfp): test.run_sconsign(arguments=database_name, stdout=r"""=== .: -SConstruct: None \d+ \d+ -fake_cc\.py: %(sig_re)s \d+ \d+ -fake_link\.py: %(sig_re)s \d+ \d+ +SConstruct: None \d+(\.\d*)? \d+ +fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ +fake_link\.py: %(sig_re)s \d+(\.\d*)? \d+ === sub1: -hello.c: %(sig_re)s \d+ \d+ -hello.exe: %(sig_re)s \d+ \d+ - %(sub1_hello_obj)s: %(sig_re)s \d+ \d+ - fake_link\.py: %(sig_re)s \d+ \d+ +hello.c: %(sig_re)s \d+(\.\d*)? \d+ +hello.exe: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_link\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] === sub2: -hello.c: %(sig_re)s \d+ \d+ -hello.exe: %(sig_re)s \d+ \d+ - %(sub2_hello_obj)s: %(sig_re)s \d+ \d+ - fake_link\.py: %(sig_re)s \d+ \d+ +hello.c: %(sig_re)s \d+(\.\d*)? \d+ +hello.exe: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_hello_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_link\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.obj: %(sig_re)s \d+ \d+ - %(sub2_hello_c)s: %(sig_re)s \d+ \d+ - %(sub2_inc1_h)s: %(sig_re)s \d+ \d+ - %(sub2_inc2_h)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc1_h)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc2_h)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -inc1.h: %(sig_re)s \d+ \d+ -inc2.h: %(sig_re)s \d+ \d+ +inc1.h: %(sig_re)s \d+(\.\d*)? \d+ +inc2.h: %(sig_re)s \d+(\.\d*)? \d+ """ % locals()) test.run_sconsign(arguments="--raw " + database_name, stdout=r"""=== .: -SConstruct: {'csig': None, 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} -fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} -fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} +SConstruct: {'csig': None, 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} +fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} +fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} === sub1: -hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} -hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - %(sub1_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} +hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} +hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + %(sub1_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} %(sig_re)s \[.*\] -hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - %(sub1_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} +hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + %(sub1_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} %(sig_re)s \[.*\] === sub2: -hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} -hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - %(sub2_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} +hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} +hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + %(sub2_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} %(sig_re)s \[.*\] -hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - %(sub2_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - %(sub2_inc1_h)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - %(sub2_inc2_h)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} +hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + %(sub2_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + %(sub2_inc1_h)s: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + %(sub2_inc2_h)s: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} %(sig_re)s \[.*\] -inc1.h: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} -inc2.h: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} +inc1.h: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} +inc2.h: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} """ % locals()) expect = r"""=== .: SConstruct: csig: None - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ fake_cc\.py: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ fake_link\.py: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ === sub1: hello.c: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ hello.exe: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ implicit: %(sub1_hello_obj)s: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ fake_link\.py: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ action: %(sig_re)s \[.*\] hello.obj: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ implicit: %(sub1_hello_c)s: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ fake_cc\.py: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ action: %(sig_re)s \[.*\] === sub2: hello.c: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ hello.exe: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ implicit: %(sub2_hello_obj)s: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ fake_link\.py: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ action: %(sig_re)s \[.*\] hello.obj: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ implicit: %(sub2_hello_c)s: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ %(sub2_inc1_h)s: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ %(sub2_inc2_h)s: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ fake_cc\.py: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ action: %(sig_re)s \[.*\] inc1.h: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ inc2.h: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ """ % locals() @@ -382,44 +382,44 @@ def process(infp, outfp): test.run_sconsign(arguments="-t -v " + database_name, stdout=r"""=== .: SConstruct: - timestamp: \d+ + timestamp: \d+(\.\d*)? fake_cc\.py: - timestamp: \d+ + timestamp: \d+(\.\d*)? fake_link\.py: - timestamp: \d+ + timestamp: \d+(\.\d*)? === sub1: hello.c: - timestamp: \d+ + timestamp: \d+(\.\d*)? hello.exe: - timestamp: \d+ + timestamp: \d+(\.\d*)? hello.obj: - timestamp: \d+ + timestamp: \d+(\.\d*)? === sub2: hello.c: - timestamp: \d+ + timestamp: \d+(\.\d*)? hello.exe: - timestamp: \d+ + timestamp: \d+(\.\d*)? hello.obj: - timestamp: \d+ + timestamp: \d+(\.\d*)? inc1.h: - timestamp: \d+ + timestamp: \d+(\.\d*)? inc2.h: - timestamp: \d+ + timestamp: \d+(\.\d*)? """) test.run_sconsign(arguments="-e hello.obj " + database_name, stdout=r"""=== .: === sub1: -hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] === sub2: -hello.obj: %(sig_re)s \d+ \d+ - %(sub2_hello_c)s: %(sig_re)s \d+ \d+ - %(sub2_inc1_h)s: %(sig_re)s \d+ \d+ - %(sub2_inc2_h)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc1_h)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc2_h)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] """ % locals(), stderr=r"""sconsign: no entry `hello\.obj' in `\.' @@ -428,34 +428,34 @@ def process(infp, outfp): test.run_sconsign(arguments="-e hello.obj -e hello.exe -e hello.obj " + database_name, stdout=r"""=== .: === sub1: -hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.exe: %(sig_re)s \d+ \d+ - %(sub1_hello_obj)s: %(sig_re)s \d+ \d+ - fake_link\.py: %(sig_re)s \d+ \d+ +hello.exe: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_link\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] === sub2: -hello.obj: %(sig_re)s \d+ \d+ - %(sub2_hello_c)s: %(sig_re)s \d+ \d+ - %(sub2_inc1_h)s: %(sig_re)s \d+ \d+ - %(sub2_inc2_h)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc1_h)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc2_h)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.exe: %(sig_re)s \d+ \d+ - %(sub2_hello_obj)s: %(sig_re)s \d+ \d+ - fake_link\.py: %(sig_re)s \d+ \d+ +hello.exe: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_hello_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_link\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.obj: %(sig_re)s \d+ \d+ - %(sub2_hello_c)s: %(sig_re)s \d+ \d+ - %(sub2_inc1_h)s: %(sig_re)s \d+ \d+ - %(sub2_inc2_h)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc1_h)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc2_h)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] """ % locals(), stderr=r"""sconsign: no entry `hello\.obj' in `\.' diff --git a/test/sconsign/script/Signatures.py b/test/sconsign/script/Signatures.py index 2225f83bbf..a355068de3 100644 --- a/test/sconsign/script/Signatures.py +++ b/test/sconsign/script/Signatures.py @@ -169,26 +169,26 @@ def process(infp, outfp): test.run_sconsign( arguments=f"-e hello.exe -e hello.obj sub1/{database_name}", - stdout=r"""hello.exe: %(sig_re)s \d+ \d+ - %(sub1_hello_obj)s: %(sig_re)s \d+ \d+ - fake_link\.py: None \d+ \d+ + stdout=r"""hello.exe: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_link\.py: None \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: None \d+ \d+ - fake_cc\.py: None \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: None \d+(\.\d*)? \d+ + fake_cc\.py: None \d+(\.\d*)? \d+ %(sig_re)s \[.*\] """ % locals(), ) test.run_sconsign( arguments=f"-e hello.exe -e hello.obj -r sub1/{database_name}", - stdout=r"""hello.exe: %(sig_re)s '%(date_re)s' \d+ - %(sub1_hello_obj)s: %(sig_re)s '%(date_re)s' \d+ - fake_link\.py: None '%(date_re)s' \d+ + stdout=r"""hello.exe: %(sig_re)s '%(date_re)s' \d+(\.\d*)? + %(sub1_hello_obj)s: %(sig_re)s '%(date_re)s' \d+(\.\d*)? + fake_link\.py: None '%(date_re)s' \d+(\.\d*)? %(sig_re)s \[.*\] -hello.obj: %(sig_re)s '%(date_re)s' \d+ - %(sub1_hello_c)s: None '%(date_re)s' \d+ - fake_cc\.py: None '%(date_re)s' \d+ +hello.obj: %(sig_re)s '%(date_re)s' \d+(\.\d*)? + %(sub1_hello_c)s: None '%(date_re)s' \d+(\.\d*)? + fake_cc\.py: None '%(date_re)s' \d+(\.\d*)? %(sig_re)s \[.*\] """ % locals(), ) diff --git a/test/sconsign/script/dblite.py b/test/sconsign/script/dblite.py index 24d8403c47..0d05d5522c 100644 --- a/test/sconsign/script/dblite.py +++ b/test/sconsign/script/dblite.py @@ -116,24 +116,24 @@ def escape_drive_case(s): manifest = '' expect = r"""=== sub1: -hello%(_exe)s: %(sig_re)s \d+ \d+ - %(sub1_hello_obj)s: %(sig_re)s \d+ \d+ - %(LINK)s: None \d+ \d+ +hello%(_exe)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + %(LINK)s: None \d+(\.\d*)? \d+ %(sig_re)s \[.*%(manifest)s\] -hello%(_obj)s: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: None \d+ \d+ - %(CC)s: None \d+ \d+ +hello%(_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: None \d+(\.\d*)? \d+ + %(CC)s: None \d+(\.\d*)? \d+ %(sig_re)s \[.*\] """ % locals() expect_r = r"""=== sub1: -hello%(_exe)s: %(sig_re)s '%(date_re)s' \d+ - %(sub1_hello_obj)s: %(sig_re)s '%(date_re)s' \d+ - %(LINK)s: None '%(date_re)s' \d+ +hello%(_exe)s: %(sig_re)s '%(date_re)s' \d+(\.\d*)? + %(sub1_hello_obj)s: %(sig_re)s '%(date_re)s' \d+(\.\d*)? + %(LINK)s: None '%(date_re)s' \d+(\.\d*)? %(sig_re)s \[.*%(manifest)s\] -hello%(_obj)s: %(sig_re)s '%(date_re)s' \d+ - %(sub1_hello_c)s: None '%(date_re)s' \d+ - %(CC)s: None '%(date_re)s' \d+ +hello%(_obj)s: %(sig_re)s '%(date_re)s' \d+(\.\d*)? + %(sub1_hello_c)s: None '%(date_re)s' \d+(\.\d*)? + %(CC)s: None '%(date_re)s' \d+(\.\d*)? %(sig_re)s \[.*\] """ % locals() diff --git a/test/sconsign/script/no-SConsignFile.py b/test/sconsign/script/no-SConsignFile.py index 7a41c72cb2..5760bf230f 100644 --- a/test/sconsign/script/no-SConsignFile.py +++ b/test/sconsign/script/no-SConsignFile.py @@ -168,62 +168,62 @@ def process(infp, outfp): sig_re = r'[0-9a-fA-F]{32,64}' -expect = r"""hello.c: %(sig_re)s \d+ \d+ -hello.exe: %(sig_re)s \d+ \d+ - %(sub1_hello_obj)s: %(sig_re)s \d+ \d+ - fake_link\.py: %(sig_re)s \d+ \d+ +expect = r"""hello.c: %(sig_re)s \d+(\.\d*)? \d+ +hello.exe: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_link\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] """ % locals() test.run_sconsign(arguments = f"sub1/{database_name}", stdout=expect) test.run_sconsign(arguments = f"--raw sub1/{database_name}", - stdout = r"""hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} -hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - %(sub1_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} + stdout = r"""hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} +hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + %(sub1_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} %(sig_re)s \[.*\] -hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - %(sub1_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} - fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2} +hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + %(sub1_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} + fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+(\.\d*)?L?, 'size': \d+L?, '_version_id': 2} %(sig_re)s \[.*\] """ % locals()) test.run_sconsign(arguments = f"-v sub1/{database_name}", stdout = r"""hello.c: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ hello.exe: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ implicit: %(sub1_hello_obj)s: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ fake_link\.py: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ action: %(sig_re)s \[.*\] hello.obj: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ implicit: %(sub1_hello_c)s: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ fake_cc\.py: csig: %(sig_re)s - timestamp: \d+ + timestamp: \d+(\.\d*)? size: \d+ action: %(sig_re)s \[.*\] """ % locals()) @@ -248,73 +248,73 @@ def process(infp, outfp): test.run_sconsign(arguments = f"-t -v sub1/{database_name}", stdout = r"""hello.c: - timestamp: \d+ + timestamp: \d+(\.\d*)? hello.exe: - timestamp: \d+ + timestamp: \d+(\.\d*)? hello.obj: - timestamp: \d+ + timestamp: \d+(\.\d*)? """ % locals()) test.run_sconsign(arguments = f"-e hello.obj sub1/{database_name}", - stdout = r"""hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ + stdout = r"""hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] """ % locals()) test.run_sconsign(arguments = f"-e hello.obj -e hello.exe -e hello.obj sub1/{database_name}", - stdout = r"""hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ + stdout = r"""hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.exe: %(sig_re)s \d+ \d+ - %(sub1_hello_obj)s: %(sig_re)s \d+ \d+ - fake_link\.py: %(sig_re)s \d+ \d+ +hello.exe: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_link\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] """ % locals()) test.run_sconsign(arguments = f"sub2/{database_name}", - stdout = r"""hello.c: %(sig_re)s \d+ \d+ -hello.exe: %(sig_re)s \d+ \d+ - %(sub2_hello_obj)s: %(sig_re)s \d+ \d+ - fake_link\.py: %(sig_re)s \d+ \d+ + stdout = r"""hello.c: %(sig_re)s \d+(\.\d*)? \d+ +hello.exe: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_hello_obj)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_link\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.obj: %(sig_re)s \d+ \d+ - %(sub2_hello_c)s: %(sig_re)s \d+ \d+ - %(sub2_inc1_h)s: %(sig_re)s \d+ \d+ - %(sub2_inc2_h)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc1_h)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc2_h)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -inc1.h: %(sig_re)s \d+ \d+ -inc2.h: %(sig_re)s \d+ \d+ +inc1.h: %(sig_re)s \d+(\.\d*)? \d+ +inc2.h: %(sig_re)s \d+(\.\d*)? \d+ """ % locals()) #test.run_sconsign(arguments = "-i -v sub2/{}".format(database_name), -# stdout = r"""hello.c: %(sig_re)s \d+ \d+ -#hello.exe: %(sig_re)s \d+ \d+ +# stdout = r"""hello.c: %(sig_re)s \d+(\.\d*)? \d+ +#hello.exe: %(sig_re)s \d+(\.\d*)? \d+ # implicit: -# hello.obj: %(sig_re)s \d+ \d+ -#hello.obj: %(sig_re)s \d+ \d+ +# hello.obj: %(sig_re)s \d+(\.\d*)? \d+ +#hello.obj: %(sig_re)s \d+(\.\d*)? \d+ # implicit: -# hello.c: %(sig_re)s \d+ \d+ -# inc1.h: %(sig_re)s \d+ \d+ -# inc2.h: %(sig_re)s \d+ \d+ +# hello.c: %(sig_re)s \d+(\.\d*)? \d+ +# inc1.h: %(sig_re)s \d+(\.\d*)? \d+ +# inc2.h: %(sig_re)s \d+(\.\d*)? \d+ #""" % locals()) test.run_sconsign(arguments = f"-e hello.obj sub2/{database_name} sub1/{database_name}", - stdout = r"""hello.obj: %(sig_re)s \d+ \d+ - %(sub2_hello_c)s: %(sig_re)s \d+ \d+ - %(sub2_inc1_h)s: %(sig_re)s \d+ \d+ - %(sub2_inc2_h)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ + stdout = r"""hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc1_h)s: %(sig_re)s \d+(\.\d*)? \d+ + %(sub2_inc2_h)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] -hello.obj: %(sig_re)s \d+ \d+ - %(sub1_hello_c)s: %(sig_re)s \d+ \d+ - fake_cc\.py: %(sig_re)s \d+ \d+ +hello.obj: %(sig_re)s \d+(\.\d*)? \d+ + %(sub1_hello_c)s: %(sig_re)s \d+(\.\d*)? \d+ + fake_cc\.py: %(sig_re)s \d+(\.\d*)? \d+ %(sig_re)s \[.*\] """ % locals()) From 3f6be17927839c2cf0a94216929911a4136a833b Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 15 Dec 2024 14:47:01 -0700 Subject: [PATCH 221/386] Flip SCons st_mtime usage back to tuple index A previous change proposed flipping all of the Python stat references from the older tuple index style (st[stat.ST_XXX]) to the more modern stat structure references (st.st_xxx). This change rolls back the one place in the actual SCons code (in Node/FS) that used the stat mtime attribute. There's a small chance that switching scons versions back and forth could cause some time comparison issues, since st_mtime is a float, but for backwards compatibility reasons with ancient Python 2 versions Python retained the integer behavior of the index form. While problems from this seem a low probability, avoid the issue for now. Signed-off-by: Mats Wichmann --- SCons/Node/FS.py | 4 +++- SCons/Node/FSTests.py | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/SCons/Node/FS.py b/SCons/Node/FS.py index ec0a69a7b4..694ff9f87a 100644 --- a/SCons/Node/FS.py +++ b/SCons/Node/FS.py @@ -762,7 +762,9 @@ def getmtime(self): st = self.stat() if st: - return st.st_mtime + # TODO: switch to st.st_mtime, however this changes granularity + # (ST_MTIME is an int for backwards compat, st_mtime is float) + return st[stat.ST_MTIME] else: return None diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py index ae98bc5a59..1b3704da9f 100644 --- a/SCons/Node/FSTests.py +++ b/SCons/Node/FSTests.py @@ -774,7 +774,8 @@ def test_update(self) -> None: ni.update(fff) - mtime = st.st_mtime + # TODO: flip this to st.st_mtime when Node/FS.py does + mtime = st[stat.ST_MTIME] assert ni.timestamp == mtime, (ni.timestamp, mtime) size = st.st_size assert ni.size == size, (ni.size, size) @@ -786,7 +787,8 @@ def test_update(self) -> None: st = os.stat('fff') - mtime = st.st_mtime + # TODO: flip this to st.st_mtime when Node/FS.py does + mtime = st[stat.ST_MTIME] assert ni.timestamp != mtime, (ni.timestamp, mtime) size = st.st_size assert ni.size != size, (ni.size, size) From fe00f8bf24b0c5b879eab5c9c4e29e7f7cb8440d Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 15 Dec 2024 15:41:51 -0800 Subject: [PATCH 222/386] [ci skip] Fix typos --- SCons/Variables/__init__.py | 2 +- doc/man/scons.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 2d151e0c9a..2ac95d0673 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -412,7 +412,7 @@ def GenerateHelpText(self, env, sort: bool | Callable = False) -> str: (must take two arguments and return ``-1``, ``0`` or ``1``) or a boolean to indicate if it should be sorted. """ - # TODO this interface was designed when Pythin sorted() took an + # TODO this interface was designed when Python's sorted() took an # optional comparison function (pre-3.0). Since it no longer does, # we use functools.cmp_to_key() since can't really change the # documented meaning of the "sort" argument. Maybe someday? diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 764134140d..2d5241498b 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -5054,7 +5054,7 @@ if omitted defaults to an empty list. FormatVariableHelpText -is normally not called directy, but by +is normally not called directly, but by &GenerateHelpText;, which does the work of obtaining the necessary values. You can patch in your own From 3ee18c08c72fe8a6ce6cc2c39c355a880472213e Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 13 Dec 2024 07:20:23 -0700 Subject: [PATCH 223/386] Update docs on special method evaluation The User Guide uses an example (in the Command chapter) of making a target name out of the source name, a special attribute, and concatenation with a new suffix. The special attribute part is not something that has been introduced. Some searching suggests it's never actually described in the User Guide, though .file, .abspath and .srcdir are used in examples. The attribute used, .basename, doesn't actually exist - there's a .base and a .filebase. Furthermore, the substitution suggested doesn't work. Expansion of special variables like $SOURCE into nodes is deferred - see the docstring of SCons.Subst.NLWrapper - so the internal expansion ends up trying to lookup the attribute on a string, which fails with an AttributeError. The way the user guide entry is written, it was not actually evaluated: it was described as an , but an incomplete one, and since there was no corresponding the problem was not detected. The changes fix up the example to have it use an existing attribute (.base) and do File() on the source, and add a sidebar to provide a bit of an explanation so this isn't just "magic". A subsequent example (ex4, which I added) is dropped as it doesn't add enough value by itself, and the final example (formerly ex5, renamed to ex3) now includes this substitution so it's actually run by the doc machinery, and can be seen to be working correctly. This finally fixes #2905, which was closed in 2018 as having been addressed, though the non-working example actually remained. The issue is also mentioned in #4660, which is not resolved by changing the docs - leaving that open, at least for the time being. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 + RELEASE.txt | 4 ++ ...s_ex5_1.xml => builderscommands_ex3_1.xml} | 0 doc/user/builders-commands.xml | 64 +++++++++++-------- doc/user/environments.xml | 2 +- doc/user/nodes.xml | 14 ++-- 6 files changed, 54 insertions(+), 33 deletions(-) rename doc/generated/examples/{builderscommands_ex5_1.xml => builderscommands_ex3_1.xml} (100%) diff --git a/CHANGES.txt b/CHANGES.txt index 5383cb7b5f..285dcda2f8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -186,6 +186,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER and other attributes rather than indexing into stat return. - The update-release-info test is adapted to accept changed help output introduced in Python 3.12.8/3.13.1. + - Update the User Guide Command() example which now shows a target name + being created from '${SOURCE.base}.out' to use a valid special + attribute and to explain what's being done in the example. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index d386cb3397..25d4862abd 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -211,6 +211,10 @@ DOCUMENTATION - Improved Variables documentation. +- Update the User Guide Command() example which now shows a target name + being created from '${SOURCE.base}.out' to use a valid special + attribute and to explain what's being done in the example. + DEVELOPMENT ----------- diff --git a/doc/generated/examples/builderscommands_ex5_1.xml b/doc/generated/examples/builderscommands_ex3_1.xml similarity index 100% rename from doc/generated/examples/builderscommands_ex5_1.xml rename to doc/generated/examples/builderscommands_ex3_1.xml diff --git a/doc/user/builders-commands.xml b/doc/user/builders-commands.xml index d7e8c34235..697193cbf5 100644 --- a/doc/user/builders-commands.xml +++ b/doc/user/builders-commands.xml @@ -142,37 +142,49 @@ foo.in - Note that &cv-link-SOURCE; and &cv-link-TARGET; are expanded - in the source and target as well, so you can write: + &cv-link-SOURCE; and &cv-link-TARGET; are expanded + in the source and target as well: - - -env.Command('${SOURCE.basename}.out', 'foo.in', build) - - - - - - which does the same thing as the previous example, but allows you - to avoid repeating yourself. - - + + +env.Command('${SOURCE.base}.out', File('foo.in'), build) + - It may be helpful to use the action - keyword to specify the action, is this makes things more clear - to the reader: + Which does the same thing as the previous example, but allows you + to write a more generic rule for transforming the source filename + to the target filename, since unlike regular Builders, + &Command; does not have any built-in rules for that. - - -env.Command('${SOURCE.basename}.out', 'foo.in', action=build) - - + + + The example uses a Node special attribute + (.base, the file without its suffix), + a concept which has not been introduced yet, + but will appear in several subsequent examples + (see details in the Reference Manual section + Substitution: Special Attributes). + Due to the quirks of &SCons;' deferred evaluation scheme, + node special attribues do not currently work + in source and target arguments if the + replacement is a string (like 'foo.in'). + They do work fine in strings describing actions. + You can give &SCons; a little help by + manually converting the filename string to a Node + (see ), + which is the approach used in the example. + + @@ -187,11 +199,13 @@ env.Command('${SOURCE.basename}.out', 'foo.in', action=build) which include a message based on the type of action. However, you can also construct the &Action; object yourself to pass to &f-Command;, which gives you much more control. + Using the action keyword can also help + make such lines easier to read. Here's an evolution of the example from above showing this approach: - + env = Environment() @@ -200,7 +214,7 @@ def build(target, source, env): return None act = Action(build, cmdstr="Building ${TARGET}") -env.Command('foo.out', 'foo.in', action=act) +env.Command('${SOURCE.base}.out', File('foo.in'), action=act) foo.in @@ -213,7 +227,7 @@ foo.in - + scons -Q diff --git a/doc/user/environments.xml b/doc/user/environments.xml index 6807694ece..ac99608529 100644 --- a/doc/user/environments.xml +++ b/doc/user/environments.xml @@ -1876,9 +1876,9 @@ env = Environment() env.Command('foo', [], '__ROOT__/usr/bin/printenv.py') -#!/usr/bin/env python import os import sys + if len(sys.argv) > 1: keys = sys.argv[1:] else: diff --git a/doc/user/nodes.xml b/doc/user/nodes.xml index a652e79c79..eba601465e 100644 --- a/doc/user/nodes.xml +++ b/doc/user/nodes.xml @@ -40,7 +40,7 @@ This file is processed by the bin/SConsDoc.py module. -
+
Builder Methods Return Lists of Target Nodes @@ -150,7 +150,7 @@ int main() { printf("Goodbye, world!\n"); }
-
+
Explicitly Creating File and Directory Nodes @@ -228,7 +228,7 @@ xyzzy = Entry('xyzzy')
-
+
Printing &Node; File Names @@ -290,7 +290,7 @@ int main() { printf("Hello, world!\n"); }
-
+
Using a &Node;'s File Name as a String @@ -337,7 +337,7 @@ int main() { printf("Hello, world!\n"); }
-
+
&GetBuildPath;: Getting the Path From a &Node; or String @@ -384,7 +384,7 @@ print(env.GetBuildPath([n, "sub/dir/$VAR"])) - + + + - + - + - + - User's view + User's view - + - Folder 1 + Folder 1 @@ -46,10 +46,11 @@ - + - XML files (src/user/man/...) + XML files (user guide, +manpage, source tree) @@ -63,10 +64,10 @@ - + - XML validation + XML validation @@ -83,7 +84,8 @@ - Writer + Writer + @@ -102,11 +104,10 @@ - + - Creating entity lists - + Creating entity lists @@ -120,11 +121,11 @@ - + - Check that example -names are unique + Check that example + names are unique @@ -141,7 +142,7 @@ names are unique - Create example outputs + Create example outputs @@ -158,8 +159,8 @@ names are unique - Resolve XIncludes for text -and examples + Resolve XIncludes for + text and examples @@ -173,10 +174,10 @@ and examples - + - Create HTML, PDF, Man + Create HTML, PDF, Man @@ -190,10 +191,10 @@ and examples - + - Install in proper place + Install in proper place for packaging @@ -208,11 +209,11 @@ for packaging - + - Create API doc -(Epydoc) + Create API doc +(Sphinx) @@ -229,7 +230,8 @@ for packaging - get validated + get validated + @@ -267,7 +269,9 @@ for packaging - + + + @@ -300,7 +304,8 @@ for packaging - switching to Docbook + switching to Docbook + @@ -333,13 +338,14 @@ for packaging - edits/creates + edits/creates + - + <?xml version="1.0" encoding="utf-8"?> <!-- Generator: Adobe Illustrator 15.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> diff --git a/doc/images/overview.png b/doc/images/overview.png index f1189c920826b93f35043ec60ea9cec89e0e4806..b83e9556aab0dd95695c98ea4600779b7cbe7240 100644 GIT binary patch literal 24170 zcmb5WbwE_nw>At0C@COa3L;7hNJ*=Jf^-W50s>M)$*P4#v)hb`mt@`e|N-5+$d z=*H;8yk3|be$kfLdN;jHQCR71kZ>@bf6!Gnq8oUhuHn(JDZWq?O1;lSLp^fnMY&o$ zf-c%$ctpH-?BPk=GqL)6nPccMaZ>-dbXdjewzP^mwcPYA<=fIE2=GTEm$b@lX|(FU zKW`Vv|L=cZ{_x`4|GuxlGBw5P>~Or?p&@{n_{NPg&(kAWMvtF!u_-Bxf8zBEmv>hN zb&E_1`*}_e0@L(}5X;NU=f^eYW8a?zFnS(L(?~}Ll9H0Ds;b)BE_Ty<8a<3{$PEuC zj+4KoY?3D(OcBZ;_BBPjeLi2e#`CnSOo-_N8)XXRpYV%o*$?m}&ZD;Rh>3{_2{|)I zA7MKA1j#}VWU8f?PI~Ib9yHV1yk8qFxu(1I_b*!=9>QtkY{Tn30h#u|s8lt}<9ILZ zhUH+^pxZ{3*2f&IC>Cu)b@c(b_1Gs!5ECq$17>V&Ou1+=n57ZRZ7@V)J6-R`uK7Dc z1>52bg>ag^BP2wXr(fx;5&#LtOWWI++$amlhoUN&Wy7ZKaB&f>y>^;K78u{Xd)LB0 zE-p?XK_D}IiQz~Rt=h9P`c%?WSeV7Asq1F=NZG9{Ou^K7W#I%1=G zSl=y2r^plvg}%vfzv&Wj*hFbyh0HgMysz*W|}QM8ZWJ#zW`)`cKyn@pYrp)Os=MW=D%G!>J@{nCqUbBsIsoS+^9MCyvfgh{au4e zbtCkLOejGDUv{sGZ4jf^iJgo4;oo_Q^TQrK)6QPQ;R@P~c|6>2YYshP0eER_PB@cS&&69if9=7vFjG@c53$~}nLskuh)=;snl#gL=VLZC;2BCx zIcYSPGOFD6nZ|~4bnQZ{GER1u|2#pzcDkB|Wl0At!m;)Pc3oYa*ZJ{eiO|9*m$XeR z8!onF+0hV7eHXevE!frdU~}3JpYqBtTh2-MZG~tjtfh*RFOi`O%~C4Bd8U00U^1nVyBVeXo$3Fjkxt@$kG zn`ys(F`0~)*@eYVOU$IO7!#iz4c8DcijU`aUn#KE{{F+=!^3hQqrmIjbIv=@f~wTA zk1)fq!C%aE2g^E5>2%aG&*N_!YX+esDFW=*=x`zi(NFAK7=w5HbbxPE&*+=|;91E+ zXMAK#%>7sex80TNooyeJTA+vBjWVau|acO}ecobty2NMyQL$pI-{0H2n2m*eCS{blE&b1qB6Qe?MdD z4bj5puztiLYV`B!+k;@y9P2rg(e*lS4P|aXU$ z=7W$X_pWM${rfo1r4crBv3RMVF}BkZRKjfyMJCuo%BFStAU0NT8}L}b#R1bF)UdE% zlPJJkFiAJ?I*qrAO|Mt&@OLQx7%-bXV5Ou^7O(l?^)QfI0pFo3Pq!%vX z@`F)qRs7KH4E}4}&`!EY~y z!q&W|n)?YN!+Tr5l#dTOezN>EpLVziYGVI9h~x6R=Cvo+N5@L@AmUytn-dK@x?f;9 z=XaS70yYOFeDmt0ROgNwc)rWd)_aNG_BzzFFl|<%;{pcP$W?_(T^-8l#Ft z|0Rux4Mi>QyBrLstbIjs_uEMB=-Hg0_|wn1BsMaxHyB;HzcyBUfF(&+!XpfGTd6DS zFjKRcxZ@WHmTG=9_fLiA{LEGrNkO)|Qx`qeuEIuHc?wJCMv<44fvmhkf}=X-cunimo$$gz^3wBN<|E60z8 zV!M^F(a7(=gkzcH{k{wjfWoi9pj?9kKgo& zZnT=rhBpUHmc?{?|NC*Tk`~uJD1?Nk<=CxeqC20wdUM^Tvi_F`9Zv%v6uR-;!`~CJ zxHnn;cD(JUw!hd?V;RN{*T=itpA?!|9*ozynR>mxW4rHptTQky=JaYmByoF0|NO3< zJdSktIz4|jWxpePjfmO)v1h}_jzv}ZA3Y$d#m7gJE`^Mx>5(8p-u5m(wXmNnyoqpk zyWhQGyf!Vd>2K%$Q~zt?rFI0h;C_q3>@1~6_3Enr6D6086YQ}?f+sYL z*uQm)tBsn1@~V&I#MZ#VuRj(Q9WPJ^ajev8gef@XiDMRNsK6}jp;jZ;EX*-?{Vf06 zqsg9`&R9y)#eEdtqMCDA-{hVM)trNQ`UdJ}8vF;lm2Aw|m)KKJ#6>ac|J z#AL0)HqK_Facxp(Qg^J(ZuUpGI9vF1t@z(oX0PM1$$>wZ@DxDI2;;cEkSyV)-(djk z>UI^X{d@MJlJxag!EsxGBi4_F%0CfUO0o+1PCS{CQ78qGn|wE!)@tJ^Li$EzD=B~Wu+7rRIra(y{wz38w(G=m`ooet4ikLbvGXekaSF$-V#hGt3e(Dvfhc!*4m%+1eVB4m^3x?ovCL z2x)Xo0ub<`r$xkf)d#(wwBfs);}rv%Ov&OA;2F~l=K%LmmE?4|VD)(Dl6 zF;X?_Aj}|9>Vy=WXxU#YTyJg;r9Iy(tuZk*{rFv|3byuDbdU>uC27>n(jN(_a^3aV zN9~tN_{L7SEYi0WUO3r#%G?Cchesllo7Bl+p6g-MDfe&)YRYZAzgf4l>B>c*(3{b7 zgN9C@*Lv$iQ1#QHCly*V?%t@iq9e*rkeAv929sQniqS8!(GW}<4+onHUVummL{ z*gAk>govxm>CO7q6gTs8(UJ552XWN2-~*HM^($^O)6-kwUMI#wzjglZ6!nNVWLD%` zdZPfU!NJANKJbsshA2gJ+xyjnO@`TO%+!Awo|M@Nu-Upcb}BtCF!EqBb#CU^tft{x zbgCP_BQ+M8?U$C@Efyob!H5)tkHg-NFd`xEM1&?|9&ZdK$4_!E5wp53b}GTRXdIsi zf3Hm@pcNo62sqwbi%gefRUGt#;G>U@k0rqNb9NjlG!Y674aJ1|1VNjLTnGY}(Y?2) zCok17_A=(!A+mUL4MK^IaH?~T-fv*soY|gGk;{(QK|Y&<+Avbpr)e^>d`{ueIBB}9 zk*g9?UY}nJvrXn)9-AaSNE%xrn_ex4p#%EZ&Ger{sty^jE-zDZ>%RpP-8YtF+g=e( zU23-sU*3~-+j2S{V0bBCx&HjTC1xj7lI;%u=oyujVBO?kIgw3nshhiBD>DT(#TXHH zG5yz+U{pE$s)OgLr>fkP*=h?cXl$v6&<=!9ON8E7*Pp(!%||tNdCCzy&yV&(juvJ+ z62wbA&eN;Km)-%)In0MWQV~R`ySycEgw`5wfu(VH_na+=XCqMegZA#)v)P zS0|A(tY?+hwFNAUDI=5?r?!tUJ1*C9S#C63h20E&QZZcZl42d-f4a$=hfry-nmk6% z$c~XVj}duVi@+OWD%xQdNsmNeK31%nqlza5^|o&dICUy2CP%;2j*q}k4aSUQ&nt3z z)*hw&R@926>o=u7T<7n&idt-I+@E`bA ztXHih>enF5w|-YtwA7BX&}cd3uHim%im~3fr#lycCOmht44+)v;rrJv%KeF(sQG^T zGVOiOO1Fgo2|st!VCwr6)HAXpd!a=viuv?$R`Oq=A}671t?pw5P4HQH>lQFC+?1=t zvizN5aUJ80@$iG2@Fm0jdhuhIDzl-w2$52#z3)E^CvGy)DU^Jxe8S20q;tPe!2QH+ z;Epc(hOS<21A7~O%6{!_Ly1=G2$XCTzBhZGbHJ+(LIIYrqGhL ztpaI6xb#jH?O!h)l3AM+iO%W4!2jSNoX>A1AYyQ*;rOKB!l$?kR>uPrd;=6Byy2BM z4FVk}8n7L#Jr-7Nl!+~#r#(l{GQ}>;t}HT2A=pl_&scEF zo1hhEN`|=GB)8Ps?0m98yo2a)ZfJiSnx3-o$=(_);_@2Bw=u0A(>2|r?FI1~zg-eh z6rWyM)g6ul+E`1W4{pKFE3c&sNjPly?RFDwb&m2&b8wGWcWvFnU=mA2cf|gCO{3a1 z1rJZc-4ikUZ*SQ3UQ257CoW(+MR5LgD1he|P1@*+dg>t>Z#+5dp2TOZ@1)+YS6-ls9 zotFrQv$)^M)4@@vMZ~h=Ew0m<2`a!z@NCj%YNPw?O3CMmk8lLe3B$x5OPghn#$G+=2-OHufUES-k7dV~dwnUsS03PyE((@2ldAL+fBO8OfH-sU}>{%H-%K4Tu^K6a#3hJS*bKVVmt!wZ@hoPd3n0 zRz3l=eH;4P8;M!;?o~wnx*Yy;4B3i{jV$9>H+Id&k-I_f(&tHku0TBx>Iwhbi5v{j zl1WCEOYC+YvX_<(bbyoqnO2%T*p(m{ei8?N$Yo-95cp76^_O1?HI2Ky?NSMkT}|kb zxY<%z$mdn1&YMIAC7xXi5z!(7ha011w%e}fC3V=eL)U*B)Yvyw5b_PR5E*z`yjW6V zb{apkZ&?$UGM~U^_0N4GZ0h(obxqa@E&o!?;Vxff0bwH-$Vg-YQRUmSBlu%`zdDH} z1o+XilVZaZ3`Zm9$h;qL2u0x>zm`f zb4kZxkc#*5#LBlo?y_3Z-dP!M%VFIX#vJ)n&D}VUTd`Z;lgUgv=+R_Z$oB;9x*qz| z(>&uDFIxHpK8t8l#`OhMRp&!X{!4Q#b_!0`zFhR>m`;C4S!!ab9X1=}e$*i&+jVqbV z4K`lY;vQ3@#etuPPxsD6w~uuVeDw}Q=h|aGON0lJQ2Rp>BVKFsXW9M!Hgb#zW-pNB7^zdJG#TT6qAwTWhO~@k{b@y0;zp{Dc1R0$}Np#7#Cwpzi+ISt9 zw6BNs45xW8d=Y=|wy=0tt7=%Eu5h|P!+N-7qmo~IW~gja*Vb9q8nIk7ztGx6-5_X6 zpyO2KH8rx8O)Rq}V$Y9q^n!$963x}ua!NI%Gzp^S+czgSYz`O^mS*djlOGk&;VYe0 zgJx^+6?D}%zWtN%b6#eyS&6Z{9fQcd!%+t{VKnIK-!MrKXori7CWIy6moBlkt$qkVK0|Kj@f^wa9S%13llw*x1W z&aC^UQ&^?-6R2Yg4q8zxvg=o>$(Zwc6w9{Ph)0UZJQ>JfgJzaTb4#7eZcwG*axs=C zma6JQzL3)Dpt3X_vwUNxB^;>=k7~Z#gs)rRsdvG z_{3{#CN+P~Zmg$A{>WvyzyNygJpX>ZimQ|e0!$Dx z=i?L4lg1Nk+3Hcdn`NB=D0PzB{FM+7LoLW#tdDjTM~P(xrUq|ckOUJvC<6=P_Zofw zMAUMlr`pa>`)!1PGiukfBP`%KD;Xve(UjuUrWCV#_P02W;Fg2kUsRf`7X)@PJpWJ_ zJ5!i_{_i^DTr_-Pe1j=@I%H_DYfIv6^~;YM#j!{p-kq5!6tx5K+tmVgn@nj_g^14t zGNl8>LpJk=@pU&ZG7b&ps>LP`YI|2$ebV)SbuPJT#B)by{y9J6?BWGurf?FYI`D;! zH1?PBB=^vbBC|}nbz@zQjzw#^(DnND0aPigtck*Sw8=r4Z&*1~oYh*iQfwI=3?Q>| zCK%+=7ef>0{*&HD^MvqMNSJB;o&#A!|NXTW33SNYHD@HP+)%vNo=z+Y>fs4|}yV(wEQ^HtM_}LECfSn|Ns75Dy|z zxVD?C^X9T)UAlj+F#p@Z%sQ!b9FP6k+Gq(+hPbQ}Des5k-Y-3ANx`60G#C-%Hl+uD zx3;G5*j_@Wn1>=lH^9830fYiP4ltGrNCF@+0pMvcEEm#|{1O@%mA?9i*p?8R{E+U5fa1aPZ5lpwoZ z>Q9q5uf#Wd&3a*>c*yGxJt-1-^Lc((u3nV@DTuha32ED@lSZHuj1}xKP6Q7`dG?S-DoJ3 zMLy=YZYfdVyKI#oGKJrA9UDISL)d;@kfiE2-%#dSc75ds4>b2sPvs!w!)?+!g?J>~ zTDPSHvSuPq^p)Q-COJ7b#|0#vk~vmax--``0wIHx0%(6x8Nq&sLBIknExm&XcH+^% zGUNWkxjqXtb^=`QKFVow^6ZwaKfCL_DmUJ-K<3_wx1xTHhCvu)Rtu zb!XT%p5KU5eD&%YT76IKXz7QyxA#id6InD>K71z%l5u8 zcgLC@u{ES?^SvHaf(XUT2eYDsAl9q?*Xa?@FKiulv^QqM^B&Zvj%R|oZ9(nnNm+_e zuXslUSmUGbS-5y9eTSOLdR2bA=%OA5AzR7V>663l;ao^jZeszh5NCdrp%f(cg4*s- zlsMfXOH^o*>WJm$PNR?)@clN!5qwcNiYj_tX8S#?oLFo}Ioj8F8Q^TdUXyoEUpFg3 z(hJ$%2}o1^FH^Cbo1Oj91XWkR-SYE4SI41t@0a^o2B%LKCC--%j|KlFVKGUSm6i8k z=?V$WeRnQ1tF&A&73JmShEyfQ%E}mTLyAbtz`lwjxcgf;Fd1gTZ|Jp7egW@N+3gnrA zN3leJqzIG{jACUl(|U0YnL`ERmTC)9vEK_cZt-EF4Ki!d`0ZqI@T<425X}nEgHfsd zn|)|y|E}YFZ&u~)ys-8?;h8t;+-X$-SV2eqWfM#+E}^Hlsf{QShUgaa1~OUxZp5)G z+R^cFPfC5Z)+RP-TMF`nGzbo!Uo`|{%?SF43Lq%?w10$L z6c12%~@LW!=8=) z_EM9d3O;}OT}axmHP3op=I38&CzY$!0m?bAi*$ll5Nh~GMBM?9hM{t}yb9OKT0NH) zS*>4>$e;*g@H1031=`W_o7_SVTXoRu#OnGUipp-QR#Hq2(*C)5_@dL!r|+Ap;9qXt zA;tDM7)Yq5Sguo(4zBp>ZNDSO@Pp53c6&(X-YkLTBP;y9Sczc4PgYCmaPn}o+dV`^ zF4xMQGejr*b6QN^u3*K#g0PS(8Uj@a`(<4VdVTU&eG`;lkk>!S?y832uy>^tI<&M& zP>Y77OHm} zA;@%{2;M`|@r}5A{yN(Wv|QfWH11kyh#VfGECaWT^J(Lz!ltU)^t)Ak3vCG-DXb(F z=3>|C$c?jTI8HE}pdk3WbEnv3l@uq8P*;6YJ<@BA9Nnk?Hs3QHY&C#H#6)gJvXyI!p)RZW__s2H}v+&YicU3O}qk$~o58{Cvk@cm{? zXlfmANNK}(XtEfNv#2}9Z>8d)_KtE=*Y1pI-op3AbDZFzzTzkOz}6pV)p5BO;5bU# z#=)$yi~Eg+(KL+I#r=2tX%O}|$#1*C3$#VA=e+$%^Jqs>!7g<2$Ys8*Cftf zyxgSLew7y1S0_ok)*f78;~AUGRu*v-hVsE!HPJUhfM1&7!Ce(=pxbA7_6K4>`D-$eL_c7XqeswrT8qwm2w1iwskdr|ZqV{u`ozmDddXU|Mm zZ^i!>g>;qx?n23YhwN%*Tfo)>mG2`xZ$A*Xrx)34Kk+d1$^)bgA>wx>R0|aVdh!2 z4tm(U_L1aGjHo~RlbP8{1;-NNgf@eDZ|oMg*j0R@_51EGOX{P^&@Ga}aZt^`ku!b2 z(aa9J1fKHaSQ_+oCspy+rrwZUZ^+|ND@EvQQ+VsQ?B4t`>5?T*kP-qPq%kt4!5HLs zp5~}%#}@A48boh6Va-xTFs%B-ZuFzP41R>LW*iOEl+k75C`{u8aPE0s>Kf?lUeog-EoG)zrir6UOFy1YO?wa!3+0CCp+3h$D?!fA*7ZYww z79FY?jXev-jFfjgv%@xltHHWi64py*J-tDR!bkGGD&5yktodNy;T1TDQ~t;rZXGwI zsd$T>HG}Qlm+GfQGycm5avx^(IwuPNR5vPpYCL>M-@DOB@p0BxSYzsmP9W5ayCtcD z1HNoOZc30_JVXMs?|C%%K?0%`3^&*DZgN~!t6EF2<^|TTM~{$pNRfwm<$Xc@$~GGP z6fXa!fgZ%QMkscckw^q5qt>UEx9qcL`Wja3beF~Q^-}p+qPHN!xCrILAl6^7GF+h- z>4^-$5|~}s&&GyHynw8M3&W3Sv?Dn^?_HH%zNiJ4r~-`eaCbGpN4YVZwoR~al9{dv1EJ%1j$vhwh$;k~8B712x@0aff za<5*UY@DCjggqkxJzWsms2O$+4s^7Z{2=|5MI&nX*|TqRN{?sQHWM=IZKdYWizWGCjp!<0EIx z3qMTTs?wj8a)b2Tn}W#G6_eOZ87+o$t4um5*1Q!{=CiILRNh?tj1)nB8)5p5<2B2H z8^GF3%7ytTFfD*VkVIDfchW`8XUfqLeboQo_1ZT;CL^@Fy`8OJ1t4(E`tmY0HFajr zy+@C1+rF@@EQ8A9Vpl?d58zjwR)^M-oFFp~!q} zf_}{_6XvB`0C0mRWw*tm$ODO0^fz^&QsSVX2HGPQ1h!lj>Jdlos=u)WCmTl?h#(+D zQnx9)?=Jso86MUW!&K;|ym1Hq0LcGRwC*NGuO%bn|0+eyO~C!-YPQ=O9#K&`Tu?*L z)yUS=)Qox6BITM(BM7CVr?>tDiMZXBDLo3e&Q|R&D1(2~!Squ2ua{C?_d0AnfV*8Q zTz4(>a;B9EkhdKJ8C2*@xai*- zCu+~oslpL`1+@N6j@Jo~%{A{{L@B7SvD0jG=+sx&(Gp9oKvt~o%~CIp{Dd!GnCN?s zc2^q$V{#v}X+c!V8cdPWPZD)XvVg4ZJFV$uE82HKRMQ&++3pN4onoq9fSB#nhYFmp z@$rE>p!m(dINcDiRcij{7m!$)MweO);!VjgxX>^#5JB5p$HB7BPMY~c@^|ZS8Kd@F zx|2i#-oJ17T?VH$J$?K;0<%*~PYUp*Wn~+9uGx;2SPp{~11h4-j%p?G!?)WDU7v}3 z4-XIT_DxPQQXaSdCUjOVdDq8u68`H|eCn^t-67pgRRg!R5+I49RTgOAx)zd3{x*_R zPL76R*s&o$#gzQAN?vkyy5VIrUe<>m1WGPx*XpePKYzIOE433TR8GLmbkF&&x&j?l zD!Cv!-SD5$o3MT0=iqpizzbLgFfJ~g!fUxupvaTk%N)uHS0dhUp@uRUhxSFq z$KPzxw7bRJ20|wZVh1KKudlEFWC3n9@}=P^M$0%1lizCoN`wFsj`{pcM;McOcG%;U z)#j(y$6A7yb+`Y-f(zOTnHJ6cpD2*?3R7GJ9$EL~1WWw@ z$4)_t>AL31nMQfu*Ceb<_o=Z~i%x^w#jx3XnXXbjSx{r)f9}$%@Ny&~ zR%pgJZN3Jb?%5FS&G>L|!wC;jrxyKT%O%}51>1=Z9hbfHF0XR`YlhseQb{k}xPoxC zBHj2W_3EYE_ZB}6Fj-y;&1TaF{i+ewgJrw3L$-1ZcxJ1r7^?ucAOQtxwa;s4LsT9jWw`8(f!gD5~uM%li zN3Y}Dthd-Ol^Lk8c7WD()e@)Fdob)7l`7xt8|ruaUZU%3pU0C8cpgX{GvN^6>hRQm zJ$W{Q7^gzqG4EP&k(N|iWAbq%tdJXUvxAJXHMk-R@Tao69Vfdnd5lqMCn1A@Pepwl z-F7AeqAVg{GHCU5`x*$%r6v=D19R6%{}Dd_hd;pV-cJB9VqAfA zLB|Um%&yM5a1H;Rz#5_dw4tSOp9#Qax=Xi@ z@hD6%3=y&e+{VR2%42*e2GTVGJ_p!=Y^rQfo58(aUDQCj7$!2jHK5Zp(783 zh0B4`o4a!Jsf-McL;=vQ3bM1~E6V!d5DqAuq^71uOjI~pq82<~nYjl3G%hr3|6Crm zY81;hWXpFgD76)v2pbF%&CAIOa|kZt$-c4qz6L%Qs4a8|^}TYHKBlfL}+bx8Yak%yP#d z5C}696Fb`nz_-dtGcYhbK@-tGiE4NXrb**99s$9C;eKa49~}j7!kPW5ii$vG*#6SI+B_EC4i4 zOlUCe@sfTJ@j7!4y9x$mf~1=NYoDV;*%SyN$+sL+a)3BUq5r0I2P$AcZ^r2VK#K=m}jw zQZ_<08rCP~bXhAR9ABctUFyG`L-x>5vJsd&OK znuJQhmo2?3{lwctUrgt5GNwCwX;UBvrLvT}q8UKB4?}Cltozo)ej9LmzQt%v(6##q zzWx`6Tw|T2S*_gI^8zMMZAN z%+@;P&uoR5cNNn12v96bMMmLT1dVPc`TBo7VGhr4{c~W~r}WlRTY?+XlxxU{+2`_T zJpA~FcZ}fAu?kyf>rlQ90Y0;numjhjo62}%l-MZ00Gqs8>c1NWzA0eR}p|VSt z;{F>8)3|F1=hufel^l9DIte;mibh(Ht5Sl!)N3NVt5*wjuT1kd@jwpbsV8IjEvxdS zge|02&tgq}D{0h2-Q7=Q)Y1iHBwowKv{g7zefoP4ce7F7r>y5ian}^YM zG^YF_fgot@8Skh;h(9x4D-yqjV7aBQ_R;&kP<3_oc)i}h5Wc(@e~sgL$6xuhAQyy9 z%_*$JV(bN-ytq@<9{kKNEq-5$?3gGWBvQ(e(9@cGKn|6sK*sJ8anVk z&rm(PI^)#`x!29Wwh%w7&S!}#cv>{%xLn2-l_It~+O?JY=}FtbSwkviV4tKz)udKG zoaS+Qha~#-5^#65xhHns=Z7ih@%)yWGBPsD3eHmvTkyCh(#Vy;ESzFgAm^ct+GBO3 z1tCIYHEjy+_{@$slJmK9DBbBV!ln}PjJFiLGp`PU+qsLVXu{1Jr3n@5H0PGj zz15`n{K>zT5fR&+;eDGCga@nfAHEs>s8Up{A8%jAJzX8dPcq&kE&1 zA?|2rIWj7$(XDmaV~H zBO&+*ifR5)Zn5A960>@PVkAGoNS$=WSL#4;zNZ+f{XG)7%V=y+)MbB!J`Tb^y zb^@ieUPYzB_v|5_XKmezlzn_XqO*&OvS?o3LOi2krb-!n{-OvH)&_&$1qhF@jOShB zr<`Ii&MTThK9ENwlVttTFn5a9im9;j8K{r@qqeP>uUuEFP)|8|->;9m^dyV@C890R zZAujmL|v)3d>@`j>mMRSeP$F2anV(3o}?ob&>kzfq0RHV_qSuqw?sp+#+j0kN;@2c zR{eIh@zkA;bl2iR8q+&4PH3ig5oy`_`a0SDSG`S630yW2C6bgN!1%Q$Z& zzgkNe{Z<&D8bD!r6pgkLkI})=Z0<{Jgx>TWrt%ND&H19K6N{szb(@q}`*X}K<)}sL zw;vqrcyA$;!I`)Z*jGX5?69n-_2;8mADy3ThWPV@4PpBYCHS`0c3mkwUKta%LEU-d zo~&hffK$>JVKMbR7 zx1nyxP&I5i#2*0@F0zX?3^De`_A^cJ&Gb&_w%XS?J*!OPT%$K{5X^wQt)0{3M^ zY!dtLmW#+@R}P*wtFzNCv`gP?kX;Uh4A#!B@BlMJbu7&%Ec}qJwTHCoLg8 zK(oQ ze*1s-^TJGEoj1gey%d{ztPqW+;}2` z2{f?{(6;qa6M92)JFTv^h2K9|r$Z^MDX-u5l@Gf*rNQ!B+M0!-@#RHK8kSZFhKO_3 z>AYZunWsSu1zj1;c_2WT=dc zjM&R8%+0;|T2Wr!l_HTc`^@eO8AAOg{&o)vMNhT`JkpJuH|y&DC`p9Yfbi1Z+B$Wh zoHz%d=-+zBnAwdGBC1Cpm@n_jTmpsmvTJ|3S^;8rczf|=Y@Ckk<2oYr82rftW(FJt zyG>3`o|g0(P&D&#mrYfC5v2ZxLr60>H)kt(c^d?fU2W8DFJvyJtNkgUOF{QpnmMEM z9kIeR>g36DwOp*i1-dE&=}IX;Kx0prktNo4XlvV4pZm1DQg8K-w4IUGHT3A-AH89{ zTZof%hyQEsI{vpcp7&jBynlh(fVB(bV+;V`*wcnWqo_oW=t8~`yZWF%<5j-A9>*Bl z$a#gNACx!Wxcgg;w#mu%C6Nxmv5l2XF;%pA?>!*G#cbS7**^+B6!lY#DPoPxj=b*t zZnY8ZHh3Ix7sZ1isLdO5eM&rDDigfGi_a8QQKH_m^Q+~uYI~Qa!#pi84>DoT;uhUN!xjDFHJ@3_5eujb;aV> zW*uH52@b!(uqfd-r|K7zR;9>|>;2Q{i4w5zgK6cK+E~_!_NQW58jQVM+NV zA1loa_cJ3n0LunTqPsS@hs%~H&dNmt@1}SlNXtm~*!n>bGc7>9{oo|F^g8zA)=2V5ExKMQfnA*2mGy>VJHxw5;-&^ce*62IK1Qh8eEWuV zM`sOXRVN#{V91oxeNc`Y*B)`W-3Kd_M7KNLrF?wv0Sl)8B*RC8HoQe!QayEcTp)}* zrflNUE9%v05Am)~5TUhDB96}-*HXyb|AhLGv#(#|fQS5Ob0hE8qr69O{8jh>f)lizt7s^;F=;0CjjqOIT6Lw+CVQ?d0 zVTu}aBYoa@LyEQ@%A@tG-CgQ^Z@TTF?dZ`)L{8@HXW<7|)`t{p&h)P}NbIO;&r53X zFn^LWzZIl8#FCFsJFLs`H_ItNGQ*5EcuegZDU!14h0C{Jo1Z=}wzZ)9uG=1c5FYTU zF?cU{w$IJTDx*wGiPkQMYuS=+dDjhpfTz4kY(E}AmGK+2E5uM%*b%p=~NMQMw~ z_+BUM3w6>RLHwLhb+EtHiCF^^AILX2aBch+1Mg53jv7ZzI;%Isy;%Gy&mX$?)a^Em zN6PDxN18<6Ks05RzcYdCKjeR~kCe`%OBvGmAmhn~x0&Byciy!#lCa}vZctIamHxodFhOlP+>}~W1ew=nJHNQ~ ziXr(uXM64p4gT^S%G_M#yVde@3OAzdT7BesocCLY@~g!l!Kf?8=2WP6REEn!H~e!b zwJ9iFD7Io7xpH_71@GPaX*~wFwSvsY5-RvJz5R9In^2V%I#%?_C`#9&wpPd``&mcf zl?Fym-de=9EdSkGOMUt_!te~jFyF{*adABHtWvTFv-xYsJWIcImDoXjq%YKe>HW?R zLS62BEANYb`rkNh%`~lKebfX8+*{k*?dfeO&u)t!QXn;A2f9+PdbqNMmj`QjCSD2a z=N>}Q<`a1pcVi8c_D^&^vQin-5c-)F5PX$iMlFGr|M?^JF%7dL@IXtVFnV35U%wqs zSPFDWat_9PG^!%#9bwHaElwK~RWhM8T!_{WJ)+vi@QExIKg0J^XOdEq2M0_sX_R=`&?@Y{Dq}PL#dHSjg-#k`@Ju%wUU-G3uf$_MvdvB@V7Lkiz@}pEDSDc zogNl!!&S4XC{+YFI1u$vE!lbI=5EaB)@%!e73u`?*UVh;pPq)g$(ETW@rLj3=wH7! zADi)Bp5>a<%iS+EwVSfR1d_UL#C9K6J*W+URsCPG@HDoW(VxOfUuu;c(0f-Tn3Lfx`P zEyDwI)+kFXJ8IUD;PPd;rloi*_WS{tO_y$f#8;Erd*;eon=kb5ekXs))IgEc$d$kA zP6k3_l747!PxJp_C$M5IY_NXNiBu(T?8p9fJpR{9&g~n@s9O}8IN-QNH%1<+SBCN3 z74awGs|bWr<;}V`zA_i(!q#%?{U$Y5YU5OrK7e(_42q(X!TsvTK*4s>cX;rERrj$T zpZE5I&sxP$Dj7i<8SN({6?`U1AvN#K(NCx)XQ}9BwUsSR?fAkHg5s@-HQ%&`ZErp4 z_C-_4biOOFMB=W}cO2M0vN63qBDeQF0T20vk{XZb8xKKPK{ZxsSilRq56M-o0v>}G}$ z8p(EEQKR!Qrt?u$ceoFgF&egVdt~+$mKZ*4M~Vm|;bVh!9Vuu0r}gFn<}mMvrgc0O zAK@o8bcXbTKcH-;aIrYYR>ki#j){%A#*HCEKLkUDAKJF?UXIA5)GKb=j*nu@hy1&| z31gmG27SjRW!K*>p&tO4L{{*?&8xRSGy~+d9qfX;E{c#r+t>fOB(VV0?|uqc>zhgd zyI+p{0;-)aCnCwQ5K+M$gZc7y0EL5Ntg}RzcfR~z4DP=xn7Ryxqr7-8%mu-VDU|+i zR{+ENUnKD5Z~k+Ih8_H4Wla|nPRjfKH1l}{8FPpQd7UTE zpk|#wtn1c~A!YX+kxcNamXDPW3c-t9z#XXvpxB9d#Xwp+E~2lm54@r!fEEef`BGe5 z4Bj2%<>hrqdZWbs(B4JV^W@-%Z1@b;#0=Q;;EgNVKnpH7>Hepq17G@WGN|D@b`1jj z&SEr_XCPraC8=KhwvDtzTIVWJWmT2R5KluWtzh-R4F0|DWHFHkTBAfF0vnID}4bzu8x@4`=wjWcUf4PDt^z7-6SGfA1meN#l3bdV+4p5vo)Kbii%+;_ootn zeu50wbM>l$SEztj#7u7!|GLM*GJU1;W#s)Y_{0(?o8WLVd$`n-@&^ya6y~l;-B(Nm zZ+scTr{uar?_BREI^G5f0Uw4*c|c`-GK$^F$?4~+yG9WKpv%Ewd7ID=p{;&h!VZU=pi>FiP0rN7$)Q<(M6OX+89KM9=!*NPP7m`N`xr!?BThe>wT~5 z`S5;ueH-W4bIx)0+Iy}4@4qrKAQz>KI^E1d%*&Z-l9G~u%6$$kb6LP{WC2febS?k@ z>fDZZd0qphX4fA|@u<&RPm@H)a2X9DRAweJmDlEuo8qM4mNOGty1Ha$y%v>AOoAU1x)R0g zr7wVc^eTamFrZ|P0N>r&ZDX4WGPN1H8RA9afK5zRR+caxi`@db*(*x4!I1q6g&dQ? zI)87sH2_CkNH|*2QB+g}U0U#M8{kd|4Sc{`S61reanqVyWpxQ)h%DAOcz3>kDmE?V z?C%-VWUK+LKZuq#rfRl*j`p`=>8e^lCxngjdh*EbsB2`Tvt!^D)(e-n5-89iC915c z@kgQX@^zl`8ZjlC!toDECl? zD#cB`hYMYC>DQ!TJdhdr;4h#(LuiW7hypguTC0suH+g30*ZPF_%9l>hB9jq`RQ*S6 z_gkkyXPl-j>v`U)NRnGU(0!Gq0nmdx&<90xDX?%ZXVOl&Q3uza8eL{3-EppRKTeUWrn(^G6Ax0 zun(f7=#CeD=4Ic3(faX>gb@=xL22^q2x z`#pe7@s?-FoMH>PPM6g`^aUw{)xh#HzWam$tS*% zkzsE9U^;HF=yfc!(ImC0o(baFh3YRYLE0lDJK1OK!f;-cxW9{yUC$hG{>sTyFalxB z3!Y6L2Nu79ighYc@xnFC6>o3-!B=?&&oJ&#o0969Hr%~uro?49QGbOSn;)DTSsdzF zw;s_bOGs8RQ`$1KKY00fISr25*=kAm+YrX&L2n;D?oJbT!=NH(>3FPrE9`NDS#{fA z-C(&tQ7R~+pS|&srZ;)}rt=Gj_47gbes=x$ZN;_x&}Ws0go7DS-tnM>e&E!CyrF`(JkS0qb5|K_@&4q82 z$AuF4S3KF$UJ{C-vs3(?sHsKGx>i*Ly zDG|Z6OD!Wrcr2afTJRT_OA$fM>T`Lzzj`#!WG|cK6)5 zUVKiM{XJ3hbrn~P01|S3fI8r>-S;>BpITp$DtwcG@t}M=Z+pn$84^7z+-YvMF(6t^ zlpy{1P?yTfe|~-XC5aZV)rjU>53yC`LK+)N|s|Ju(PEbS4!N9 zT3T9Kv87>i&Ed`|^ z%i65Y;~g95mZ!}$?ni0sxh}vK85ZFK!*!;Kc%3rUvaLA;Zr@L6&*<;-fPOi5|>zMrW;FOqov z@|+ki6NE(msmT5dZPnL(wycjd_P*_o=;*4~w6@@&D0?%K`q}I|Aqg{X>6h6+#`{72 zD&(eKz4^ZWrkX7qB7H1w1WiH9mOeH29TaO5#T~mqfz8b=2v?)=sir<+h*Lg zr~435Q_r722fNs%U#0~69Ux8T=jQ>%QMrvAfkE3kpCmFxgb9qpNJ?j$jw1Z*$h5yg z;B@HnbJxQpfsQQ{$6WCMdpkKQ4Lv#_RvfLKlx1uKv>w%PI$p41f!ZI|h0U4RPWuWp zM|w04`ITPDrJ>lkN1Yt|6FzH~{>{(}S>iUVRW<-;Tr*+!@CL1GWM}F(n`nS*O}r^% z5P9ACF5~rsrrp1vKjs%5NXo}dJ&1M?#fEwEap$OIA|oTLzp&JGq`5>{*S=j8Vn%ze zPiH1=Y6(Xs8oLjGae2_jzyw(YDk&>X!3hbbz-5(9XT&wk^F!)h7rEyf_UZOS^4H^- zVk9=WS~LRYkgH<;rGb2J9j+=y&wDp)ooA` zTL##qi_5##AIhePwWbDxh7?aKKplujjjv6Z?>C&A-`%V9LOu(rS(y48^<1tcR4ubQ zM=ako?Iq1Yw~MA-0zE|nuG85=(0&0IVNEk`GD!7;2D-cQ-Mw}j_))TqSwkpm=LS)q zM_{FRcy&`m%8!AJCqmqV|>t7gLmqV zC^&|knZZ`?O&Tmf6S3%8S><|MtMVLjwlwBT&Ca$}_=<_R?2WbAm9ygbm#939zFLwf z_`f$z^MLVB9tj)jVw}?EVw+vnvpko=tcGpA;!W zX`jkaquC!gPk}73(-{k*6G%AleyL6PA_AoJZ)g+P`v{~J0m1+f7RZ%BUTSjU9{Ew` zG$&5|wxFQKLHz$9PyOFCE$U$a9Y1{VfW!F;(-u>EH~}op50$iUdk!XuEDUx-j%ROe zYzLG(pF<@h=T!-bxp5Pap->MWHe0QYm&5uPJIp{LvIY#i{`8Wos8UW&`e zBxR|bj?dzExL&CsFE34ZdXD2;31`y!>*P5bS(sL6 zMGfcHJJqEED4f^@%Z)?5KfWOB_)NstDgw1^-?y0G;YP>!1hSsKJ2)EqkwaD9zPs%E zF@t$gr4Em5!j{yv_bS!we(c4)F9O!g?0+aJn3DK^5Lx~LzmH^g-luNL0<^Je7$DfD z`(Ub#K7q2-nf}ss=L?>%3U8;|-wdJQr%91iTOKr79=`1$TG*eY`0hs^iopR6>{?PM zIt4j|qK23H+Mk6_piuZq_bv^9rlf;gUO}{{8X}n+d42L@nXk~w0K}!D&jgq&eV=6X zo5M($0NB%yr43tmg|I`F5H{*?$m(|k`eogVkv17ur32?3RjxDdT}0T(mUre0Js?hB zYCKrZ90(-}p?^9R^3Z|cC0g-NK_1-^8~)? zPX(hd>{NPH*G&UtZnRg2rOd}54hb#a3Qrr^4&E1Aj{0TQv6q~co{7j5hTk*y8S4nR z{xywGi8(qKcjM||dZ2f;iZZ-Ps2}p${4-%vR`a8W{N1WX+q0|X+s-`%F!!IOqir=YaEbq9%HEWa{rw(kE0=t|g z7yW|MR>kGUD0Z!->4+_ScVz+fMwDu^dC*N&c#1-q1 z)U0%oM5d?nP!I6qj?1S>6~Txc}8ddNNfY?k}}n=FZU1;y=!_s-aIxj(qj z?*k42TyovXl3kW8%8U#*{XP<%H-etlhW!c(7{-H_zBSVe#?Z8EMQJ{@9aC*CT@sep zUH2pA7zGr;D8+BWBhLk!N$rxJ2pe+=YroOn&Q2-_L?P%%S}7I0?v^t6i>Im$#?s}u z7By!mg=qntl9$9bg)UPru(XO~a9Y__`j!~sX(pAX)v6|X-x%UF*yM6&E{TS_6SjQd zZ993tHE$PDQ5U}bcZ%67eukQ`-FH8lPz-ZN&pal!@7k5CSkp<}ua>~4#Ked>cD(}O zXS}(YWOE_>C7AATYV>O{AJ=M2)oAvOmUxrZ*sx7!)7DCTqCZ8j=nxR zTG{{riN0^xyXMl*eL8=EQ6d$Xy*A4_vJEOERs?w0a&mG22l^fiRXuH10NO+lVAgm- zI9MChW~%|E1Dr__^l{fqB?IlXY7X?m4)H=MkLlJ#QLS8N=Zl4;Yt9-ZNZx!#`1=I(S?Jx}uR#V+vtX=#6MZt9mD{`{=x?CiYr5||f^ z8EU4cyDBGivYXu(rWkuSVGT?4?>#!9m$rjgZ z_#5j#7LF=x1kn$FMciB#LX*`n3S{~X^si;WHq62}<;r-H%|AvStule&3c4cuEG9Vv zs_qm0RnUe+0NA6M^+=!Zo5}ML$tV3qZZdjYmpVPHV|V>wrE)_+@K|A0`?WdWYp=*^ z4DwGX`Zh35CV0c6cb|ZTW`FiAW-7m;g9zG{J0&DV0$=-MbK8BD*G+k~7rN}dHgutV zq?tr=$A+vPQnFgbldP@+Y^z{lSa@#jEJgD0!DErDmAkQ$L|A*p-UJu3quzL5_F7q} z{m2&55gQCx3>o7COmqKVg27u%!_NjL+oA7t_`~sy%G6Y%*R^BO<=3tv#0F~@ltLW z%vBmT5&7X9$PNslU3_*dZX6toFI6Ee5M~!?{{qqqP%Z!f literal 36232 zcmce8byQT}+b>S%UdO1u4O8Ho%DnZds+=sYiOb-bwf*9FJ8|oNnH;|TKU7ca0K`ky;HZ^V^Zo% ztB%&{U433*u>?@aIlcQ!9(oykDjtMj$2@`T|HF-0s?ejOBjYb`F3|~^M~F>MPHt>$_$4J>Z8%z! zc%1)~)ESLZYzt%TJ{qrf#K+<@ZY4=-bC9UasaU9H6QEpO;TcT&sH5Uuy&_MKAG?r*iOwr(CCndW_&`^Q_on$-?4ubn|HJA3;(CC|T3 z|NI#|<$K2Hd$L1IrTFy2#$m5!bkBa@B3zJeA51Z!(+oceX5t)W-4cE+M9 zGFfV(oaZ~=m)K58kJq}A@2yS9=RYmbDAJdDR_oGjl=rmYt@T*N9Zjl06-P1qAGkHH z>tVDIee)9Znjxs?=35P=m*>VuBNn z_SRcQMy}X4o(LmMyNS%q%mM-e;wyW>cVNg^AzNZnQng)SDYp$QIf7(K_uJ64HYAc* zEm2TqbGE`}f*#yIbjYJhpmN8F!x934gffWTp3KaKKT=K45b26}2VL4RzwT4G5 z>LFrxZ?7%%ia^wH)ul_9_7BlT>_1t^DEAnDb>2Ja(_ zlM1$g4-{bd-rsy;w>-cF#*1{U!dgCPTJ_ZxHwu^%G)Rn=z~N-Lh753{qpm< zpFMkqnI_dZ-h|4e`|MtO6Rw!q1CP*X^zlxX_8zU1^4wjakW1IhQlgXbk(~5gm)b%| zLECn`P%lOhY{|*V5i-6$APVF(sla$lZ}rM3_auw;m6|;&LSg(7@yU3_hAvffgEC|8q}>XtUetBa(~t7Db?mZ)2pD14ha4I=xJ;qtRFkuqw=tvgz)HO0oq#Z7nn^~1$;FwJX|P~2%r7MhcJLjiSq zdfJ{WCP2m@YN+qJGRzMakqj6U@1tEsFoxZTCwS}uO)*@Ly3q7O76H${zDotSax~#sWzybf>F3NvajZj;cu7S_?uW@S?Uel{_(k{242J5c#J$GOFoeKmk z6ciL#ikf_`YtQx%55a8J`JCvWD|uON;C*`s$0wx^@H*IJb8ARMs5AcB*l22F_O)^y z8_d_z%zMnWwfPF4Tq8?~^n+>~876kp(wGbN>n$erOHI|b24_+3XD567hD}&oKJVy- zUM3~!CR>M~51u1)(tO^=^5}xsF-Un(fu~iDFzKOwSyzUHgd9}KobI!dF-gAp@iin1 z#31N8=~dw`k*vzaX1z>ZX)*@?v=1zv@Z*E6N2nKq`uh6qX)=uy*2-ZFcRt>G{~Rvk zwsFnpWJjxRZ_*PpLoO@BMzND1*)T6U3R?`cORg}cecAOYfeqwpw#V{ZwV9}S4gP+f znwm1L1+knn(Go~155ja~`tyYtZY{F6ndw?Z`uo$b$nEyl^kR5)Y2IG<90sk;LfPuc z-6fKR3UEbrM03a!_+zr=urREXDk*6+{ysbS^Lwl^6$r~L28qCv3>tmfpW{j&%v}x$3L3Kt=SA;V9Dz8ekeePd@KppU zFoV-UUr%o+=K(V(5lrn(B$zL=?nIb?SxUqPndxCKSVNda==JL>?QpFt1O(`tjSV}v zK_k<8{a$Lf*DsKJ`#|Jxjjx>??fvRgW;WX2m;s}WNzIe)i;P>v#hIMLrDo)q9Dh~B zCVuFv7Rp{KElbdLk^x$3s#v67|J*7B(=uI6su*59`YQsaWeJyk!K_E}8!O@B)f}l+fLT; zuO-q*{c>*@rlg?o11Vz$d_x#L_`Lw6%w~{Vl*5_cY1Izy@9(F}`s2I_4HYaxk5>y2 zQQtJt&xRB7lhs7-fT6fVN%``+;@fo4w2j?eC6LM)v%w~i`zVL(<;$0Xw3k{wIAOZ= z%EqML=fq>6$bk86rr~6rJDsFk@q`Skc?#iW_Tkm{e4&>dK=QB}t5EwBu^-`A0)q{- z&D7rTr^BzXzh~#58nqmf)DY}Km2=}IOeuL7o^2KiAI!^e_B$CxuE%T;dp+5f?j^kfNA(wqZH6^ zzw?Iw^Wp!YH~-r+F2GAo;{yT^>|aEelu+jvum4;mV}9})(`3-Pk{h=vU2XMSrWV6y zmPfMokH-1Os~l)%$k#2Wgj;R?4rsD;)ddNfcdhpt!?Y!8h{3RjkY?2t`o4yM<}t?%uC&{!Ezg^Xsf7ux#>*ck6Q+-@+X1 z9Pn2dr)b8M+@GVDgthtMj6FeXsMB#0s(%Jkd>&!sMUJa(700O}vFu*7cXUO$64HNs z9Qt>_!T6}}DkwdlsA39UQ)}bE&hlyXFBd46(Yo-TK~q1qO86P#eR;GYGBoZ;@awK- zD%swMa5kg*#nVp*2iZFSb<*Kav?n^EI+FVFD~#K|@CL8*haVFnFABHx1T&UcdwE(h z{(1yI3RGWz^YN9Gkj!Ek)1OIvWzuo2t$t>hZ8expTFu^NP_|A~_^PN7v#1zt+z@$l z;7mcWFXE(nohNH_T4CC>yH}#%Po#}viCwB9)NQ-5@)9wvvRdq7t{P9X*ODK-<=;>2 z|A_bl{6Y5x^`5V#-(Cz$7fQY4C=dU&e<6Cz7)fH=-7Cd&{uunuQLGX1e*7bjHYy9N z5$QZ3;V_F=Z7lu%0wI0^#2?`glsgVT#0aT(VXJpFFK8Cn+uaMIYlHHakur_f7aSbq z4BGX8g#`B2U*iXhL{G`X&lF)-PbBRfK9bv^m_=(vZ{`)#V&yC<@4y$aI7}~am}0k? z84enfGbAp+JmG!%RZho#3q^NyNdK%os9B1nn5Q`{j#AqL)YW52d&eQs>2b>GWZ#(L zlaKITUYwp=Z~$U8yRWC%2+QeRrAk_`ngi3N1RYjCcyr|8oW>R!cu; zR7d2rP=ATB6IxWU-q}h*Qd&~r-he7RfN`+)X;4dV7ruJ$1@&$`bsZ0ss4%G~yQ^&a zlNZAkk*T6}u;BmNvJq)p&fPHA@lt((GPY)Nb^IPkc_(Fp<7tyve#V9Xab|}{HWW1^ zeE59UPIcX5F&;JC=o0(yr-*_6jZ_R(Sk<2xnR!|sRZ$&e zU1TP8SyY-)RG3=y_7!wsxuAhng?AapZc$PJrgJUgAU|G!rL>m90>it%l{veFTE7yo z^vVhr*Xe=a_2k!EqEmMmrsv?C&<7X7)Ud}5(DA6y9N2I|7eCwmwof&A*R*_7Jv8o@_vptcR7H zK+A?Um!DRK713uH`=?`9iDnVTk{(`)j0_j@CH-#kPfkY9AzM4V?d;wv4(lo%zrj!P z#KN!hkMM7rk?|97{Fq(@kJ5yB+jH4{W)`jFyoYl1@3$!O@ED-jH<@n7z2?MV=SEVT zLmu;<$<56)Xkjrm_TTaM-q;gmjOG-hViNqsSnJ{|?- zFu2b2+PEcvX|u@uo%!pnkWuVA-t&C>cbKO)44VW=M8$DL`N>2@hORFYYdQ|*a#`EL43C|! zHU70?a*E@tc7}e*mC}+5L`QGZVs`s9n?8MNsy=u)V77phV+K|5en!26+ayRMDo8mz zMmvnc?tOaV_eR6FyI1UY_5x`~_F%Eu-im36u=6dmpLgTvZXx->J)@vg>;SU&XG}~u z4D=)nOhoZYILb;Flv}ZtTd^{-{91fuH)O9BGZ$wO7f%oqn1>gOm0vp8*MzSEaKPuV z*z>Me`iKPV`FuqXKtE7$sImU(>Cw7ovEiMrWU>A=dnPH50DwNG0oF@wtk5~%0xxuq z8dkd=i8r4l{TSTsX=<2sSo1hr*OXfEN5pxXN8Eo?^vu=Hua1tVs&QPVSfsd;wm1jB zxb|vsDRFVu<>G=1sfFe*-@LM)Du*kE(Ci;9rgbB8)KlbYoPK_*_jKK=`SwkNghBM% zj6DsHPC#)nALm=R1n=YAh=n5idw+J}QNQ3S2dwZoVeH-AP)ZjAt{*3js$9%;hSau( zzP@56Oamf`xxUyg&hIlP*IsKS>9BS+zNj1J^FDN<;(pp?T5Z%4$Z60}=ROfYc)1v` z8Qc!!UBtp~yd!MR6x7ukJ+I~`B{d)6W|+qL;*5spB=g3E@*1uDK#Kn=qrdg)rIK`} z>u2@!MT;`thk^kVRSxq+fXo1v9FU%V&7?g7+jVW+Ka=oG$IOf6WoLzr!Vn{6BpqV69CH-Z7%kP162Wt z7yw?6*VzJat&yw2@~qD7(%I<|M5oe*R8ez!WhHblfBvLxbqAiKmJ|qxCF}L;IDk(8 zuu3QH#0f?NaI$LweB|Zi%+d^Z?%WwY)Bkbh59R=P0(Je;82*or%~ppscURXD%gKKE zPR|Dqkeh334rzLNJdOin^&1Yx+yT9ZG~q=|>Kl$C0PU}imbZ+Qn)T&tK{(&m<-*N+ zQ$>N!QdeK!eYCqgNDN4jaj5YZHuSysH)q0SPWd3cX)?@!hP}+xHu2tFfp+_n2mi&c zP_qKH>!-J8el;HwB>f^yEqK8TtIO9dQy}y(TjW%6{emu7-7rz{R_L-<98OLoD>@D1 zd6a$sGvJ<~fOaH!_5R^6c_g6EEA5qRT&97%xR!Cf3U8`zq zvs_k3?Oa>}K_gDq8_z6GJ@4NCJ6mFw)|>mImlSTjz_GFn=TN1e?pv|Yh#hF#lF^+R zj1_JYhLk27*ld3(73$t_yThZ||%iz_=@(Ul?sOUWKiwa)L0$AY^}pupN`4LbGKLA%nrb>f_#O zVtw1K=29Uhjbk*+a~tP?Uiiy52U9bBaEmwbS{FJIN^Yk)IcmM0BcYK?4Wjn)9qSE8 zybZ^js#^{wCO1F?1lcwmw3;^Vw;v9d7-Ps3m7;%TrASYzR0tpmSvutwZpYfEcOw1# zu(pofEaJ*DF}q-vEcB|V>dT@mbmvt%aaZGkWG7d5>;y4~hQqoBj66NPk@eph)dWB9NO;;cuFn zWEGT@GEKV^?HnCn)z(Td$@qk}x8EhXavK*AxWJ$w{6t~vw?NmzguD4Spp9Wz)aTEi z6VIe6>Hk8{1<<)A#Pp4exY8|t)~@^>zp``lKO3vss^ah*cz@R49yumxv~hbQjpViP z(N`i`#BjKna{o=f6@Fp-m8R*}wSA6jK5jhqZ~gK0pHKV8E!F=d%jO+>gO&*Ftc_88 zP)rZ?^)}g3aDxyjO%+_)=eb6CBCI@}b0vq*?z6PjWI40z8iyBI(m#Z#TO2*7YVzSF zn`PzmrkC|}#lLLtiyqU>k_BXK<)JsEwuu^o42-8g$1PD)1PVJo$QoODrCscG<@A{Z zGY1a)kI|k;(PDf0r*NH@{HIBM_C1%}4h-ZPQzG}hhb|vi@KByqD5URh?~93`AaM^mpp+yRRvhCE8PZ$EM066LO}@kLf+E+~1rY zr8aYDujO~vEY=*EOPo<&yeQf1$1}6<{YbQir}4Fo0J-60;(()8@xZ^>%TEF4k*E8G zQXybBG5-9TR@-d`k4E3iUy*jc@j~U)ot!5kGo@#EUN6&(n;OhECQqnNTsbi#Y`tL6 zoI7oIUu=^~TK7z`je3aZ=%Vziz0{ng##uri;x_?{U;t`QdA%+$eKu1StrK~jb(Utd zRLMfpxNq>;wsdd$``4|+M)nt6f5u}W+4;Nam#7K4@%i}#AY3896UpN@Wb z{})_-J{I?6Zeyf_kH7CR%9>(6fKvQJ`XS5w^qJ!xUTWd(wJ`aR#m7p+SYif zkbq?5lK-7CCZlPTGF`D4L!&K8o9)%+Z>}fu+xp!7_C5A1V{sm9jRkv{h8Y6}R6?Rx zceOuU>a!QOCZziMeBH{rmiFl$H@s?>2wBvT7N`HdnTeJ%kSqj}fzH!47dpCSn(Fly znv-hON@xj{`N~|&l-~Ukxm2~n4C3~4`l=CK!ybR&nolT;1S_BU5o?k=dg_!!erm_a z6uPH>L?K0!{3RpeLSp07Pg3ww!1FawFF1{9Z9liC2gSRp_h>@iE^9(g&>x}SxzF5R zKLI&&(D`6%v(dl^8dFSb;NdTQ_4IRS^aH-V6~`{=6B2<3r$H~;QWJxkIhcH2`j4$; z?A0XEF){>4`bTbz9nrP8$h0zJ+Ty8@j99DRWf8wm{CPELNXWPQp`++;F{J;lFdoPb zJZFY$35)T*YhpC|#oKvtdfvg*L;6O8J6hV_vqGrx7^w4!&{G9M&z}a68DFTrSH<+P zV^>Jj&YLDaH=c$7F6GpCn3VJ!VJnjcpSX4~3;OdIYZq;TSd{Y~_1TX~Xx+VIHMu`D z58z;Fk<){#jXdISMsm`O)u6RT~ttq4UMYfZhO+;Qgm1=E^;KNA8$9$LEhQ#tn>Udd)YgU~?Rl1rZg z?J~JM(FQc^XJU6^W7sGD|18cQ2>UT!l2IR80rle`Of>Z*m~G;eZuOQdWy14g>n+DZ zx}F|iX0s^Q@}6g-(ev=fqt!P&tzL}xTo5x&^9W4P)OsZm>Y;r)44A3gXWM6JdiFWd9A!tNUIkma!1p17+KgUwgOtd8O5myX)yWu zBq^xrfz&I5Y56h(C&J6}X@(D%9^Dt+#+@a*Nmpv{IXC@o=rC>Yj3<-v60b5MOmCom zNcszexYuZEZ6dKT_7guQlIb*1pCqM2$e%Aq=3rK<>iXffK-Z(iLP$Gz9j!sdUEOzQ z?{m4EvJ~nuVI#4};MgM>UB4WCW~87&H_B7@COG54t@`CxL`v&ge789%eN3Us!_}{# zRR?6IVI~uOXBT4&Paka!-7fzFz{TI2{i<2rm^_4JGkMbw)>hDyg)bXHsy4>G9&9Ma z%{|-wq53qcfnqRA8oaDmQlR%qzAGe&!C5|rFz5%r0V8r%iLs+*ESXi*W3Kpo{C@A4 zLYL3|qQ#C@rO3Z7 zdwyU3Q2@HXThR8_JIhL*`F1eASw?<3;YQf)0A?be_evju3$(5 zbqJi*bLkVn$Ekl^iv79#9SWc;oR%l&sveL!g9Sjt{%J{qNTjod1_f}pG%Bp1TQ&T~ zt?7Uo1l#liXut6ppV|Dh#+j zVCs;7zxRI)u>d1X)N>cU&`ADwC1TniJ%9*H4d|>(gqu7`xJDLT)CdE}qN6V#A0KOcddOx1dUFAv%xQRyQNkr1pyhpF3Vs8o0Pr}+j*~$b z6#_}FOfN5E*wM`QkE&$h)I(x76#s%~am$m|)c=l&_yao#gHwTl!Fu~PB`|TyenOSk zUns9b$pH`qL;pIn9fJ2H|EZ9zK*D`sF@p9H5EILT?vxm{T;zQF5*R#Lz(-#2PDx48 z%zw%w>9tQG;kxz(0A0ok&&8e;z(N8J+>x<7mi^ITlcg?Bx0Dwn9{-KtMflEDPekG4 z>FXNie`^6gQJ(J4l5cGS-w5MjftLXN-*C9tXu*1Gp<6Wq4h+^;lx+75f%{{6Deu?T z)~oPva=@zq+lUY_QwP8{u@Y$QQ^%#HrH-L9&dRF|_OlnoT~^)zq@AH(?}35fz}+&P z6sdtTG9vrLZan!TxF?7jKR^NVGZ2uM@rj9ch7hFm3uC1N7{-mQ;3Zb5Ph0K0yu7vpr48;|?AKXY zFI~A341Bnjjt)Y6GKQvl&pi#GNJ#n|(*bJ(j7xhYYmi#+Auv?H1ii`}22XdP<$8z< zyvl{0!MrD+!a=$1?J7gm=3et}?GLPcPeiJ;w|L%@d{Y`TSy?R5W59Ej-qF8ynD0zj zgoD^bYoKI~H!iT>kM{#CJHvLWVPkVs4lGj8$Jw@Uj4A@`W5e!5A@j+)>I3_knHf%< zGEP*1F1}*q&Je6KhM!zwEE+ThICG5k9y>2H@BRd)?T``*kBF)X;~82E-n^w(GcZ%~ zTq5YhalPGd0Z_o6y(0f}5XqI7m z0_z1I@aWgzfk{CV_#||G;3@-*+X5gz5>KrbBVzsrI7rb~>&I=%Kyku8a5jZ(gY^Fb z4xlT0ywf-79uEA+UP0M%T6+JzOfLO>`Nv|xq)MOjREZ7i9{vE{xKL~NDVP_n63xy6 zA}XR-%pbL=nAlA1dV@ewY3vRVSt3EMpcA=Z^iVqaH_W>q3P$1@xYN`q zACW5I$_s9K4NQLXNhM4Z?w%Vys0W45s$W`-AQC)>*;He{03IDF;c8>jo%p`qBIU2g zfd#rr1+o}M;89ms$C%^bVR$UKo`d2T>l0&$fnA{}x89N^Ky2j+n)@H=!b&&1lD_zPo>%LgaM4}N68$3 zBGqt@7PIV3R4H)33qVg>B0H~!hI3i63xNp;_=2C|d;t!gV_XJcV8lM=@%J21 z&?z#C+=KVW0^u-KB8GU54M~OcJJkM_j>JwcZrT}#$hPF+{Ol(#JDWTOG?u#kC{~tR zqym(y@;=PC9N9mjUcZxZc0znco$+39|`nP55|o_}uq#?iDwY3*oZwsno&cX$T(QVvUf%Ri!IZ}w*IT*y* zX^3oDE9dSo)(*+LLyg_*u_@|bx+=52!HdI4uN&L4*XelhsnF_`)<8u@ipiLGHFDK` ziZWOKj?I|hopg1!gLsR1yoE#UFp(kUbTeXq%Ou>qSX$0wnd;L42QdZf6OXT`g`o_b z7I9+1Joazf5@M7f7Q$qycsO$)G~|~!_3ocC2r5e#zE5fjEZJ23b~YK?PtaHy**KLq zKYv##%fcb4eBf+?x3}Ke+dtL@r?H}pL~W?(`g9D`@ILv-F3!R8Q=dUcb@)nB6;apm zNjIz;vva(fF*z}eqw3q68lBS5&y+QZpc?AD5Tz3BQ0hDimcvx7omE2eHtZ{&M@^>*vG$vF!FVd0MCmX>?N!f9w!aGYN#8s4x4T zX1LhxyzU2ks(13`+l)Qh7u^|eHfl(bmDN>@ly;&o=`+S6Tu$#-NLYm*d#CEN?A0?~ z+tV5wDM(m#3Q*R*`vtjJp9+i`)-PkDVG<3Sh|V8@=pD zh(MLybxL=DpJ!Z*AvX z;cXp4Q+8|bKqU+}7fFrOZrHCQqo&&lVc;>>enR%*?ECmr6l&=9Tqm!c>BkdC_4|P? zQBppb{AA$7ijK)EWm;hi;wL_PknY|+4D*K7*WLCq5gAE(#RY@a8%B(y;Mr_FY0w)E z4%G~d?LTgOysT$xkrxx)a=5&Sl*h@cRV_JLxAqUNw}zcqs*bf)7TR)4p>caJ*&U4{ zR_>z0MvrGaA4V*@qY7@t6~GF1(+ggUEXNHS=&lPph)x`;BJ(0Q7e_A4;XS`s{A?F} z^IJtm0bC@k@)m77hn-dMd6S)E`?0}paH9bvXxYsn=vjT=?c;hM`0BBl6SU=qXN(wv zP;xwl54xVpVrMp%)ZsDYBzA0dvd{6^f$m_?NNkTJx-PTM=m|2&cU#cDoP?|8Mo@r3 zP^T3GWYH}Au#ML;&E;V#3f)r}bw*~+?YX>sco{iIi{p)TY-N3#4PUi4MAR*m zbkuCpK0FPMbWXasIf~~z77_L<&UqUQtdFHN7x{7{je^tGchi+31t z#m@|(Yu_L5`aSV)Fp#|`EDSZ!W{u#8n_r%fz}vje7mwR;{CN7i_!tBSnmp%>&DRl9 zIdC5ChwUuZJA1{h*(*kE?C6bpgvi=bx$_P5jNV-FetyfQv$JzpBRnf@t6Xgpr(uz0 zH_r9``(CpN)PfLCbI#vB^oXH-g;n@zyC6Xij3`Abb>1ovxDRe+yhhOKTuYN3%H|I* zu`6z(WrHzRc))%^y5~p)poLQ8g&Yc$T`5jA)F+tSJ3Y3|0vtNAlC{9}*N?9NDF%q% zP-{Vb!@dCb z$H)@ie~YcoWr_brKEc5Df4x7^8Fi>;5xKK5)1nF7K#YL`%2NBXlsO-MAOO{%myM@@ z^I26@l@363$7ErGfLSIW152PrHHsTOq-BNLPNlI2G=RTmhK(ipCrKJ_LM3Kmx9Xm!L!APFhk_8LwnyfA^ z20b#r8EWt3;q28I9th;(Znm3h*KnnS21`hVsmfGmz zErBzZLp7;D0jl7O@3_v(19t#Pj*#~D7_YK#u>lR51$J=+ubwzA9$rEvn{sdBzu4kR zN6lYRV#MQg53oE6xu))JQc#|6=jy5qDmuTF7%Ow$J2t6zn7;>1ei9&KfL>R59|;2j z55xk^8mAYSihUp=v`4UPY;Oz7^hAOt9RTwbQ%K8aJ1M#4!ok4-Z0^4E<^Y$OC7`&P zNJ?x0P?hr{OHhQUQ&hrXNlP0i>WK;>W4vwayS;Sj^kmNf^KM2arm)H>pQ}JlkO!t@ z7uxsmCx@H6dqSMIm)8#-OJ(Z@yBQo%Ea0+APo=m!EnwP3mk$S1Fz)2Xdbl=+wa&>$`!WFI0H|gga1xoZ~JndF>joI!ocJ^=^wPt{Y2ztXnleD@zA8&AV%B_J!9pZ^e4j{XRhf`Y1tQPa~(VDr-nS;Q<@3qAa|bOwro zF#-(6I|c_FGJ#|yEM?7p&?0tTZPxuIVPG>zGW2hSDrQ1KKK^&{gXO;j5Ks&8Ki5OR z@SjUHHA@KnGV@E|fP za-|?D`8QblqjIQ(sHj7^&pSi(8|K{Cx$n^VJL?0b<1yHfc`zIB@23FSL?r}s4ok%! zgP}U~%+#O)IzVs=xPb?5*9J4oUI1#5e_!ST?dacM2gS+XpM$_8P$9x7H5u@PGCB+( zi^BQmO&BrZyuIgd!i?1oE{p{88Ub#_2r>^bg3H^;Wwuj zi%H`U?(D9GJD_LKE zc=Hs|llmkzX{UzT2d9_J;_T=irnKfyw=SXK4~i(-BkmX;*!dSr;Y|5*Z-@(Kd&%(7 zKWeJi?U;l!nJ{WiK1O}QoRIrHGGB8YQF@6)R32LC#+UtPRgGRQXtV5?`4{xOMx-Pc*$#!}C~$Niv3am^73 zT;VhoElYp`90&o~BeO!bRtbm&^A(3raFcHnhyG+Sh{HLEItvMj{aet35+!$0dEs+b zaIy*%D20oJb3I(e4gt?Z>KlPFV#^LRi$nK&ck+{x${WN>*`C~SeQXTd!FQ)*B9Ux@{HO4UK(+o&?4^E)BJ=b{29MG3^ZQ%4h%EH%^NLOuN_n>cw^P~6iI69W_<;kK1g!c^hj zyOnnhpU!6{L2q}!D2le~Q_F_gQzF3=b?G>djIovpd!)D_LN?5*qUGQ#@yKqd^2v3L zoG8h)9L0}zv)t%T1A@>sn-Lm8<=yPT$!b-uG551)BlFcC6n|@!vQW$9&-#c;?R{UI zZp&6)G&lMqpR!A|lwthjGZbHkk=>b$31oXu*kK~S#lhUCLj&ofo`6?q8>Wle@}4&~ zj$U2C#z~7S_PVoy;qykvu#*{2oUMyG6)AdbMR{kgo?YHXQT4~hs^7EWV!!Qtp{}W` zJS|h`wtGKuyGtOJ@g@)vWnoovkp{%1l>Bs$@R2p$&?D*W-Um2~Pm#`NR#Dc~w9a^k zXbu0+D?d2U){hwW;A4UjTieSOER2`g(YBqVyYLHUZ7Wb9?M9RZ+L%~>5VsWKu|P7F z);w!`TgRtL{lE#DL^HN~6AX$^Y6&sAM_4T9Sxyve!WVyTqe{H8&NVy6)e9r+G@VV- zXiMl|ruJ@J&-HJBNaMXIB2&$%6(8$7obW_-CZd)i~yVbbCz!B)sWwa{lav4~6xTS|wYXKjRq0#5fNxldAZ<;N64l zM0&v!qh2!-=ew|B#Fe1}CNAcoFHe|69w}j07U7*TK&Foqyix&$z4@!tpq;zIvob22 z6nd@EX&{et?CI%ccN_dgWJo>z%18`XWCGYX z>iBCSox$h8=gkDCcb0~VAklBY@k3frN(or*Cn11iGymBk9Z_9LA5A)5D=a}3`et*{OOe%S95msmAXJHV{6-&uarErWYy43Jvn zM6GM2l&4F<6apNTmD56iLvpv@((vRR;IkNC?bPuVb6sNyV-U;S(T$~ul3Tim!ukeI zegZGs;?$^JTMnQPh<+V7^>pv|u~#gNH3o=d&H?^)RzSA7F=q)oOuiplpMQbN5^H}G z39;XpzSAv32Z%f50C-)R2on{6x|GqC94t3p_Z7gg4Z%@Nx5I6&o)ii2q>qvEP|GCU zN}JEUz2xA;8Cdlzt<|nK0}v04@z2FZEz6!$?bDdvD|iE*37iSUg8^SgNwLRjMf zoP$%z&7iuO@A(%3(AyuNh8xQdKW&&B|yQ!WNH2jLdMc|xqO$f>n@ZX9N zDwzQWCdNm5rl;49hQp*lNwWzumh@4M`e0sg0L61r@WxlbW*sBYeZP||=7_OC=Q`i3 z&eBlIy(vkDtB`<`vmt=bUk4`@?VO#zVv2J?74U_F&3SoH)jpEX4nhO?OfD}bYJ>nl z^}7W{VBEm=6bX?43O95?Wtk18rDUL`imQ6WEL}!L;K+QLKZ3pF+_G~l{CoL)qSo~M zXRi?KWSGaDn{}AcAb_bcIIK@fnRLbw_UAkps^J1n*N)Od)w-^?`e9F!w3x>O=ElCK zEA_Ex^_kepav5M9mZQS!PMp3SxI>=WC?_5y{VXzEGNjkJU|W zU#C>pDn0V1LAbb`P25U6Q>Q!m2=Z#G6exW#5PbSj0oA~WYxtZ_DI{4}4Vup&=|;hu z*7)&YJ)qB^b>{=Xu7Q1gG39um7zOBPux`G@Ktu&NDO&Ox0Z^<`zXpm1AkzvBRE$uI zIeq6S4y^kNATS71IyHHyZaO?+SPtw$Bv~&wEs24Q1R?<())OR!Spy&TIfw?RKxlRb zoCh2mCca1ra3p|>2ez+n;&3asTRVE;zNMwyFNb|UNwu-Ml>0=piOcZ^6Ei|K{rMID z%{22fU-b}xXf(iX?W1>j&+VW0zA1t$J4OHSqO^jFREHPbJ#uF+fE!U(HHe)e$nh15xd=@XpY$`jY7O-Gpr{$XmkiB&{(dtt|W^DNb?&bdv+Jvi_32mv-puMKM2 zqCgsps8QrTQkl-F!5Qo%?%Ih&7r+=1vcSii|F#>IQL&0a%NFhc!#cz_ID1#?zu)nz zz)>V3iaFArPE(6*a~Gm9*VF|FadXPI61jsNjVFC{cls61rkks)7iR_UQ% zXrxMZ;H75N1j#&cNmZRLd1H%5IzIXW_m~Xy!t%|$!HtD0G&5>Hxf|_r$2`j-9FL0R z@e&7Piiqz6Z0YQ;lCCR&eI|1Y$@>v^JRi9=E~IzORUIIPdEqHChoqYIA1n?7vp;l@ z=;L2Ou|w@TDEo2|kALEir2*l>MZJd}?R~VXF*twm32ydow2aoNT#sk|SS(qzlv=z= zQRSpaMVmO~pyy*4W6j;&DuaM8r^mntxqAn>S(UmO^0KDRzD{#7 zep$KKCqI?xr+$7EID1(QP5@DR?Wa=xX#N~~q8~&QdZ<{4aYOqvHzA)tjp^MZy!Cd} zfH1c0*z(o0NnyowD-~n|AS*tR9+c&chlfz4c;T)R`R}Eof=2j&Edo`jceTiSmKA-4 zEbw!|W$&IEsG9$;`ng`_1`f6Xc=r8?&R7Um0U9}D>)BS<0KF!Ek*l0x#45-wi$mHG z)qjLE{&YqT+{UUpeO~5di1Nh&@7XA4^xFDnHF>xDyiTzQcUyX9MeWz z4^-(avzAA?#68?p#l63Y45q*_@|@dD1(5|0H^|Slu0@DI5N#{ov$X z(T)&+^F?5Tn+|jww>jrWr}gy=NBC9^LaH{;c^y(@QAB0?;kyr#E;{#g;)Jj;!YoMk zbRss%Ehj%%(PP*TMQKsPg}yZg4ZWc zEIfHRPqCr(@v<}tQxcq(w1V1TdL^J^G$$o_uUvo6yvX_pgwWPD%0LMi6ieR+5RxBe zlR8Fg%nC%8z?u1aR0h1i3clAJbv9E3)%#*^?tfh2YUi^lnNu$^xOQbT^KyYf_)?z^e>0Qgw|R*?=E4^i*su!?XHYq>PoafGERLj0D(!d z+<)S5;G7%d7}Of;&Cuad819!}sz~B#*15{p0l{{6fby;pL9cmw^NsXCWh6L*LOJ$I zrdqf1@irIV%aAW$JCl)!+VOYaJgXPilFofq05{vNENe+EgTNzc{iKH3;?~z=q)eT2 zeI{kO`aLD^COcRm1g}&K-Dur^4P)FdCubi4sF^dO3);IG=H2rpIP@SVh}5Ag#C^*oo4AL$fPSR1RAQ`f-}7NFt8>Am2X%jP7;N9HG8eM+6Hd8fxe zPs^!j<1%n@(-n;T2U+J|tFO~W+#VKFyVp^azjCM2>GDP{Eb_*EhvA9^ElgEK*7M2> zAJWGgVzr)}J=P-nxkc$9T4&iGzv{_&bI~8I6={GAj4;P}cLzd}-KaDfm})2Y;2?3u z;n}T5Ea07`u>j9n%p-p8$?Uvz?4ks`^knEu%>;q%)VQWKA_mYXBG~`(hkA*5!B=`DM+LO+XkT0O1BhBm=#X;@T=wqAG z26`_qi2L|M=8mc0EKKf8350pulplwG?7@aEy1zNDs5H(4qq;wagG z2O{`lp1tvF(5+%gX%;q`zV+-6vGQP(n!?4~93fhDepjJZ`gk3zS;vze#fqI)8uzc}sn0f(%IY_x0b#K`ObPLd#_x_~=6&B-JSA)i z)n&fQ?r*GWQmZ=@xAm<5sBN-vcvp>>KMIb`wF01@7QUXYay3W0KX%LC4f&=0`m(a z1WZZ5?{blcasF}0<@%7)kBfjsA|4OD=e+fmDy_zQ5) z_Zqy9ZPTdaxk6tHpS3W1_VIdf!N=7bXLDb#g^kjtxr}u^ zM(peBi^Xu!>^V7R;z*;D_fif#yLO~>+Vdes2uO=w|NLue6gm|bId+9T`^$9}M4`6Q z*|l7RYp8jg{7{=&0nu})G4HYH_5Afl)X!* zg2HI!Kn{O?R|lwj0F;4r0C0)^IL!NO;@vE4{eHc}Kq@>f=q~npa3b29@n(^OWa(|t zaqtUk0u1mPjt6d=O$-aUKqV}4>K(x^)NtMEp#Lt@1`X^f7&9$8#%1@PZ<+sUX@8P| ztBfdoI_V(w`D^*6YLIv`^Y5_We+vfz5DYxPk@sbwNDM$o@pnjib}p;RqWNQlPM$sY zvQHUst~%Wqq;1iiO>9Km)SgsvKq zG=v4h3k>p3iHfa|f)vyujqay*LrC=lAOqx~Ckt*_OVsh19t4jjdDj^9l;#`ot+07~ zQrb2q|H+9I)~V@OoVwk&IlQ#$P-bfpe#t}+9H0aw81hsF2)g&JGT@}w&;zlHEET@k z<{cUbFnP3#;)Cs4N&euFOD3S~dD3lF`43ihj(az2y$IJGh?Sq!sU&817uucQ+3c}H zS({KYv$l&j0Al*)MF}V=D7OuA1~5g(RWqna%A(_7dCAvO3u#5iy?A8#q-@+F!oBny zb$j1yWV<9g!stf8B?@7mDHP1~nGbCr-yRsDJ!Q2=s$QOB+Isx+`H%C%~vtXz$? z-0K2PeYpO@{~0w;RG7`|Rmrx^p>+MAdslRZHfv*|e9LABjoi;(j*Ia;x!k}wCmhc( z^#cHE_m&$w()hk%(Us&CLnt!V1MDXt8%y`Z-E0*w%Ic+~%)KYcNf0_DS>uLOrZCBNQXo(67de~<6$gZ-EICX63 z;lqP1A~>VjeyZ-cOeHMbowuTkyOt^5%G98_&AFYP{he<_H zA;MNofSr5$*=1c#fqYU1w;@*MCnD4j(~z9y($o@2mv8dM0$%#ReDFN zbdk^kp%+D^3P=mmMVc5w@Aa(szQ6bT&b@csG0r&uoa0|A;mO|n*=@}==Ufu(YVZn{ zcK#GGviU3ioTnNvv(>n6A$c%xEv#_#*pk!x+l{9~K^K$nmoc+rXJdym%zX6AJbxY2 zC+-ygcpYHuiqCxX+#*iPUvdAW0ShDCZVgV|?=0k<8adl{VD?s!O$~t7Q{#XIqN&EX7gU}= z3bfP7H00L0kCDUHzS_Wwcf?1B2mkpp*Yk)br%Pi!+>0?8!2KR-ZeBIF`_$)(66hPF}9sg zZuvHB9@&yC=_%8>B0U)^zM&%SIiFkZQZq-0M zd+1zq!O{qo8+v50HS%j0jz(XUhQ(tCv%g=^8yyqbZ>*@hIV)8;@Y?E1YWpHn2X@_= zS8Vdx*CpP`n~F9Q!w1Pj@8GQ-|J?l!<0RHO$TU7^X>{=pX;%AZ-9Z2|ef>nM1bgck z9k}P5M)_djW_v^i-x1zKg$hci!8?(b;-?}Z4%Fq=gmF#|0{zYJ7;lco)zC*jL>i7< zp+0SrfsV89&3!m9c5mbYDRms^(j38n# z`ys{!6o6dq9Q_n(|Ak_!9yTdmNIA;5(CC~R$bbNO~E=@9AczEF?i}XjV z4P<{Kea^DVgXHE`wFc^#J7~wjpx{<(9KYQ&s*@~r@EH@*XCTka0#YC2HHA1(Z>E$}Ss86fhLoXbd*{JjM zwq5fdW3&c7N!rQIQMZ*z0f=|MRMI~G_DV-$_|F%}A#V-*7uH5e`%$;^=Wan)s9O_U zk>J;O;^fKkWKoto$J@$k;r;3InbZY?o7dCPnt?xtEFD|FjP1MEST;9WJG8tI_UZfM`6Pt*K|6U2#>9gt{N-+d*=*07PbYr6{yOUy zN=(o*GJo2(I@=TeqkDTH;#0iD-8B53%hsMKvm=%XpKUf?`OZR{in2~1 zPl5eYn@?Glo75B!xj1RUG%gMqtlPwAX;goZZF}PPIgeIHMd!VNQJSpy9;1obCo#Vd zE}Y+(lQ;S;Y{NS%myR4e7zicgg`dm$UQ^f5@fA-uJbWCPEW?44e|Ho~JW`pDxbG_& zRHFV$mf*j?CyN@^F2efD`C_6QKItNN(D!MpzHk{zW~bgnm&(ejd@{6q^1%*1V7h>P z6}Q%s@9_C>Ktn&8C%#kkxzBJk&E0)tcP-HoRIHeibn@dS%jX|#9Rez-6rMOY(u zZ(*AKN<~8rJIy-3i!{?W?C6HWryNDZ5GdWwa@;}eyePyP3#*RTwQjF}m7_RP&9O?v ze8=G!`$KC5)Dw)zogV(~H5|`)h?U{izKhS#ud&q=^)r#_Hpyvm|IFj(kVVUY*Ad&U z5^8S})_Sk4I=;d?T1e#Un2*d}wCo-Z`ouv{X6AZO$b(y2E1~(*r%_R!UhKA5t^{Gi}E#cN*LZT1$IrS@tpdR{@?W%4scm}B(9JGq-PLY+j-Cp~)7 zoxLL6UgNxl0Xkf_>{BzV%_1hpH;}YU3!I|o24@ln<*UvH_ox5YH2g@1Fp*^YHL!v8 zu0cmOY}PGr*v)qLCKRh#7hY%0s-&mdqIMs5-STL6oaL@_Gc0Rb*SR<iL_b2eSUuGXtqXKbb}0a^SUIzVruPrvI@gB z+%&UuTS#w_t$2^4Zpr$3@@|nqnq#!1qUUCUC*2@HuVnq7mGZKtS)w390pl3m5KgoA z181gXCPd-jO6?~Q#x*R#BAlb>L*qS6NV1rdt0Ef@Kd51Cm=d-ZYuldGIoloMIH30W ze41NzS^35aI*RrU%` zHhr~!9wvt}M%u$>XZo4n;tfAml(|RumCohfhKKmdEj8n}?=?!^^j6^#Pebwm4cySb z>wYoIq844LV9|eWK)OV`ENUWCX0yxHuPlUCej{=s)h1%lqWFZBZJ+r=qOD(lYG2{f z*P+M`zdbB*&~l`xtRY)Gax|KyvbwB#vom!n=_^`;gMmx*aN|UZdA@L0>L8maPa^>u zywTqpgvlG7+I745^$)>vj-ddnrW`wMJt0op%K0>zAFa-ix%dFRP*16gn3DwybYtFpci;@QII5(9m;Q-Lb+W$Xw|QZ?TqkP#X^{7>ynizh zf70)ODbnlU)|Eo#*HJ_~Wil#~Ie*!(1eSM~Ys7EgmseFvP#*658SaYhOZby#<4`hB zjm`6oWS;wY@2f9x?(TSP4L!som1ng_>&B*d2=T`$_(S_Topeh&{?+ajX&%dV3C#QF z?GlC9j&_&jag;-phM<;hcj?|%45_)_LGMXA+}PDqcrvxjsh7D38HH_>y4IJrLQ1W8 zIIM*84??F`bz#t9L^OQ#jI;;nvrB#9eN^Bj17M;tFyjj7WU%j}fM zzB9j#89#H)s+{uOXRY#!{VUk$4X*MVC;hF2$rql`B zFXhHJCFm7?htV_GyVh%-@^9|^7QX9SEL?acT^Z9bNWZ1*gI#|)%=4zZr1nI<(Ruos z%*%vcLwUZ!6ut~}Pb9K;F=I(rYB+*%HTf|uVllTT0USG{O_B`ck>Y}QW@Hqbq3g^K>RfWLGWqn zu8L(`WPaV=@9okP{d2qWw z0f*3UydmjgXkvA?tu;E9DSv2YSGuJ>wj=&N@=RWtlvVg?-`$NJ!5g>-nUhTq|H{S<=BMzX?mSr*ZG}ansC<5v`}}j~1uIjL+HPF-B_riHVMcKjUS=+K zLD!>w)<*Eg+Bdb0vCmOnj)>~&(cP}?C5(y{rSMLOCSDryjC1D_FwE%PXTJXa=?W27 z2e6|56Ts->-i)(EMpaKM!g1y*W4C}!S|L!^eb7jcmp*=oYF2(=KYh3MdGW4g z>N$4Y_v6BAD~8uiV|9!RJhqA)6|a{?e+?;pjuk{|SUy(HrN!sZ@2b2|^hwIe70J)( z{88boBApXdPVQox#EY{c4v%$DZJLO*lxgC7{FUX;^<*BL!Qs9emd5+P3en(jreab8Z8#{bPK1$o&6|i7pD{L^bOMw#|?}4q>fHk z(hpypkNs>Usy+Emb-+W&_-MIztWaTGaBq)PnU3}6L6p(;abDj3n#t+mkdmyeevU5> zd+ypMR7{6m$hB@VBG)T>7_9WjwcU+ev-wd-Yox}6_jB;{QhOeU6?j3is z9x7q(OvQu<6S$47@jU@kNyX2W!gpR4Tl8>Omp|Vxn9obvNO01YYj>aR>CQ^Kd@1P{ z>DF~++u__ zeFMv1aO!$+RhcvntVwC&-F!*0g+mF)8#ZEi_!*2rc|o{Rgj6#jor9iW-u@z5ELU|+ zJ+khhnEBn(XAJL((K~HKL9S}`SH0$<7ZcLisV{V`DxP7vf1$gq?czUNfc`|2k^gLA z_!Cwl-pKUH>PD%nMY?RQ4^)T)^R*LGUOLqV)}f8LK|{u8x?=fx9b_sWTK^tY^3n|p z#7K%n#ccm#5Dz=OLDdm21b6L@0&)vW4r+qV}+=jEmgy4rbPj#mcvqu)z`J!gCUh^V_+hq74h0ESD zv$1<|W$XfkA;1{mi-?<&2hQ{x&?anGuKqa1C;Dg1zTfh4;CMl2RS{`-Z`GeqN9hQd zufHoydC~&Wew(U43*jU1Hwf9`FTJP96|L>M zEx;$nqe!By)kpN@=$DL2oZsj|#EYxj)%=hh#c zBfcH5pNz)kWS#axFx-Gp^1nh#$u{>DL&UukRBxp<^FDa1cxc70pXi8VZw^!^x;i9% zqxX|LszbEx#IBbK{cN9D&cwK>2fkyTeP5U!U8kSg2#0LDdE-`SJAwPw@SSYQntEA0 z3;qI4yntb-)eMy??Q7I?R(KN0$k0-7nxJ{I{3!5MQpw@gVma z4rOC* zvw5IoH@N>OYsFfA|CDT>0)cR_GA~!2cBpJIAaI7aJ=fE(z3ao}x|fI;bW(QO6D`EIVC+IA-G6u4rG+9BokK?Cpq*ZZn&-A3QP zr!zP~q{^omn3BiaHaH-hO3sb+dYoSZxe&r*_;S#^)^Y6?2BVEL2mbUf20xs74ruA6iXAqYj*ljpp zt!E-49bG+7i5CbfAFQU#p++c*PjtonDOo=_64Y7mr;$QWg90GCK>Wh+Gz;q!Rx6pW}mdL zkY^)ojCpFbBy0wNJn1J$esnB-&dAQ|kpM?hMH4(|4zz(7exevyP4Vy#*+3j%TqGYk zSqu>Dvw_%u<}m>J0wlKm85AI5Bv&_IJ>bfykjQUe^#6ArwI99)S<+w-pWW;%Y-?`5 zt)g<8LDbgpr`1m4=KyL}K0YcI78V6n)q4&Og5clc2ie03kg;qjkxHN_C@9>%d-v|4 zDSZ9xIoajo(4?gR%)#!A%mn;&Un?NFTSQ`Yx6!#b$YVepMXx)mOiTbG~Xeiyl zzGPJCa17LSa)ySCV4RpzShKhInc6$zLw)yk-$^DI60M8vKaX*u&|2TI$N!q3fZx3y z$foL+Fd&Dn+#hUdX$cGr{0NSv-#{>;-7~SOf&lg3bPY zFfUqc7aeomQ`SC-K%-($goPoXqA#)L=L>8%eMs`rEFdGC~Geu)MqnptAJ#Z&tF~po0_uNn)54$uJcfQUR_& z(mQiEK`=;iP9l2&#y{yGY^w0ylfYuJ;YmqzIQ+bRi8NT*w#M=Xq4!r($zHgOx{*VR zzQQ{lsT+;LfX7i#^?VigPHp+q0YS+S;G8~&G7SU$P|t(Wdw?5g~25?@FoXNca zjy#|}Y?dwu^jYlr^l7q3A730ANbP}I5Z^1Fnbj;cUdnC?#;U2SH^Bg7>*-__#&5#{ zHAIep&%oid-s>K~-tpnK5kPxTV9!PdT-1-tGs3{1^21IW&ptssg z$O0e4Xo2^y(t0sijG6*$Ptx|u%E?8%eLL&e4oML=sqCyv@QdHRsg&9bT>v+%#m%iV zvb#rM1wV^ftKVPa2lfI3*!|uEk()hK_?R;zIRKT}4BddL9YA zPJ|82yltr>S*e1r>&SEyu9M{C0_$ik-WlwXih*rCM|?xe{c9pj<~Z4deEq&z35&gP z5(uUI+**L_1<7Oz6pFSc3yOGB-68n>Vci>YCfAPy0p1btX@;eAm5Yla6}?|ML^v`^;X^MkUU*OjiWShx;*L zJN2j70_p)h4HHHY<6~_>wMsJefm$H%z-#*HarVq`(xzU3wS_h8y+afw`>1Q;Me=}R z;pRR?7XJ3_Ygir=Z!J>}k&%`rY5yp->PhN$q_qYI;l;0XCXb-n&W}1TN!S5=;^X7z zyoY8|(M*MP+gvYR#jKI!irPhR0K*rMYloVdV(tj%)`VN&Z( z3-4MMDBAW=#EGnVX*9$OKe)zcSV4CCxTlqnCp0Awwjzj6L&W~KmkPm5q z1@dfpQt?Y;V>(be;)PKcLN81D!w&fiPMby;t>>A0b0wYWpi_O)_kk8BydO~hNlwsU zF3}z6f=<4cHP*ZD!k?6`)JRg?&6=w9(cR6a(77X5Ml4ML2g>!6O=Y?=1psA4iIzB-1fPMMX$y zefs$F$WouK35$dTgQlkDMJe~v8D>hB%-f_1^QmU|Hz=%=FR%{OxZRA5jO6)tmc@4x zvS`^yOE6&cJ=<$JQf@akS7#=B;v@~+5|nPV>7#TOdUB}USAV}`PxTZWYx6!Qt8j<( zs$L;T7Y)ce{r}!eUW?cak|fM(JBC2^>S2=>jDYOgSyDm~T&&?PaOL{->mI3^c+%2* zL&qNqQydXR?ZL%DsEY z{vtwet`AsVy@O4cLo@v-s?Ya~Rx_OTyreQ}Po8jGy?XTw>|ijtWjVj<3s+7ciXR_~ z&X~)XhvyH&;r{5$=dU-gbgbd&+qGYdY=%Vg&`jPy zn`i2>?uYAyL(X{yhF6#9DeP|?=5%-Fx`&98Ja0x!z*);4ZI!qIA9N8SeZ7Lh324u- zKbGp-J50;vcDMtaWn${k7iKAkB=}7Z!j)f}OA~q(_AIPn0w5izS?yMio%w7_a_!4Q zFMpwit15l8(-{GfcMIj2&`j+330(9JIb`%E{m!1X&cWns$Ory?&SNN>6MswW%Q7=9 z>*hp{C@n*8p7ok~%I2ZJ?V-8~IXBZ|$jfjK`ujvkCsJ^85Pn`4#n!psH3cxRgO^t8 z7vumrPpdv`*0bER>*2B1(R4KpUa2#Y)=;p?ICJjm!u9KFX$UF5rYdej>7b;*IV_C$1fUW8Ej|-u?fnEwMw^U3!6mnxaJ!I1i4E&6fX;S7g?Dy7j zd~R)dJYb&@m#072@$%bQcA$R{7@|F+`JEh=3h)`i+k+c6w-H(8~ z(Sz%kR#q*hF)C-x0INLIpU)@T)8Q0@D=!`X8qGp6Hf*~M*{L*zvt9;4f%bl(?U~sr zKeSAjm{$jDorj|^sfyaa&*j-2RP-b8#nhZNtC`-ri`I$@fJ<4)(N>i?t1a*+CY5s% zuDrrp(C7enqJ z-|Z4?F!adJ!_;O?@g?*{C6e*B#%j6RMz4rlv=5JlQvcyn{V`Yu`6yg9tub0hX8-Gu z!)}AO)wF0G-be#~=dLZ%CdUduaur7X-0xiaX;0oW%G9c+X}Jyb!@pd8Wr-h_T~@X! z_Ri5>2#%^=g9)+VGF5|Tw^JCmqR%h#$Q|Bl6plpYlxbG1nvO2lJ;oXq%8I*r$p-f- zQSrE-Zxv_BR()xCz2F$SU!^h7?njiL8>4yY70FZL;q$?QD)7Y^!K zGf;VfD&VQigCRE)Z*wWPBB31=%okvLB%|=m`Q0c$L&f`B$IraIL>lg^qS&&>gZHM7 zLkfS2pVGx|(kF%@D`RqD!I>lYcN%k^(L;H!1{PW15urPodHqI>Qim3q94@P7+cyid zHh6Vz>~?je64vRmObENu8%>?_T2C~z6MDamqU-N2K+e89l&7G`(yC+Z6Xa=R&YN8L z_zpvC0y`FxVQx6NJ0 zI6W(+g{XI9_Qu;9`fdsRaoOv9#odmWHIGgPmc6=@0^3jQ)vf5w)*SubOgRZy3)BRZ z>IvuPEOwzNq=wp@tJ%bN2GLs|w<_38>qQ=>8B@|PNuf)l3vF$tYLUoRN=1jHz#k(| zAg$(be&3#a&)@h|t3+UP2&O&T$Z%UD%^kTFVUxsrL?k+rZ9FI-(r9!9Aeo4$ycN_?s*DkdyF>}6s88T5GI)cTe+J4gn-^Ru^vuODh_q+@oZZu{mUQXw@1FmmV%=>YSbFeX9atP4 zSdm1Dn^xh&%|<`?$2_{epE-R=gj9v*(GMOH=mA$$j;y52TKjR?R*Bm_u)RBQqe`sk z5-=e?;suwZrVYS-a$IUs#!<}tQPXc@!H-vQA*Y|Z&s-OuR>Qj`J8Omu6G&;Sl^2E4UZLL+%RaFvS zm+OLgMSWvnu*)2B0chG6WNbX^Hs!?ZU{%)M5fwszZ($4{{ZTk&6HU)ZSC zH3yD>g*LcG?PR~$=8c}ks<)q!1veN4-)N$Y4?0tk z*WR-cdWJF)h92!MlFK5*!&C>&3{>>VZnj}{$(Il&Qt5>PPcOK|tc7_XmHLjxJv#<> z5fka~JE*}VlrADsH8~AKYzJK@$?7F6694aO+SPIx6z8tmWa+7o#hPR2rSM=H@63!q zr-s``W$fjx`qi&_@BM+)_K{{aO#h5CO7(ZbX@{G{hMwrxVLB~WSxoOuRp5gu12T05 zwG#RcDsGR}JG|k+GWRPO+tqaRu|}%7#`kW^=yCc?4+=-1Hl7yV-*B$Nuv6p7Z7cnq zYu`*wIW^`!o)2YjYFQKcw!FKKOJwDJ%-~uu6-La@*<0914igt?-6)J6uGw38v5|Yz zV5W^yBmV=%ka2ioK~Z{IU}$I~)W*r*pFMw`6bC~jUHG5=y=n`&;Qgiij2q$^G4DQ& zw=Ua^=eLFv9VKPdB!CN|I+g4BFzYPfusw=2swj4O+gmikJB7Hoq>^y!)%Q0jZTzPo zTI}czFx4V(#ic33j2ZpD74>CAEaDRVF@BU-Qj_Tfq|_gt|8h< zC!42Wm0C_WR1#r{l;{lQfF+C=YYyK?%O?`U;6o?=xj_1HlE@+N?oCJoj#&2<@!qOs z`C~9=c`D=P&%N&J(U9esol?a%qyB|?$kd#ict!QMPxOAY(N2q6@mOPN&Y2^=q4_au zWTjb7iM{QJo&0BEJl018zG2@#e3BC~?UWaU|EF6*=Du$HV4mQHSIe+i2 zb5`l@iyhu;l77qZA%9ZrO3Rj&>&TF}dQOs1^HvM`*yaCrU(dRMlrpPOIVL=RGY=4c zEn(?(r(d_dybd5Q->Yi#!CWb+x2ljSBJssH%56fhFu|v=l@NSHUQ#G3VE0LoEPW0H zYV5g{sG;ZgTOD=LUONBt!}sRQ#8ZdnxCjTA+r_1d8>KpMgLI7wl!im01=od%@Gbf$ z>=h^xoDmr3l;TeJ5tUgN5#tvSI8Ju0UYnWTn7jzvVsHv!aDFie6!1ETs;AI3Vhmq&#{L9Z(%hHh2?!upT?tV~rmyA&%P@AnNO7nViiqh;q9> zh9x5SRT{~Qw1r?olxOf2 z_f7Rgw|16I-}2|L!Po4ykDH3y9j(IkrC!?@lcU&p(2Mkv`n<*Vv#C~m2NM!Tr)E9n zd8A8HdS-=+gE0IYiY$Tz`l5_24QrU@*8G^6y}nG_ifOstVt3Hrc#cB_F?Nfu)8(L_ znB_0UyEIf%3tUS}MEwoW&qDQ*_@E*YYSGb_g zW{La>&PQ;j0p`q3!p3R%jTxiZ)jLQGZ6LGV1wQo+z|ixs3MV+P{QX=MqvP-IhX5QJ zXuwmfLUm=$Y!!Nruc?$QpU99l%$xOmEuWwsus`4=>5w^r0T%?%K_5~%1Axv>P=c3Y zLim-J*fMJ1XDfs0#=B@)@F@6;%s>q{B~JW@lP3XX*zu0aT_7W)WLCU;=fTGVx7P-x zgE%b;$na<=u?Pv#fJb4e1j;x3@T?B~n;1W-YlLGk(1`~psY9y}ec z=vwwX>OiWk{wGzC#SKgXs9kC>_50hXyRj$aHbS#5o=#0)_c|LdYM0U{gOpu94ZY`p z42gWm2hA690vDwrkcGMokCmY>?=T%N`XC!38i9FL&l1#3N6m{-d}uK~5du!&-s`jmlJ?N`-DYn-U>dQR_#TG}5Yik16q&iYtc z^Jji$sFBo#AMY16YzR}dC!X1c$;t-H-W3D?GxonPduW+Y&RM}uuul;2AMMG&C zMc=&6lLz<5=lJ$^PadI#GSI(=5G>+q%v*88H?W#CP;{Azc-OHZF{~gOkzv_xMw_s!!>-c5|!Co!@w!<)Mt)hSzJ`#Tb>Xq-tEuLYmx7ztz6c6-5|oIno3hY)V)g zMP7X)$BnGE^zc*knOrbK(dyqL%t-f{Y)6eZBTA&})w{dPnr?k4vQisG!mYcHwF=X| z{F~2uIOE3qyj`#BMrk?^{o0==_oUp^Dx(Y5ZXSMjs@j_$>w>YwIB&ajj(<(9d1QqSJ?;zO*^<#ICK#Q0v!yDx1@>1g#V_oHk8+U( z^k&z)_1{W`=?{euKoYaULTIG^J;coxL{>_4q2u}@6h1h9K1?CxTQWvhRXeaC>y*v@ zle3O@S_Tc6MqYQuub(obN$7P-6=Bqp(@`29l9}Bx_L3iexij2$j;fN=&Hcsx;H20r zpDUnuHfDZZwZk(baknlNq6xmefjE6pMu)k8k!6>^h>nQmw#C-BJWTz;zK6^~`pFil zziryPLKy45XKT;lS$8kNfG)&^pTNQ?D6BG+vR8q_ir}@_D{>K+ki-HDDZo#J3E6zY zgwC?xO}6H)9o779BCXqVd@XIA8+df#tBzfTr+P9BHX3ip_^fDwmyzuwla6FB_K9o}?zJZ_y*0H#uI%?X! z#mpE<@5U<3E$Y~_6h9X?!~Q?Z_8F}C{@UF#K}W8scvt5 zNOs@vJe08V8UY7TDZK?hgR{e@{o#A6X!ur)cW-t}y} z1;Vl4qiRQc^Ydk=p$;pBKc)`pQP~+$|B*N3c$af%JM~P&I3jS5h zK4-&8xkbZz?~y+T{)&A6IWs>0I9&&sU#N_86=YIMoFt1}fE6wrw4P*fzW<+t1DKJ) z-F#5K#ZUD_3*CPX7}vzAEJaBud$cj?VaIY%b&}=NtN$ENl?WOnlmjAVNR%L1tWf_{ zR^dk4KgXNAaqK0dvZwS=>XI;H0!D&A{ViC&!Cb zj_IqFA!?ZF6S*5CgF{H)a$W)JUE#BTwnhohg94^2?5NrY78uSgWYRUT_W~pEAAFf74v`58xPA!h1!wtA^sF zqo*Hd(TH@!3Kz#{iPa2>w;!9BeEVHaYf|w+`ya*gJN(SeOQ&|Oj{rmu+pGGTi}4DO zjS-(cAJMt-OeV~VBa@5Obj}7@U7`MdLwV&lVYw(wB!r}Pp(Z?8dNJZS=v+jxxkYl@Aryv0F~dY(CFsDVFp6(u%l%G8*!_Trk`jbn`6 zsY9EG_Fn4@Y8Ooh_kMlq`5^0poc7dK_n;Zq4m;;9yir{8It`Z?O(W?L3H>+ZzRh}F z#!EZxd0P@z&4icwVwdk6Z#$5CUvez)MT1aa`2pXAhluy_L66>)N9)T+#S4-X8QEV2 z$c@(r(9Gv@h?4v5)oMa64lRcue~%fF;&$&)*h6$BG}jG{OSPxvZnN@?hg-_T1xEuG zMr+K-jhpEep>WD;>v{iEBVOb|%59_y0nm12a^)TCVNt===QUZC3rN5B=eDD2eum%I zPiWcRF~aWkt5VeS=P`3@K|<6GBMD@rlWH6UvY55E$yd21nt$u2M(ATT%?mo?oY_$m zD@9?2QT;~Jm?G;0Sx4SlJiw6qJj7CSt9|FVY3k}Jd4Rl{H&%GJ1?;Q}lG8M@B4;+{ z6!C)|w{WT;)!E$}2~A4v%)CSLvjo?!BMvTP{6>F1nG>z`U; zwBh)R!{IFf#uaj^zxC->JK!7h+sO@*{4LG^n=jrNGp7tV9ov)eci%1>X@9HXOFeC}ANrq$NB{yFV?ZE_&Sz~ESu`~sF~$Y9!DG@o6yfAGm7T+cGU zyuRYm)F%94gtxuVzL|vE3bt;KQwh4x#Zl8l>bGR@lRIl}{<&s^(Woj$w)6z8eU=quD$`+ zIryi-^M_lt1?^1sT#YZDzu>Fc2-4obw4VJx3zVU?%0A<{vJuwARu@=&cIlm{TYV;U zdk3MdjD&hyuHB8hKuP=eR`3mrzi=n`4UPvfoo`` is an easy typing error to make, as is starting a ````, typing text, and not adding the -closing ````). XML editors make it much harder to make -those errors, which minor though they seem, will completely -break the document build. +closing ````, as is mistyping one of the two +tag markers). XML editors make it much harder to make +those errors, which, minor though they seem, will completely +break the document build. Unfortunately, the validation tools +(which rely on the capabilities of the XML parser in use) +are not that hard to fool, so you may get errors which are +not that easy to deciper. There isn't much we can do about that. Everything's looking okay, all validation passed? Good, then simply commits your new work, and create a pull request on Github. That's it! @@ -71,6 +82,8 @@ the actual XML files. You can call from within the directory, and have the MAN pages or HTML created...even PDF, if you have a renderer installed (``fop``, ``xep`` or ``jw``). +At least ``fop`` doesn't like everything the docbook/xml chain +produces and will spew a lot of errors, which we *think* are harmless. Validation ========== @@ -149,7 +162,7 @@ By calling the script you can recreate the lists of entities (``*.mod``) in the ``generated`` folder. At the same time, this will generate the matching ``*.gen`` -files, which list the full description of all the Builders, Tools, +files, which hold the full descriptions of all the Builders, Tools, Functions and CVars for the MAN page and the User Guide's appendix. Thus, you want to regenerate when there's a change to any of those four special elements, or an added or deleted element. @@ -217,7 +230,7 @@ For example, when documenting a Tool in ``SCons/Tool/newtool.xml`` using the ```` tag, and noting that the tool ```` or ```` certain construction variables, -those construction variables can be documented +those construction variables can be documented right there as well using ```` tags. When processed with ``SConsDoc`` module, this will generate xml from the @@ -279,6 +292,7 @@ part so that the version control system can show any unexpected changes in the outputs after editing the docs: :: + git diff doc/generated/examples Some of the changes in example text are phony: despite best From 38386dfaf4e31a16f93dcc7c7387798fe7576334 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sun, 23 Feb 2025 12:17:44 -0800 Subject: [PATCH 255/386] Fix running individual test files when ninja is not installed The code in testing/framework/TestSCons.py attempted to handle an ImportError if ninja is not available. However, when running individual test files from the scons/test/ directory, this directory is included as the first entry in sys.path. When this happens, the `import ninja` statement succeeds, finding the scons/test/ninja/ directory and treating it as a package. This results in an AttributeError being thrown later attempting to access `ninja.BIN_DIR`, rather than an ImportError. I have confirmed that this change now allows `./runtest.py test/Help.py` to succeed, even when ninja is not installed. --- testing/framework/TestSCons.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index fda32b8130..a5ca8a0f5f 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -107,10 +107,17 @@ dll_ = dll_prefix try: + # Note: if the ninja python package is not installed, this import statement can end + # up finding the scons/test/ninja directory instead, and successfully importing + # that directory as an implicit namespace package. Therefore if ninja is + # unavailable, we may not get an ImportError here, but can instead get an + # AttributeError when attempting to access ninja.BIN_DIR below. This happens + # when running individual test files in the test/ directory, since the test/ + # directory will then be listed as the first entry in sys.path import ninja NINJA_BINARY = os.path.abspath(os.path.join(ninja.BIN_DIR, 'ninja' + _exe)) -except ImportError: +except (ImportError, AttributeError): NINJA_BINARY = None if sys.platform == 'cygwin': From a6af8994c21c9b499f438574ac74d0749bfdac61 Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sun, 23 Feb 2025 11:42:04 -0800 Subject: [PATCH 256/386] Fix infinite hang in wait_for_process_to_die() on Windows The ninja generator uses wait_for_process_to_die() to wait for a previous scons daemon process to terminate. It previously had 3 separate implemementations: one using psutil if that module was not available, plus a fallback implementation for Windows, and one for non-Windows platforms. I was encountering problems with the fallback implementation on Windows hanging forever, and not being interruptible even with Ctrl-C. Apparently the win32 OpenProcess() function can return a valid process handle even for already terminated processes. This change adds an extra call to GetExitCodeProcess() to avoid infinitely looping if the process has in fact already exited. I also added a timeout so that this function will eventually fail rather than hanging forever if a process does not exit. I also removed the psutil implementation: it seemed simpler to me to avoid having multiple separate implementations with different behaviors. This appeared to be the only place in scons that depends on psutil outside of tests. Also note that this wait_for_process_to_die() function is only used by the ninja tool. I have added unit tests checking the behavior of wait_for_process_to_die(). Without the `GetExitCodeProcess()` check added by this PR, the simple code in `test_wait_for_process_to_die_success()` would hang forever. --- SCons/Tool/ninja_tool/NinjaState.py | 5 +- SCons/Util/UtilTests.py | 23 +++++++++ SCons/Util/__init__.py | 76 ++++++++++++++++++----------- 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/SCons/Tool/ninja_tool/NinjaState.py b/SCons/Tool/ninja_tool/NinjaState.py index ef11b5038a..be64a13065 100644 --- a/SCons/Tool/ninja_tool/NinjaState.py +++ b/SCons/Tool/ninja_tool/NinjaState.py @@ -694,9 +694,8 @@ def check_generated_source_deps(build): pass # wait for the server process to fully killed - # TODO: update wait_for_process_to_die() to handle timeout and then catch exception - # here and do something smart. - wait_for_process_to_die(pid) + # TODO: catch TimeoutException here and possibly do something smart. + wait_for_process_to_die(pid, timeout=10) if os.path.exists(scons_daemon_dirty): os.unlink(scons_daemon_dirty) diff --git a/SCons/Util/UtilTests.py b/SCons/Util/UtilTests.py index 0f457c3ce0..57ea2c2cd3 100644 --- a/SCons/Util/UtilTests.py +++ b/SCons/Util/UtilTests.py @@ -27,6 +27,7 @@ import hashlib import io import os +import subprocess import sys import unittest import unittest.mock @@ -73,6 +74,7 @@ to_String, to_bytes, to_str, + wait_for_process_to_die, ) from SCons.Util.envs import is_valid_construction_var from SCons.Util.hashes import ( @@ -847,6 +849,27 @@ def test_intern(self) -> None: s4 = silent_intern("spam") assert id(s1) == id(s4) + def test_wait_for_process_to_die_success(self) -> None: + cmd = [sys.executable, "-c", ""] + p = subprocess.Popen(cmd) + p.wait() + wait_for_process_to_die(p.pid, timeout=10.0) + + def test_wait_for_process_to_die_timeout(self) -> None: + # Run a python script that will keep running until we close it's stdin + cmd = [sys.executable, "-c", "import sys; data = sys.stdin.read()"] + p = subprocess.Popen(cmd, stdin=subprocess.PIPE) + + # wait_for_process_to_die() should time out while the process is running + with self.assertRaises(TimeoutError): + wait_for_process_to_die(p.pid, timeout=0.2) + + p.stdin.close() + p.wait() + + # wait_for_process_to_die() should complete normally now + wait_for_process_to_die(p.pid, timeout=10.0) + class HashTestCase(unittest.TestCase): diff --git a/SCons/Util/__init__.py b/SCons/Util/__init__.py index 0398c1fad7..5bd84dd775 100644 --- a/SCons/Util/__init__.py +++ b/SCons/Util/__init__.py @@ -1297,36 +1297,56 @@ def print_time(): return print_time -def wait_for_process_to_die(pid) -> None: +def wait_for_process_to_die(pid: int, timeout: float = 60.0) -> None: """ - Wait for specified process to die, or alternatively kill it - NOTE: This function operates best with psutil pypi package - TODO: Add timeout which raises exception + Wait for the specified process to die. + + A TimeoutError will be thrown if the timeout is hit before the process terminates. + Specifying a negative timeout will cause wait_for_process_to_die() to wait + indefinitely, and never throw TimeoutError. """ - # wait for the process to fully killed - try: - import psutil # pylint: disable=import-outside-toplevel - while True: - if pid not in [proc.pid for proc in psutil.process_iter()]: - break - time.sleep(0.1) - except ImportError: - # if psutil is not installed we can do this the hard way - while True: - if sys.platform == 'win32': - import ctypes # pylint: disable=import-outside-toplevel - PROCESS_QUERY_INFORMATION = 0x1000 - processHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid) - if processHandle == 0: - break - ctypes.windll.kernel32.CloseHandle(processHandle) - time.sleep(0.1) - else: - try: - os.kill(pid, 0) - except OSError: - break - time.sleep(0.1) + start_time = time.time() + while True: + if not _is_process_alive(pid): + break + if timeout >= 0.0 and time.time() - start_time > timeout: + raise TimeoutError(f"timed out waiting for process {pid}") + time.sleep(0.1) + + +if sys.platform == 'win32': + def _is_process_alive(pid: int) -> bool: + import ctypes # pylint: disable=import-outside-toplevel + PROCESS_QUERY_INFORMATION = 0x1000 + STILL_ACTIVE = 259 + + processHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid) + if processHandle == 0: + return False + + # OpenProcess() may successfully return a handle even for terminated + # processes when something else in the system is still holding a + # reference to their handle. Call GetExitCodeProcess() to check if the + # process has already exited. + try: + exit_code = ctypes.c_ulong() + success = ctypes.windll.kernel32.GetExitCodeProcess( + processHandle, ctypes.byref(exit_code)) + if success: + return exit_code.value == STILL_ACTIVE + finally: + ctypes.windll.kernel32.CloseHandle(processHandle) + + return True + +else: + def _is_process_alive(pid: int) -> bool: + try: + os.kill(pid, 0) + return True + except OSError: + return False + # From: https://stackoverflow.com/questions/1741972/how-to-use-different-formatters-with-the-same-logging-handler-in-python class DispatchingFormatter(Formatter): From 4848319545cdc2f984e83d31b328cad766afcc9a Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Mon, 24 Feb 2025 19:22:01 -0800 Subject: [PATCH 257/386] Apply review feedback --- CHANGES.txt | 4 ++++ RELEASE.txt | 3 +++ SCons/Tool/ninja_tool/NinjaState.py | 5 ++-- SCons/Util/UtilTests.py | 37 ++++++++++++++++------------- SCons/Util/__init__.py | 22 +++++++++++++---- 5 files changed, 48 insertions(+), 23 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 60b6bda6f4..d66314ea4e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -214,6 +214,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER `local_pool`, hoping it will fix a race condition that can occurs when Ninja defers to SCons to build. + From Adam Simpkins: + - Fixed a hang in `wait_for_process_to_die()` on Windows, affecting + clean-up of the SCons daemon used for Ninja builds. + RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 0d1b63763a..75fa5ada21 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -183,6 +183,9 @@ FIXES - Handle case of "memoizer" as one member of a comma-separated --debug string - this was previously missed. +- Fixed a hang in `wait_for_process_to_die()` on Windows, affecting + clean-up of the SCons daemon used for Ninja builds. + IMPROVEMENTS ------------ diff --git a/SCons/Tool/ninja_tool/NinjaState.py b/SCons/Tool/ninja_tool/NinjaState.py index be64a13065..ef11b5038a 100644 --- a/SCons/Tool/ninja_tool/NinjaState.py +++ b/SCons/Tool/ninja_tool/NinjaState.py @@ -694,8 +694,9 @@ def check_generated_source_deps(build): pass # wait for the server process to fully killed - # TODO: catch TimeoutException here and possibly do something smart. - wait_for_process_to_die(pid, timeout=10) + # TODO: update wait_for_process_to_die() to handle timeout and then catch exception + # here and do something smart. + wait_for_process_to_die(pid) if os.path.exists(scons_daemon_dirty): os.unlink(scons_daemon_dirty) diff --git a/SCons/Util/UtilTests.py b/SCons/Util/UtilTests.py index 57ea2c2cd3..88ff923a6a 100644 --- a/SCons/Util/UtilTests.py +++ b/SCons/Util/UtilTests.py @@ -33,6 +33,7 @@ import unittest.mock import warnings from collections import UserDict, UserList, UserString, namedtuple +from typing import Callable import TestCmd @@ -75,6 +76,7 @@ to_bytes, to_str, wait_for_process_to_die, + _wait_for_process_to_die_non_psutil, ) from SCons.Util.envs import is_valid_construction_var from SCons.Util.hashes import ( @@ -84,6 +86,13 @@ _set_allowed_viable_default_hashes, ) +try: + import psutil + has_psutil = True +except ImportError: + has_psutil = False + + # These Util classes have no unit tests. Some don't make sense to test? # DisplayEngine, Delegate, MethodWrapper, UniqueList, Unbuffered, Null, NullSeq @@ -849,26 +858,20 @@ def test_intern(self) -> None: s4 = silent_intern("spam") assert id(s1) == id(s4) - def test_wait_for_process_to_die_success(self) -> None: - cmd = [sys.executable, "-c", ""] - p = subprocess.Popen(cmd) - p.wait() - wait_for_process_to_die(p.pid, timeout=10.0) + @unittest.skipUnless(has_psutil, "requires psutil") + def test_wait_for_process_to_die_success_psutil(self) -> None: + self._test_wait_for_process(wait_for_process_to_die) - def test_wait_for_process_to_die_timeout(self) -> None: - # Run a python script that will keep running until we close it's stdin - cmd = [sys.executable, "-c", "import sys; data = sys.stdin.read()"] - p = subprocess.Popen(cmd, stdin=subprocess.PIPE) + def test_wait_for_process_to_die_success_non_psutil(self) -> None: + self._test_wait_for_process(_wait_for_process_to_die_non_psutil) - # wait_for_process_to_die() should time out while the process is running - with self.assertRaises(TimeoutError): - wait_for_process_to_die(p.pid, timeout=0.2) - - p.stdin.close() + def _test_wait_for_process( + self, wait_fn: Callable[[int], None] + ) -> None: + cmd = [sys.executable, "-c", ""] + p = subprocess.Popen(cmd) p.wait() - - # wait_for_process_to_die() should complete normally now - wait_for_process_to_die(p.pid, timeout=10.0) + wait_fn(p.pid) class HashTestCase(unittest.TestCase): diff --git a/SCons/Util/__init__.py b/SCons/Util/__init__.py index 5bd84dd775..ff693c8d7c 100644 --- a/SCons/Util/__init__.py +++ b/SCons/Util/__init__.py @@ -1297,14 +1297,28 @@ def print_time(): return print_time -def wait_for_process_to_die(pid: int, timeout: float = 60.0) -> None: +def wait_for_process_to_die(pid: int) -> None: """ Wait for the specified process to die. - A TimeoutError will be thrown if the timeout is hit before the process terminates. - Specifying a negative timeout will cause wait_for_process_to_die() to wait - indefinitely, and never throw TimeoutError. + TODO: Add timeout which raises exception """ + # wait for the process to fully killed + try: + import psutil # pylint: disable=import-outside-toplevel + while True: + # TODO: this should use psutil.process_exists() or psutil.Process.wait() + # The psutil docs explicitly recommend against using process_iter()/pids() + # for checking the existence of a process. + if pid not in [proc.pid for proc in psutil.process_iter()]: + break + time.sleep(0.1) + except ImportError: + # if psutil is not installed we can do this the hard way + _wait_for_process_to_die_non_psutil(pid, timeout=-1.0) + + +def _wait_for_process_to_die_non_psutil(pid: int, timeout: float = 60.0) -> None: start_time = time.time() while True: if not _is_process_alive(pid): From e355bff354f32241d932a68ff702619a9ef3adec Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 28 Feb 2025 12:20:53 -0700 Subject: [PATCH 258/386] Framework: fixture methods now handle lists for dstdir TestCmd.dir_fixture and TestCmd.file_fixture are described as accepting a list for both srcdir and dstdir. For example, the docstring for `dir_fixture`: srcdir or dstdir may be a list, in which case the elements are first joined into a pathname. However, the implementation only handled this for srcdir/srcfile. Added the same stanza for dstdir/dstfile.. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ testing/framework/TestCmd.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 60b6bda6f4..efb55abdb3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -208,6 +208,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER --debug string - this was previously missed. - test YACC/live.py fixed - finally started failing on an "old-style" (K&R flavor) function declaration, updated. + - Test framework - add recognizing list-of-path-components for + the destination of fixtures too (matches docstrings now). From Adam Scott: - Changed Ninja's TEMPLATE rule pool to use `install_pool` instead of diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 8e799b995d..059ad608b6 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -1454,6 +1454,9 @@ def dir_fixture(self, srcdir, dstdir=None) -> None: """ if is_List(srcdir): srcdir = os.path.join(*srcdir) + if is_List(dstdir): + dstdir = os.path.join(*dstdir) + spath = srcdir if srcdir and self.fixture_dirs and not os.path.isabs(srcdir): for dir in self.fixture_dirs: @@ -1504,6 +1507,8 @@ def file_fixture(self, srcfile, dstfile=None) -> None: """ if is_List(srcfile): srcfile = os.path.join(*srcfile) + if is_List(dstfile): + dstfile = os.path.join(*dstfile) srcpath, srctail = os.path.split(srcfile) spath = srcfile From 8807cc0ad833655cd1ca29da86e1037f597436a5 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 2 Mar 2025 13:12:34 -0800 Subject: [PATCH 259/386] Update NEXT_RELEASE to 4.9.0 --- SCons/Environment.py | 6 +++--- SCons/Environment.xml | 4 ++-- SCons/EnvironmentTests.py | 8 ++++---- SCons/SConf.py | 4 ++-- SCons/Scanner/C.py | 2 +- SCons/Script/SConsOptions.py | 2 +- SCons/Script/SConscript.py | 2 +- SCons/Script/__init__.py | 2 +- SCons/Variables/PackageVariable.py | 2 +- SCons/Variables/__init__.py | 2 +- doc/generated/builders.gen | 2 +- doc/generated/functions.gen | 4 ++-- doc/man/scons.xml | 8 ++++---- template/RELEASE.txt | 2 +- 14 files changed, 25 insertions(+), 25 deletions(-) diff --git a/SCons/Environment.py b/SCons/Environment.py index 468da237df..c18808db5b 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -1731,7 +1731,7 @@ def Dictionary(self, *args: str, as_dict: bool = False): Raises: KeyError: if any of *args* is not in the construction environment. - .. versionchanged:: NEXT_RELEASE + .. versionchanged:: 4.9.0 Added the *as_dict* keyword arg to specify always returning a dict. """ if not args: @@ -1763,7 +1763,7 @@ def Dump(self, *key: str, format: str = 'pretty') -> str: Raises: ValueError: *format* is not a recognized serialization format. - .. versionchanged:: NEXT_RELEASE + .. versionchanged:: 4.9.0 *key* is no longer limited to a single construction variable name. If *key* is supplied, a formatted dictionary is generated like the no-arg case - previously a single *key* displayed just the value. @@ -2746,7 +2746,7 @@ def Dictionary(self, *args, as_dict: bool = False): Raises: KeyError: if any of *args* is not in the construction environment. - .. versionchanged: NEXT_RELEASE + .. versionchanged: 4.9.0 Added the *as_dict* keyword arg to always return a dict. """ d = {} diff --git a/SCons/Environment.xml b/SCons/Environment.xml index f24c2af3ea..e59ab02f7b 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -1656,7 +1656,7 @@ can be verified. -Changed in NEXT_RELEASE: +Changed in 4.9.0: as_dict added. @@ -1745,7 +1745,7 @@ the JSON equivalent of a &Python; dict.. -Changed in NEXT_RELEASE: +Changed in 4.9.0: More than one key can be specified. The returned string always looks like a dict (or equivalent in other formats); diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index 22b9074705..9f93fd2e91 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -2137,7 +2137,7 @@ def test_Dictionary(self) -> None: xxx, zzz = env.Dictionary('XXX', 'ZZZ') assert xxx == 'x' assert zzz == 'z' - # added in NEXT_RELEASE: as_dict flag + # added in 4.9.0: as_dict flag with self.subTest(): expect = {'XXX': 'x'} self.assertEqual(env.Dictionary('XXX', as_dict=True), expect) @@ -3211,7 +3211,7 @@ def test_Dump(self) -> None: """Test the Dump() method""" env = self.TestEnvironment(FOO='foo', FOOFLAGS=CLVar('--bar --baz')) - # changed in NEXT_RELEASE: single arg now displays as a dict, + # changed in 4.9.0: single arg now displays as a dict, # not a bare value; more than one arg is allowed. with self.subTest(): # one-arg version self.assertEqual(env.Dump('FOO'), "{'FOO': 'foo'}") @@ -3851,7 +3851,7 @@ def test___delitem__(self) -> None: """Test deleting variables from an OverrideEnvironment""" env, env2, env3 = self.envs - # changed in NEXT_RELEASE: delete does not cascade to underlying envs + # changed in 4.9.0: delete does not cascade to underlying envs # XXX is in all three, del from env3 should affect only it del env3['XXX'] with self.subTest(): @@ -3947,7 +3947,7 @@ def test_Dictionary(self) -> None: # test deletion in top override del env3['XXX'] self.assertRaises(KeyError, env3.Dictionary, 'XXX') - # changed in NEXT_RELEASE: *not* deleted from underlying envs + # changed in 4.9.0: *not* deleted from underlying envs assert 'XXX' in env2.Dictionary() assert 'XXX' in env.Dictionary() diff --git a/SCons/SConf.py b/SCons/SConf.py index 36bb9142fa..7d7874bdd7 100644 --- a/SCons/SConf.py +++ b/SCons/SConf.py @@ -1108,7 +1108,7 @@ def CheckLib(context, library = None, symbol: str = "main", Note that library may also be None to test whether the given symbol compiles without flags. - .. versionchanged:: NEXT_RELEASE + .. versionchanged:: 4.9.0 Added the *extra_libs* keyword parameter. The actual implementation is in :func:`SCons.Conftest.CheckLib` which already accepted this parameter, so this is only exposing existing functionality. @@ -1140,7 +1140,7 @@ def CheckLibWithHeader(context, libs, header, language, As in :func:`CheckLib`, we support library=None, to test if the call compiles without extra link flags. - .. versionchanged:: NEXT_RELEASE + .. versionchanged:: 4.9.0 Added the *extra_libs* keyword parameter. The actual implementation is in :func:`SCons.Conftest.CheckLib` which already accepted this parameter, so this is only exposing existing functionality. diff --git a/SCons/Scanner/C.py b/SCons/Scanner/C.py index 1d7e101e4e..bfd897dcb1 100644 --- a/SCons/Scanner/C.py +++ b/SCons/Scanner/C.py @@ -89,7 +89,7 @@ def dictify_CPPDEFINES(env, replace: bool = False) -> dict: Args: replace: if true, simulate macro replacement - .. versionchanged:: NEXT_RELEASE + .. versionchanged:: 4.9.0 Simple macro replacement added, and *replace* arg to enable it. """ def _replace(mapping: Dict) -> Dict: diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index ef27b70669..2690a086c9 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -242,7 +242,7 @@ class SConsOption(optparse.Option): syntax from :mod:`argparse`, and is added to the ``CHECK_METHODS`` list. Overridden :meth:`convert_value` supports this usage. - .. versionchanged:: NEXT_RELEASE + .. versionchanged:: 4.9.0 The *settable* attribute is added to ``ATTRS``, allowing it to be set in the option. A parameter to mark the option settable was added in 4.8.0, but was not initially made part of the option object itself. diff --git a/SCons/Script/SConscript.py b/SCons/Script/SConscript.py index fa6fce7754..f98cf3b21a 100644 --- a/SCons/Script/SConscript.py +++ b/SCons/Script/SConscript.py @@ -552,7 +552,7 @@ def Help(self, text, append: bool = False, local_only: bool = False) -> None: .. versionchanged:: 4.6.0 The *keep_local* parameter was added. - .. versionchanged:: NEXT_RELEASE + .. versionchanged:: 4.9.0 The *keep_local* parameter was renamed *local_only* to match manpage """ text = self.subst(text, raw=1) diff --git a/SCons/Script/__init__.py b/SCons/Script/__init__.py index 0243a9cf67..cf6c9de31f 100644 --- a/SCons/Script/__init__.py +++ b/SCons/Script/__init__.py @@ -267,7 +267,7 @@ def HelpFunction(text, append: bool = False, local_only: bool = False) -> None: .. versionchanged:: 4.6.0 The *keep_local* parameter was added. - .. versionchanged:: NEXT_RELEASE + .. versionchanged:: 4.9.0 The *keep_local* parameter was renamed *local_only* to match manpage """ global help_text diff --git a/SCons/Variables/PackageVariable.py b/SCons/Variables/PackageVariable.py index 2ecedfe77a..c615ac4240 100644 --- a/SCons/Variables/PackageVariable.py +++ b/SCons/Variables/PackageVariable.py @@ -71,7 +71,7 @@ def _converter(val: str | bool, default: str) -> str | bool: *default* unless *default* is an enabling or disabling string, in which case ignore *default* and return ``True``. - .. versionchanged: NEXT_RELEASE + .. versionchanged: 4.9.0 Now returns the default in case of a truthy value, matching what the public documentation always claimed, except if the default looks like one of the true/false strings. diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 2ac95d0673..ca18432153 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -95,7 +95,7 @@ class Variables: .. deprecated:: 4.8.0 *is_global* is deprecated. - .. versionadded:: NEXT_RELEASE + .. versionadded:: 4.9.0 The :attr:`defaulted` attribute now lists those variables which were filled in from default values. """ diff --git a/doc/generated/builders.gen b/doc/generated/builders.gen index cfcb361768..cb04e7ccdb 100644 --- a/doc/generated/builders.gen +++ b/doc/generated/builders.gen @@ -479,7 +479,7 @@ a "live" location in the system. -See also &FindInstalledFiles;. +See also &f-link-FindInstalledFiles;. For more thoughts on installation, see the User Guide (particularly the section on Command-Line Targets and the chapters on Installing Files and on Alias Targets). diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index b5fcfcc7ac..26f21c7f67 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -1644,7 +1644,7 @@ can be verified. -Changed in NEXT_RELEASE: +Changed in 4.9.0: as_dict added. @@ -1726,7 +1726,7 @@ the JSON equivalent of a &Python; dict.. -Changed in NEXT_RELEASE: +Changed in 4.9.0: More than one key can be specified. The returned string always looks like a dict (or equivalent in other formats); diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 5a05466ba0..4d3c7d46e5 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -105,7 +105,7 @@ The CPython project retired 3.6 in Sept 2021: . -Changed in version NEXT_RELEASE: +Changed in version 4.9.0: support for &Python; 3.6 is removed. @@ -4221,7 +4221,7 @@ at least one should be supplied. parameters. -Changed in version NEXT_RELEASE: added the +Changed in version 4.9.0: added the extra_libs parameter. @@ -4286,7 +4286,7 @@ it will not be added again. The default is False. parameters. -Changed in version NEXT_RELEASE: added the +Changed in version 4.9.0: added the extra_libs parameter. @@ -5261,7 +5261,7 @@ This is the same information that is returned by the -Added in NEXT_RELEASE: +Added in 4.9.0: the defaulted attribute. diff --git a/template/RELEASE.txt b/template/RELEASE.txt index 77ce83b327..128daa16fe 100755 --- a/template/RELEASE.txt +++ b/template/RELEASE.txt @@ -6,7 +6,7 @@ Past official release announcements appear at: ================================================================== -A new SCons release, NEXT_RELEASE, is now available on the SCons download page: +A new SCons release, 4.9.0, is now available on the SCons download page: https://scons.org/pages/download.html From aa3cbbf83f78425d94ff12c1d95683af2500ec05 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 2 Mar 2025 13:15:20 -0800 Subject: [PATCH 260/386] Regenerated docs for 4.9.0 release. --- .../examples/caching_ex-random_1.xml | 2 +- .../examples/troubleshoot_explain1_3.xml | 2 +- .../examples/troubleshoot_stacktrace_2.xml | 2 +- .../troubleshoot_taskmastertrace_1.xml | 50 +++++++++---------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml index d7e5ecca36..da1fc2d5d9 100644 --- a/doc/generated/examples/caching_ex-random_1.xml +++ b/doc/generated/examples/caching_ex-random_1.xml @@ -1,7 +1,7 @@ % scons -Q cc -o f2.o -c f2.c -cc -o f4.o -c f4.c cc -o f3.o -c f3.c +cc -o f4.o -c f4.c cc -o f1.o -c f1.c cc -o f5.o -c f5.c cc -o prog f1.o f2.o f3.o f4.o f5.o diff --git a/doc/generated/examples/troubleshoot_explain1_3.xml b/doc/generated/examples/troubleshoot_explain1_3.xml index d301ff3229..e658d89fd2 100644 --- a/doc/generated/examples/troubleshoot_explain1_3.xml +++ b/doc/generated/examples/troubleshoot_explain1_3.xml @@ -2,5 +2,5 @@ cp file.in file.oout scons: warning: Cannot find target file.out after building -File "/Users/bdbaddog/devel/scons/git/users/prs/scripts/scons.py", line 97, in <module> +File "/Users/bdbaddog/devel/scons/git/as_scons/scripts/scons.py", line 97, in <module> diff --git a/doc/generated/examples/troubleshoot_stacktrace_2.xml b/doc/generated/examples/troubleshoot_stacktrace_2.xml index 4db0f9d8d8..068a1e148d 100644 --- a/doc/generated/examples/troubleshoot_stacktrace_2.xml +++ b/doc/generated/examples/troubleshoot_stacktrace_2.xml @@ -3,7 +3,7 @@ scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'. scons: internal stack trace: File "SCons/Taskmaster/Job.py", line 670, in _work task.prepare() - File "SCons/Script/Main.py", line 208, in prepare + File "SCons/Script/Main.py", line 209, in prepare return SCons.Taskmaster.OutOfDateTask.prepare(self) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "SCons/Taskmaster/__init__.py", line 195, in prepare diff --git a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml index fdc755d32c..d08e179db1 100644 --- a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml +++ b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml @@ -1,8 +1,8 @@ % scons -Q --taskmastertrace=- prog -Job.NewParallel._work(): [Thread:8445988672] Gained exclusive access -Job.NewParallel._work(): [Thread:8445988672] Starting search -Job.NewParallel._work(): [Thread:8445988672] Found 0 completed tasks to process -Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks +Job.NewParallel._work(): [Thread:8646594432] Gained exclusive access +Job.NewParallel._work(): [Thread:8646594432] Starting search +Job.NewParallel._work(): [Thread:8646594432] Found 0 completed tasks to process +Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'prog'> and its children: @@ -18,12 +18,12 @@ Taskmaster: Evaluating <pending 0 'prog.c'> Task.make_ready_current(): node <pending 0 'prog.c'> Task.prepare(): node <up_to_date 0 'prog.c'> -Job.NewParallel._work(): [Thread:8445988672] Found internal task +Job.NewParallel._work(): [Thread:8646594432] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'prog.c'> Task.postprocess(): node <up_to_date 0 'prog.c'> Task.postprocess(): removing <up_to_date 0 'prog.c'> Task.postprocess(): adjusted parent ref count <pending 1 'prog.o'> -Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks +Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'inc.h'> and its children: @@ -31,12 +31,12 @@ Taskmaster: Evaluating <pending 0 'inc.h'> Task.make_ready_current(): node <pending 0 'inc.h'> Task.prepare(): node <up_to_date 0 'inc.h'> -Job.NewParallel._work(): [Thread:8445988672] Found internal task +Job.NewParallel._work(): [Thread:8646594432] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'inc.h'> Task.postprocess(): node <up_to_date 0 'inc.h'> Task.postprocess(): removing <up_to_date 0 'inc.h'> Task.postprocess(): adjusted parent ref count <pending 0 'prog.o'> -Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks +Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog.o'> and its children: @@ -46,19 +46,19 @@ Taskmaster: Evaluating <pending 0 'prog.o'> Task.make_ready_current(): node <pending 0 'prog.o'> Task.prepare(): node <executing 0 'prog.o'> -Job.NewParallel._work(): [Thread:8445988672] Found task requiring execution -Job.NewParallel._work(): [Thread:8445988672] Executing task +Job.NewParallel._work(): [Thread:8646594432] Found task requiring execution +Job.NewParallel._work(): [Thread:8646594432] Executing task Task.execute(): node <executing 0 'prog.o'> cc -o prog.o -c -I. prog.c -Job.NewParallel._work(): [Thread:8445988672] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8445988672] Gained exclusive access -Job.NewParallel._work(): [Thread:8445988672] Starting search -Job.NewParallel._work(): [Thread:8445988672] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8646594432] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8646594432] Gained exclusive access +Job.NewParallel._work(): [Thread:8646594432] Starting search +Job.NewParallel._work(): [Thread:8646594432] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog.o'> Task.postprocess(): node <executed 0 'prog.o'> Task.postprocess(): removing <executed 0 'prog.o'> Task.postprocess(): adjusted parent ref count <pending 0 'prog'> -Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks +Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog'> and its children: @@ -67,21 +67,21 @@ Taskmaster: Evaluating <pending 0 'prog'> Task.make_ready_current(): node <pending 0 'prog'> Task.prepare(): node <executing 0 'prog'> -Job.NewParallel._work(): [Thread:8445988672] Found task requiring execution -Job.NewParallel._work(): [Thread:8445988672] Executing task +Job.NewParallel._work(): [Thread:8646594432] Found task requiring execution +Job.NewParallel._work(): [Thread:8646594432] Executing task Task.execute(): node <executing 0 'prog'> cc -o prog prog.o -Job.NewParallel._work(): [Thread:8445988672] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8445988672] Gained exclusive access -Job.NewParallel._work(): [Thread:8445988672] Starting search -Job.NewParallel._work(): [Thread:8445988672] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8646594432] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8646594432] Gained exclusive access +Job.NewParallel._work(): [Thread:8646594432] Starting search +Job.NewParallel._work(): [Thread:8646594432] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog'> Task.postprocess(): node <executed 0 'prog'> -Job.NewParallel._work(): [Thread:8445988672] Searching for new tasks +Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. -Job.NewParallel._work(): [Thread:8445988672] Found no task requiring execution, and have no jobs: marking complete -Job.NewParallel._work(): [Thread:8445988672] Gained exclusive access -Job.NewParallel._work(): [Thread:8445988672] Completion detected, breaking from main loop +Job.NewParallel._work(): [Thread:8646594432] Found no task requiring execution, and have no jobs: marking complete +Job.NewParallel._work(): [Thread:8646594432] Gained exclusive access +Job.NewParallel._work(): [Thread:8646594432] Completion detected, breaking from main loop From 66f8972b546e12e0b8bf31648a2a076d706daec0 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 2 Mar 2025 13:22:15 -0800 Subject: [PATCH 261/386] Updates for 4.9.0 release --- CHANGES.txt | 6 +++--- RELEASE.txt | 54 ++++++++++++++++----------------------------------- ReleaseConfig | 4 ++-- SConstruct | 2 +- 4 files changed, 23 insertions(+), 43 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index efb55abdb3..6c1694a14d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,10 +7,10 @@ NOTE: The 4.0.0 release of SCons dropped Python 2.7 support. Use 3.1.2 if Python 2.7 support is required (but note old SCons releases are unsupported). NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. -NOTE: Python 3.6 support is deprecated and will be dropped in a future release. - python.org no longer supports 3.6 or 3.7, and will drop 3.8 in Oct. 2024. +NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. -RELEASE VERSION/DATE TO BE FILLED IN LATER + +RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 From Ruben Di Battista: - Expose `extra_libs` kwarg in Configure checks `CheckLibWithHeader` diff --git a/RELEASE.txt b/RELEASE.txt index 0d1b63763a..fad8724f47 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,35 +1,16 @@ -If you are reading this in the git repository, the contents -refer to *unreleased* changes since the last SCons release. -Past official release announcements appear at: - - https://scons.org/tag/releases.html - -================================================================== - -A new SCons release, 4.4.1, is now available on the SCons download page: +A new SCons release, 4.9.0, is now available on the SCons download page: https://scons.org/pages/download.html +Here is a summary of the changes since 4.8.1: -Here is a summary of the changes since 4.4.0: - -NEW FUNCTIONALITY ------------------ - -- List new features (presumably why a checkpoint is being released) - -DEPRECATED FUNCTIONALITY ------------------------- +NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. -- List anything that's been deprecated since the last release CHANGED/ENHANCED EXISTING FUNCTIONALITY --------------------------------------- - Expose the `extra_libs` keyword argument in `CheckLibWithHeader` and 'CheckLib' -- List modifications to existing features, where the previous behavior - wouldn't actually be considered a bug - - Removed Python 3.6 support. - Override environments, created when giving construction environment @@ -186,10 +167,6 @@ FIXES IMPROVEMENTS ------------ -- List improvements that wouldn't be visible to the user in the - documentation: performance improvements (describe the circumstances - under which they would be observed), or major code cleanups - - For consistency with the optparse "add_option" method, AddOption accepts an SConsOption object as a single argument (this failed previously). Calling AddOption with the full set of arguments (option names and @@ -202,18 +179,10 @@ IMPROVEMENTS for clang-cl, the version of the frontend that uses cl.exe-compatible command line switches. -PACKAGING ---------- - -- List changes in the way SCons is packaged and/or released DOCUMENTATION ------------- -- List any significant changes to the documentation (not individual - typo fixes, even if they're mentioned in src/CHANGES.txt to give - the contributor credit) - - Some manpage cleanup for the gettext and pdf/ps builders. - Some clarifications in the User Guide "Environments" chapter. @@ -233,8 +202,6 @@ DOCUMENTATION DEVELOPMENT ----------- -- List visible changes in the way SCons is developed - - Ruff/Mypy: Excluded items now synced. - Ruff: Linter includes new rules - `FA`, `UP006`, `UP007`, and `UP037` - to @@ -254,4 +221,17 @@ Thanks to the following contributors listed below for their contributions to thi ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.0.1..HEAD + git shortlog --no-merges -ns 4.8.1..HEAD + 50 Mats Wichmann + 46 William Deegan + 19 Joseph Brill + 10 Alex Thiessen + 4 Thaddeus Crews + 3 Ruben Di Battista + 2 Adam Scott + 2 Keith F. Prussing + 2 Prabhu Singh Khalsa + 1 Adam Simpkins + 1 Alex James + 1 Yevhen Babiichuk (DustDFG) + diff --git a/ReleaseConfig b/ReleaseConfig index 3ec9df0973..487a3c7a28 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -31,7 +31,7 @@ # If the release type is not 'final', the patchlevel is set to the # release date. This value is mandatory and must be present in this file. #version_tuple = (2, 2, 0, 'final', 0) -version_tuple = (4, 8, 2, 'a', 0) +version_tuple = (4, 9,0) # Python versions prior to unsupported_python_version cause a fatal error # when that version is used. Python versions prior to deprecate_python_version @@ -50,7 +50,7 @@ deprecated_python_version = (3, 7, 0) #month_year = 'December 2012' # If copyright years is not given, the release year is used as the end. -copyright_years = '2001 - 2024' +copyright_years = '2001 - 2025' # Local Variables: # tab-width:4 diff --git a/SConstruct b/SConstruct index 7a03f435e6..4a53dbdaec 100644 --- a/SConstruct +++ b/SConstruct @@ -36,7 +36,7 @@ copyright_years = strftime('2001 - %Y') # This gets inserted into the man pages to reflect the month of release. month_year = strftime('%B %Y') project = 'scons' -default_version = '4.8.2' +default_version = '4.9.0' copyright = f"Copyright (c) {copyright_years} The SCons Foundation" # We let the presence or absence of various utilities determine whether From 0ac18d76365e812ea0302586dc33f58a80ca387a Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 2 Mar 2025 13:29:17 -0800 Subject: [PATCH 262/386] updates for release --- SCons/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/SCons/__init__.py b/SCons/__init__.py index bbf9eebe6c..143512515b 100644 --- a/SCons/__init__.py +++ b/SCons/__init__.py @@ -1,9 +1,9 @@ -__version__="4.8.1" -__copyright__="Copyright (c) 2001 - 2024 The SCons Foundation" +__version__="4.9.0" +__copyright__="Copyright (c) 2001 - 2025 The SCons Foundation" __developer__="bdbaddog" -__date__="Tue, 03 Sep 2024 17:46:32 -0700" +__date__="Sun, 02 Mar 2025 13:25:06 -0700" __buildsys__="M1Dog2021" -__revision__="08661ed4c552323ef3a7f0ff1af38868cbabb05e" -__build__="08661ed4c552323ef3a7f0ff1af38868cbabb05e" +__revision__="66f8972b546e12e0b8bf31648a2a076d706daec0" +__build__="66f8972b546e12e0b8bf31648a2a076d706daec0" # make sure compatibility is always in place import SCons.compat # noqa \ No newline at end of file From 99a8c86de1ce91d23b102520e185c54ebd968924 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 2 Mar 2025 13:43:13 -0800 Subject: [PATCH 263/386] minor tweaks to RELEASE.txt --- RELEASE.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE.txt b/RELEASE.txt index fad8724f47..d9e1547220 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -28,12 +28,14 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - MSVS: Add an optional keyword argument, auto_filter_projects, to MSVSSolution. Accepted values for auto_filter_projects are: + - None [default]: raise an exception when solution file names or nodes are detected in the projects argument list. - True or evaluates True: automatically remove solution file names and nodes from the project argument list. - False or evaluates False: leave solution file names and nodes in the project argument list. An exception is not raised. + Solution file names and/or nodes in the project argument list cause erroneous Project records to be produced in the generated solution file. As a convenience, a user may elect to ignore solution file names and nodes @@ -41,6 +43,7 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY names and nodes from the MSVSProject return values. - SCons C preprocessor: + - Update the optional integer suffixes to include the z|Z and wb|WB suffixes. - Add support for binary integer constants. From f6f2fd98b2a94871b506e517873af4d6faf04ed9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 2 Mar 2025 14:15:50 -0800 Subject: [PATCH 264/386] updates --- RELEASE.txt | 2 +- SCons/__init__.py | 6 +++--- pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/RELEASE.txt b/RELEASE.txt index d9e1547220..da03a385ca 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -35,7 +35,7 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY nodes from the project argument list. - False or evaluates False: leave solution file names and nodes in the project argument list. An exception is not raised. - + Solution file names and/or nodes in the project argument list cause erroneous Project records to be produced in the generated solution file. As a convenience, a user may elect to ignore solution file names and nodes diff --git a/SCons/__init__.py b/SCons/__init__.py index 143512515b..c492d6c535 100644 --- a/SCons/__init__.py +++ b/SCons/__init__.py @@ -1,9 +1,9 @@ __version__="4.9.0" __copyright__="Copyright (c) 2001 - 2025 The SCons Foundation" __developer__="bdbaddog" -__date__="Sun, 02 Mar 2025 13:25:06 -0700" +__date__="Sun, 02 Mar 2025 14:04:50 -0700" __buildsys__="M1Dog2021" -__revision__="66f8972b546e12e0b8bf31648a2a076d706daec0" -__build__="66f8972b546e12e0b8bf31648a2a076d706daec0" +__revision__="99a8c86de1ce91d23b102520e185c54ebd968924" +__build__="99a8c86de1ce91d23b102520e185c54ebd968924" # make sure compatibility is always in place import SCons.compat # noqa \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 7d47aa2d05..d1f8623561 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,7 @@ namespaces = false [tool.setuptools.package-data] "*" = ["*.txt", "*.rst", "*.1"] -"scons.tool.docbook" = ["*.*"] +"SCons.tool.docbook" = ["*.*"] [tool.distutils.sdist] dist-dir = "build/dist" From fd2015cd0f9628c37c41599d4703fb207922eb99 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 2 Mar 2025 14:21:45 -0800 Subject: [PATCH 265/386] post release --- CHANGES.txt | 14 ++ RELEASE.txt | 239 +++++---------------------------- ReleaseConfig | 2 +- SConstruct | 2 +- doc/user/main.xml | 4 +- testing/framework/TestSCons.py | 2 +- 6 files changed, 51 insertions(+), 212 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6c1694a14d..92e992c847 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,20 @@ NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. +RELEASE VERSION/DATE TO BE FILLED IN LATER + + From John Doe: + + - Whatever John Doe did. + + +RELEASE VERSION/DATE TO BE FILLED IN LATER + + From John Doe: + + - Whatever John Doe did. + + RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 From Ruben Di Battista: diff --git a/RELEASE.txt b/RELEASE.txt index da03a385ca..128daa16fe 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,240 +1,65 @@ -A new SCons release, 4.9.0, is now available on the SCons download page: - - https://scons.org/pages/download.html - -Here is a summary of the changes since 4.8.1: - -NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. - - -CHANGED/ENHANCED EXISTING FUNCTIONALITY ---------------------------------------- -- Expose the `extra_libs` keyword argument in `CheckLibWithHeader` and 'CheckLib' - -- Removed Python 3.6 support. - -- Override environments, created when giving construction environment - keyword arguments to Builder calls (or manually, through the - undocumented Override method), were modified not to "leak" on item deletion. - The item will now not be deleted from the base environment. - -- Added support for tracking beamer themes in the LaTeX scanner. - -- MSVS: msvs project files are always generated before the corresponding - msvs solution files. This changes the behavior of clean for a project - generated with auto_build_solution disabled and explicit solution - generation: when the solution files are cleaned, the project files are - also cleaned. The tests for vs 6.0-7.1 were updated accordingly. - -- MSVS: Add an optional keyword argument, auto_filter_projects, to - MSVSSolution. Accepted values for auto_filter_projects are: - - - None [default]: raise an exception when solution file names or nodes - are detected in the projects argument list. - - True or evaluates True: automatically remove solution file names and - nodes from the project argument list. - - False or evaluates False: leave solution file names and nodes in the - project argument list. An exception is not raised. - - Solution file names and/or nodes in the project argument list cause - erroneous Project records to be produced in the generated solution file. - As a convenience, a user may elect to ignore solution file names and nodes - in the projects argument list rather than manually removing solution file - names and nodes from the MSVSProject return values. - -- SCons C preprocessor: - - - Update the optional integer suffixes to include the z|Z and wb|WB - suffixes. - - Add support for binary integer constants. - - Add support for octal integer constants. Previously, octal integers - were evaluated as decimal integers. A literal zero (0) is treated as an - octal number. - - Change the method for attempted conversion of a define expansion value - to an integer from a literal to a constant expression evaluation. - -- Add a tag to each CacheDir to let systems ignore backing it up - (per https://bford.info/cachedir/). Update the way a CacheDir - is created, since it now has to create two files. - -- The Dictionary method now has an as_dict flag. If true, Dictionary - always returns a dict. The default remains to return different - types depending on whether zero, one, or multiple construction - -- A Variables object now makes available a "defaulted" attribute, - a list of variable names that were set in the environment with - their values taken from the default in the variable description - (if a variable was set to the same value as the default in one - of the input sources, it is not included in this list). - -- If a build Variable is created with no aliases, the name of the - Variable is no longer listed in its aliases. Internally, the name - and aliases are considered together anyway so this should not have - any effect except for being visible to custom help text formatters. - -FIXES ------ - -- PackageVariable now does what the documentation always said it does - if the variable is used on the command line with one of the enabling - string as the value: the variable's default value is produced (previously - it always produced True in this case). - -- Temporary files created by TempFileMunge() are now cleaned up on - scons exit, instead of at the time they're used. Fixes #4595. - -- AddOption now correctly adds short (single-character) options. - Previously an added short option would always report as unknown, - while long option names for the same option worked. Short options - that take a value require the user to specify the value immediately - following the option, with no spaces (e.g. -j5 and not -j 5). - -- Fix a problem with compilation_db component initialization - the - entries for assembler files were not being set up correctly. - -- On Darwin, PermissionErrors are now handled while trying to access - /etc/paths.d. This may occur if SCons is invoked in a sandboxed environment - (such as Nix). - -- Added error handling when creating MSVC detection debug log file specified - by SCONS_MSCOMMON_DEBUG. - -- MSVS: Modify select msvs test scripts to run on platforms not supported by - the msvs/msvc tool implementation via a default host architecture for - unsupported platforms. - -- MSVS: Fixed early loop exit in select msvs test scripts. Select msvs test - scripts were being invoked for msvc version 8.0 only. Additional msvs - tool and test changes due to the msvs test scripts being run for all msvc - versions (i.e., minor test and tool issues went undetected). +If you are reading this in the git repository, the contents +refer to *unreleased* changes since the last SCons release. +Past official release announcements appear at: -- MSVS: for variant build configurations, msvs solution files are - generated in the source directory and a placeholder file is generated in - the variant build directory. This mirrors the behavior of generated - msvs project files. + https://scons.org/tag/releases.html -- MSVS: msvs project files are generated before the corresponding msvs - solution file. User-specified project GUIDs should now be correctly - written to the solution file. +================================================================== -- SCons C preprocessor: Preserve literals that contain valid integer - substring specifications. Previously, the integer suffix could be - stripped from a symbol that contained an integer and suffix substring. - -- SCons C preprocessor: Update the optional integer suffixes to include - support for the alternate orderings of unsigned with long or long long as - defined in the c/cpp grammar. +A new SCons release, 4.9.0, is now available on the SCons download page: -- SCons C preprocessor: Update the optional integer suffixes for case - insensitive specifications as defined in the c/cpp grammar. + https://scons.org/pages/download.html -- Fix nasm test for missing include file, cleanup. -- Skip running a few validation tests if the user is root and the test is - not designed to work for the root user. +Here is a summary of the changes since 4.4.0: -- Make sure unknown variables from a Variables file are recognized - as such. Previously only unknowns from the command line were - recognized (issue #4645). +NEW FUNCTIONALITY +----------------- -- Update ninja tool to use ninja.BIN_DIR to find pypi packaged ninja binary. - python ninja package version 1.11.1.2 changed the location and previous - logic no longer worked. +- List new features (presumably why a checkpoint is being released) -- The (optional) C Conditional Scanner now does limited macro - replacement on the contents of CPPDEFINES, to improve finding deps - that are conditionally included. Previously replacement was only - done on macro definitions found in the file being scanned. - Only object-like macros are replaced (not function-like), and - only on a whole-word basis; recursion is limited to five levels - and does not error out if that limit is reached (issue #4523). +DEPRECATED FUNCTIONALITY +------------------------ -- Minor modernization: make use of stat object's st_mode, st_mtime - and other attributes rather than indexing into stat return. +- List anything that's been deprecated since the last release -- Ninja's TEMPLATE rule pool changed from `local_pool` to `install_pool` - hoping it will fix a race condition that can occurs when Ninja defers - to SCons to build. +CHANGED/ENHANCED EXISTING FUNCTIONALITY +--------------------------------------- -- Renamed env.Help() & Help()'s argument `keep_local` to `local_only`, previously the documentation - specified `local_only`, but the code and tests were using `keep_local`. The functionality - more closely matches local only. NOTE: It doesn't seem like any code in the wild was using - local_only as we'd not received any reports of such until PR #4606 from hedger. +- List modifications to existing features, where the previous behavior + wouldn't actually be considered a bug -- Fix Issue #2281, AddPreAction() & AddPostAction() were being ignored if no action - was specified when the Alias was initially created. +FIXES +----- -- Handle case of "memoizer" as one member of a comma-separated - --debug string - this was previously missed. +- List fixes of outright bugs IMPROVEMENTS ------------ -- For consistency with the optparse "add_option" method, AddOption accepts - an SConsOption object as a single argument (this failed previously). - Calling AddOption with the full set of arguments (option names and - attributes) to set up the option is still the recommended approach. +- List improvements that wouldn't be visible to the user in the + documentation: performance improvements (describe the circumstances + under which they would be observed), or major code cleanups -- Add clang and clang++ to the default tool search orders for POSIX - and Windows platforms. These will be searched for after gcc and g++, - respectively. Does not affect explicitly requested tool lists. Note: - on Windows, SCons currently only has builtin support for clang, not - for clang-cl, the version of the frontend that uses cl.exe-compatible - command line switches. +PACKAGING +--------- +- List changes in the way SCons is packaged and/or released DOCUMENTATION ------------- -- Some manpage cleanup for the gettext and pdf/ps builders. - -- Some clarifications in the User Guide "Environments" chapter. - -- Clarify documentation of Repository() in manpage and user guide. - -- Many grammatical and spelling fixes in the documentation. - -- Update Clean and NoClean documentation. - -- Improved Variables documentation. - -- Update the User Guide Command() example which now shows a target name - being created from '${SOURCE.base}.out' to use a valid special - attribute and to explain what's being done in the example. +- List any significant changes to the documentation (not individual + typo fixes, even if they're mentioned in src/CHANGES.txt to give + the contributor credit) DEVELOPMENT ----------- -- Ruff/Mypy: Excluded items now synced. - -- Ruff: Linter includes new rules - `FA`, `UP006`, `UP007`, and `UP037` - to - detect and upgrade legacy type-hint syntax. - -- Removed "SCons.Util.sctyping.py", as the functionality can now be substituted - via top-level `from __future__ import annotations`. - -- Implemented type hints for Nodes. - -- Added TestSCons.NINJA_BINARY to TestSCons to centralize logic to find ninja binary - -- Refactored SCons.Tool.ninja -> SCons.Tool.ninja_tool, and added alias so env.Tool('ninja') - will still work. This avoids conflicting with the pypi module ninja. +- List visible changes in the way SCons is developed Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.8.1..HEAD - 50 Mats Wichmann - 46 William Deegan - 19 Joseph Brill - 10 Alex Thiessen - 4 Thaddeus Crews - 3 Ruben Di Battista - 2 Adam Scott - 2 Keith F. Prussing - 2 Prabhu Singh Khalsa - 1 Adam Simpkins - 1 Alex James - 1 Yevhen Babiichuk (DustDFG) - + git shortlog --no-merges -ns 4.0.1..HEAD diff --git a/ReleaseConfig b/ReleaseConfig index 487a3c7a28..9ab93e1a6c 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -31,7 +31,7 @@ # If the release type is not 'final', the patchlevel is set to the # release date. This value is mandatory and must be present in this file. #version_tuple = (2, 2, 0, 'final', 0) -version_tuple = (4, 9,0) +version_tuple = (4, 9, 2, 'a', 0) # Python versions prior to unsupported_python_version cause a fatal error # when that version is used. Python versions prior to deprecate_python_version diff --git a/SConstruct b/SConstruct index 4a53dbdaec..39c398badd 100644 --- a/SConstruct +++ b/SConstruct @@ -36,7 +36,7 @@ copyright_years = strftime('2001 - %Y') # This gets inserted into the man pages to reflect the month of release. month_year = strftime('%B %Y') project = 'scons' -default_version = '4.9.0' +default_version = '4.9.1' copyright = f"Copyright (c) {copyright_years} The SCons Foundation" # We let the presence or absence of various utilities determine whether diff --git a/doc/user/main.xml b/doc/user/main.xml index b0e54a7fe7..623a132289 100644 --- a/doc/user/main.xml +++ b/doc/user/main.xml @@ -36,10 +36,10 @@ This file is processed by the bin/SConsDoc.py module. The SCons Development Team - Released: Mon, 03 Sep 2024 18:13:57 -0700 + Released: Mon, 02 Mar 2025 14:20:11 -0700 - 2004 - 2024 + 2004 - 2025 The SCons Foundation diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index a5ca8a0f5f..620657181f 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -54,7 +54,7 @@ # here provides some independent verification that what we packaged # conforms to what we expect. -default_version = '4.8.2ayyyymmdd' +default_version = '4.9.1' # TODO: these need to be hand-edited when there are changes python_version_unsupported = (3, 7, 0) From 86887ebde913d34522402919effcd22940eabf24 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 2 Mar 2025 14:26:50 -0800 Subject: [PATCH 266/386] minor cleanup of CHANGES.txt --- CHANGES.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 92e992c847..699ccfc088 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,13 +10,6 @@ NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. -RELEASE VERSION/DATE TO BE FILLED IN LATER - - From John Doe: - - - Whatever John Doe did. - - RELEASE VERSION/DATE TO BE FILLED IN LATER From John Doe: From 32a0f691808ad6462f8e7bc674e01c774083481c Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 6 Mar 2025 16:16:13 -0700 Subject: [PATCH 267/386] Fix typos in CCFLAGS test tool=[] -> tools=[] Signed-off-by: Mats Wichmann --- CHANGES.txt | 7 +++++-- test/CC/CCFLAGS.py | 7 +++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 699ccfc088..d9b2e8711e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,9 +12,12 @@ NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. RELEASE VERSION/DATE TO BE FILLED IN LATER - From John Doe: + From John Doe: + - Whatever John Doe did. - - Whatever John Doe did. + From Mats Wichmann: + - Fix typos in CCFLAGS test. Didn't affect the test itself, but + didn't correctly apply the DefaultEnvironment speedup. RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 diff --git a/test/CC/CCFLAGS.py b/test/CC/CCFLAGS.py index 7269b9cf24..343cb4ad09 100644 --- a/test/CC/CCFLAGS.py +++ b/test/CC/CCFLAGS.py @@ -30,7 +30,7 @@ if sys.platform == 'win32': import SCons.Tool.MSCommon as msc - + if not msc.msvc_exists(): fooflags = '-DFOO' barflags = '-DBAR' @@ -44,7 +44,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', """ -DefaultEnvironment(tool=[]) +DefaultEnvironment(tools=[]) foo = Environment(CCFLAGS = '%s') bar = Environment(CCFLAGS = '%s') foo.Object(target = 'foo%s', source = 'prog.c') @@ -88,8 +88,7 @@ """) test.write('SConstruct', """ -DefaultEnvironment(tool=[]) - +DefaultEnvironment(tools=[]) bar = Environment(CCFLAGS = '%s') bar.Object(target = 'foo%s', source = 'prog.c') bar.Object(target = 'bar%s', source = 'prog.c') From 04a42e505d20cba22bfa6085d9ccf747da6b7963 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 3 Jan 2025 10:26:54 -0700 Subject: [PATCH 268/386] Manpage: note pre/post actions effect on an alias [skip appveyor] AddPostAction and AddPreAction apply to the action of the alias, which may not have been clear previously (see #2281). Expanded the example for AddPreAction using a multi-step build. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 4 ++- SCons/Environment.xml | 60 +++++++++++++++++++++++++++++++++---------- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d9b2e8711e..080fcd6a98 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Mats Wichmann: - Fix typos in CCFLAGS test. Didn't affect the test itself, but didn't correctly apply the DefaultEnvironment speedup. + - Clarify that pre/post actions on an alias apply to the alias' action. RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 128daa16fe..52a49ff970 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -53,6 +53,8 @@ DOCUMENTATION typo fixes, even if they're mentioned in src/CHANGES.txt to give the contributor credit) +- Clarify that pre/post actions on an alias apply to the alias' action. + DEVELOPMENT ----------- @@ -62,4 +64,4 @@ Thanks to the following contributors listed below for their contributions to thi ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.0.1..HEAD + git shortlog --no-merges -ns 4.9.0..HEAD diff --git a/SCons/Environment.xml b/SCons/Environment.xml index e59ab02f7b..b6e6c565e8 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -348,12 +348,15 @@ env.other_method_name('another arg') -Arranges for the specified +Arrange for the specified action to be performed after the specified target has been built. +target +may be a string or Node object, +or a list of strings or Node objects. action may be an Action object, or anything that can be converted into an Action object. @@ -374,6 +377,15 @@ foo = Program('foo.c') AddPostAction(foo, Chmod('$TARGET', "a-x")) + +If a target is an Alias target, +action is associated with the +action of the alias, +not of any targets that alias may refer to. +Thus, &AddPostAction; will have no effect +if called on an alias which has no action. + + @@ -383,12 +395,15 @@ AddPostAction(foo, Chmod('$TARGET', "a-x")) -Arranges for the specified +Arrange for the specified action to be performed before the specified target is built. +target +may be a string or Node object, +or a list of strings or Node objects. action may be an Action object, or anything that can be converted into an Action object. @@ -406,31 +421,50 @@ one or more targets in the list. Note that if any of the targets are built in multiple steps, the action will be invoked just -before the "final" action that specifically +before the action step that specifically generates the specified target(s). -For example, when building an executable program -from a specified source +It may not always be obvious +if the process is multi-step - for example, +when building an executable program from a specified source .c -file via an intermediate object file: +file, &scons; will build an intermediate object file first, +and the pre-action will follow this. +Example: foo = Program('foo.c') -AddPreAction(foo, 'pre_action') +AddPreAction(foo, 'echo "Running pre-action"') -The specified -pre_action -would be executed before +The specified pre-action is executed before &scons; calls the link command that actually generates the executable program binary foo, -not before compiling the +but after compiling the foo.c -file into an object file. +file into an object file: + + +$ scons -Q +gcc -o foo.o -c foo.c +echo "Running pre-action" +Running pre-action +gcc -o foo foo.o + + + +If a target is an Alias target, +action is associated with the +action of the alias, +not with any targets that alias may refer to. +Thus, &AddPreAction; will have no effect +if called on an alias which has no action. + + @@ -440,7 +474,7 @@ file into an object file. -Creates an alias target that +Create an Alias target that can be used as a reference to zero or more other targets, specified by the optional source parameter. Aliases provide a way to give a shorter or more descriptive From 086a92b6c3b91aed50d9c2183022311a02c6c7d6 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 21 Jan 2025 10:43:22 -0700 Subject: [PATCH 269/386] Update Pre/PostAction Alias note [skip appveyor] The initial version of PR #4872 proposed to document that pre- and post-actions on an alias have no effect if the alias has no action itself. Subsequently, PR #4874 was merged to change this behavior so that statement is backed out, but the other proposed doc improvements in this area are retained. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 +- RELEASE.txt | 2 +- SCons/Environment.xml | 32 +++++++++----------------------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 080fcd6a98..e55e608688 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,7 +18,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Mats Wichmann: - Fix typos in CCFLAGS test. Didn't affect the test itself, but didn't correctly apply the DefaultEnvironment speedup. - - Clarify that pre/post actions on an alias apply to the alias' action. + - Clarify how pre/post actions on an alias work. RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 52a49ff970..46a6675d17 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -53,7 +53,7 @@ DOCUMENTATION typo fixes, even if they're mentioned in src/CHANGES.txt to give the contributor credit) -- Clarify that pre/post actions on an alias apply to the alias' action. +- Clarify how pre/post actions on an alias work. DEVELOPMENT ----------- diff --git a/SCons/Environment.xml b/SCons/Environment.xml index b6e6c565e8..bf1b3cbd1b 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -380,10 +380,7 @@ AddPostAction(foo, Chmod('$TARGET', "a-x")) If a target is an Alias target, action is associated with the -action of the alias, -not of any targets that alias may refer to. -Thus, &AddPostAction; will have no effect -if called on an alias which has no action. +action of the alias, if specified. @@ -425,10 +422,13 @@ before the action step that specifically generates the specified target(s). It may not always be obvious if the process is multi-step - for example, -when building an executable program from a specified source -.c -file, &scons; will build an intermediate object file first, -and the pre-action will follow this. +if you use the &Program; builder to +construct an executable program from a +.c source file, +&scons; builds an intermediate object file first; +the pre-action is invoked after this step +and just before the link command to +generate the executable program binary. Example: @@ -437,17 +437,6 @@ foo = Program('foo.c') AddPreAction(foo, 'echo "Running pre-action"') - -The specified pre-action is executed before -&scons; -calls the link command that actually -generates the executable program binary -foo, -but after compiling the -foo.c -file into an object file: - - $ scons -Q gcc -o foo.o -c foo.c @@ -459,10 +448,7 @@ gcc -o foo foo.o If a target is an Alias target, action is associated with the -action of the alias, -not with any targets that alias may refer to. -Thus, &AddPreAction; will have no effect -if called on an alias which has no action. +action of the alias, if specified.
From 396ec3f9dec4db3827db08395d52f5a2e7e9e337 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 10 Mar 2025 09:13:09 -0700 Subject: [PATCH 270/386] Update appveyor to new python floor version 3.7 --- .appveyor.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 1674bade2a..42ab61a7a5 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -39,7 +39,7 @@ environment: - WINPYTHON: "Python312" - WINPYTHON: "Python310" - WINPYTHON: "Python38" - - WINPYTHON: "Python36" + - WINPYTHON: "Python37" # remove sets of build jobs based on criteria below # to fine tune the number and platforms tested @@ -52,7 +52,7 @@ matrix: - image: Visual Studio 2017 WINPYTHON: "Python310" - image: Visual Studio 2017 - WINPYTHON: "Python36" + WINPYTHON: "Python37" # test python 3.10 on Visual Studio 2019 image - image: Visual Studio 2019 @@ -60,7 +60,7 @@ matrix: - image: Visual Studio 2019 WINPYTHON: "Python38" - image: Visual Studio 2019 - WINPYTHON: "Python36" + WINPYTHON: "Python37" # test python 3.12 on Visual Studio 2022 image - image: Visual Studio 2022 @@ -68,7 +68,7 @@ matrix: - image: Visual Studio 2022 WINPYTHON: "Python38" - image: Visual Studio 2022 - WINPYTHON: "Python36" + WINPYTHON: "Python37" # Remove some binaries we don't want to be found # Note this is no longer needed, git-windows bin/ is quite minimal now. From 2e54b6f45bc7c461252f1295e4091c132315a2a9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 10 Mar 2025 09:22:38 -0700 Subject: [PATCH 271/386] Update appveyor to new python floor version 3.7 --- .appveyor.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 42ab61a7a5..00dcb27432 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -51,8 +51,6 @@ matrix: WINPYTHON: "Python312" - image: Visual Studio 2017 WINPYTHON: "Python310" - - image: Visual Studio 2017 - WINPYTHON: "Python37" # test python 3.10 on Visual Studio 2019 image - image: Visual Studio 2019 From b1304d2004e7e575ce848f4e2157a05c7752a0ac Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 10 Mar 2025 10:22:54 -0600 Subject: [PATCH 272/386] Fix CacheDir initialization so works on 3.7 also Although there is no indication of a change in this area of the tempfile module, it seems if the temporary directory has been renamed so the original no longer exists, the context manager fails on 3.7, though not on any later Python versions (maybe a bugfix rather than a planned behavior change?). Now use mkdtemp instead, and clean it up by hand if the rename failed (also use os.replace instead of os.rename, so it will actually fail if the directory existed and is nonempty, on Linux os.rename doesn't fail in this case). Fixes #4694 Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ RELEASE.txt | 3 +++ SCons/CacheDir.py | 32 +++++++++++++++++++------------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d9b2e8711e..f1326e85fb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Mats Wichmann: - Fix typos in CCFLAGS test. Didn't affect the test itself, but didn't correctly apply the DefaultEnvironment speedup. + - New CacheDir initialization code failed on Python 3.7 for unknown + reason (worked on 3.8+). Adjusted the approach a bit. Fixes #4694. RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 128daa16fe..e42847e674 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -34,6 +34,9 @@ FIXES - List fixes of outright bugs +- New CacheDir initialization code failed on Python 3.7 for unknown + reason (worked on 3.8+). Adjusted the approach a bit. Fixes #4694. + IMPROVEMENTS ------------ diff --git a/SCons/CacheDir.py b/SCons/CacheDir.py index 25e3f666f4..3460fc3889 100644 --- a/SCons/CacheDir.py +++ b/SCons/CacheDir.py @@ -27,6 +27,7 @@ import atexit import json import os +import shutil import stat import sys import tempfile @@ -76,7 +77,6 @@ def CacheRetrieveFunc(target, source, env) -> int: def CacheRetrieveString(target, source, env) -> str: t = target[0] - fs = t.fs cd = env.get_CacheDir() cachedir, cachefile = cd.cachepath(t) if t.fs.exists(cachefile): @@ -209,22 +209,28 @@ def _mkdir_atomic(self, path: str) -> bool: if os.path.exists(directory): return False + # TODO: tried to use TemporaryDirectory() here and the result as a + # context manager, but that fails on Python 3.7 (works on 3.8+) after + # the directory has successfully been renamed. Not sure why. try: - tempdir = tempfile.TemporaryDirectory(dir=os.path.dirname(directory)) + tempdir = tempfile.mkdtemp(dir=os.path.dirname(directory)) except OSError as e: msg = "Failed to create cache directory " + path raise SCons.Errors.SConsEnvironmentError(msg) from e - self._add_config(tempdir.name) - with tempdir: - try: - os.rename(tempdir.name, directory) - return True - except Exception as e: - # did someone else get there first? - if os.path.isdir(directory): - return False - msg = "Failed to create cache directory " + path - raise SCons.Errors.SConsEnvironmentError(msg) from e + self._add_config(tempdir) + try: + os.replace(tempdir, directory) + return True + except OSError as e: + # did someone else get there first? attempt cleanup. + if os.path.isdir(directory): + try: + shutil.rmtree(tempdir) + except Exception: # we tried, don't worry about it + pass + return False + msg = "Failed to create cache directory " + path + raise SCons.Errors.SConsEnvironmentError(msg) from e def _readconfig(self, path: str) -> None: """Read the cache config from *path*. From acab1e79c90a2633ab326d70a16fa42681fc0fdb Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 11 Mar 2025 07:47:05 -0600 Subject: [PATCH 273/386] Update AppVeyor test matrix Mainly restores VS 2022 image builds, which were commented out. If they're still broken, we can revert. Signed-off-by: Mats Wichmann --- .appveyor.yml | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 00dcb27432..0ad936fb59 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -8,7 +8,7 @@ image: # linux builds done in Travis CI for now - Visual Studio 2017 - Visual Studio 2019 - #- Visual Studio 2022 # Temporary disable while failing on 14.40 build tools + - Visual Studio 2022 cache: - downloads -> appveyor.yml @@ -35,36 +35,35 @@ environment: SCONS_CACHE_MSVC_CONFIG: "true" matrix: # Test oldest and newest supported Pythons, and a subset in between. - # Skipping 3.7 and 3.9 at this time - - WINPYTHON: "Python312" - - WINPYTHON: "Python310" - - WINPYTHON: "Python38" + # Skipping 3.8, 3.10, 3.12 at this time + - WINPYTHON: "Python313" + - WINPYTHON: "Python311" + - WINPYTHON: "Python39" - WINPYTHON: "Python37" # remove sets of build jobs based on criteria below # to fine tune the number and platforms tested matrix: exclude: - # XXX test python 3.6 on Visual Studio 2017 image - # test python 3.8 on Visual Studio 2017 image + # test python 3.7 on Visual Studio 2017 image - image: Visual Studio 2017 - WINPYTHON: "Python312" + WINPYTHON: "Python313" - image: Visual Studio 2017 - WINPYTHON: "Python310" + WINPYTHON: "Python311" + - image: Visual Studio 2017 + WINPYTHON: "Python39" - # test python 3.10 on Visual Studio 2019 image + # test python 3.9 on Visual Studio 2019 image - image: Visual Studio 2019 - WINPYTHON: "Python312" + WINPYTHON: "Python313" - image: Visual Studio 2019 - WINPYTHON: "Python38" + WINPYTHON: "Python311" - image: Visual Studio 2019 WINPYTHON: "Python37" - # test python 3.12 on Visual Studio 2022 image - - image: Visual Studio 2022 - WINPYTHON: "Python310" + # test python 3.11, 3.13 on Visual Studio 2022 image - image: Visual Studio 2022 - WINPYTHON: "Python38" + WINPYTHON: "Python39" - image: Visual Studio 2022 WINPYTHON: "Python37" From 21dbfdffcdb63943465f106d12f6f06ca39da9e8 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 12 Mar 2025 08:37:01 -0600 Subject: [PATCH 274/386] Rewrite CacheDir _mkdir_atomic to include old version The failure on Python 3.7 caused a rewrite. Redo that so the version released with SCons 4.9.0 is still present, but commented out, so it's easier to restore it in future. tempfile.TemporaryDirectory has better cleanup logic, so we'll want to flip to that when Python 3.7 support is retired. Signed-off-by: Mats Wichmann --- SCons/CacheDir.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/SCons/CacheDir.py b/SCons/CacheDir.py index 3460fc3889..a5184b6e9c 100644 --- a/SCons/CacheDir.py +++ b/SCons/CacheDir.py @@ -209,14 +209,31 @@ def _mkdir_atomic(self, path: str) -> bool: if os.path.exists(directory): return False - # TODO: tried to use TemporaryDirectory() here and the result as a - # context manager, but that fails on Python 3.7 (works on 3.8+) after - # the directory has successfully been renamed. Not sure why. try: + # TODO: Python 3.7. See comment below. + # tempdir = tempfile.TemporaryDirectory(dir=os.path.dirname(directory)) tempdir = tempfile.mkdtemp(dir=os.path.dirname(directory)) except OSError as e: msg = "Failed to create cache directory " + path raise SCons.Errors.SConsEnvironmentError(msg) from e + + # TODO: Python 3.7: the context manager raises exception on cleanup + # if the temporary was moved successfully (File Not Found). + # Fixed in 3.8+. In the replacement below we manually clean up if + # the move failed as mkdtemp() does not. TemporaryDirectory's + # cleanup is more sophisitcated so prefer when we can use it. + # self._add_config(tempdir.name) + # with tempdir: + # try: + # os.replace(tempdir.name, directory) + # return True + # except OSError as e: + # # did someone else get there first? + # if os.path.isdir(directory): + # return False # context manager cleans up + # msg = "Failed to create cache directory " + path + # raise SCons.Errors.SConsEnvironmentError(msg) from e + self._add_config(tempdir) try: os.replace(tempdir, directory) From c9e65f5308caef79a1dc9476d44a25d2faba56ec Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 12 Mar 2025 11:04:44 -0600 Subject: [PATCH 275/386] Exp: try to make the Windows Docbook tests better Try to fix Windows fails on Docbook tests in case xsltproc is found. On both GitHub Actions and AppVeyor, it's found as part of StrawberryPerl, which is part of the default install. Intermittent fails seem caused by network issues (whether actual problems, or forced revectoring to https not supported by xsltproc according to some internet issue reports), so propagate two xsltproc flags for this that were in three of the "live" (named "cmd" here) tests, but not the others. Also made other parts of the setup of these tests more consistent (passing found xsltproc wasn't done) Signed-off-by: Mats Wichmann --- CHANGES.txt | 6 ++++ SCons/Tool/docbook/__init__.py | 30 +++++++++++-------- .../basedir/htmlchunked/htmlchunked_cmd.py | 4 +-- .../basedir/htmlchunked/image/SConstruct.cmd | 4 +++ test/Docbook/basedir/htmlhelp/htmlhelp_cmd.py | 4 +-- .../basedir/htmlhelp/image/SConstruct.cmd | 4 +++ .../basedir/slideshtml/image/SConstruct.cmd | 3 ++ .../basedir/slideshtml/slideshtml_cmd.py | 4 +-- test/Docbook/basic/epub/image/SConstruct.cmd | 2 +- test/Docbook/basic/html/html_cmd.py | 2 +- test/Docbook/basic/html/image/SConstruct.cmd | 2 +- .../basic/htmlchunked/image/SConstruct.cmd | 2 +- .../basic/htmlhelp/image/SConstruct.cmd | 2 +- test/Docbook/basic/man/image/SConstruct.cmd | 2 +- test/Docbook/basic/pdf/image/SConstruct.cmd | 2 +- .../basic/slideshtml/image/SConstruct.cmd | 3 ++ .../basic/slideshtml/slideshtml_cmd.py | 4 +-- .../basic/slidespdf/image/SConstruct.cmd | 3 ++ test/Docbook/basic/slidespdf/slidespdf_cmd.py | 4 +-- 19 files changed, 58 insertions(+), 29 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d9b2e8711e..06544817bb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Mats Wichmann: - Fix typos in CCFLAGS test. Didn't affect the test itself, but didn't correctly apply the DefaultEnvironment speedup. + - Try to fix Windows fails on Docbook tests in case xsltproc is found. + On both GitHub Actions and AppVeyor, it's found as part of + StrawberryPerl, which is part of the default install. Intermittent + fails seem caused by network issues, so propagate two xsltproc flags + for this that were in three of the "live" (named "cmd" here) tests, + but not the others. Also made other parts of the setup more consistent. RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 diff --git a/SCons/Tool/docbook/__init__.py b/SCons/Tool/docbook/__init__.py index 3adb3148bb..f0e9ef5077 100644 --- a/SCons/Tool/docbook/__init__.py +++ b/SCons/Tool/docbook/__init__.py @@ -157,16 +157,22 @@ def __create_output_dir(base_dir) -> None: # TODO: Set minimum version of saxon-xslt to be 8.x (lower than this only supports xslt 1.0. # see: https://saxon.sourceforge.net/saxon6.5.5/ # see: https://saxon.sourceforge.net/ -xsltproc_com = {'xsltproc' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE', - 'saxon' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE $DOCBOOK_XSLTPROCPARAMS', - # Note if saxon-xslt is version 5.5 the proper arguments are: (swap order of docbook_xsl and source) - # 'saxon-xslt' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $SOURCE $DOCBOOK_XSL $DOCBOOK_XSLTPROCPARAMS', - 'saxon-xslt' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE $DOCBOOK_XSLTPROCPARAMS', - 'xalan' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -q -out $TARGET -xsl $DOCBOOK_XSL -in $SOURCE'} -xmllint_com = {'xmllint' : '$DOCBOOK_XMLLINT $DOCBOOK_XMLLINTFLAGS --xinclude $SOURCE > $TARGET'} -fop_com = {'fop' : '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -fo $SOURCE -pdf $TARGET', - 'xep' : '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -valid -fo $SOURCE -pdf $TARGET', - 'jw' : '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -f docbook -b pdf $SOURCE -o $TARGET'} +xsltproc_com = { + 'xsltproc': '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE', + 'saxon': '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE $DOCBOOK_XSLTPROCPARAMS', + # Note if saxon-xslt is version 5.5 the proper arguments are: (swap order of docbook_xsl and source) + # 'saxon-xslt' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $SOURCE $DOCBOOK_XSL $DOCBOOK_XSLTPROCPARAMS', + 'saxon-xslt': '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE $DOCBOOK_XSLTPROCPARAMS', + 'xalan': '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -q -out $TARGET -xsl $DOCBOOK_XSL -in $SOURCE', +} +xmllint_com = { + 'xmllint': '$DOCBOOK_XMLLINT $DOCBOOK_XMLLINTFLAGS --xinclude $SOURCE > $TARGET' +} +fop_com = { + 'fop': '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -fo $SOURCE -pdf $TARGET', + 'xep': '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -valid -fo $SOURCE -pdf $TARGET', + 'jw': '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -f docbook -b pdf $SOURCE -o $TARGET', +} def __detect_cl_tool(env, chainkey, cdict, cpriority=None) -> None: """ @@ -180,11 +186,11 @@ def __detect_cl_tool(env, chainkey, cdict, cpriority=None) -> None: cpriority = cdict.keys() for cltool in cpriority: if __debug_tool_location: - print("DocBook: Looking for %s"%cltool) + print(f"DocBook: Looking for {cltool}") clpath = env.WhereIs(cltool) if clpath: if __debug_tool_location: - print("DocBook: Found:%s"%cltool) + print(f"DocBook: Found:{cltool}") env[chainkey] = clpath if not env[chainkey + 'COM']: env[chainkey + 'COM'] = cdict[cltool] diff --git a/test/Docbook/basedir/htmlchunked/htmlchunked_cmd.py b/test/Docbook/basedir/htmlchunked/htmlchunked_cmd.py index e6032327d3..f540ffbb66 100644 --- a/test/Docbook/basedir/htmlchunked/htmlchunked_cmd.py +++ b/test/Docbook/basedir/htmlchunked/htmlchunked_cmd.py @@ -40,11 +40,11 @@ test.dir_fixture('image') # Normal invocation -test.run(arguments=['-f','SConstruct.cmd'], stderr=None) +test.run(arguments=['-f','SConstruct.cmd','DOCBOOK_XSLTPROC=%s'%xsltproc], stderr=None) test.must_not_be_empty(test.workpath('output/index.html')) # Cleanup -test.run(arguments=['-f','SConstruct.cmd','-c']) +test.run(arguments=['-f','SConstruct.cmd','-c','DOCBOOK_XSLTPROC=%s'%xsltproc]) test.must_not_exist(test.workpath('output/index.html')) test.pass_test() diff --git a/test/Docbook/basedir/htmlchunked/image/SConstruct.cmd b/test/Docbook/basedir/htmlchunked/image/SConstruct.cmd index 40dc569ce4..d4e0cfccab 100644 --- a/test/Docbook/basedir/htmlchunked/image/SConstruct.cmd +++ b/test/Docbook/basedir/htmlchunked/image/SConstruct.cmd @@ -4,4 +4,8 @@ DefaultEnvironment(tools=[]) env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) +DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") +if DOCBOOK_XSLTPROC: + env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC +env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookHtmlChunked('manual', xsl='html.xsl', base_dir='output/') diff --git a/test/Docbook/basedir/htmlhelp/htmlhelp_cmd.py b/test/Docbook/basedir/htmlhelp/htmlhelp_cmd.py index ebefb92335..9d6a4b1614 100644 --- a/test/Docbook/basedir/htmlhelp/htmlhelp_cmd.py +++ b/test/Docbook/basedir/htmlhelp/htmlhelp_cmd.py @@ -40,13 +40,13 @@ test.dir_fixture('image') # Normal invocation -test.run(arguments=['-f','SConstruct.cmd'], stderr=None) +test.run(arguments=['-f','SConstruct.cmd','DOCBOOK_XSLTPROC=%s'%xsltproc], stderr=None) test.must_not_be_empty(test.workpath('output/index.html')) test.must_not_be_empty(test.workpath('htmlhelp.hhp')) test.must_not_be_empty(test.workpath('toc.hhc')) # Cleanup -test.run(arguments=['-f','SConstruct.cmd','-c']) +test.run(arguments=['-f','SConstruct.cmd','-c','DOCBOOK_XSLTPROC=%s'%xsltproc]) test.must_not_exist(test.workpath('output/index.html')) test.must_not_exist(test.workpath('htmlhelp.hhp')) test.must_not_exist(test.workpath('toc.hhc')) diff --git a/test/Docbook/basedir/htmlhelp/image/SConstruct.cmd b/test/Docbook/basedir/htmlhelp/image/SConstruct.cmd index f76e99ba82..c41e3c2ec8 100644 --- a/test/Docbook/basedir/htmlhelp/image/SConstruct.cmd +++ b/test/Docbook/basedir/htmlhelp/image/SConstruct.cmd @@ -4,5 +4,9 @@ DefaultEnvironment(tools=[]) env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) +DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") +if DOCBOOK_XSLTPROC: + env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC +env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookHtmlhelp('manual', xsl='htmlhelp.xsl', base_dir='output/') diff --git a/test/Docbook/basedir/slideshtml/image/SConstruct.cmd b/test/Docbook/basedir/slideshtml/image/SConstruct.cmd index 151e6039c7..5ed5d40e1d 100644 --- a/test/Docbook/basedir/slideshtml/image/SConstruct.cmd +++ b/test/Docbook/basedir/slideshtml/image/SConstruct.cmd @@ -13,6 +13,9 @@ if v >= (1, 78, 0): DefaultEnvironment(tools=[]) env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) +DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") +if DOCBOOK_XSLTPROC: + env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookSlidesHtml('virt'+ns_ext, xsl='slides.xsl', base_dir='output/') diff --git a/test/Docbook/basedir/slideshtml/slideshtml_cmd.py b/test/Docbook/basedir/slideshtml/slideshtml_cmd.py index b0f1f2512c..b395c05eba 100644 --- a/test/Docbook/basedir/slideshtml/slideshtml_cmd.py +++ b/test/Docbook/basedir/slideshtml/slideshtml_cmd.py @@ -40,12 +40,12 @@ test.dir_fixture('image') # Normal invocation -test.run(arguments=['-f','SConstruct.cmd'], stderr=None) +test.run(arguments=['-f','SConstruct.cmd','DOCBOOK_XSLTPROC=%s'%xsltproc], stderr=None) test.must_not_be_empty(test.workpath('output/index.html')) test.must_contain(test.workpath('output/index.html'), 'sfForming') # Cleanup -test.run(arguments=['-f','SConstruct.cmd','-c'], stderr=None) +test.run(arguments=['-f','SConstruct.cmd','-c','DOCBOOK_XSLTPROC=%s'%xsltproc]) test.must_not_exist(test.workpath('output/index.html')) test.pass_test() diff --git a/test/Docbook/basic/epub/image/SConstruct.cmd b/test/Docbook/basic/epub/image/SConstruct.cmd index b86c78d224..0e39fea2fc 100644 --- a/test/Docbook/basic/epub/image/SConstruct.cmd +++ b/test/Docbook/basic/epub/image/SConstruct.cmd @@ -7,5 +7,5 @@ env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") if DOCBOOK_XSLTPROC: env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC - +env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookEpub('manual') diff --git a/test/Docbook/basic/html/html_cmd.py b/test/Docbook/basic/html/html_cmd.py index 122d8bcf79..7e9a0a3ce5 100644 --- a/test/Docbook/basic/html/html_cmd.py +++ b/test/Docbook/basic/html/html_cmd.py @@ -38,7 +38,7 @@ test.dir_fixture('image') # Normal invocation -test.run(arguments=['-f','SConstruct.cmd','DOCBOOK_XSLTPROC=%s'%xsltproc]) +test.run(arguments=['-f','SConstruct.cmd','DOCBOOK_XSLTPROC=%s'%xsltproc], stderr=None) test.must_not_be_empty(test.workpath('manual.html')) # Cleanup diff --git a/test/Docbook/basic/html/image/SConstruct.cmd b/test/Docbook/basic/html/image/SConstruct.cmd index ef4ecebb90..d4a6939f12 100644 --- a/test/Docbook/basic/html/image/SConstruct.cmd +++ b/test/Docbook/basic/html/image/SConstruct.cmd @@ -7,6 +7,6 @@ env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") if DOCBOOK_XSLTPROC: env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC - +env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookHtml('manual') diff --git a/test/Docbook/basic/htmlchunked/image/SConstruct.cmd b/test/Docbook/basic/htmlchunked/image/SConstruct.cmd index 765864a006..83d6f3d012 100644 --- a/test/Docbook/basic/htmlchunked/image/SConstruct.cmd +++ b/test/Docbook/basic/htmlchunked/image/SConstruct.cmd @@ -7,6 +7,6 @@ env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") if DOCBOOK_XSLTPROC: env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC - +env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookHtmlChunked('manual') diff --git a/test/Docbook/basic/htmlhelp/image/SConstruct.cmd b/test/Docbook/basic/htmlhelp/image/SConstruct.cmd index 854a266c3c..cf6a0af005 100644 --- a/test/Docbook/basic/htmlhelp/image/SConstruct.cmd +++ b/test/Docbook/basic/htmlhelp/image/SConstruct.cmd @@ -7,6 +7,6 @@ env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") if DOCBOOK_XSLTPROC: env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC - +env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookHtmlhelp('manual') diff --git a/test/Docbook/basic/man/image/SConstruct.cmd b/test/Docbook/basic/man/image/SConstruct.cmd index 6474f49faf..a64d27415c 100644 --- a/test/Docbook/basic/man/image/SConstruct.cmd +++ b/test/Docbook/basic/man/image/SConstruct.cmd @@ -7,6 +7,6 @@ env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") if DOCBOOK_XSLTPROC: env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC - +env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookMan('refdb') diff --git a/test/Docbook/basic/pdf/image/SConstruct.cmd b/test/Docbook/basic/pdf/image/SConstruct.cmd index 5d709051ff..46ca35bd5e 100644 --- a/test/Docbook/basic/pdf/image/SConstruct.cmd +++ b/test/Docbook/basic/pdf/image/SConstruct.cmd @@ -7,6 +7,6 @@ env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") if DOCBOOK_XSLTPROC: env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC - +env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookPdf('manual') diff --git a/test/Docbook/basic/slideshtml/image/SConstruct.cmd b/test/Docbook/basic/slideshtml/image/SConstruct.cmd index 2090bf01f7..7ef7a2b85f 100644 --- a/test/Docbook/basic/slideshtml/image/SConstruct.cmd +++ b/test/Docbook/basic/slideshtml/image/SConstruct.cmd @@ -13,6 +13,9 @@ if v >= (1, 78, 0): DefaultEnvironment(tools=[]) env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) +DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") +if DOCBOOK_XSLTPROC: + env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookSlidesHtml( 'virt' + ns_ext, diff --git a/test/Docbook/basic/slideshtml/slideshtml_cmd.py b/test/Docbook/basic/slideshtml/slideshtml_cmd.py index 9c8097b64a..ff2fde359d 100644 --- a/test/Docbook/basic/slideshtml/slideshtml_cmd.py +++ b/test/Docbook/basic/slideshtml/slideshtml_cmd.py @@ -40,12 +40,12 @@ test.dir_fixture('image') # Normal invocation -test.run(arguments=['-f','SConstruct.cmd'], stderr=None) +test.run(arguments=['-f','SConstruct.cmd','DOCBOOK_XSLTPROC=%s'%xsltproc], stderr=None) test.must_not_be_empty(test.workpath('index.html')) test.must_contain(test.workpath('index.html'), 'sfForming') # Cleanup -test.run(arguments=['-f','SConstruct.cmd','-c'], stderr=None) +test.run(arguments=['-f','SConstruct.cmd','-c','DOCBOOK_XSLTPROC=%s'%xsltproc]) test.must_not_exist(test.workpath('index.html')) test.pass_test() diff --git a/test/Docbook/basic/slidespdf/image/SConstruct.cmd b/test/Docbook/basic/slidespdf/image/SConstruct.cmd index 18ef25bf4e..eec556d7a9 100644 --- a/test/Docbook/basic/slidespdf/image/SConstruct.cmd +++ b/test/Docbook/basic/slidespdf/image/SConstruct.cmd @@ -4,6 +4,9 @@ DefaultEnvironment(tools=[]) env = Environment(DOCBOOK_PREFER_XSLTPROC=1, tools=['docbook']) +DOCBOOK_XSLTPROC = ARGUMENTS.get('DOCBOOK_XSLTPROC', "") +if DOCBOOK_XSLTPROC: + env['DOCBOOK_XSLTPROC'] = DOCBOOK_XSLTPROC env.Append(DOCBOOK_XSLTPROCFLAGS=['--novalid', '--nonet']) env.DocbookSlidesPdf('virt') diff --git a/test/Docbook/basic/slidespdf/slidespdf_cmd.py b/test/Docbook/basic/slidespdf/slidespdf_cmd.py index 4b7bd2c265..65e2801d4f 100644 --- a/test/Docbook/basic/slidespdf/slidespdf_cmd.py +++ b/test/Docbook/basic/slidespdf/slidespdf_cmd.py @@ -44,12 +44,12 @@ test.dir_fixture('image') # Normal invocation -test.run(arguments=['-f','SConstruct.cmd'], stderr=None) +test.run(arguments=['-f','SConstruct.cmd','DOCBOOK_XSLTPROC=%s'%xsltproc], stderr=None) test.must_not_be_empty(test.workpath('virt.fo')) test.must_not_be_empty(test.workpath('virt.pdf')) # Cleanup -test.run(arguments=['-f','SConstruct.cmd','-c'], stderr=None) +test.run(arguments=['-f','SConstruct.cmd','-c','DOCBOOK_XSLTPROC=%s'%xsltproc]) test.must_not_exist(test.workpath('virt.fo')) test.must_not_exist(test.workpath('virt.pdf')) From 3398fbb70fa6f23b8630000b2352adb9fd8a7e82 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 12 Mar 2025 12:52:27 -0600 Subject: [PATCH 276/386] Tweak CHANGES for xsltproc test changes. Signed-off-by: Mats Wichmann --- CHANGES.txt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 06544817bb..dc53457153 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -19,11 +19,15 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Fix typos in CCFLAGS test. Didn't affect the test itself, but didn't correctly apply the DefaultEnvironment speedup. - Try to fix Windows fails on Docbook tests in case xsltproc is found. - On both GitHub Actions and AppVeyor, it's found as part of - StrawberryPerl, which is part of the default install. Intermittent - fails seem caused by network issues, so propagate two xsltproc flags - for this that were in three of the "live" (named "cmd" here) tests, - but not the others. Also made other parts of the setup more consistent. + It's still not certain why this started failing. On both GitHub + Actions and AppVeyor, it's found as part of StrawberryPerl, part of + the default install - maybe this wasn't the case before? The xsltproc + from choco install is considerably older and may have been more lenient? + Anyway, intermittent fails seem caused by something network related, + so propagate two xsltproc flags that avoid loading the dtd that were + present in three of the 11 "live" tests, but not the other eight. + Also, all 11 now pass the test-discovered xslt processor the same + way, which was not the case previously. RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 From 2d27400f9a95d73c2d15ce8e6996238acae3aa97 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 21 Mar 2025 13:35:51 -0600 Subject: [PATCH 277/386] Update progress-printing for tools Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ bin/SConsDoc.py | 18 ++++++------------ bin/SConsExamples.py | 4 +--- runtest.py | 8 +++----- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c1d335e37f..ac84431b2b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -30,6 +30,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER present in three of the 11 "live" tests, but not the other eight. Also, all 11 now pass the test-discovered xslt processor the same way, which was not the case previously. + - Update progress printing on three tools for SCons developers - + the test runner and two of the doc generators. RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 diff --git a/bin/SConsDoc.py b/bin/SConsDoc.py index 5e4c4e797e..bac1bca1a7 100644 --- a/bin/SConsDoc.py +++ b/bin/SConsDoc.py @@ -199,12 +199,11 @@ def __init__(self, name_, uri_): self.name = name_ self.uri = uri_ - def getEntityString(self): - txt = """ - %(perc)s%(name)s; -""" % {'perc': perc, 'name': self.name, 'uri': self.uri} - - return txt + def getEntityString(self) -> str: + return f"""\ + + %{self.name}; +""" class DoctypeDeclaration: @@ -417,8 +416,6 @@ def __del__(self): if self.xpath_context is not None: self.xpath_context.xpathFreeContext() -perc = "%" - def validate_all_xml(dpaths, xsdfile=default_xsd): xmlschema_context = etree.parse(xsdfile) @@ -438,10 +435,7 @@ def validate_all_xml(dpaths, xsdfile=default_xsd): fails = [] fpaths = sorted(fpaths) for idx, fp in enumerate(fpaths): - fpath = os.path.join(path, fp) - print("%.2f%s (%d/%d) %s" % (float(idx + 1) * 100.0 /float(len(fpaths)), - perc, idx + 1, len(fpaths), fp)) - + print(f"{(idx + 1) / len(fpaths):7.2%} ({idx + 1}/{len(fpaths)}) {fp}") if not tf.validateXml(fp, xmlschema_context): fails.append(fp) continue diff --git a/bin/SConsExamples.py b/bin/SConsExamples.py index e7a20ce2ea..7e47c9613e 100644 --- a/bin/SConsExamples.py +++ b/bin/SConsExamples.py @@ -309,9 +309,7 @@ def createAllExampleOutputs(dpath): for key, value in examples.items(): # Process all scons_output tags - print("%.2f%s (%d/%d) %s" % (float(idx + 1) * 100.0 / float(total), - perc, idx + 1, total, key)) - + print(f"{(idx + 1) / total:7.2%} ({idx + 1}/{total}) {key}") create_scons_output(value) # Process all scons_example_file tags for r in value.files: diff --git a/runtest.py b/runtest.py index 262576b903..c8b3e990f6 100755 --- a/runtest.py +++ b/runtest.py @@ -796,11 +796,9 @@ def run_test(t, io_lock=None, run_async=True): t.command_str = " ".join(t.command_args) if args.printcommand: if args.print_progress: - t.headline += "%d/%d (%.2f%s) %s\n" % ( - t.testno, total_num_tests, - float(t.testno) * 100.0 / float(total_num_tests), - "%", - t.command_str, + t.headline += ( + f"{t.testno}/{total_num_tests} " + f"({t.testno / total_num_tests:7.2%}) {t.command_str}\n" ) else: t.headline += t.command_str + "\n" From 7e244b1c22c4253b866abb0e858749746520fa51 Mon Sep 17 00:00:00 2001 From: Thaddeus Crews Date: Mon, 10 Mar 2025 13:20:45 -0500 Subject: [PATCH 278/386] Allow parsing Nodes as `PathLike` --- CHANGES.txt | 3 +++ RELEASE.txt | 2 ++ SCons/Builder.py | 10 ++++++---- SCons/BuilderTests.py | 6 +++--- SCons/Environment.xml | 4 ++-- SCons/Node/__init__.py | 3 +++ SCons/SConfTests.py | 2 +- bin/SConsExamples.py | 2 +- test/Actions/append.py | 4 ++-- test/Actions/exitstatfunc.py | 2 +- test/Actions/function.py | 2 +- test/Actions/pre-post-fixture/work2/SConstruct | 2 +- test/Actions/pre-post-fixture/work3/SConstruct | 2 +- test/Actions/pre-post.py | 6 +++--- test/Actions/timestamp.py | 2 +- .../unicode-signature-fixture/SConstruct | 2 +- test/Alias/action.py | 5 ++--- test/Alias/scanner.py | 5 ++--- test/Batch/Boolean.py | 2 +- test/Batch/callable.py | 2 +- test/Batch/generated.py | 4 ++-- test/Builder-factories.py | 2 +- test/Builder/errors.py | 2 +- test/Builder/multi/different-actions.py | 6 +++--- test/Builder/multi/different-environments.py | 6 +++--- test/Builder/multi/different-multi.py | 6 +++--- test/Builder/multi/different-order.py | 6 +++--- test/Builder/multi/different-overrides.py | 6 +++--- test/Builder/multi/different-target-lists.py | 6 +++--- test/Builder/multi/error.py | 6 +++--- test/Builder/multi/lone-target-list.py | 6 +++--- test/Builder/multi/multi.py | 6 +++--- test/Builder/multi/same-actions.py | 6 +++--- test/Builder/multi/same-overrides.py | 6 +++--- test/Builder/multi/same-targets.py | 6 +++--- test/Builder/non-multi.py | 6 +++--- test/Builder/same-actions-diff-envs.py | 2 +- test/Builder/same-actions-diff-overrides.py | 2 +- test/Builder/wrapper.py | 6 +++--- test/CacheDir/CacheDir.py | 2 +- test/CacheDir/SideEffect.py | 8 +++----- test/CacheDir/VariantDir.py | 2 +- test/CacheDir/debug.py | 2 +- test/CacheDir/environment.py | 2 +- test/CacheDir/option--cd.py | 2 +- test/CacheDir/option--cf.py | 2 +- test/CacheDir/option--cr.py | 2 +- test/CacheDir/option--cs.py | 2 +- test/Chmod.py | 2 +- test/Climb/explicit-parent--D.py | 2 +- test/Climb/explicit-parent--U.py | 2 +- test/Climb/explicit-parent-u.py | 2 +- test/Climb/option-u.py | 2 +- test/Command.py | 2 +- test/Copy-Action.py | 5 ++--- test/Decider/switch-rebuild.py | 2 +- test/Delete.py | 10 ++++------ test/Dir/source.py | 2 +- test/Errors/Exception.py | 2 +- test/Exit.py | 10 ++++------ test/FindFile.py | 8 ++++---- test/Flatten.py | 5 ++--- test/Glob/Repository.py | 5 ++--- test/Glob/VariantDir.py | 14 +++++++------- test/Glob/basic.py | 8 ++++---- test/Glob/exclude.py | 8 ++++---- test/Glob/source.py | 8 ++++---- test/Glob/strings.py | 8 ++++---- test/Glob/subdir.py | 8 ++++---- test/Glob/subst.py | 6 +++--- test/HeaderGen.py | 4 ++-- test/Install/Install.py | 5 ++--- test/Install/wrap-by-attribute.py | 5 ++--- test/Interactive/option-j.py | 8 ++++---- test/Mkdir.py | 5 ++--- test/Move.py | 5 ++--- test/NodeOps.py | 10 +++++----- test/Repository/LIBPATH.py | 2 +- test/Repository/SConscript.py | 10 ++++------ test/Repository/option-f.py | 5 ++--- test/Requires/basic.py | 6 +++--- test/Requires/eval-order.py | 6 +++--- test/Scanner/Scanner.py | 2 +- test/Scanner/empty-implicit.py | 8 ++++---- test/Scanner/exception.py | 5 ++--- test/Scanner/no-Dir-node.py | 2 +- test/Scanner/scan-once.py | 8 ++++---- test/SideEffect/Issues/3013/files/SConstruct | 3 +-- test/SideEffect/basic.py | 4 ++-- test/SideEffect/variant_dir.py | 6 +++--- test/TARGET-dir.py | 5 ++--- test/Touch.py | 5 ++--- test/Value/Value.py | 4 ++-- test/VariantDir/Clean.py | 3 +-- test/VariantDir/SConscript-variant_dir.py | 5 ++--- test/VariantDir/VariantDir.py | 4 ++-- test/VariantDir/errors.py | 5 ++--- test/Win32/default-drive.py | 5 ++--- test/chained-build.py | 4 ++-- test/duplicate-sources.py | 6 +++--- test/emitter.py | 2 +- test/implicit-cache/basic.py | 2 +- test/implicit/changed-node.py | 18 ++++++++---------- test/no-target.py | 5 ++--- test/option/debug-memoizer.py | 2 +- test/option/debug-memory.py | 2 +- test/option/debug-multiple.py | 2 +- test/option/debug-objects.py | 2 +- test/option/debug-presub.py | 5 ++--- test/option/fixture/SConstruct_debug_count | 2 +- test/option/option--random.py | 5 ++--- test/option/warn-duplicate-environment.py | 6 +++--- test/option/warn-misleading-keywords.py | 8 ++++---- test/sconsign/corrupt.py | 2 +- test/sconsign/ghost-entries.py | 6 +++--- test/sconsign/nonwritable.py | 6 +++--- test/srcchange.py | 2 +- test/suffixes.py | 5 ++--- test/timestamp-fallback.py | 2 +- test/toolpath/VariantDir.py | 2 +- timings/hundred/SConstruct | 2 +- 121 files changed, 265 insertions(+), 287 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ac84431b2b..c17b1a68b9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,6 +15,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From John Doe: - Whatever John Doe did. + From Thaddeus Crews: + - Nodes are now treated as PathLike objects. + From Mats Wichmann: - Fix typos in CCFLAGS test. Didn't affect the test itself, but didn't correctly apply the DefaultEnvironment speedup. diff --git a/RELEASE.txt b/RELEASE.txt index 9a9fa1e2fc..ed7a0e0c34 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -29,6 +29,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - List modifications to existing features, where the previous behavior wouldn't actually be considered a bug +- Nodes are now treated as PathLike objects. + FIXES ----- diff --git a/SCons/Builder.py b/SCons/Builder.py index c81d104143..870596f0c7 100644 --- a/SCons/Builder.py +++ b/SCons/Builder.py @@ -114,6 +114,7 @@ from SCons.Debug import logInstanceCreation from SCons.Errors import InternalError, UserError from SCons.Executor import Executor +from SCons.Node import Node class _Null: pass @@ -487,10 +488,11 @@ def _adjustixes(self, files, pre, suf, ensure_suffix: bool=False): # fspath() is to catch PathLike paths. We avoid the simpler # str(f) so as not to "lose" files that are already Nodes: # TypeError: expected str, bytes or os.PathLike object, not File - with suppress(TypeError): - f = os.fspath(f) - if SCons.Util.is_String(f): - f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) + if not isinstance(f, Node): + with suppress(TypeError): + f = os.fspath(f) + if SCons.Util.is_String(f): + f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) result.append(f) return result diff --git a/SCons/BuilderTests.py b/SCons/BuilderTests.py index adfa648280..fff26171e9 100644 --- a/SCons/BuilderTests.py +++ b/SCons/BuilderTests.py @@ -702,7 +702,7 @@ def test_single_source(self) -> None: """Test Builder with single_source flag set""" def func(target, source, env) -> None: """create the file""" - with open(str(target[0]), "w"): + with open(target[0], "w"): pass if len(source) == 1 and len(target) == 1: env['CNT'][0] = env['CNT'][0] + 1 @@ -759,7 +759,7 @@ def test_lists(self) -> None: """Testing handling lists of targets and source""" def function2(target, source, env, tlist = [outfile, outfile2], **kw) -> int: for t in target: - with open(str(t), 'w') as f: + with open(t, 'w') as f: f.write("function2\n") for t in tlist: if t not in list(map(str, target)): @@ -790,7 +790,7 @@ def function2(target, source, env, tlist = [outfile, outfile2], **kw) -> int: def function3(target, source, env, tlist = [sub1_out, sub2_out]) -> int: for t in target: - with open(str(t), 'w') as f: + with open(t, 'w') as f: f.write("function3\n") for t in tlist: if t not in list(map(str, target)): diff --git a/SCons/Environment.xml b/SCons/Environment.xml index e59ab02f7b..d5e436531d 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -1248,7 +1248,7 @@ env.Command( import os def rename(env, target, source): - os.rename('.tmp', str(target[0])) + os.rename('.tmp', target[0]) env.Command( @@ -3626,7 +3626,7 @@ def create(target, source, env): Writes 'prefix=$SOURCE' into the file name given as $TARGET. """ - with open(str(target[0]), 'wb') as f: + with open(target[0], 'wb') as f: f.write(b'prefix=' + source[0].get_contents() + b'\n') # Fetch the prefix= argument, if any, from the command line. diff --git a/SCons/Node/__init__.py b/SCons/Node/__init__.py index 055c3090cb..dba1c968c6 100644 --- a/SCons/Node/__init__.py +++ b/SCons/Node/__init__.py @@ -622,6 +622,9 @@ def __init__(self) -> None: # what line in what file created the node, for example). Annotate(self) + def __fspath__(self) -> str: + return str(self) + def disambiguate(self, must_exist: bool = False): return self diff --git a/SCons/SConfTests.py b/SCons/SConfTests.py index 520b02ff09..4a3c8b25dd 100644 --- a/SCons/SConfTests.py +++ b/SCons/SConfTests.py @@ -295,7 +295,7 @@ def test_TryAction(self) -> None: """Test SConf.TryAction """ def actionOK(target, source, env): - with open(str(target[0]), "w") as f: + with open(target[0], "w") as f: f.write("RUN OK\n") return None def actionFAIL(target, source, env) -> int: diff --git a/bin/SConsExamples.py b/bin/SConsExamples.py index 7e47c9613e..6f57085126 100644 --- a/bin/SConsExamples.py +++ b/bin/SConsExamples.py @@ -547,7 +547,7 @@ def process(source_file, ofp): elif line[:11] != "STRIP CCCOM": ofp.write(line) - with open(str(target[0]), "w") as fp: + with open(target[0], "w") as fp: for src in map(str, source): process(src, fp) fp.write('debug = ' + ARGUMENTS.get('debug', '0') + '\\n') diff --git a/test/Actions/append.py b/test/Actions/append.py index 8205e1beff..dcc9898387 100644 --- a/test/Actions/append.py +++ b/test/Actions/append.py @@ -45,13 +45,13 @@ env=Environment() def before(env, target, source): - with open(str(target[0]), "wb") as f: + with open(target[0], "wb") as f: f.write(b"Foo\\n") with open("before.txt", "wb") as f: f.write(b"Bar\\n") def after(env, target, source): - with open(str(target[0]), "rb") as fin, open("after%s", "wb") as fout: + with open(target[0], "rb") as fin, open("after%s", "wb") as fout: fout.write(fin.read()) env.Prepend(LINKCOM=Action(before)) diff --git a/test/Actions/exitstatfunc.py b/test/Actions/exitstatfunc.py index 7d5b2b976d..53e8477065 100644 --- a/test/Actions/exitstatfunc.py +++ b/test/Actions/exitstatfunc.py @@ -38,7 +38,7 @@ def always_succeed(s): return 0 def copy_fail(target, source, env): - with open(str(source[0]), 'rb') as infp, open(str(target[0]), 'wb') as f: + with open(source[0], 'rb') as infp, open(target[0], 'wb') as f: f.write(infp.read()) return 2 diff --git a/test/Actions/function.py b/test/Actions/function.py index 0b191e4044..fd5d5f0257 100644 --- a/test/Actions/function.py +++ b/test/Actions/function.py @@ -69,7 +69,7 @@ def writeDeps(target, source, env, b=%(b)s, r=r %(extraarg)s , header=header, tr def foo(b=b): return %(nestedfuncexp)s - with open(str(target[0]), 'wb') as f: + with open(target[0], 'wb') as f: f.write(bytearray(header, 'utf-8')) for d in env['ENVDEPS']: f.write(bytearray(d+'%(separator)s', 'utf-8')) diff --git a/test/Actions/pre-post-fixture/work2/SConstruct b/test/Actions/pre-post-fixture/work2/SConstruct index 347dcbe6f1..b2079f320e 100644 --- a/test/Actions/pre-post-fixture/work2/SConstruct +++ b/test/Actions/pre-post-fixture/work2/SConstruct @@ -3,7 +3,7 @@ # Copyright The SCons Foundation def b(target, source, env): - with open(str(target[0]), 'wb') as f: + with open(target[0], 'wb') as f: f.write((env['X'] + '\n').encode()) DefaultEnvironment(tools=[]) diff --git a/test/Actions/pre-post-fixture/work3/SConstruct b/test/Actions/pre-post-fixture/work3/SConstruct index 54f537ac8d..db63503d71 100644 --- a/test/Actions/pre-post-fixture/work3/SConstruct +++ b/test/Actions/pre-post-fixture/work3/SConstruct @@ -9,7 +9,7 @@ def post(target, source, env): pass def build(target, source, env): - with open(str(target[0]), 'wb') as f: + with open(target[0], 'wb') as f: f.write(b'build()\n') DefaultEnvironment(tools=[]) diff --git a/test/Actions/pre-post.py b/test/Actions/pre-post.py index 0d9f36fce9..14a9b85292 100644 --- a/test/Actions/pre-post.py +++ b/test/Actions/pre-post.py @@ -52,7 +52,7 @@ def before(env, target, source): f.write(b"Foo\\n") os.chmod(a, os.stat(a).st_mode | stat.S_IXUSR) with open("before.txt", "ab") as f: - f.write((os.path.splitext(str(target[0]))[0] + "\\n").encode()) + f.write((os.path.splitext(target[0])[0] + "\\n").encode()) def after(env, target, source): t = str(target[0]) @@ -104,11 +104,11 @@ def after(env, target, source): DefaultEnvironment(tools=[]) def pre_action(target, source, env): - with open(str(target[0]), 'ab') as f: + with open(target[0], 'ab') as f: f.write(('pre %%s\\n' %% source[0]).encode()) def post_action(target, source, env): - with open(str(target[0]), 'ab') as f: + with open(target[0], 'ab') as f: f.write(('post %%s\\n' %% source[0]).encode()) env = Environment(tools=[]) diff --git a/test/Actions/timestamp.py b/test/Actions/timestamp.py index d94a287ce4..e13ec005a6 100644 --- a/test/Actions/timestamp.py +++ b/test/Actions/timestamp.py @@ -45,7 +45,7 @@ test.write('SConstruct', """\ def my_copy(target, source, env): - with open(str(target[0]), 'w') as f, open(str(source[0]), 'r') as infp: + with open(target[0], 'w') as f, open(source[0], 'r') as infp: f.write(infp.read()) env = Environment() env.Decider('timestamp-match') diff --git a/test/Actions/unicode-signature-fixture/SConstruct b/test/Actions/unicode-signature-fixture/SConstruct index 95c969d29c..3e56b98861 100644 --- a/test/Actions/unicode-signature-fixture/SConstruct +++ b/test/Actions/unicode-signature-fixture/SConstruct @@ -5,7 +5,7 @@ fnode = File(u'foo.txt') def funcact(target, source, env): - with open(str(target[0]), 'wb') as f: + with open(target[0], 'wb') as f: f.write(b"funcact\n") for i in range(300): pass diff --git a/test/Alias/action.py b/test/Alias/action.py index 7eff1c7787..9a26be1dd4 100644 --- a/test/Alias/action.py +++ b/test/Alias/action.py @@ -33,10 +33,9 @@ test.write('SConstruct', """ def cat(target, source, env): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) def foo(target, source, env): diff --git a/test/Alias/scanner.py b/test/Alias/scanner.py index 4f7473b672..2d983c04d6 100644 --- a/test/Alias/scanner.py +++ b/test/Alias/scanner.py @@ -35,10 +35,9 @@ test.write('SConstruct', """ DefaultEnvironment(tools=[]) def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) XBuilder = Builder(action = cat, src_suffix = '.x', suffix = '.c') diff --git a/test/Batch/Boolean.py b/test/Batch/Boolean.py index 702d24befb..f4bc2ce6d1 100644 --- a/test/Batch/Boolean.py +++ b/test/Batch/Boolean.py @@ -37,7 +37,7 @@ def batch_build(target, source, env): for t, s in zip(target, source): - with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: + with open(t, 'wb') as f, open(s, 'rb') as infp: f.write(infp.read()) env = Environment(tools=[]) bb = Action(batch_build, batch_key=True) diff --git a/test/Batch/callable.py b/test/Batch/callable.py index 5c4d816d3c..cc7e8e371c 100644 --- a/test/Batch/callable.py +++ b/test/Batch/callable.py @@ -40,7 +40,7 @@ DefaultEnvironment(tools=[]) def batch_build(target, source, env): for t, s in zip(target, source): - with open(str(t), 'wb') as f, open(str(s), 'rb') as infp: + with open(t, 'wb') as f, open(s, 'rb') as infp: f.write(infp.read()) if ARGUMENTS.get('BATCH_CALLABLE'): def batch_key(action, env, target, source): diff --git a/test/Batch/generated.py b/test/Batch/generated.py index cb76ff2b7c..59db0f82e6 100644 --- a/test/Batch/generated.py +++ b/test/Batch/generated.py @@ -37,11 +37,11 @@ def batch_build(target, source, env): for t, s in zip(target, source): - with open(str(t), 'wb') as fp: + with open(t, 'wb') as fp: if str(t) == 'f3.out': with open('f3.include', 'rb') as f: fp.write(f.read()) - with open(str(s), 'rb') as f: + with open(s, 'rb') as f: fp.write(f.read()) env = Environment(tools=[]) bb = Action(batch_build, batch_key=True) diff --git a/test/Builder-factories.py b/test/Builder-factories.py index e1fb65ce3e..229b4972a0 100644 --- a/test/Builder-factories.py +++ b/test/Builder-factories.py @@ -47,7 +47,7 @@ def mkdir(env, source, target): f.write(b"MakeDirectory\\n") MakeDirectory = Builder(action=mkdir, target_factory=Dir) def collect(env, source, target): - with open(str(target[0]), 'wb') as out: + with open(target[0], 'wb') as out: dir = str(source[0]) for f in sorted(os.listdir(dir)): f = os.path.join(dir, f) diff --git a/test/Builder/errors.py b/test/Builder/errors.py index 7a713a3aac..1e6a3a0fef 100644 --- a/test/Builder/errors.py +++ b/test/Builder/errors.py @@ -38,7 +38,7 @@ DefaultEnvironment(tools=[]) def buildop(env, source, target): - with open(str(target[0]), 'wb') as outf, open(str(source[0]), 'r') as infp: + with open(target[0], 'wb') as outf, open(source[0], 'r') as infp: for line in inpf.readlines(): if line.find(str(target[0])) == -1: outf.write(line) diff --git a/test/Builder/multi/different-actions.py b/test/Builder/multi/different-actions.py index ec07b624e0..53141e8400 100644 --- a/test/Builder/multi/different-actions.py +++ b/test/Builder/multi/different-actions.py @@ -36,9 +36,9 @@ test.write('SConstruct', """\ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=Action(build, varlist=['XXX']), multi=1) diff --git a/test/Builder/multi/different-environments.py b/test/Builder/multi/different-environments.py index 82b10ecb1f..41b59d81cc 100644 --- a/test/Builder/multi/different-environments.py +++ b/test/Builder/multi/different-environments.py @@ -39,10 +39,10 @@ test.write('build.py', r"""\ import sys def build(num, target, source): - with open(str(target), 'wb') as f: + with open(target[0], 'wb') as f: f.write('%s\n' % num) - for s in source: - with open(str(s), 'rb') as infp: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) build(sys.argv[1],sys.argv[2],sys.argv[3:]) """) diff --git a/test/Builder/multi/different-multi.py b/test/Builder/multi/different-multi.py index 3084bf5381..0adabf7d74 100644 --- a/test/Builder/multi/different-multi.py +++ b/test/Builder/multi/different-multi.py @@ -37,9 +37,9 @@ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) def build2(env, target, source): diff --git a/test/Builder/multi/different-order.py b/test/Builder/multi/different-order.py index 4018159e18..6fc800d5f6 100644 --- a/test/Builder/multi/different-order.py +++ b/test/Builder/multi/different-order.py @@ -39,9 +39,9 @@ DefaultEnvironment(tools=[]) def build(env, target, source): for t in target: - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=build, multi=1) diff --git a/test/Builder/multi/different-overrides.py b/test/Builder/multi/different-overrides.py index c4267f300c..57418aa2b6 100644 --- a/test/Builder/multi/different-overrides.py +++ b/test/Builder/multi/different-overrides.py @@ -36,9 +36,9 @@ test.write('SConstruct', """\ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=build, multi=1) diff --git a/test/Builder/multi/different-target-lists.py b/test/Builder/multi/different-target-lists.py index 4b6c49e723..5c3ea76605 100644 --- a/test/Builder/multi/different-target-lists.py +++ b/test/Builder/multi/different-target-lists.py @@ -43,9 +43,9 @@ DefaultEnvironment(tools=[]) def build(env, target, source): for t in target: - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=build, multi=1) diff --git a/test/Builder/multi/error.py b/test/Builder/multi/error.py index 3b2a8d493c..ae3f48d795 100644 --- a/test/Builder/multi/error.py +++ b/test/Builder/multi/error.py @@ -37,9 +37,9 @@ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=build, multi=0) diff --git a/test/Builder/multi/lone-target-list.py b/test/Builder/multi/lone-target-list.py index 885d34a99c..0a1450066b 100644 --- a/test/Builder/multi/lone-target-list.py +++ b/test/Builder/multi/lone-target-list.py @@ -37,9 +37,9 @@ def build(env, target, source): for t in target: - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(t, 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=build, multi=1) diff --git a/test/Builder/multi/multi.py b/test/Builder/multi/multi.py index aec09513e5..140b8c4378 100644 --- a/test/Builder/multi/multi.py +++ b/test/Builder/multi/multi.py @@ -37,9 +37,9 @@ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=build, multi=1) diff --git a/test/Builder/multi/same-actions.py b/test/Builder/multi/same-actions.py index f2a8fe3ec2..7b56dd045d 100644 --- a/test/Builder/multi/same-actions.py +++ b/test/Builder/multi/same-actions.py @@ -37,9 +37,9 @@ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=build, multi=1) diff --git a/test/Builder/multi/same-overrides.py b/test/Builder/multi/same-overrides.py index c545329ef6..9b5698bca3 100644 --- a/test/Builder/multi/same-overrides.py +++ b/test/Builder/multi/same-overrides.py @@ -37,10 +37,10 @@ test.write('build.py', r"""\ import sys def build(num, target, source): - with open(str(target), 'wb') as f: + with open(target, 'wb') as f: f.write(bytearray('%s\n'% num,'utf-8')) - for s in source: - with open(str(s), 'rb') as infp: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) build(sys.argv[1], sys.argv[2], sys.argv[3:]) """) diff --git a/test/Builder/multi/same-targets.py b/test/Builder/multi/same-targets.py index 17f6f990dc..f343a82e4e 100644 --- a/test/Builder/multi/same-targets.py +++ b/test/Builder/multi/same-targets.py @@ -38,9 +38,9 @@ def build(env, target, source): for t in target: - with open(str(t), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(t, 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=build, multi=1) diff --git a/test/Builder/non-multi.py b/test/Builder/non-multi.py index d540f6bf8b..83fc5230d3 100644 --- a/test/Builder/non-multi.py +++ b/test/Builder/non-multi.py @@ -36,9 +36,9 @@ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) B = Builder(action=build, multi=0) diff --git a/test/Builder/same-actions-diff-envs.py b/test/Builder/same-actions-diff-envs.py index 709b10850f..9d6fcbb049 100644 --- a/test/Builder/same-actions-diff-envs.py +++ b/test/Builder/same-actions-diff-envs.py @@ -36,7 +36,7 @@ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'w') as f: + with open(target[0], 'w') as f: f.write('1') B = Builder(action=build) diff --git a/test/Builder/same-actions-diff-overrides.py b/test/Builder/same-actions-diff-overrides.py index ba3ee9078d..cd7c1527d4 100644 --- a/test/Builder/same-actions-diff-overrides.py +++ b/test/Builder/same-actions-diff-overrides.py @@ -36,7 +36,7 @@ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'w') as f: + with open(target[0], 'w') as f: f.write('1') B = Builder(action=build) diff --git a/test/Builder/wrapper.py b/test/Builder/wrapper.py index 60f8b21baa..a4de067a88 100644 --- a/test/Builder/wrapper.py +++ b/test/Builder/wrapper.py @@ -38,9 +38,9 @@ import os.path import string def cat(target, source, env): - with open(str(target[0]), 'wb') as fp: - for s in map(str, source): - with open(s, 'rb') as infp: + with open(target[0], 'wb') as fp: + for src in source: + with open(src, 'rb') as infp: fp.write(infp.read()) Cat = Builder(action=cat) def Wrapper(env, target, source): diff --git a/test/CacheDir/CacheDir.py b/test/CacheDir/CacheDir.py index 05134e613a..45533b553c 100644 --- a/test/CacheDir/CacheDir.py +++ b/test/CacheDir/CacheDir.py @@ -56,7 +56,7 @@ def cat(env, source, target): f.write(target + "\\n") with open(target, "w") as f: for src in source: - with open(str(src), "r") as f2: + with open(src, "r") as f2: f.write(f2.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') diff --git a/test/CacheDir/SideEffect.py b/test/CacheDir/SideEffect.py index ee808d6e40..88b3b043c0 100644 --- a/test/CacheDir/SideEffect.py +++ b/test/CacheDir/SideEffect.py @@ -42,12 +42,10 @@ def copy(source, target): f.write(f2.read()) def build(env, source, target): - s = str(source[0]) - t = str(target[0]) - copy(s, t) + copy(source[0], target[0]) if target[0].side_effects: - with open(str(target[0].side_effects[0]), "a") as side_effect: - side_effect.write(s + ' -> ' + t + '\\n') + with open(target[0].side_effects[0], "a") as side_effect: + side_effect.write(str(source[0]) + ' -> ' + str(target[0]) + '\\n') CacheDir(r'%(cache)s') diff --git a/test/CacheDir/VariantDir.py b/test/CacheDir/VariantDir.py index 8c449173a5..1b9d2bfa64 100644 --- a/test/CacheDir/VariantDir.py +++ b/test/CacheDir/VariantDir.py @@ -47,7 +47,7 @@ def cat(env, source, target): f.write(target + "\\n") with open(target, "w") as f: for src in source: - with open(str(src), "r") as f2: + with open(src, "r") as f2: f.write(f2.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') diff --git a/test/CacheDir/debug.py b/test/CacheDir/debug.py index a8c4e835d3..cf16f98d10 100644 --- a/test/CacheDir/debug.py +++ b/test/CacheDir/debug.py @@ -56,7 +56,7 @@ def cat(env, source, target): f.write(target + "\\n") with open(target, "w") as f: for src in source: - with open(str(src), "r") as f2: + with open(src, "r") as f2: f.write(f2.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') diff --git a/test/CacheDir/environment.py b/test/CacheDir/environment.py index 23e35087ac..26655ce7fc 100644 --- a/test/CacheDir/environment.py +++ b/test/CacheDir/environment.py @@ -57,7 +57,7 @@ def cat(env, source, target): f.write(target + "\\n") with open(target, "w") as f: for src in source: - with open(str(src), "r") as f2: + with open(src, "r") as f2: f.write(f2.read()) env_cache = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env_nocache = env_cache.Clone() diff --git a/test/CacheDir/option--cd.py b/test/CacheDir/option--cd.py index df9ab47041..e3915880b4 100644 --- a/test/CacheDir/option--cd.py +++ b/test/CacheDir/option--cd.py @@ -43,7 +43,7 @@ def cat(env, source, target): f.write(target + "\\n") with open(target, "w") as f: for src in source: - with open(str(src), "r") as f2: + with open(src, "r") as f2: f.write(f2.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') diff --git a/test/CacheDir/option--cf.py b/test/CacheDir/option--cf.py index b34d706b77..52d107b7a0 100644 --- a/test/CacheDir/option--cf.py +++ b/test/CacheDir/option--cf.py @@ -44,7 +44,7 @@ def cat(env, source, target): f.write(target + "\\n") with open(target, "w") as f: for src in source: - with open(str(src), "r") as f2: + with open(src, "r") as f2: f.write(f2.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') diff --git a/test/CacheDir/option--cr.py b/test/CacheDir/option--cr.py index 4c423cfd19..a1d32d2277 100644 --- a/test/CacheDir/option--cr.py +++ b/test/CacheDir/option--cr.py @@ -43,7 +43,7 @@ def cat(env, source, target): f.write(target + "\\n") with open(target, "w") as f: for src in source: - with open(str(src), "r") as f2: + with open(src, "r") as f2: f.write(f2.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') diff --git a/test/CacheDir/option--cs.py b/test/CacheDir/option--cs.py index 62c0026f76..4a0bb33ffa 100644 --- a/test/CacheDir/option--cs.py +++ b/test/CacheDir/option--cs.py @@ -59,7 +59,7 @@ def cat(env, source, target): f.write(target + "\\n") with open(target, "w") as f: for src in source: - with open(str(src), "r") as f2: + with open(src, "r") as f2: f.write(f2.read()) DefaultEnvironment(tools=[]) # test speedup diff --git a/test/Chmod.py b/test/Chmod.py index 87e7b15acb..c1a83422b3 100644 --- a/test/Chmod.py +++ b/test/Chmod.py @@ -47,7 +47,7 @@ def cat(env, source, target): target = str(target[0]) with open(target, "wb") as f: for src in source: - with open(str(src), "rb") as infp: + with open(src, "rb") as infp: f.write(infp.read()) Cat = Action(cat) diff --git a/test/Climb/explicit-parent--D.py b/test/Climb/explicit-parent--D.py index c44dc38401..1d5c7460bd 100644 --- a/test/Climb/explicit-parent--D.py +++ b/test/Climb/explicit-parent--D.py @@ -40,7 +40,7 @@ def cat(env, source, target): target = str(target[0]) with open(target, 'wb') as ofp: for src in source: - with open(str(src), 'rb') as ifp: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) env.Cat('f1.out', 'f1.in') diff --git a/test/Climb/explicit-parent--U.py b/test/Climb/explicit-parent--U.py index a37d5a1608..e129b4aedd 100644 --- a/test/Climb/explicit-parent--U.py +++ b/test/Climb/explicit-parent--U.py @@ -40,7 +40,7 @@ def cat(env, source, target): target = str(target[0]) with open(target, 'wb') as ofp: for src in source: - with open(str(src), 'rb') as ifp: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) diff --git a/test/Climb/explicit-parent-u.py b/test/Climb/explicit-parent-u.py index c8c5da23e0..337e0b3827 100644 --- a/test/Climb/explicit-parent-u.py +++ b/test/Climb/explicit-parent-u.py @@ -41,7 +41,7 @@ def cat(env, source, target): target = str(target[0]) with open(target, 'wb') as ofp: for src in source: - with open(str(src), 'rb') as ifp: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=cat)}) diff --git a/test/Climb/option-u.py b/test/Climb/option-u.py index b7298d2801..de98040786 100644 --- a/test/Climb/option-u.py +++ b/test/Climb/option-u.py @@ -45,7 +45,7 @@ def cat(env, source, target): target = str(target[0]) with open(target, 'wb') as ofp: for src in source: - with open(str(src), 'rb') as ifp: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env = Environment(tools=[]) env.Append(BUILDERS = {'Cat' : Builder(action=cat)}) diff --git a/test/Command.py b/test/Command.py index 5abdf169a6..687c13fffb 100644 --- a/test/Command.py +++ b/test/Command.py @@ -46,7 +46,7 @@ import sys def buildIt(env, target, source): - with open(str(target[0]), 'w') as f, open(str(source[0]), 'r') as infp: + with open(target[0], 'w') as f, open(source[0], 'r') as infp: xyzzy = env.get('XYZZY', '') if xyzzy: f.write(xyzzy + '\\n') diff --git a/test/Copy-Action.py b/test/Copy-Action.py index 1b1356e30e..c591011ad4 100644 --- a/test/Copy-Action.py +++ b/test/Copy-Action.py @@ -46,10 +46,9 @@ Execute(Copy('d7.out', Glob('f?.in'))) def cat(env, source, target): - target = str(target[0]) - with open(target, "w") as f: + with open(target[0], "w") as f: for src in source: - with open(str(src), "r") as ifp: + with open(src, "r") as ifp: f.write(ifp.read()) Cat = Action(cat) diff --git a/test/Decider/switch-rebuild.py b/test/Decider/switch-rebuild.py index 29264553cb..4cd09d6f5e 100644 --- a/test/Decider/switch-rebuild.py +++ b/test/Decider/switch-rebuild.py @@ -38,7 +38,7 @@ Decider('%s') def build(env, target, source): - with open(str(target[0]), 'wt') as f, open(str(source[0]), 'rt') as ifp: + with open(target[0], 'wt') as f, open(source[0], 'rt') as ifp: f.write(ifp.read()) B = Builder(action=build) env = Environment(tools=[], BUILDERS = { 'B' : B }) diff --git a/test/Delete.py b/test/Delete.py index fc4ab4fd3c..fff8946ddd 100644 --- a/test/Delete.py +++ b/test/Delete.py @@ -43,10 +43,9 @@ Execute(Delete('symlinks/dirlink')) def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as ofp: + with open(target[0], "wb") as ofp: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: ofp.write(ifp.read()) Cat = Action(cat) env = Environment() @@ -193,10 +192,9 @@ def cat(env, source, target): test.write("SConstruct", """\ def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as ifp: + with open(target[0], "wb") as ifp: for src in source: - with open(str(src), "rb") as ofp: + with open(src, "rb") as ofp: ofp.write(ifp.read()) Cat = Action(cat) env = Environment() diff --git a/test/Dir/source.py b/test/Dir/source.py index a0ba987956..21516bcdbd 100644 --- a/test/Dir/source.py +++ b/test/Dir/source.py @@ -44,7 +44,7 @@ DefaultEnvironment(tools=[]) def writeTarget(target, source, env): - f = open(str(target[0]), 'w') + f = open(target[0], 'w') f.write("stuff\\n") f.close() return 0 diff --git a/test/Errors/Exception.py b/test/Errors/Exception.py index cdd6a3c5ec..9c5f1681a2 100644 --- a/test/Errors/Exception.py +++ b/test/Errors/Exception.py @@ -30,7 +30,7 @@ test.write('SConstruct', """\ def foo(env, target, source): print(str(target[0])) - with open(str(target[0]), 'wt') as f: + with open(target[0], 'wt') as f: f.write('foo') def exit(env, target, source): diff --git a/test/Exit.py b/test/Exit.py index abaa382c0a..3aa690e44f 100644 --- a/test/Exit.py +++ b/test/Exit.py @@ -103,10 +103,9 @@ test.write(['subdir', 'SConscript'], """\ def exit_builder(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) Exit(27) env = Environment(BUILDERS = {'my_exit' : Builder(action=exit_builder)}) @@ -132,10 +131,9 @@ def exit_scanner(node, env, target): exitscan = Scanner(function = exit_scanner, skeys = ['.k']) def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: + with open(target[0], 'wb') as ofp: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: outf.write(ifp.read()) env = Environment(BUILDERS={'Cat':Builder(action=cat)}) diff --git a/test/FindFile.py b/test/FindFile.py index fa195c4d04..53612ae091 100644 --- a/test/FindFile.py +++ b/test/FindFile.py @@ -40,16 +40,16 @@ test.write('SConstruct', """ env = Environment(FILE = 'file', BAR = 'bar') file1 = FindFile('testfile1', [ 'foo', '.', 'bar', 'bar/baz' ]) -with open(str(file1), 'r') as f: +with open(file1, 'r') as f: print(f.read()) file2 = env.FindFile('test${FILE}1', [ 'bar', 'foo', '.', 'bar/baz' ]) -with open(str(file2), 'r') as f: +with open(file2, 'r') as f: print(f.read()) file3 = FindFile('testfile2', [ 'foo', '.', 'bar', 'bar/baz' ]) -with open(str(file3), 'r') as f: +with open(file3, 'r') as f: print(f.read()) file4 = env.FindFile('testfile2', [ '$BAR/baz', 'foo', '.', 'bar' ]) -with open(str(file4), 'r') as f: +with open(file4, 'r') as f: print(f.read()) """) diff --git a/test/Flatten.py b/test/Flatten.py index 40bbb3e08c..7f3acec513 100644 --- a/test/Flatten.py +++ b/test/Flatten.py @@ -36,10 +36,9 @@ test.write(['work', 'SConstruct'], """ def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as ofp: + with open(target[0], "wb") as ofp: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: ofp.write(ifp.read()) env = Environment(BUILDERS={'Cat':Builder(action=cat)}) f1 = env.Cat('../file1.out', 'file1.in') diff --git a/test/Glob/Repository.py b/test/Glob/Repository.py index 714bafa57a..b2b6195760 100644 --- a/test/Glob/Repository.py +++ b/test/Glob/Repository.py @@ -49,10 +49,9 @@ test.write(['repository', 'SConstruct'], """\ DefaultEnvironment(tools=[]) def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as ofp: + with open(target[0], 'wb') as ofp: for src in source: - with open(str(src), "rb") as ifp: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) # Verify that we can glob a repository-only Node that exists diff --git a/test/Glob/VariantDir.py b/test/Glob/VariantDir.py index 6340bb8007..3b64848b04 100644 --- a/test/Glob/VariantDir.py +++ b/test/Glob/VariantDir.py @@ -49,12 +49,12 @@ """) test.write(['src', 'SConscript'], """\ -env = Environment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): - with open(str(target[0]), 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + with open(target[0], 'wb') as ofp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env['BUILDERS']['Concatenate'] = Builder(action=concatenate) @@ -67,9 +67,9 @@ def concatenate(target, source, env): env = Environment(tools=[]) def concatenate(target, source, env): - with open(str(target[0]), 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + with open(target[0], 'wb') as ofp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env['BUILDERS']['Concatenate'] = Builder(action=concatenate) diff --git a/test/Glob/basic.py b/test/Glob/basic.py index 5e5cdb5ac2..53077ba93c 100644 --- a/test/Glob/basic.py +++ b/test/Glob/basic.py @@ -34,12 +34,12 @@ test.write('SConstruct', """\ DefaultEnvironment(tools=[]) -env = Environment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): - with open(str(target[0]), 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + with open(target[0], 'wb') as ofp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env['BUILDERS']['Concatenate'] = Builder(action=concatenate) diff --git a/test/Glob/exclude.py b/test/Glob/exclude.py index 658d99acd7..5c909cb186 100644 --- a/test/Glob/exclude.py +++ b/test/Glob/exclude.py @@ -37,12 +37,12 @@ test.write('SConstruct', """\ DefaultEnvironment(tools=[]) -env = Environment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): - with open(str(target[0]), 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + with open(target[0], 'wb') as ofp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env['BUILDERS']['Concatenate'] = Builder(action=concatenate) diff --git a/test/Glob/source.py b/test/Glob/source.py index 33aff85163..807d2d7de2 100644 --- a/test/Glob/source.py +++ b/test/Glob/source.py @@ -38,12 +38,12 @@ test.write('SConstruct', """\ DefaultEnvironment(tools=[]) -env = Environment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): - with open(str(target[0]), 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + with open(target[0], 'wb') as ofp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env['BUILDERS']['Concatenate'] = Builder(action=concatenate) diff --git a/test/Glob/strings.py b/test/Glob/strings.py index 2a4a624db6..76824f00f1 100644 --- a/test/Glob/strings.py +++ b/test/Glob/strings.py @@ -46,12 +46,12 @@ """) test.write(['src', 'SConscript'], """\ -env = Environment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): - with open(str(target[0]), 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + with open(target[0], 'wb') as ofp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env['BUILDERS']['Concatenate'] = Builder(action=concatenate) diff --git a/test/Glob/subdir.py b/test/Glob/subdir.py index 22439f7d07..63135ba08c 100644 --- a/test/Glob/subdir.py +++ b/test/Glob/subdir.py @@ -37,12 +37,12 @@ test.write('SConstruct', """\ DefaultEnvironment(tools=[]) -env = Environment(tools=[]) +env = Environment(tools=[]) def concatenate(target, source, env): - with open(str(target[0]), 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + with open(target[0], 'wb') as ofp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env['BUILDERS']['Concatenate'] = Builder(action=concatenate) diff --git a/test/Glob/subst.py b/test/Glob/subst.py index efbc916470..4b38779fc7 100644 --- a/test/Glob/subst.py +++ b/test/Glob/subst.py @@ -38,9 +38,9 @@ env = Environment(tools=[], PATTERN = 'f*.in') def copy(target, source, env): - with open(str(target[0]), 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + with open(target[0], 'wb') as ofp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) env['BUILDERS']['Copy'] = Builder(action=copy) diff --git a/test/HeaderGen.py b/test/HeaderGen.py index f66ef574de..10fa68639f 100644 --- a/test/HeaderGen.py +++ b/test/HeaderGen.py @@ -35,7 +35,7 @@ test.write('SConstruct', """\ def writeFile(target, contents): - with open(str(target[0]), 'w') as f: + with open(target[0], 'w') as f: f.write(contents) return 0 @@ -59,7 +59,7 @@ def writeFile(target, contents): env = Environment() def gen_a_h(target, source, env): - with open(str(target[0]), 'w') as t, open(str(source[0]), 'r') as s: + with open(target[0], 'w') as t, open(source[0], 'r') as s: s.readline() t.write(s.readline()[:-1] + ';\\n') diff --git a/test/Install/Install.py b/test/Install/Install.py index 802b10dae6..8bb9ed719c 100644 --- a/test/Install/Install.py +++ b/test/Install/Install.py @@ -50,10 +50,9 @@ test.write(['work', 'SConstruct'], """\ DefaultEnvironment(tools=[]) def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: + with open(target[0], 'wb') as ofp: for src in source: - with open(str(src), 'rb') as ifp: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) def my_install(dest, source, env): diff --git a/test/Install/wrap-by-attribute.py b/test/Install/wrap-by-attribute.py index c18ea7757e..7cb645b2da 100644 --- a/test/Install/wrap-by-attribute.py +++ b/test/Install/wrap-by-attribute.py @@ -46,10 +46,9 @@ def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as ofp: + with open(target[0], 'wb') as ofp: for src in source: - with open(str(src), 'rb') as ifp: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) diff --git a/test/Interactive/option-j.py b/test/Interactive/option-j.py index 356a06749a..60b8fcc7f4 100644 --- a/test/Interactive/option-j.py +++ b/test/Interactive/option-j.py @@ -40,8 +40,8 @@ def cat(target, source, env): t = str(target[0]) os.mkdir(t + '.started') with open(t, 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) os.mkdir(t + '.finished') @@ -64,8 +64,8 @@ def must_wait_for_f2_b_out(target, source, env): while not os.path.exists(f2_b_started): time.sleep(1) with open(t, 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) os.mkdir(t + '.finished') diff --git a/test/Mkdir.py b/test/Mkdir.py index 367c834952..d177232b66 100644 --- a/test/Mkdir.py +++ b/test/Mkdir.py @@ -40,10 +40,9 @@ Execute(Mkdir('d1')) Execute(Mkdir(Dir('#d1-Dir'))) def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) Cat = Action(cat) env = Environment() diff --git a/test/Move.py b/test/Move.py index 1da3aa7b8a..aca8fa424e 100644 --- a/test/Move.py +++ b/test/Move.py @@ -36,10 +36,9 @@ Execute(Move('f1.out', 'f1.in')) Execute(Move('File-f1.out', File('f1.in-File'))) def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) Cat = Action(cat) env = Environment() diff --git a/test/NodeOps.py b/test/NodeOps.py index a2db429dbd..5a19c642f8 100644 --- a/test/NodeOps.py +++ b/test/NodeOps.py @@ -64,9 +64,9 @@ if %(_E)s: import os derived = [N.is_derived() for N in Nodes] - real1 = [os.path.exists(str(N)) for N in Nodes] + real1 = [os.path.exists(N) for N in Nodes] exists = [N.exists() for N in Nodes] - real2 = [os.path.exists(str(N)) for N in Nodes] + real2 = [os.path.exists(N) for N in Nodes] for N,D,R,E,F in zip(Nodes, derived, real1, exists, real2): print('%%s: %%s %%s %%s %%s'%%(N,D,R,E,F)) foo.SharedLibrary(target = 'foo', source = 'foo%(_obj)s') @@ -119,13 +119,13 @@ Import('*') def mycopy(env, source, target): - with open(str(target[0]), 'wt') as fo, open(str(source[0]), 'rt') as fi: + with open(target[0], 'wt') as fo, open(source[0], 'rt') as fi: fo.write(fi.read()) def exists_test(node): - before = os.path.exists(str(node)) # doesn't exist yet in VariantDir + before = os.path.exists(node) # doesn't exist yet in VariantDir via_node = node.exists() # side effect causes copy from src - after = os.path.exists(str(node)) + after = os.path.exists(node) node.is_derived() import SCons.Script if GetOption('no_exec'): diff --git a/test/Repository/LIBPATH.py b/test/Repository/LIBPATH.py index 646b5d7e4f..9e19b3f64d 100644 --- a/test/Repository/LIBPATH.py +++ b/test/Repository/LIBPATH.py @@ -46,7 +46,7 @@ def write_LIBDIRFLAGS(env, target, source): pre = env.subst('$LIBDIRPREFIX') suf = env.subst('$LIBDIRSUFFIX') - with open(str(target[0]), 'w') as f: + with open(target[0], 'w') as f: for arg in env.subst('$_LIBDIRFLAGS', target=target).split(): if arg[:len(pre)] == pre: arg = arg[len(pre):] diff --git a/test/Repository/SConscript.py b/test/Repository/SConscript.py index 72e2d27bc5..3208304915 100644 --- a/test/Repository/SConscript.py +++ b/test/Repository/SConscript.py @@ -51,10 +51,9 @@ test.write(['rep1', 'src', 'SConscript'], """\ def cat(env, source, target): - target = str(target[0]) - with open(target, "w") as ofp: + with open(target[0], "w") as ofp: for src in source: - with open(str(src), "r") as ifp: + with open(src, "r") as ifp: ofp.write(ifp.read()) env = Environment(BUILDERS={'Cat':Builder(action=cat)}) @@ -88,10 +87,9 @@ def cat(env, source, target): test.write(['rep2', 'src', 'SConscript'], """\ def cat(env, source, target): - target = str(target[0]) - with open(target, "w") as ofp: + with open(target[0], "w") as ofp: for src in source: - with open(str(src), "r") as ifp: + with open(src, "r") as ifp: ofp.write(ifp.read()) env = Environment(BUILDERS={'Cat':Builder(action=cat)}) diff --git a/test/Repository/option-f.py b/test/Repository/option-f.py index f1b2cc6945..0dfea7504c 100644 --- a/test/Repository/option-f.py +++ b/test/Repository/option-f.py @@ -42,10 +42,9 @@ test.write(['repository', 'SConstruct'], """\ Repository(r'%s') def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as ofp: + with open(target[0], "wb") as ofp: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: ofp.write(ifp.read()) env = Environment(BUILDERS={'Build':Builder(action=cat)}) diff --git a/test/Requires/basic.py b/test/Requires/basic.py index 4851ac8b5b..2948d9da61 100644 --- a/test/Requires/basic.py +++ b/test/Requires/basic.py @@ -35,9 +35,9 @@ test.write('SConstruct', """ def append_prereq_func(target, source, env): - with open(str(target[0]), 'wb') as ofp: - for s in source: - with open(str(s), 'rb') as ifp: + with open(target[0], 'wb') as ofp: + for src in source: + with open(src, 'rb') as ifp: ofp.write(ifp.read()) with open('prereq.out', 'rb') as ifp: ofp.write(ifp.read()) diff --git a/test/Requires/eval-order.py b/test/Requires/eval-order.py index fddf232284..b076d51132 100644 --- a/test/Requires/eval-order.py +++ b/test/Requires/eval-order.py @@ -34,9 +34,9 @@ test.write('SConstruct', """ def copy_and_create_func(target, source, env): - with open(str(target[0]), 'w') as ofp: - for s in source: - with open(str(s), 'r') as ifp: + with open(target[0], 'w') as ofp: + for src in source: + with open(src, 'r') as ifp: ofp.write(ifp.read()) with open('file.in', 'w') as f: f.write("file.in 1\\n") diff --git a/test/Scanner/Scanner.py b/test/Scanner/Scanner.py index 4889d0f280..d64a7ab6cd 100644 --- a/test/Scanner/Scanner.py +++ b/test/Scanner/Scanner.py @@ -117,7 +117,7 @@ def third(env, target, source): contents = source[0].get_contents() # print("TYPE:"+str(type(contents))) contents = contents.replace(b'getfile', b'MISSEDME') - with open(str(target[0]), 'wb') as f: + with open(target[0], 'wb') as f: f.write(contents) kbld = Builder(action=r'%(_python_)s build.py $SOURCES $TARGET', diff --git a/test/Scanner/empty-implicit.py b/test/Scanner/empty-implicit.py index a1e7b032eb..33482d2a7c 100644 --- a/test/Scanner/empty-implicit.py +++ b/test/Scanner/empty-implicit.py @@ -37,11 +37,11 @@ import os.path def scan(node, env, envkey, arg): - print('XScanner: node = '+os.path.split(str(node))[1]) + print('XScanner: node = '+os.path.split(node)[1]) return [] def exists_check(node, env): - return os.path.exists(str(node)) + return os.path.exists(node) XScanner = Scanner(name = 'XScanner', function = scan, @@ -50,8 +50,8 @@ def exists_check(node, env): skeys = ['.x']) def echo(env, target, source): - t = os.path.split(str(target[0]))[1] - s = os.path.split(str(source[0]))[1] + t = os.path.split(target[0])[1] + s = os.path.split(source[0])[1] print('create %s from %s' % (t, s)) with open(t, 'wb') as ofb, open(s, 'rb') as ifb: ofb.write(ifb.read()) diff --git a/test/Scanner/exception.py b/test/Scanner/exception.py index 90791cb06c..c87d93df0b 100644 --- a/test/Scanner/exception.py +++ b/test/Scanner/exception.py @@ -67,10 +67,9 @@ def process(outf, inf): outf.write(line) def cat(env, source, target): - target = str(target[0]) - with open(target, 'wb') as outf: + with open(target[0], 'wb') as outf: for src in source: - with open(str(src), 'rb') as inf: + with open(src, 'rb') as inf: process(outf, inf) env = Environment(BUILDERS={'Cat':Builder(action=cat)}) diff --git a/test/Scanner/no-Dir-node.py b/test/Scanner/no-Dir-node.py index ef90934247..e10dc5d983 100644 --- a/test/Scanner/no-Dir-node.py +++ b/test/Scanner/no-Dir-node.py @@ -81,7 +81,7 @@ def process(infp, outfp): test.write('SConstruct', """\ def foo(target, source, env): - fp = open(str(target[0]), 'w') + fp = open(target[0], 'w') for c in sorted(source[0].children(), key=lambda t: t.name): fp.write('%s\\n' % c) fp.close() diff --git a/test/Scanner/scan-once.py b/test/Scanner/scan-once.py index bbe2594c2d..304b523762 100644 --- a/test/Scanner/scan-once.py +++ b/test/Scanner/scan-once.py @@ -36,11 +36,11 @@ import os.path def scan(node, env, envkey, arg): - print('XScanner: node = '+ os.path.split(str(node))[1]) + print('XScanner: node = '+ os.path.split(node)[1]) return [] def exists_check(node, env): - return os.path.exists(str(node)) + return os.path.exists(node) XScanner = Scanner(name = 'XScanner', function = scan, @@ -49,8 +49,8 @@ def exists_check(node, env): skeys = ['.x']) def echo(env, target, source): - t = os.path.split(str(target[0]))[1] - s = os.path.split(str(source[0]))[1] + t = os.path.split(target[0])[1] + s = os.path.split(source[0])[1] print('create %s from %s' % (t, s)) Echo = Builder(action = Action(echo, None), diff --git a/test/SideEffect/Issues/3013/files/SConstruct b/test/SideEffect/Issues/3013/files/SConstruct index 9294cb0308..9d897c2a4e 100644 --- a/test/SideEffect/Issues/3013/files/SConstruct +++ b/test/SideEffect/Issues/3013/files/SConstruct @@ -6,7 +6,7 @@ DefaultEnvironment(tools=[]) env = Environment() def make_file(target, source, env): - with open(str(target[0]), 'w') as f: + with open(target[0], 'w') as f: f.write('gobldygook') with open(str(target[0]) + '_side_effect', 'w') as side_effect: side_effect.write('anything') @@ -23,4 +23,3 @@ SConscript( exports={'env':env}, duplicate=0 ) - diff --git a/test/SideEffect/basic.py b/test/SideEffect/basic.py index b5b6381e21..aa8099c5d2 100644 --- a/test/SideEffect/basic.py +++ b/test/SideEffect/basic.py @@ -41,9 +41,9 @@ def copy(source, target): f.write(f2.read()) def build(env, source, target): - copy(str(source[0]), str(target[0])) + copy(source[0], target[0]) if target[0].side_effects: - with open(str(target[0].side_effects[0]), "ab") as side_effect: + with open(target[0].side_effects[0], "ab") as side_effect: side_effect.write(('%%s -> %%s\\n'%%(str(source[0]), str(target[0]))).encode()) Build = Builder(action=build) diff --git a/test/SideEffect/variant_dir.py b/test/SideEffect/variant_dir.py index 0e9ae532a5..3f815d313c 100644 --- a/test/SideEffect/variant_dir.py +++ b/test/SideEffect/variant_dir.py @@ -35,16 +35,16 @@ test = TestSCons.TestSCons() -test.write('SConstruct', +test.write('SConstruct', """ def copy(source, target): with open(target, "wb") as f, open(source, "rb") as f2: f.write(f2.read()) def build(env, source, target): - copy(str(source[0]), str(target[0])) + copy(source[0], target[0]) if target[0].side_effects: - with open(str(target[0].side_effects[0]), "ab") as side_effect: + with open(target[0].side_effects[0], "ab") as side_effect: side_effect.write(('%s -> %s\\n'%(str(source[0]), str(target[0]))).encode()) Build = Builder(action=build) diff --git a/test/TARGET-dir.py b/test/TARGET-dir.py index 652cf77516..a3cb8da08f 100644 --- a/test/TARGET-dir.py +++ b/test/TARGET-dir.py @@ -42,10 +42,9 @@ test.write('SConstruct', """ def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) f.close() env = Environment(CPPPATH='${TARGET.dir}') diff --git a/test/Touch.py b/test/Touch.py index 3538c7d338..e3619466aa 100644 --- a/test/Touch.py +++ b/test/Touch.py @@ -38,10 +38,9 @@ Execute(Touch(File('f1-File'))) def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) Cat = Action(cat) diff --git a/test/Value/Value.py b/test/Value/Value.py index 7bb48ca9f1..2995364201 100644 --- a/test/Value/Value.py +++ b/test/Value/Value.py @@ -52,7 +52,7 @@ def __str__(self): C = Custom(P) def create(target, source, env): - with open(str(target[0]), 'wb') as f: + with open(target[0], 'wb') as f: f.write(source[0].get_contents()) DefaultEnvironment(tools=[]) # test speedup @@ -68,7 +68,7 @@ def create_value(target, source, env): target[0].write(source[0].get_contents()) def create_value_file(target, source, env): - with open(str(target[0]), 'wb') as f: + with open(target[0], 'wb') as f: f.write(source[0].read()) env['BUILDERS']['B2'] = Builder(action=create_value) diff --git a/test/VariantDir/Clean.py b/test/VariantDir/Clean.py index 2e0d4c6827..1fc1269c3e 100644 --- a/test/VariantDir/Clean.py +++ b/test/VariantDir/Clean.py @@ -42,8 +42,7 @@ def build_sample(target, source, env): targetdir = str(target[0].dir) - target = str(target[0]) - with open(target, 'w') as ofd, open(str(source[0]), 'r') as ifd: + with open(target[0], 'w') as ofd, open(source[0], 'r') as ifd: ofd.write(ifd.read()) with open(targetdir+'/sample.junk', 'w') as f: f.write('Side effect!\\n') diff --git a/test/VariantDir/SConscript-variant_dir.py b/test/VariantDir/SConscript-variant_dir.py index 43c3638a1f..36198ceda2 100644 --- a/test/VariantDir/SConscript-variant_dir.py +++ b/test/VariantDir/SConscript-variant_dir.py @@ -57,10 +57,9 @@ var9 = Dir('../build/var9') def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as ofp: + with open(target[0], "wb") as ofp: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: ofp.write(ifp.read()) DefaultEnvironment(tools=[]) # test speedup diff --git a/test/VariantDir/VariantDir.py b/test/VariantDir/VariantDir.py index a173b86531..ef10022ae2 100644 --- a/test/VariantDir/VariantDir.py +++ b/test/VariantDir/VariantDir.py @@ -94,8 +94,8 @@ def buildIt(target, source, env): if not os.path.exists('build'): os.mkdir('build') - f1=open(str(source[0]), 'r') - f2=open(str(target[0]), 'w') + f1=open(source[0], 'r') + f2=open(target[0], 'w') f2.write(f1.read()) f2.close() f1.close() diff --git a/test/VariantDir/errors.py b/test/VariantDir/errors.py index 7400056e7e..b50eb318c7 100644 --- a/test/VariantDir/errors.py +++ b/test/VariantDir/errors.py @@ -59,10 +59,9 @@ def fake_scan(node, env, target): return [] def cat(env, source, target): - target = str(target[0]) - with open(target, "w") as f: + with open(target[0], "w") as f: for src in source: - with open(str(src), "r") as f2: + with open(src, "r") as f2: f.write(f2.read()) DefaultEnvironment(tools=[]) # test speedup diff --git a/test/Win32/default-drive.py b/test/Win32/default-drive.py index 7d19c57a5d..e1d8d4bf62 100644 --- a/test/Win32/default-drive.py +++ b/test/Win32/default-drive.py @@ -44,10 +44,9 @@ test.write(['src', 'SConstruct'], """ def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as ofp: + with open(target[0], "wb") as ofp: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: ofp.write(ifp.read()) env = Environment(BUILDERS={'Build':Builder(action=cat)}) diff --git a/test/chained-build.py b/test/chained-build.py index 10d0b46854..2cc3106771 100644 --- a/test/chained-build.py +++ b/test/chained-build.py @@ -37,7 +37,7 @@ SConstruct1_contents = """\ def build(env, target, source): - with open(str(target[0]), 'wt') as fo, open(str(source[0]), 'rt') as fi: + with open(target[0], 'wt') as fo, open(source[0], 'rt') as fi: fo.write(fi.read()) env=Environment(BUILDERS={'B' : Builder(action=build)}) @@ -46,7 +46,7 @@ def build(env, target, source): SConstruct2_contents = """\ def build(env, target, source): - with open(str(target[0]), 'wt') as fo, open(str(source[0]), 'rt') as fi: + with open(target[0], 'wt') as fo, open(source[0], 'rt') as fi: fo.write(fi.read()) env=Environment(BUILDERS={'B' : Builder(action=build)}) diff --git a/test/duplicate-sources.py b/test/duplicate-sources.py index 3ad29287c4..48a106dc53 100644 --- a/test/duplicate-sources.py +++ b/test/duplicate-sources.py @@ -35,9 +35,9 @@ test.write('SConstruct', """\ def cat(target, source, env): - with open(str(target[0]), 'wb') as t: - for s in source: - with open(str(s), 'rb') as s: + with open(target[0], 'wb') as t: + for src in source: + with open(src, 'rb') as s: t.write(s.read()) env = Environment(BUILDERS = {'Cat' : Builder(action = cat)}) env.Cat('out.txt', ['f1.in', 'f2.in', 'f1.in']) diff --git a/test/emitter.py b/test/emitter.py index d2a8b67b72..e454dc7110 100644 --- a/test/emitter.py +++ b/test/emitter.py @@ -41,7 +41,7 @@ test.write('src/SConscript',""" def build(target, source, env): for t in target: - with open(str(t), "wt") as f: + with open(t, "wt") as f: f.write(str(t)) def emitter(target, source, env): diff --git a/test/implicit-cache/basic.py b/test/implicit-cache/basic.py index 7eece88fe8..aa25327f97 100644 --- a/test/implicit-cache/basic.py +++ b/test/implicit-cache/basic.py @@ -62,7 +62,7 @@ SConscript('variant/SConscript', "env") def copy(target, source, env): - with open(str(target[0]), 'wt') as fo, open(str(source[0]), 'rt') as fi: + with open(target[0], 'wt') as fo, open(source[0], 'rt') as fi: fo.write(fi.read()) nodep = env.Command('nodeps.c', 'nodeps.in', action=copy) env.Program('nodeps', 'nodeps.c') diff --git a/test/implicit/changed-node.py b/test/implicit/changed-node.py index d89c14b8af..7b52302e0e 100644 --- a/test/implicit/changed-node.py +++ b/test/implicit/changed-node.py @@ -46,13 +46,12 @@ def lister(target, source, env): import os - with open(str(target[0]), 'w') as ofp: - s = str(source[0]) - if os.path.isdir(s): - for l in os.listdir(str(source[0])): + with open(target[0], 'w') as ofp: + if os.path.isdir(source[0]): + for l in os.listdir(source[0]): ofp.write(l + '\\n') else: - ofp.write(s + '\\n') + ofp.write(str(source[0]) + '\\n') builder = Builder(action=lister, source_factory=Dir, @@ -82,13 +81,12 @@ def lister(target, source, env): def lister(target, source, env): import os.path - with open(str(target[0]), 'w') as ofp: - s = str(source[0]) - if os.path.isdir(s): - for l in os.listdir(str(source[0])): + with open(target[0], 'w') as ofp: + if os.path.isdir(source[0]): + for l in os.listdir(source[0]): ofp.write(l + '\\n') else: - ofp.write(s + '\\n') + ofp.write(str(source[0]) + '\\n') builder = Builder(action=lister, source_factory=File) diff --git a/test/no-target.py b/test/no-target.py index bf5b94ae35..e16c8c07f7 100644 --- a/test/no-target.py +++ b/test/no-target.py @@ -41,10 +41,9 @@ test.write(subdir_SConscript, r""" def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) b = Builder(action=cat, suffix='.out', src_suffix='.in') diff --git a/test/option/debug-memoizer.py b/test/option/debug-memoizer.py index 2425eaa0ca..7882646165 100644 --- a/test/option/debug-memoizer.py +++ b/test/option/debug-memoizer.py @@ -33,7 +33,7 @@ test.write('SConstruct', """ def cat(target, source, env): - with open(str(target[0]), 'wb') as f, open(str(source[0]), 'rb') as infp: + with open(target[0], 'wb') as f, open(source[0], 'rb') as infp: f.write(infp.read()) DefaultEnvironment(tools=[]) diff --git a/test/option/debug-memory.py b/test/option/debug-memory.py index 2958e0b7b7..1364da5d12 100644 --- a/test/option/debug-memory.py +++ b/test/option/debug-memory.py @@ -45,7 +45,7 @@ test.write('SConstruct', """ DefaultEnvironment(tools=[]) def cat(target, source, env): - with open(str(target[0]), 'wb') as f, open(str(source[0]), 'rb') as ifp: + with open(target[0], 'wb') as f, open(source[0], 'rb') as ifp: f.write(ifp.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=Action(cat))}) env.Cat('file.out', 'file.in') diff --git a/test/option/debug-multiple.py b/test/option/debug-multiple.py index 124e033e67..dc86c519ac 100644 --- a/test/option/debug-multiple.py +++ b/test/option/debug-multiple.py @@ -36,7 +36,7 @@ test.write('SConstruct', """ DefaultEnvironment(tools=[]) def cat(target, source, env): - with open(str(target[0]), 'wb') as f, open(str(source[0]), 'rb') as infp: + with open(target[0], 'wb') as f, open(source[0], 'rb') as infp: f.write(infp.read()) env = Environment(BUILDERS={'Cat':Builder(action=Action(cat))}) env.Cat('file.out', 'file.in') diff --git a/test/option/debug-objects.py b/test/option/debug-objects.py index 9c8536cb1f..57888460f0 100644 --- a/test/option/debug-objects.py +++ b/test/option/debug-objects.py @@ -36,7 +36,7 @@ test.write('SConstruct', """ DefaultEnvironment(tools=[]) def cat(target, source, env): - with open(str(target[0]), 'wb') as f, open(str(source[0]), 'rb') as infp: + with open(target[0], 'wb') as f, open(source[0], 'rb') as infp: f.write(infp.read()) env = Environment(tools=[], BUILDERS={'Cat':Builder(action=Action(cat))}) env.Cat('file.out', 'file.in') diff --git a/test/option/debug-presub.py b/test/option/debug-presub.py index cbd5242eaf..0c5d2e9fe1 100644 --- a/test/option/debug-presub.py +++ b/test/option/debug-presub.py @@ -40,10 +40,9 @@ test.write('SConstruct', """\ DefaultEnvironment(tools=[]) def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as infp: + with open(src, "rb") as infp: f.write(infp.read()) FILE = Builder(action="$FILECOM") TEMP = Builder(action="$TEMPCOM") diff --git a/test/option/fixture/SConstruct_debug_count b/test/option/fixture/SConstruct_debug_count index 332815bde1..1a39d17e5f 100644 --- a/test/option/fixture/SConstruct_debug_count +++ b/test/option/fixture/SConstruct_debug_count @@ -4,7 +4,7 @@ if ARGUMENTS.get('JSON',False): DefaultEnvironment(tools=[]) def cat(target, source, env): - with open(str(target[0]), 'wb') as f, open(str(source[0]), 'rb') as infp: + with open(target[0], 'wb') as f, open(source[0], 'rb') as infp: f.write(infp.read()) env = Environment(BUILDERS={'Cat':Builder(action=Action(cat))}) env.Cat('file.out', 'file.in') diff --git a/test/option/option--random.py b/test/option/option--random.py index 1dafca4507..1521e3a44b 100644 --- a/test/option/option--random.py +++ b/test/option/option--random.py @@ -34,10 +34,9 @@ test.write('SConscript', """\ def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) env = Environment(BUILDERS={'Cat':Builder(action=cat)}) env.Cat('aaa.out', 'aaa.in') diff --git a/test/option/warn-duplicate-environment.py b/test/option/warn-duplicate-environment.py index 1000647118..f19c533e82 100644 --- a/test/option/warn-duplicate-environment.py +++ b/test/option/warn-duplicate-environment.py @@ -36,9 +36,9 @@ test.write('SConstruct', """ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) WARN = ARGUMENTS.get('WARN') diff --git a/test/option/warn-misleading-keywords.py b/test/option/warn-misleading-keywords.py index 45236bb4b7..5f78e63f94 100644 --- a/test/option/warn-misleading-keywords.py +++ b/test/option/warn-misleading-keywords.py @@ -36,9 +36,9 @@ test.write('SConstruct', """ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wb') as f: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as f: + for src in source: + with open(src, 'rb') as infp: f.write(infp.read()) WARN = ARGUMENTS.get('WARN') @@ -58,7 +58,7 @@ def build(env, target, source): scons: warning: Did you mean to use `(target|source)' instead of `(targets|sources)'\? """ + TestSCons.file_expr -test.run(arguments='.', +test.run(arguments='.', stderr=expect + expect) test.must_match(['file3a'], 'file3a.in\n') diff --git a/test/sconsign/corrupt.py b/test/sconsign/corrupt.py index fa6a0e9cbc..301fd1b852 100644 --- a/test/sconsign/corrupt.py +++ b/test/sconsign/corrupt.py @@ -47,7 +47,7 @@ SConstruct_contents = """\ def build1(target, source, env): - with open(str(target[0]), 'wb') as ofp, open(str(source[0]), 'rb') as ifp: + with open(target[0], 'wb') as ofp, open(source[0], 'rb') as ifp: ofp.write(ifp.read()) return None diff --git a/test/sconsign/ghost-entries.py b/test/sconsign/ghost-entries.py index 59a1ec2054..2c9c98c035 100644 --- a/test/sconsign/ghost-entries.py +++ b/test/sconsign/ghost-entries.py @@ -51,9 +51,9 @@ test.write('SConstruct', """\ def cat(target, source, env): - with open(str(target[0]), 'wb') as fp: - for s in source: - with open(str(s), 'rb') as infp: + with open(target[0], 'wb') as fp: + for src in source: + with open(src, 'rb') as infp: fp.write(infp.read()) env=Environment() Export('env') diff --git a/test/sconsign/nonwritable.py b/test/sconsign/nonwritable.py index e9520780bd..ee7b13eb95 100644 --- a/test/sconsign/nonwritable.py +++ b/test/sconsign/nonwritable.py @@ -52,16 +52,16 @@ SConstruct_contents = """\ def build1(target, source, env): - with open(str(target[0]), 'wb') as fo, open(str(source[0]), 'rb') as fi: + with open(target[0], 'wb') as fo, open(source[0], 'rb') as fi: fo.write(fi.read()) return None def build2(target, source, env): import os import os.path - with open(str(target[0]), 'wb') as fo, open(str(source[0]), 'rb') as fi: + with open(target[0], 'wb') as fo, open(source[0], 'rb') as fi: fo.write(fi.read()) - dir, file = os.path.split(str(target[0])) + dir, file = os.path.split(target[0]) os.chmod(dir, 0o555) return None diff --git a/test/srcchange.py b/test/srcchange.py index e72d923a22..2eac09d743 100644 --- a/test/srcchange.py +++ b/test/srcchange.py @@ -52,7 +52,7 @@ def subrevision(target, source ,env): new = re.sub(r'\$REV.*?\$', '$REV: %%s$'%%source[0].get_text_contents().strip(), target[0].get_text_contents()) - with open(str(target[0]),'w') as outf: + with open(target[0], 'w') as outf: outf.write(new) SubRevision = Action(subrevision) diff --git a/test/suffixes.py b/test/suffixes.py index 7684ee83a9..a7a2c9d65f 100644 --- a/test/suffixes.py +++ b/test/suffixes.py @@ -34,10 +34,9 @@ test.write('SConstruct', """ def cat(env, source, target): - target = str(target[0]) - with open(target, "wb") as f: + with open(target[0], "wb") as f: for src in source: - with open(str(src), "rb") as ifp: + with open(src, "rb") as ifp: f.write(ifp.read()) Cat = Builder(action=cat, suffix='.out') env = Environment(BUILDERS = {'Cat':Cat}) diff --git a/test/timestamp-fallback.py b/test/timestamp-fallback.py index b3e812b911..d3f6774a09 100644 --- a/test/timestamp-fallback.py +++ b/test/timestamp-fallback.py @@ -57,7 +57,7 @@ DefaultEnvironment(tools=[]) def build(env, target, source): - with open(str(target[0]), 'wt') as ofp, open(str(source[0]), 'rt') as ifp: + with open(target[0], 'wt') as ofp, open(source[0], 'rt') as ifp: ofp.write(ifp.read()) B = Builder(action = build) diff --git a/test/toolpath/VariantDir.py b/test/toolpath/VariantDir.py index 652dde59b5..94376fc7c0 100644 --- a/test/toolpath/VariantDir.py +++ b/test/toolpath/VariantDir.py @@ -50,7 +50,7 @@ from SCons.Script import Builder def generate(env): def my_copy(target, source, env): - with open(str(target[0]), 'wb') as f, open(str(source[0]), 'rb') as ifp: + with open(target[0], 'wb') as f, open(source[0], 'rb') as ifp: f.write(ifp.read()) env['BUILDERS']['MyCopy'] = Builder(action = my_copy) diff --git a/timings/hundred/SConstruct b/timings/hundred/SConstruct index b321d10854..dce9ca79d3 100644 --- a/timings/hundred/SConstruct +++ b/timings/hundred/SConstruct @@ -25,7 +25,7 @@ target_count = int(ARGUMENTS['TARGET_COUNT']) def copy_files( env, target, source ): for t, s in zip(target, source): - open(str(t), 'wb').write(open(str(s), 'rb').read()) + open(t, 'wb').write(open(s, 'rb').read()) source_list = ['source_%04d' % t for t in range(target_count)] target_list = ['target_%04d' % t for t in range(target_count)] From 39a12f34d532ab2493e78a7b73aeab2250852790 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 27 Mar 2025 11:43:33 -0700 Subject: [PATCH 279/386] Regenerated docs for 4.9.1 release --- CHANGES.txt | 5 +- RELEASE.txt | 56 ++----------------- .../examples/caching_ex-random_1.xml | 4 +- .../troubleshoot_taskmastertrace_1.xml | 50 ++++++++--------- doc/user/main.xml | 2 +- 5 files changed, 34 insertions(+), 83 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ac84431b2b..f6b31df266 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,10 +10,7 @@ NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. -RELEASE VERSION/DATE TO BE FILLED IN LATER - - From John Doe: - - Whatever John Doe did. +RELEASE 4.9.0 - Thu, 27 Mar 2025 11:40:20 -0700 From Mats Wichmann: - Fix typos in CCFLAGS test. Didn't affect the test itself, but diff --git a/RELEASE.txt b/RELEASE.txt index 9a9fa1e2fc..ad26a5de7e 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,71 +1,25 @@ -If you are reading this in the git repository, the contents -refer to *unreleased* changes since the last SCons release. -Past official release announcements appear at: - - https://scons.org/tag/releases.html - -================================================================== - -A new SCons release, 4.9.0, is now available on the SCons download page: +A new SCons release, 4.9.1, is now available on the SCons download page: https://scons.org/pages/download.html Here is a summary of the changes since 4.4.0: -NEW FUNCTIONALITY ------------------ - -- List new features (presumably why a checkpoint is being released) - -DEPRECATED FUNCTIONALITY ------------------------- - -- List anything that's been deprecated since the last release - -CHANGED/ENHANCED EXISTING FUNCTIONALITY ---------------------------------------- - -- List modifications to existing features, where the previous behavior - wouldn't actually be considered a bug - FIXES ----- - -- List fixes of outright bugs - - New CacheDir initialization code failed on Python 3.7 for unknown reason (worked on 3.8+). Adjusted the approach a bit. Fixes #4694. - Fixed a hang in `wait_for_process_to_die()` on Windows, affecting clean-up of the SCons daemon used for Ninja builds. -IMPROVEMENTS ------------- - -- List improvements that wouldn't be visible to the user in the - documentation: performance improvements (describe the circumstances - under which they would be observed), or major code cleanups - -PACKAGING ---------- - -- List changes in the way SCons is packaged and/or released - -DOCUMENTATION -------------- - -- List any significant changes to the documentation (not individual - typo fixes, even if they're mentioned in src/CHANGES.txt to give - the contributor credit) - -DEVELOPMENT ------------ -- List visible changes in the way SCons is developed Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.0.1..HEAD + git shortlog --no-merges -ns 4.9.0..HEAD + 7 Mats Wichmann + 4 William Deegan + 2 Adam Simpkins diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml index da1fc2d5d9..0e4663cf92 100644 --- a/doc/generated/examples/caching_ex-random_1.xml +++ b/doc/generated/examples/caching_ex-random_1.xml @@ -1,8 +1,8 @@ % scons -Q -cc -o f2.o -c f2.c -cc -o f3.o -c f3.c cc -o f4.o -c f4.c cc -o f1.o -c f1.c cc -o f5.o -c f5.c +cc -o f3.o -c f3.c +cc -o f2.o -c f2.c cc -o prog f1.o f2.o f3.o f4.o f5.o diff --git a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml index d08e179db1..8fa6b5e159 100644 --- a/doc/generated/examples/troubleshoot_taskmastertrace_1.xml +++ b/doc/generated/examples/troubleshoot_taskmastertrace_1.xml @@ -1,8 +1,8 @@ % scons -Q --taskmastertrace=- prog -Job.NewParallel._work(): [Thread:8646594432] Gained exclusive access -Job.NewParallel._work(): [Thread:8646594432] Starting search -Job.NewParallel._work(): [Thread:8646594432] Found 0 completed tasks to process -Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks +Job.NewParallel._work(): [Thread:8286867328] Gained exclusive access +Job.NewParallel._work(): [Thread:8286867328] Starting search +Job.NewParallel._work(): [Thread:8286867328] Found 0 completed tasks to process +Job.NewParallel._work(): [Thread:8286867328] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'prog'> and its children: @@ -18,12 +18,12 @@ Taskmaster: Evaluating <pending 0 'prog.c'> Task.make_ready_current(): node <pending 0 'prog.c'> Task.prepare(): node <up_to_date 0 'prog.c'> -Job.NewParallel._work(): [Thread:8646594432] Found internal task +Job.NewParallel._work(): [Thread:8286867328] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'prog.c'> Task.postprocess(): node <up_to_date 0 'prog.c'> Task.postprocess(): removing <up_to_date 0 'prog.c'> Task.postprocess(): adjusted parent ref count <pending 1 'prog.o'> -Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks +Job.NewParallel._work(): [Thread:8286867328] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <no_state 0 'inc.h'> and its children: @@ -31,12 +31,12 @@ Taskmaster: Evaluating <pending 0 'inc.h'> Task.make_ready_current(): node <pending 0 'inc.h'> Task.prepare(): node <up_to_date 0 'inc.h'> -Job.NewParallel._work(): [Thread:8646594432] Found internal task +Job.NewParallel._work(): [Thread:8286867328] Found internal task Task.executed_with_callbacks(): node <up_to_date 0 'inc.h'> Task.postprocess(): node <up_to_date 0 'inc.h'> Task.postprocess(): removing <up_to_date 0 'inc.h'> Task.postprocess(): adjusted parent ref count <pending 0 'prog.o'> -Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks +Job.NewParallel._work(): [Thread:8286867328] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog.o'> and its children: @@ -46,19 +46,19 @@ Taskmaster: Evaluating <pending 0 'prog.o'> Task.make_ready_current(): node <pending 0 'prog.o'> Task.prepare(): node <executing 0 'prog.o'> -Job.NewParallel._work(): [Thread:8646594432] Found task requiring execution -Job.NewParallel._work(): [Thread:8646594432] Executing task +Job.NewParallel._work(): [Thread:8286867328] Found task requiring execution +Job.NewParallel._work(): [Thread:8286867328] Executing task Task.execute(): node <executing 0 'prog.o'> cc -o prog.o -c -I. prog.c -Job.NewParallel._work(): [Thread:8646594432] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8646594432] Gained exclusive access -Job.NewParallel._work(): [Thread:8646594432] Starting search -Job.NewParallel._work(): [Thread:8646594432] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8286867328] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8286867328] Gained exclusive access +Job.NewParallel._work(): [Thread:8286867328] Starting search +Job.NewParallel._work(): [Thread:8286867328] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog.o'> Task.postprocess(): node <executed 0 'prog.o'> Task.postprocess(): removing <executed 0 'prog.o'> Task.postprocess(): adjusted parent ref count <pending 0 'prog'> -Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks +Job.NewParallel._work(): [Thread:8286867328] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: Considering node <pending 0 'prog'> and its children: @@ -67,21 +67,21 @@ Taskmaster: Evaluating <pending 0 'prog'> Task.make_ready_current(): node <pending 0 'prog'> Task.prepare(): node <executing 0 'prog'> -Job.NewParallel._work(): [Thread:8646594432] Found task requiring execution -Job.NewParallel._work(): [Thread:8646594432] Executing task +Job.NewParallel._work(): [Thread:8286867328] Found task requiring execution +Job.NewParallel._work(): [Thread:8286867328] Executing task Task.execute(): node <executing 0 'prog'> cc -o prog prog.o -Job.NewParallel._work(): [Thread:8646594432] Enqueueing executed task results -Job.NewParallel._work(): [Thread:8646594432] Gained exclusive access -Job.NewParallel._work(): [Thread:8646594432] Starting search -Job.NewParallel._work(): [Thread:8646594432] Found 1 completed tasks to process +Job.NewParallel._work(): [Thread:8286867328] Enqueueing executed task results +Job.NewParallel._work(): [Thread:8286867328] Gained exclusive access +Job.NewParallel._work(): [Thread:8286867328] Starting search +Job.NewParallel._work(): [Thread:8286867328] Found 1 completed tasks to process Task.executed_with_callbacks(): node <executing 0 'prog'> Task.postprocess(): node <executed 0 'prog'> -Job.NewParallel._work(): [Thread:8646594432] Searching for new tasks +Job.NewParallel._work(): [Thread:8286867328] Searching for new tasks Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. -Job.NewParallel._work(): [Thread:8646594432] Found no task requiring execution, and have no jobs: marking complete -Job.NewParallel._work(): [Thread:8646594432] Gained exclusive access -Job.NewParallel._work(): [Thread:8646594432] Completion detected, breaking from main loop +Job.NewParallel._work(): [Thread:8286867328] Found no task requiring execution, and have no jobs: marking complete +Job.NewParallel._work(): [Thread:8286867328] Gained exclusive access +Job.NewParallel._work(): [Thread:8286867328] Completion detected, breaking from main loop diff --git a/doc/user/main.xml b/doc/user/main.xml index 623a132289..de5e6c749d 100644 --- a/doc/user/main.xml +++ b/doc/user/main.xml @@ -36,7 +36,7 @@ This file is processed by the bin/SConsDoc.py module. The SCons Development Team - Released: Mon, 02 Mar 2025 14:20:11 -0700 + Released: Mon, 27 Mar 2025 11:40:11 -0700 2004 - 2025 From afb488972d25940615f6587a3722de1528af4c9b Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 27 Mar 2025 11:49:08 -0700 Subject: [PATCH 280/386] updates for 4.9.1 release --- ReleaseConfig | 2 +- SCons/__init__.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ReleaseConfig b/ReleaseConfig index 9ab93e1a6c..7cc1123e3c 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -31,7 +31,7 @@ # If the release type is not 'final', the patchlevel is set to the # release date. This value is mandatory and must be present in this file. #version_tuple = (2, 2, 0, 'final', 0) -version_tuple = (4, 9, 2, 'a', 0) +version_tuple = (4, 9, 1) # Python versions prior to unsupported_python_version cause a fatal error # when that version is used. Python versions prior to deprecate_python_version diff --git a/SCons/__init__.py b/SCons/__init__.py index c492d6c535..b831ae56c5 100644 --- a/SCons/__init__.py +++ b/SCons/__init__.py @@ -1,9 +1,9 @@ -__version__="4.9.0" +__version__="4.9.1" __copyright__="Copyright (c) 2001 - 2025 The SCons Foundation" __developer__="bdbaddog" -__date__="Sun, 02 Mar 2025 14:04:50 -0700" +__date__="Thu, 27 Mar 2025 11:44:24 -0700" __buildsys__="M1Dog2021" -__revision__="99a8c86de1ce91d23b102520e185c54ebd968924" -__build__="99a8c86de1ce91d23b102520e185c54ebd968924" +__revision__="39a12f34d532ab2493e78a7b73aeab2250852790" +__build__="39a12f34d532ab2493e78a7b73aeab2250852790" # make sure compatibility is always in place import SCons.compat # noqa \ No newline at end of file From 17e2860d9d7479a2d15f74c19fd1077cedcc2680 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 27 Mar 2025 13:42:36 -0700 Subject: [PATCH 281/386] updated packaging pip requirements --- requirements-pkg.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements-pkg.txt b/requirements-pkg.txt index 3b30462a94..aa3f1eef3e 100644 --- a/requirements-pkg.txt +++ b/requirements-pkg.txt @@ -13,3 +13,5 @@ sphinx-book-theme rst2pdf build +twine +packaging From 1d3f547fcc2ce0dce8c7725f4120e32303c0b903 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 27 Mar 2025 13:48:51 -0700 Subject: [PATCH 282/386] post release back to develop mode --- CHANGES.txt | 7 ++++ RELEASE.txt | 58 ++++++++++++++++++++++++++++------ ReleaseConfig | 2 +- doc/user/main.xml | 2 +- testing/framework/TestSCons.py | 2 +- 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f6b31df266..907893ddd7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,13 @@ NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. +RELEASE VERSION/DATE TO BE FILLED IN LATER + + From John Doe: + + - Whatever John Doe did. + + RELEASE 4.9.0 - Thu, 27 Mar 2025 11:40:20 -0700 From Mats Wichmann: diff --git a/RELEASE.txt b/RELEASE.txt index ad26a5de7e..128daa16fe 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,25 +1,65 @@ -A new SCons release, 4.9.1, is now available on the SCons download page: +If you are reading this in the git repository, the contents +refer to *unreleased* changes since the last SCons release. +Past official release announcements appear at: + + https://scons.org/tag/releases.html + +================================================================== + +A new SCons release, 4.9.0, is now available on the SCons download page: https://scons.org/pages/download.html Here is a summary of the changes since 4.4.0: +NEW FUNCTIONALITY +----------------- + +- List new features (presumably why a checkpoint is being released) + +DEPRECATED FUNCTIONALITY +------------------------ + +- List anything that's been deprecated since the last release + +CHANGED/ENHANCED EXISTING FUNCTIONALITY +--------------------------------------- + +- List modifications to existing features, where the previous behavior + wouldn't actually be considered a bug + FIXES ----- -- New CacheDir initialization code failed on Python 3.7 for unknown - reason (worked on 3.8+). Adjusted the approach a bit. Fixes #4694. -- Fixed a hang in `wait_for_process_to_die()` on Windows, affecting - clean-up of the SCons daemon used for Ninja builds. +- List fixes of outright bugs + +IMPROVEMENTS +------------ + +- List improvements that wouldn't be visible to the user in the + documentation: performance improvements (describe the circumstances + under which they would be observed), or major code cleanups + +PACKAGING +--------- + +- List changes in the way SCons is packaged and/or released + +DOCUMENTATION +------------- + +- List any significant changes to the documentation (not individual + typo fixes, even if they're mentioned in src/CHANGES.txt to give + the contributor credit) +DEVELOPMENT +----------- +- List visible changes in the way SCons is developed Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.9.0..HEAD - 7 Mats Wichmann - 4 William Deegan - 2 Adam Simpkins + git shortlog --no-merges -ns 4.0.1..HEAD diff --git a/ReleaseConfig b/ReleaseConfig index 7cc1123e3c..9ab93e1a6c 100755 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -31,7 +31,7 @@ # If the release type is not 'final', the patchlevel is set to the # release date. This value is mandatory and must be present in this file. #version_tuple = (2, 2, 0, 'final', 0) -version_tuple = (4, 9, 1) +version_tuple = (4, 9, 2, 'a', 0) # Python versions prior to unsupported_python_version cause a fatal error # when that version is used. Python versions prior to deprecate_python_version diff --git a/doc/user/main.xml b/doc/user/main.xml index de5e6c749d..14676b28a5 100644 --- a/doc/user/main.xml +++ b/doc/user/main.xml @@ -36,7 +36,7 @@ This file is processed by the bin/SConsDoc.py module. The SCons Development Team - Released: Mon, 27 Mar 2025 11:40:11 -0700 + Released: Mon, 27 Mar 2025 13:46:28 -0700 2004 - 2025 diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index 620657181f..5ac83cdb44 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -54,7 +54,7 @@ # here provides some independent verification that what we packaged # conforms to what we expect. -default_version = '4.9.1' +default_version = '4.9.2ayyyymmdd' # TODO: these need to be hand-edited when there are changes python_version_unsupported = (3, 7, 0) From 3fed5901dfe1afbb050d7cd35de1b74bf0fe790d Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 27 Mar 2025 13:52:10 -0700 Subject: [PATCH 283/386] fix typo --- RELEASE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE.txt b/RELEASE.txt index ad26a5de7e..4c068f0488 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -3,7 +3,7 @@ A new SCons release, 4.9.1, is now available on the SCons download page: https://scons.org/pages/download.html -Here is a summary of the changes since 4.4.0: +Here is a summary of the changes since 4.9.0: FIXES ----- From 2758b858336c0917636279870c64bf9334078c3f Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 27 Mar 2025 17:20:41 -0700 Subject: [PATCH 284/386] fix version in CHANGES.txt --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f6b31df266..ac01df933f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,7 +10,7 @@ NOTE: Since SCons 4.3.0, Python 3.6.0 or above is required. NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. -RELEASE 4.9.0 - Thu, 27 Mar 2025 11:40:20 -0700 +RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 From Mats Wichmann: - Fix typos in CCFLAGS test. Didn't affect the test itself, but From 5e275cf390a24fb771eba8719300ee3f8a8fe2f7 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 27 Mar 2025 17:21:13 -0700 Subject: [PATCH 285/386] fix version in CHANGES.txt --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 907893ddd7..b66fd4356c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -17,7 +17,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Whatever John Doe did. -RELEASE 4.9.0 - Thu, 27 Mar 2025 11:40:20 -0700 +RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 From Mats Wichmann: - Fix typos in CCFLAGS test. Didn't affect the test itself, but From dfd6b7e45985eb73462d2efa878651c92745099c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Fri, 28 Mar 2025 15:23:48 -0700 Subject: [PATCH 286/386] fix blurb ordering issues in CHANGES.txt --- CHANGES.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ac01df933f..553128af5b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,11 @@ NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 + + From Adam Simpkins: + - Fixed a hang in `wait_for_process_to_die()` on Windows, affecting + clean-up of the SCons daemon used for Ninja builds. + From Mats Wichmann: - Fix typos in CCFLAGS test. Didn't affect the test itself, but didn't correctly apply the DefaultEnvironment speedup. @@ -138,6 +143,11 @@ RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 From Keith F Prussing: - Added support for tracking beamer themes in the LaTeX scanner. + From Adam Scott: + - Changed Ninja's TEMPLATE rule pool to use `install_pool` instead of + `local_pool`, hoping it will fix a race condition that can occurs when + Ninja defers to SCons to build. + From Alex Thiessen: - Many grammatical and spelling fixes in the documentation. @@ -232,15 +242,6 @@ RELEASE 4.9.0 - Sun, 02 Mar 2025 17:22:20 -0700 - Test framework - add recognizing list-of-path-components for the destination of fixtures too (matches docstrings now). - From Adam Scott: - - Changed Ninja's TEMPLATE rule pool to use `install_pool` instead of - `local_pool`, hoping it will fix a race condition that can occurs when - Ninja defers to SCons to build. - - From Adam Simpkins: - - Fixed a hang in `wait_for_process_to_die()` on Windows, affecting - clean-up of the SCons daemon used for Ninja builds. - RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 From a394c8b963169079b5ed85e5d9a2f4765c9a0bf0 Mon Sep 17 00:00:00 2001 From: Bill Prendergast Date: Sat, 29 Mar 2025 17:30:11 +1100 Subject: [PATCH 287/386] Fix Variables.PackageVariable with bool default string when passed a 'true' string on the command line actually test default value against both ENABLE_STRINGS and DISABLE_STRINGS Closes: #4702 See-Also: https://bugs.gentoo.org/950584 Signed-off-by: Bill Prendergast --- CHANGES.txt | 6 ++++++ SCons/Variables/PackageVariable.py | 2 +- SCons/Variables/PackageVariableTests.py | 28 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 42679eb97a..a488d0f8af 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,6 +16,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Whatever John Doe did. + From Bill Prendergast: + - Fixed SCons.Variables.PackageVariable to correctly test the default + setting against both enable & disable strings. + - Extended unittests (crudely) to test for correct/expected response + when default setting is a boolean string. + RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/SCons/Variables/PackageVariable.py b/SCons/Variables/PackageVariable.py index c615ac4240..df20bd26dc 100644 --- a/SCons/Variables/PackageVariable.py +++ b/SCons/Variables/PackageVariable.py @@ -86,7 +86,7 @@ def _converter(val: str | bool, default: str) -> str | bool: if lval in ENABLE_STRINGS: # Can't return the default if it is one of the enable/disable strings; # test code expects a bool. - if default in ENABLE_STRINGS: + if default in ENABLE_STRINGS + DISABLE_STRINGS: return True else: return default diff --git a/SCons/Variables/PackageVariableTests.py b/SCons/Variables/PackageVariableTests.py index 00cf1e3aef..8ef52433b6 100644 --- a/SCons/Variables/PackageVariableTests.py +++ b/SCons/Variables/PackageVariableTests.py @@ -92,6 +92,34 @@ def test_converter(self) -> None: x = o.converter(False) assert not x, f"converter returned False for {t!r}" + # When the variable is created with boolean string make sure the converter + # returns the correct result i.e. a bool or a passed path + opts = SCons.Variables.Variables() + opts.Add(SCons.Variables.PackageVariable('test', 'test build variable help', 'yes')) + o = opts.options[0] + + x = o.converter(str(True)) + assert not isinstance(x, str), "converter with default str(yes) returned a string when given str(True)" + assert x, "converter with default str(yes) returned False for str(True)" + x = o.converter(str(False)) + assert not isinstance(x, str), "converter with default str(yes) returned a string when given str(False)" + assert not x, "converter with default str(yes) returned True for str(False)" + x = o.converter('/explicit/path') + assert x == '/explicit/path', "converter with default str(yes) did not return path" + + opts = SCons.Variables.Variables() + opts.Add(SCons.Variables.PackageVariable('test', 'test build variable help', 'no')) + o = opts.options[0] + + x = o.converter(str(True)) + assert not isinstance(x, str), "converter with default str(no) returned a string when given str(True)" + assert x, "converter with default str(no) returned False for str(True)" + x = o.converter(str(False)) + assert not isinstance(x, str), "converter with default str(no) returned a string when given str(False)" + assert not x, "converter with default str(no) returned True for str(False)" + x = o.converter('/explicit/path') + assert x == '/explicit/path', "converter with default str(no) did not return path" + def test_validator(self) -> None: """Test the PackageVariable validator""" opts = SCons.Variables.Variables() From ccf9dfd2dddf8e3a2fb0a3b76677e23fc7a5d7dd Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 29 Mar 2025 15:28:31 -0700 Subject: [PATCH 288/386] [ci skip] added which issue is resolved and added to RELEASE.txt --- CHANGES.txt | 2 +- RELEASE.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a488d0f8af..a4041204c0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,7 +18,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From Bill Prendergast: - Fixed SCons.Variables.PackageVariable to correctly test the default - setting against both enable & disable strings. + setting against both enable & disable strings. (Fixes #4702) - Extended unittests (crudely) to test for correct/expected response when default setting is a boolean string. diff --git a/RELEASE.txt b/RELEASE.txt index 13696d4dbb..eecbe364f3 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -32,7 +32,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY FIXES ----- -- List fixes of outright bugs +- Fixed SCons.Variables.PackageVariable to correctly test the default + setting against both enable & disable strings. (Fixes #4702) IMPROVEMENTS ------------ From 0b1b43e611a057dc69355580489ccccaff93a274 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 7 Mar 2025 09:53:08 -0700 Subject: [PATCH 289/386] Clean up CC and CXX FLAGS tests More consistency, formatting, add test/CXX/SHCCFLAGS.py for symmetry (test/CC/ had one), rename tests to -live since they use a real compiler, and also add that note to docstring. Remove some filename suffix fiddling that wasn't needed because in this usage, SCons figures it out - and we wnat that as part of the test. Signed-off-by: Mats Wichmann --- CHANGES.txt | 7 +- RELEASE.txt | 6 +- test/CC/{CCFLAGS.py => CCFLAGS-live.py} | 63 ++++---- test/CC/{CFLAGS.py => CFLAGS-live.py} | 65 ++++---- test/CC/{SHCCFLAGS.py => SHCCFLAGS-live.py} | 60 ++++---- test/CC/{SHCFLAGS.py => SHCFLAGS-live.py} | 56 +++---- test/CXX/{CCFLAGS.py => CCFLAGS-live.py} | 31 ++-- test/CXX/{CXXFLAGS.py => CXXFLAGS-live.py} | 79 +++++----- test/CXX/SHCCFLAGS-live.py | 142 ++++++++++++++++++ .../CXX/{SHCXXFLAGS.py => SHCXXFLAGS-live.py} | 84 ++++++----- 10 files changed, 378 insertions(+), 215 deletions(-) rename test/CC/{CCFLAGS.py => CCFLAGS-live.py} (62%) rename test/CC/{CFLAGS.py => CFLAGS-live.py} (66%) rename test/CC/{SHCCFLAGS.py => SHCCFLAGS-live.py} (68%) rename test/CC/{SHCFLAGS.py => SHCFLAGS-live.py} (69%) rename test/CXX/{CCFLAGS.py => CCFLAGS-live.py} (74%) rename test/CXX/{CXXFLAGS.py => CXXFLAGS-live.py} (66%) create mode 100644 test/CXX/SHCCFLAGS-live.py rename test/CXX/{SHCXXFLAGS.py => SHCXXFLAGS-live.py} (56%) diff --git a/CHANGES.txt b/CHANGES.txt index a4041204c0..d206668cbc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,9 +12,12 @@ NOTE: Since SCons 4.9.0, Python 3.7.0 or above is required. RELEASE VERSION/DATE TO BE FILLED IN LATER - From John Doe: + From John Doe: + - Whatever John Doe did. - - Whatever John Doe did. + From Mats Wichmann: + - Clean up C and C++ FLAGS tests. Tests which use a real compiler + are now more clearly distinguished (-live.py suffix and docstring). From Bill Prendergast: - Fixed SCons.Variables.PackageVariable to correctly test the default diff --git a/RELEASE.txt b/RELEASE.txt index eecbe364f3..5ff11c0d5e 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -6,12 +6,12 @@ Past official release announcements appear at: ================================================================== -A new SCons release, 4.9.0, is now available on the SCons download page: +A new SCons release, X.Y.Z, is now available on the SCons download page: https://scons.org/pages/download.html +Here is a summary of the changes since 4.9.1: -Here is a summary of the changes since 4.9.0: NEW FUNCTIONALITY ----------------- @@ -63,4 +63,4 @@ Thanks to the following contributors listed below for their contributions to thi ========================================================================================== .. code-block:: text - git shortlog --no-merges -ns 4.0.1..HEAD + git shortlog --no-merges -ns 4.9.1..HEAD diff --git a/test/CC/CCFLAGS.py b/test/CC/CCFLAGS-live.py similarity index 62% rename from test/CC/CCFLAGS.py rename to test/CC/CCFLAGS-live.py index 343cb4ad09..ab66785aec 100644 --- a/test/CC/CCFLAGS.py +++ b/test/CC/CCFLAGS-live.py @@ -23,10 +23,17 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test behavior of CCFLAGS. + +This is a live test, uses the detected C compiler. +""" + import sys + import TestSCons -_obj = TestSCons._obj +test = TestSCons.TestSCons() if sys.platform == 'win32': import SCons.Tool.MSCommon as msc @@ -41,19 +48,17 @@ fooflags = '-DFOO' barflags = '-DBAR' -test = TestSCons.TestSCons() +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +foo = Environment(CCFLAGS='{fooflags}') +bar = Environment(CCFLAGS='{barflags}') -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -foo = Environment(CCFLAGS = '%s') -bar = Environment(CCFLAGS = '%s') -foo.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -foo.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -foo.Program(target = 'prog', source = 'prog.c', - CCFLAGS = '$CCFLAGS -DBAR $BAZ', BAZ = '-DBAZ') -""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) +foo_obj = foo.Object(target='foo', source='prog.c') +bar_obj = bar.Object(target='bar', source='prog.c') +foo.Program(target='foo', source=foo_obj) +bar.Program(target='bar', source=bar_obj) +foo.Program(target='prog', source='prog.c', CCFLAGS='$CCFLAGS -DBAR $BAZ', BAZ='-DBAZ') +""") test.write('prog.c', r""" #include @@ -76,30 +81,28 @@ } """) - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('prog'), stdout = """\ +test.run(arguments='.') +test.run(program=test.workpath('foo'), stdout="prog.c: FOO\n") +test.run(program=test.workpath('bar'), stdout="prog.c: BAR\n") +test.run(program=test.workpath('prog'), stdout="""\ prog.c: FOO prog.c: BAR prog.c: BAZ """) -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -bar = Environment(CCFLAGS = '%s') -bar.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -bar.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -""" % (barflags, _obj, _obj, _obj, _obj)) +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +bar = Environment(CCFLAGS='{barflags}') -test.run(arguments = '.') +foo_obj = bar.Object(target='foo', source='prog.c') +bar_obj = bar.Object(target='bar', source='prog.c') +bar.Program(target='foo', source=foo_obj) +bar.Program(target='bar', source=bar_obj) +""") -test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") +test.run(arguments='.') +test.run(program=test.workpath('foo'), stdout="prog.c: BAR\n") +test.run(program=test.workpath('bar'), stdout="prog.c: BAR\n") test.pass_test() diff --git a/test/CC/CFLAGS.py b/test/CC/CFLAGS-live.py similarity index 66% rename from test/CC/CFLAGS.py rename to test/CC/CFLAGS-live.py index 19ae2641df..2b9341f0b1 100644 --- a/test/CC/CFLAGS.py +++ b/test/CC/CFLAGS-live.py @@ -23,14 +23,21 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test behavior of CFLAGS. + +This is a live test, uses the detected C compiler. +""" + import sys + import TestSCons test = TestSCons.TestSCons() # Make sure CFLAGS is not passed to CXX by just expanding CXXCOM -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) +test.write('SConstruct', """\ +_ = DefaultEnvironment(tools=[]) env = Environment(CFLAGS='-xyz', CCFLAGS='-abc') print(env.subst('$CXXCOM')) print(env.subst('$CXXCOMSTR')) @@ -41,8 +48,6 @@ test.must_not_contain_any_line(test.stdout(), ["-xyz"]) test.must_contain_all_lines(test.stdout(), ["-abc"]) -_obj = TestSCons._obj - # Test passing CFLAGS to C compiler by actually compiling programs if sys.platform == 'win32': import SCons.Tool.MSCommon as msc @@ -57,17 +62,17 @@ fooflags = '-DFOO' barflags = '-DBAR' +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +foo = Environment(CFLAGS="{fooflags}") +bar = Environment(CFLAGS="{barflags}") -test.write('SConstruct', """ -foo = Environment(CFLAGS = '%s') -bar = Environment(CFLAGS = '%s') -foo.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -foo.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -foo.Program(target = 'prog', source = 'prog.c', - CFLAGS = '$CFLAGS -DBAR $BAZ', BAZ = '-DBAZ') -""" % (fooflags, barflags, _obj, _obj, _obj, _obj)) +foo_obj = foo.Object(target='foo', source='prog.c') +bar_obj = bar.Object(target='bar', source='prog.c') +foo.Program(target='foo', source=foo_obj) +bar.Program(target='bar', source=bar_obj) +foo.Program(target='prog', source='prog.c', CFLAGS='$CFLAGS -DBAR $BAZ', BAZ='-DBAZ') +""") test.write('prog.c', r""" #include @@ -90,30 +95,28 @@ } """) - - -test.run(arguments = '.') - -test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('prog'), stdout = """\ +test.run(arguments='.') +test.run(program=test.workpath('foo'), stdout="prog.c: FOO\n") +test.run(program=test.workpath('bar'), stdout="prog.c: BAR\n") +test.run(program=test.workpath('prog'), stdout="""\ prog.c: FOO prog.c: BAR prog.c: BAZ """) -test.write('SConstruct', """ -bar = Environment(CFLAGS = '%s') -bar.Object(target = 'foo%s', source = 'prog.c') -bar.Object(target = 'bar%s', source = 'prog.c') -bar.Program(target = 'foo', source = 'foo%s') -bar.Program(target = 'bar', source = 'bar%s') -""" % (barflags, _obj, _obj, _obj, _obj)) +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +bar = Environment(CFLAGS='{barflags}') -test.run(arguments = '.') +foo_obj = bar.Object(target='foo', source='prog.c') +bar_obj = bar.Object(target='bar', source='prog.c') +bar.Program(target='foo', source=foo_obj) +bar.Program(target='bar', source=bar_obj) +""") -test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") +test.run(arguments='.') +test.run(program=test.workpath('foo'), stdout="prog.c: BAR\n") +test.run(program=test.workpath('bar'), stdout="prog.c: BAR\n") test.pass_test() diff --git a/test/CC/SHCCFLAGS.py b/test/CC/SHCCFLAGS-live.py similarity index 68% rename from test/CC/SHCCFLAGS.py rename to test/CC/SHCCFLAGS-live.py index a923f90010..0f5b492646 100644 --- a/test/CC/SHCCFLAGS.py +++ b/test/CC/SHCCFLAGS-live.py @@ -23,10 +23,17 @@ # 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 that $SHCCFLAGS settings are used to build shared object files. + +This is a live test, uses the detected C compiler. +""" + +import os import sys + import TestSCons -import os - + test = TestSCons.TestSCons() e = test.Environment() @@ -38,16 +45,16 @@ if sys.platform.find('irix') > -1: os.environ['LD_LIBRARYN32_PATH'] = '.' -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) -foo = Environment(SHCCFLAGS = '%s', WINDOWS_INSERT_DEF=1) -bar = Environment(SHCCFLAGS = '%s', WINDOWS_INSERT_DEF=1) +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +foo = Environment(SHCCFLAGS='{fooflags}', WINDOWS_INSERT_DEF=1) +bar = Environment(SHCCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) -foo_obj = foo.SharedObject(target = 'foo', source = 'prog.c') -foo.SharedLibrary(target = 'foo', source = foo_obj) +foo_obj = foo.SharedObject(target='foo', source='prog.c') +foo.SharedLibrary(target='foo', source=foo_obj) -bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') -bar.SharedLibrary(target = 'bar', source = bar_obj) +bar_obj = bar.SharedObject(target='bar', source='prog.c') +bar.SharedLibrary(target='bar', source=bar_obj) fooMain = foo.Clone(LIBS='foo', LIBPATH='.') foomain_obj = fooMain.Object(target='foomain', source='main.c') @@ -56,7 +63,7 @@ barMain = bar.Clone(LIBS='bar', LIBPATH='.') barmain_obj = barMain.Object(target='barmain', source='main.c') barMain.Program(target='barprog', source=barmain_obj) -""" % (fooflags, barflags)) +""") test.write('foo.def', r""" LIBRARY "foo" @@ -89,7 +96,7 @@ } """) -test.write('main.c', r""" +test.write('main.c', """\ void doIt(); @@ -101,31 +108,30 @@ } """) -test.run(arguments = '.') - -test.run(program = test.workpath('fooprog'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="prog.c: FOO\n") +test.run(program=test.workpath('barprog'), stdout="prog.c: BAR\n") -test.write('SConstruct', """ -bar = Environment(SHCCFLAGS = '%s', WINDOWS_INSERT_DEF=1) +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +bar = Environment(SHCCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) -foo_obj = bar.SharedObject(target = 'foo', source = 'prog.c') -bar.SharedLibrary(target = 'foo', source = foo_obj) +foo_obj = bar.SharedObject(target='foo', source='prog.c') +bar.SharedLibrary(target='foo', source=foo_obj) -bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') -bar.SharedLibrary(target = 'bar', source = bar_obj) +bar_obj = bar.SharedObject(target='bar', source='prog.c') +bar.SharedLibrary(target='bar', source=bar_obj) barMain = bar.Clone(LIBS='bar', LIBPATH='.') foomain_obj = barMain.Object(target='foomain', source='main.c') barmain_obj = barMain.Object(target='barmain', source='main.c') barMain.Program(target='barprog', source=foomain_obj) barMain.Program(target='fooprog', source=barmain_obj) -""" % barflags) - -test.run(arguments = '.') +""") -test.run(program = test.workpath('fooprog'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="prog.c: BAR\n") +test.run(program=test.workpath('barprog'), stdout="prog.c: BAR\n") test.pass_test() diff --git a/test/CC/SHCFLAGS.py b/test/CC/SHCFLAGS-live.py similarity index 69% rename from test/CC/SHCFLAGS.py rename to test/CC/SHCFLAGS-live.py index e143713c56..605150e875 100644 --- a/test/CC/SHCFLAGS.py +++ b/test/CC/SHCFLAGS-live.py @@ -22,12 +22,18 @@ # 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__" +""" +Test behavior of SHCFLAGS. + +This is a live test, uses the detected C compiler. +""" + +import os import sys + import TestSCons -import os - + test = TestSCons.TestSCons() e = test.Environment() @@ -39,16 +45,16 @@ if sys.platform.find('irix') > -1: os.environ['LD_LIBRARYN32_PATH'] = '.' -test.write('SConstruct', f""" -DefaultEnvironment(tools=[]) -foo = Environment(SHCFLAGS = '{fooflags}', WINDOWS_INSERT_DEF=1) -bar = Environment(SHCFLAGS = '{barflags}', WINDOWS_INSERT_DEF=1) +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +foo = Environment(SHCFLAGS='{fooflags}', WINDOWS_INSERT_DEF=1) +bar = Environment(SHCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) -foo_obj = foo.SharedObject(target = 'foo', source = 'prog.c') -foo.SharedLibrary(target = 'foo', source = foo_obj) +foo_obj = foo.SharedObject(target='foo', source='prog.c') +foo.SharedLibrary(target='foo', source=foo_obj) -bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') -bar.SharedLibrary(target = 'bar', source = bar_obj) +bar_obj = bar.SharedObject(target='bar', source='prog.c') +bar.SharedLibrary(target='bar', source=bar_obj) fooMain = foo.Clone(LIBS='foo', LIBPATH='.') foomain_obj = fooMain.Object(target='foomain', source='main.c') @@ -102,20 +108,19 @@ } """) -test.run(arguments = '.') +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="prog.c: FOO\n") +test.run(program=test.workpath('barprog'), stdout="prog.c: BAR\n") -test.run(program = test.workpath('fooprog'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +bar = Environment(SHCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) -test.write('SConstruct', f""" -DefaultEnvironment(tools=[]) -bar = Environment(SHCFLAGS = '{barflags}', WINDOWS_INSERT_DEF=1) +foo_obj = bar.SharedObject(target='foo', source='prog.c') +bar.SharedLibrary(target='foo', source=foo_obj) -foo_obj = bar.SharedObject(target = 'foo', source = 'prog.c') -bar.SharedLibrary(target = 'foo', source = foo_obj) - -bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c') -bar.SharedLibrary(target = 'bar', source = bar_obj) +bar_obj = bar.SharedObject(target='bar', source='prog.c') +bar.SharedLibrary(target='bar', source=bar_obj) barMain = bar.Clone(LIBS='bar', LIBPATH='.') foomain_obj = barMain.Object(target='foomain', source='main.c') @@ -124,10 +129,9 @@ barMain.Program(target='fooprog', source=barmain_obj) """) -test.run(arguments = '.') - -test.run(program = test.workpath('fooprog'), stdout = "prog.c: BAR\n") -test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n") +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="prog.c: BAR\n") +test.run(program=test.workpath('barprog'), stdout="prog.c: BAR\n") test.pass_test() diff --git a/test/CXX/CCFLAGS.py b/test/CXX/CCFLAGS-live.py similarity index 74% rename from test/CXX/CCFLAGS.py rename to test/CXX/CCFLAGS-live.py index 061df87a3b..8e91391112 100644 --- a/test/CXX/CCFLAGS.py +++ b/test/CXX/CCFLAGS-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,28 +22,29 @@ # 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 we can set both $CCFLAGS and $CXXFLAGS and have them both show up on the compilation lines for C++ source files. + +This is a live test, uses the detected C++ compiler. """ import TestSCons test = TestSCons.TestSCons() -test.write('SConstruct', """ +test.write('SConstruct', """\ +_ = DefaultEnvironment(tools=[]) foo = Environment() -foo.Append(CCFLAGS = '-DFOO', CXXFLAGS = '-DCXX') +foo.Append(CCFLAGS='-DFOO', CXXFLAGS='-DCXX') bar = Environment() -bar.Append(CCFLAGS = '-DBAR', CXXFLAGS = '-DCXX') -foo_obj = foo.Object(target = 'foo', source = 'prog.cpp') -bar_obj = bar.Object(target = 'bar', source = 'prog.cpp') -foo.Program(target = 'foo', source = foo_obj) -bar.Program(target = 'bar', source = bar_obj) +bar.Append(CCFLAGS='-DBAR', CXXFLAGS='-DCXX') + +foo_obj = foo.Object(target='foo', source='prog.cpp') +bar_obj = bar.Object(target='bar', source='prog.cpp') +foo.Program(target='foo', source=foo_obj) +bar.Program(target='bar', source=bar_obj) """) test.write('prog.cpp', r""" @@ -62,10 +65,10 @@ } """) -test.run(arguments = '.') +test.run(arguments='.') -test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") +test.run(program=test.workpath('foo'), stdout="prog.c: FOO\n") +test.run(program=test.workpath('bar'), stdout="prog.c: BAR\n") test.pass_test() diff --git a/test/CXX/CXXFLAGS.py b/test/CXX/CXXFLAGS-live.py similarity index 66% rename from test/CXX/CXXFLAGS.py rename to test/CXX/CXXFLAGS-live.py index 8d72708ee8..b0db68e662 100644 --- a/test/CXX/CXXFLAGS.py +++ b/test/CXX/CXXFLAGS-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,13 +22,12 @@ # 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 $CXXFLAGS settings are used to build both static and shared object files. + +This is a live test, uses the detected C++ compiler. """ import os @@ -34,26 +35,26 @@ import TestSCons -_obj = TestSCons._obj +test = TestSCons.TestSCons() if os.name == 'posix': os.environ['LD_LIBRARY_PATH'] = '.' if sys.platform.find('irix') > -1: os.environ['LD_LIBRARYN32_PATH'] = '.' -test = TestSCons.TestSCons() - e = test.Environment() -test.write('SConstruct', """ +test.write('SConstruct', """\ +_ = DefaultEnvironment(tools=[]) foo = Environment(WINDOWS_INSERT_DEF=1) -foo.Append(CXXFLAGS = '-DFOO') +foo.Append(CXXFLAGS='-DFOO') bar = Environment(WINDOWS_INSERT_DEF=1) -bar.Append(CXXFLAGS = '-DBAR') -foo_obj = foo.SharedObject(target = 'fooshared%(_obj)s', source = 'doIt.cpp') -bar_obj = bar.SharedObject(target = 'barshared%(_obj)s', source = 'doIt.cpp') -foo.SharedLibrary(target = 'foo', source = foo_obj) -bar.SharedLibrary(target = 'bar', source = bar_obj) +bar.Append(CXXFLAGS='-DBAR') + +foo_obj = foo.SharedObject(target='fooshared', source='doIt.cpp') +bar_obj = bar.SharedObject(target='barshared', source='doIt.cpp') +foo.SharedLibrary(target='foo', source=foo_obj) +bar.SharedLibrary(target='bar', source=bar_obj) fooMain = foo.Clone(LIBS='foo', LIBPATH='.') foo_obj = fooMain.Object(target='foomain', source='main.c') @@ -63,11 +64,11 @@ bar_obj = barMain.Object(target='barmain', source='main.c') barMain.Program(target='barprog', source=bar_obj) -foo_obj = foo.Object(target = 'foostatic', source = 'prog.cpp') -bar_obj = bar.Object(target = 'barstatic', source = 'prog.cpp') -foo.Program(target = 'foo', source = foo_obj) -bar.Program(target = 'bar', source = bar_obj) -""" % locals()) +foo_obj = foo.Object(target='foostatic', source='prog.cpp') +bar_obj = bar.Object(target='barstatic', source='prog.cpp') +foo.Program(target='foo', source=foo_obj) +bar.Program(target='bar', source=bar_obj) +""") test.write('foo.def', r""" LIBRARY "foo" @@ -100,8 +101,7 @@ } """) -test.write('main.c', r""" - +test.write('main.c', """\ void doIt(); int @@ -130,36 +130,33 @@ } """) -test.run(arguments = '.') - -test.run(program = test.workpath('fooprog'), stdout = "doIt.cpp: FOO\n") -test.run(program = test.workpath('barprog'), stdout = "doIt.cpp: BAR\n") -test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n") -test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n") +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="doIt.cpp: FOO\n") +test.run(program=test.workpath('barprog'), stdout="doIt.cpp: BAR\n") +test.run(program=test.workpath('foo'), stdout="prog.c: FOO\n") +test.run(program=test.workpath('bar'), stdout="prog.c: BAR\n") - -test.write('SConstruct', """ +test.write('SConstruct', """\ +_ = DefaultEnvironment(tools=[]) bar = Environment(WINDOWS_INSERT_DEF=1) -bar.Append(CXXFLAGS = '-DBAR') -foo_obj = bar.SharedObject(target = 'foo%(_obj)s', source = 'doIt.cpp') -bar_obj = bar.SharedObject(target = 'bar%(_obj)s', source = 'doIt.cpp') -bar.SharedLibrary(target = 'foo', source = foo_obj) -bar.SharedLibrary(target = 'bar', source = bar_obj) +bar.Append(CXXFLAGS='-DBAR') + +foo_obj = bar.SharedObject(target='foo', source='doIt.cpp') +bar_obj = bar.SharedObject(target='bar', source='doIt.cpp') +bar.SharedLibrary(target='foo', source=foo_obj) +bar.SharedLibrary(target='bar', source=bar_obj) barMain = bar.Clone(LIBS='bar', LIBPATH='.') foo_obj = barMain.Object(target='foomain', source='main.c') bar_obj = barMain.Object(target='barmain', source='main.c') barMain.Program(target='barprog', source=foo_obj) barMain.Program(target='fooprog', source=bar_obj) -""" % locals()) - -test.run(arguments = '.') - -test.run(program = test.workpath('fooprog'), stdout = "doIt.cpp: BAR\n") -test.run(program = test.workpath('barprog'), stdout = "doIt.cpp: BAR\n") - +""") +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="doIt.cpp: BAR\n") +test.run(program=test.workpath('barprog'), stdout="doIt.cpp: BAR\n") test.pass_test() diff --git a/test/CXX/SHCCFLAGS-live.py b/test/CXX/SHCCFLAGS-live.py new file mode 100644 index 0000000000..38c23a7ca0 --- /dev/null +++ b/test/CXX/SHCCFLAGS-live.py @@ -0,0 +1,142 @@ +#!/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 that $SHCCFLAGS settings are used to build shared object files. + +This is a live test, uses the detected C compiler. +""" + +import os +import sys + +import TestSCons + +test = TestSCons.TestSCons() + +e = test.Environment() +fooflags = e['SHCCFLAGS'] + ' -DFOO' +barflags = e['SHCCFLAGS'] + ' -DBAR' + +if os.name == 'posix': + os.environ['LD_LIBRARY_PATH'] = '.' +if sys.platform.find('irix') > -1: + os.environ['LD_LIBRARYN32_PATH'] = '.' + +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +foo = Environment(SHCCFLAGS='{fooflags}', WINDOWS_INSERT_DEF=1) +bar = Environment(SHCCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) + +foo_obj = foo.SharedObject(target='foo', source='prog.cpp') +foo.SharedLibrary(target='foo', source=foo_obj) + +bar_obj = bar.SharedObject(target='bar', source='prog.cpp') +bar.SharedLibrary(target='bar', source=bar_obj) + +fooMain = foo.Clone(LIBS='foo', LIBPATH='.') +foo_obj = fooMain.Object(target='foomain', source='main.c') +fooMain.Program(target='fooprog', source=foo_obj) + +barMain = bar.Clone(LIBS='bar', LIBPATH='.') +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=barmain_obj) +""") + +test.write('foo.def', """\ +LIBRARY "foo" +DESCRIPTION "Foo Shared Library" + +EXPORTS + doIt +""") + +test.write('bar.def', """\ +LIBRARY "bar" +DESCRIPTION "Bar Shared Library" + +EXPORTS + doIt +""") + +test.write('prog.cpp', r""" +#include + +extern "C" void +doIt() +{ +#ifdef FOO + printf("prog.cpp: FOO\n"); +#endif +#ifdef BAR + printf("prog.cpp: BAR\n"); +#endif +} +""") + +test.write('main.c', """\ + +void doIt(); + +int +main(int argc, char* argv[]) +{ + doIt(); + return 0; +} +""") + +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="prog.cpp: FOO\n") +test.run(program=test.workpath('barprog'), stdout="prog.cpp: BAR\n") + +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +bar = Environment(SHCCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) + +foo_obj = bar.SharedObject(target='foo', source='prog.cpp') +bar.SharedLibrary(target='foo', source=foo_obj) + +bar_obj = bar.SharedObject(target='bar', source='prog.cpp') +bar.SharedLibrary(target='bar', source=bar_obj) + +barMain = bar.Clone(LIBS='bar', LIBPATH='.') +foomain_obj = barMain.Object(target='foomain', source='main.c') +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=foomain_obj) +barMain.Program(target='fooprog', source=barmain_obj) +""") + +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="prog.cpp: BAR\n") +test.run(program=test.workpath('barprog'), stdout="prog.cpp: BAR\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CXX/SHCXXFLAGS.py b/test/CXX/SHCXXFLAGS-live.py similarity index 56% rename from test/CXX/SHCXXFLAGS.py rename to test/CXX/SHCXXFLAGS-live.py index 343be30f6e..7fbd8641aa 100644 --- a/test/CXX/SHCXXFLAGS.py +++ b/test/CXX/SHCXXFLAGS-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,12 +22,11 @@ # 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 $SHCXXFLAGS settings are used to build shared object files. + +This is a live test, uses the detected C compiler. """ import os @@ -33,37 +34,38 @@ import TestSCons -_obj = TestSCons._obj +test = TestSCons.TestSCons() + +e = test.Environment() +fooflags = e['SHCXXFLAGS'] + ' -DFOO' +barflags = e['SHCXXFLAGS'] + ' -DBAR' if os.name == 'posix': os.environ['LD_LIBRARY_PATH'] = '.' if sys.platform.find('irix') > -1: os.environ['LD_LIBRARYN32_PATH'] = '.' -test = TestSCons.TestSCons() +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +foo = Environment(SHCXXFLAGS='{fooflags}', WINDOWS_INSERT_DEF=1) +bar = Environment(SHCXXFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) -e = test.Environment() +foo_obj = foo.SharedObject(target='foo', source='prog.cpp') +foo.SharedLibrary(target='foo', source=foo_obj) -test.write('SConstruct', """ -foo = Environment(WINDOWS_INSERT_DEF=1) -foo.Append(SHCXXFLAGS = '-DFOO') -bar = Environment(WINDOWS_INSERT_DEF=1) -bar.Append(SHCXXFLAGS = '-DBAR') -foo_obj = foo.SharedObject(target = 'foo%(_obj)s', source = 'prog.cpp') -bar_obj = bar.SharedObject(target = 'bar%(_obj)s', source = 'prog.cpp') -foo.SharedLibrary(target = 'foo', source = foo_obj) -bar.SharedLibrary(target = 'bar', source = bar_obj) +bar_obj = bar.SharedObject(target='bar', source='prog.cpp') +bar.SharedLibrary(target='bar', source=bar_obj) fooMain = foo.Clone(LIBS='foo', LIBPATH='.') foo_obj = fooMain.Object(target='foomain', source='main.c') fooMain.Program(target='fooprog', source=foo_obj) barMain = bar.Clone(LIBS='bar', LIBPATH='.') -bar_obj = barMain.Object(target='barmain', source='main.c') -barMain.Program(target='barprog', source=bar_obj) -""" % locals()) +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=barmain_obj) +""") -test.write('foo.def', r""" +test.write('foo.def', """\ LIBRARY "foo" DESCRIPTION "Foo Shared Library" @@ -71,7 +73,7 @@ doIt """) -test.write('bar.def', r""" +test.write('bar.def', """\ LIBRARY "bar" DESCRIPTION "Bar Shared Library" @@ -94,7 +96,7 @@ } """) -test.write('main.c', r""" +test.write('main.c', """\ void doIt(); @@ -106,30 +108,30 @@ } """) -test.run(arguments = '.') +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="prog.cpp: FOO\n") +test.run(program=test.workpath('barprog'), stdout="prog.cpp: BAR\n") -test.run(program = test.workpath('fooprog'), stdout = "prog.cpp: FOO\n") -test.run(program = test.workpath('barprog'), stdout = "prog.cpp: BAR\n") +test.write('SConstruct', f"""\ +_ = DefaultEnvironment(tools=[]) +bar = Environment(SHCXXFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) -test.write('SConstruct', """ -bar = Environment(WINDOWS_INSERT_DEF=1) -bar.Append(SHCXXFLAGS = '-DBAR') -foo_obj = bar.SharedObject(target = 'foo%(_obj)s', source = 'prog.cpp') -bar_obj = bar.SharedObject(target = 'bar%(_obj)s', source = 'prog.cpp') -bar.SharedLibrary(target = 'foo', source = foo_obj) -bar.SharedLibrary(target = 'bar', source = bar_obj) +foo_obj = bar.SharedObject(target='foo', source='prog.cpp') +bar.SharedLibrary(target='foo', source=foo_obj) -barMain = bar.Clone(LIBS='bar', LIBPATH='.') -foo_obj = barMain.Object(target='foomain', source='main.c') -bar_obj = barMain.Object(target='barmain', source='main.c') -barMain.Program(target='barprog', source=foo_obj) -barMain.Program(target='fooprog', source=bar_obj) -""" % locals()) +bar_obj = bar.SharedObject(target='bar', source='prog.cpp') +bar.SharedLibrary(target='bar', source=bar_obj) -test.run(arguments = '.') +barMain = bar.Clone(LIBS='bar', LIBPATH='.') +foomain_obj = barMain.Object(target='foomain', source='main.c') +barmain_obj = barMain.Object(target='barmain', source='main.c') +barMain.Program(target='barprog', source=foomain_obj) +barMain.Program(target='fooprog', source=barmain_obj) +""") -test.run(program = test.workpath('fooprog'), stdout = "prog.cpp: BAR\n") -test.run(program = test.workpath('barprog'), stdout = "prog.cpp: BAR\n") +test.run(arguments='.') +test.run(program=test.workpath('fooprog'), stdout="prog.cpp: BAR\n") +test.run(program=test.workpath('barprog'), stdout="prog.cpp: BAR\n") test.pass_test() From 9e9e8cccb32e353794a8712dc4d99d34bdca8cf6 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 27 Mar 2025 16:55:35 -0600 Subject: [PATCH 290/386] C/CXX FLAGS tests: roll back DefaultEnvironment assignment No longer assign the result of calling DefaultEnvironment to a dummy. Signed-off-by: Mats Wichmann --- CHANGES.txt | 8 ++++---- test/CC/CCFLAGS-live.py | 4 ++-- test/CC/CFLAGS-live.py | 6 +++--- test/CC/SHCCFLAGS-live.py | 4 ++-- test/CC/SHCFLAGS-live.py | 4 ++-- test/CXX/CCFLAGS-live.py | 2 +- test/CXX/CXXFLAGS-live.py | 4 ++-- test/CXX/SHCCFLAGS-live.py | 4 ++-- test/CXX/SHCXXFLAGS-live.py | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d206668cbc..f9f420eacc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,16 +15,16 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From John Doe: - Whatever John Doe did. - From Mats Wichmann: - - Clean up C and C++ FLAGS tests. Tests which use a real compiler - are now more clearly distinguished (-live.py suffix and docstring). - From Bill Prendergast: - Fixed SCons.Variables.PackageVariable to correctly test the default setting against both enable & disable strings. (Fixes #4702) - Extended unittests (crudely) to test for correct/expected response when default setting is a boolean string. + From Mats Wichmann: + - Clean up C and C++ FLAGS tests. Tests which use a real compiler + are now more clearly distinguished (-live.py suffix and docstring). + RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/test/CC/CCFLAGS-live.py b/test/CC/CCFLAGS-live.py index ab66785aec..ae280bcc22 100644 --- a/test/CC/CCFLAGS-live.py +++ b/test/CC/CCFLAGS-live.py @@ -49,7 +49,7 @@ barflags = '-DBAR' test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) foo = Environment(CCFLAGS='{fooflags}') bar = Environment(CCFLAGS='{barflags}') @@ -91,7 +91,7 @@ """) test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) bar = Environment(CCFLAGS='{barflags}') foo_obj = bar.Object(target='foo', source='prog.c') diff --git a/test/CC/CFLAGS-live.py b/test/CC/CFLAGS-live.py index 2b9341f0b1..cae0b22eb2 100644 --- a/test/CC/CFLAGS-live.py +++ b/test/CC/CFLAGS-live.py @@ -37,7 +37,7 @@ # Make sure CFLAGS is not passed to CXX by just expanding CXXCOM test.write('SConstruct', """\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) env = Environment(CFLAGS='-xyz', CCFLAGS='-abc') print(env.subst('$CXXCOM')) print(env.subst('$CXXCOMSTR')) @@ -63,7 +63,7 @@ barflags = '-DBAR' test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) foo = Environment(CFLAGS="{fooflags}") bar = Environment(CFLAGS="{barflags}") @@ -105,7 +105,7 @@ """) test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) bar = Environment(CFLAGS='{barflags}') foo_obj = bar.Object(target='foo', source='prog.c') diff --git a/test/CC/SHCCFLAGS-live.py b/test/CC/SHCCFLAGS-live.py index 0f5b492646..a511fff0f8 100644 --- a/test/CC/SHCCFLAGS-live.py +++ b/test/CC/SHCCFLAGS-live.py @@ -46,7 +46,7 @@ os.environ['LD_LIBRARYN32_PATH'] = '.' test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) foo = Environment(SHCCFLAGS='{fooflags}', WINDOWS_INSERT_DEF=1) bar = Environment(SHCCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) @@ -113,7 +113,7 @@ test.run(program=test.workpath('barprog'), stdout="prog.c: BAR\n") test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) bar = Environment(SHCCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) foo_obj = bar.SharedObject(target='foo', source='prog.c') diff --git a/test/CC/SHCFLAGS-live.py b/test/CC/SHCFLAGS-live.py index 605150e875..46ff8952fd 100644 --- a/test/CC/SHCFLAGS-live.py +++ b/test/CC/SHCFLAGS-live.py @@ -46,7 +46,7 @@ os.environ['LD_LIBRARYN32_PATH'] = '.' test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) foo = Environment(SHCFLAGS='{fooflags}', WINDOWS_INSERT_DEF=1) bar = Environment(SHCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) @@ -113,7 +113,7 @@ test.run(program=test.workpath('barprog'), stdout="prog.c: BAR\n") test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) bar = Environment(SHCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) foo_obj = bar.SharedObject(target='foo', source='prog.c') diff --git a/test/CXX/CCFLAGS-live.py b/test/CXX/CCFLAGS-live.py index 8e91391112..fcce9a32d4 100644 --- a/test/CXX/CCFLAGS-live.py +++ b/test/CXX/CCFLAGS-live.py @@ -35,7 +35,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', """\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) foo = Environment() foo.Append(CCFLAGS='-DFOO', CXXFLAGS='-DCXX') bar = Environment() diff --git a/test/CXX/CXXFLAGS-live.py b/test/CXX/CXXFLAGS-live.py index b0db68e662..e2bf9501f0 100644 --- a/test/CXX/CXXFLAGS-live.py +++ b/test/CXX/CXXFLAGS-live.py @@ -45,7 +45,7 @@ e = test.Environment() test.write('SConstruct', """\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) foo = Environment(WINDOWS_INSERT_DEF=1) foo.Append(CXXFLAGS='-DFOO') bar = Environment(WINDOWS_INSERT_DEF=1) @@ -138,7 +138,7 @@ test.run(program=test.workpath('bar'), stdout="prog.c: BAR\n") test.write('SConstruct', """\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) bar = Environment(WINDOWS_INSERT_DEF=1) bar.Append(CXXFLAGS='-DBAR') diff --git a/test/CXX/SHCCFLAGS-live.py b/test/CXX/SHCCFLAGS-live.py index 38c23a7ca0..10b650523b 100644 --- a/test/CXX/SHCCFLAGS-live.py +++ b/test/CXX/SHCCFLAGS-live.py @@ -46,7 +46,7 @@ os.environ['LD_LIBRARYN32_PATH'] = '.' test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) foo = Environment(SHCCFLAGS='{fooflags}', WINDOWS_INSERT_DEF=1) bar = Environment(SHCCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) @@ -113,7 +113,7 @@ test.run(program=test.workpath('barprog'), stdout="prog.cpp: BAR\n") test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) bar = Environment(SHCCFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) foo_obj = bar.SharedObject(target='foo', source='prog.cpp') diff --git a/test/CXX/SHCXXFLAGS-live.py b/test/CXX/SHCXXFLAGS-live.py index 7fbd8641aa..3ec873c84f 100644 --- a/test/CXX/SHCXXFLAGS-live.py +++ b/test/CXX/SHCXXFLAGS-live.py @@ -46,7 +46,7 @@ os.environ['LD_LIBRARYN32_PATH'] = '.' test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) foo = Environment(SHCXXFLAGS='{fooflags}', WINDOWS_INSERT_DEF=1) bar = Environment(SHCXXFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) @@ -113,7 +113,7 @@ test.run(program=test.workpath('barprog'), stdout="prog.cpp: BAR\n") test.write('SConstruct', f"""\ -_ = DefaultEnvironment(tools=[]) +DefaultEnvironment(tools=[]) bar = Environment(SHCXXFLAGS='{barflags}', WINDOWS_INSERT_DEF=1) foo_obj = bar.SharedObject(target='foo', source='prog.cpp') From 1ce48959de9ac71557599ed0d8423f80e9cf2dd9 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 27 Mar 2025 16:38:15 -0600 Subject: [PATCH 291/386] Make runtest work for external tests again An earlier rework had caused external directories not to be searched, even if specified in combination with --external. Fixes #4699. Signed-off-by: Mats Wichmann --- CHANGES.txt | 4 ++ runtest.py | 58 ++++++++++------------ test/runtest/external_tests.py | 91 ++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 31 deletions(-) create mode 100644 test/runtest/external_tests.py diff --git a/CHANGES.txt b/CHANGES.txt index a4041204c0..3c290467a6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -22,6 +22,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Extended unittests (crudely) to test for correct/expected response when default setting is a boolean string. + From Mats Wichmann: + - runtest.py once again finds "external" tests, such as the tests for + tools in scons-contrib. An earlier rework had broken this. Fixes #4699. + RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/runtest.py b/runtest.py index c8b3e990f6..7e0a5d16df 100755 --- a/runtest.py +++ b/runtest.py @@ -29,7 +29,6 @@ from io import StringIO from pathlib import Path, PurePath, PureWindowsPath from queue import Queue -from typing import TextIO cwd = os.getcwd() debug: str | None = None @@ -575,38 +574,34 @@ def footer(self, f): del os.environ['_JAVA_OPTIONS'] -# ---[ test discovery ]------------------------------------ -# This section figures out which tests to run. +# ---[ Test Discovery ]------------------------------------ +# This section determines which tests to run based on three +# mutually exclusive options: +# 1. Reading test paths from a testlist file (--file or --retry option) +# 2. Using test paths given as command line arguments +# 3. Automatically finding all tests (--all option) # -# The initial testlist is made by reading from the testlistfile, -# if supplied, or by looking at the test arguments, if supplied, -# or by looking for all test files if the "all" argument is supplied. -# One of the three is required. +# Test paths can specify either individual test files, or directories to +# scan for tests. The following test types are recognized: # -# Each test path, whichever of the three sources it comes from, -# specifies either a test file or a directory to search for -# SCons tests. SCons code layout assumes that any file under the 'SCons' -# subdirectory that ends with 'Tests.py' is a unit test, and any Python -# script (*.py) under the 'test' subdirectory is an end-to-end test. -# We need to track these because they are invoked differently. -# find_unit_tests and find_e2e_tests are used for this searching. +# - Unit tests: Files ending in 'Tests.py' under the 'SCons' directory +# - End-to-end tests: Python scripts (*.py) under the 'test' directory +# - External tests: End-to-end tests in paths containing a 'test' +# component (not expected to be local) # -# Note that there are some tests under 'SCons' that *begin* with -# 'test_', but they're packaging and installation tests, not -# functional tests, so we don't execute them by default. (They can -# still be executed by hand, though). +# find_unit_tests() and find_e2e_tests() perform the directory scanning. # -# Test exclusions, if specified, are then applied. - +# After the initial test list is built, any test exclusions specified via +# --exclude-list are applied to produce the final test set. def scanlist(testfile): """ Process a testlist file """ data = StringIO(testfile.read_text()) tests = [t.strip() for t in data.readlines() if not t.startswith('#')] # in order to allow scanned lists to work whether they use forward or - # backward slashes, first create the object as a PureWindowsPath which - # accepts either, then use that to make a Path object to use for - # comparisons like "file in scanned_list". + # backward slashes, on non-Windows first create the object as a + # PureWindowsPath which accepts either, then use that to make a Path + # object for use in comparisons like "if file in scanned_list". if sys.platform == 'win32': return [Path(t) for t in tests if t] else: @@ -635,7 +630,7 @@ def find_e2e_tests(directory): if 'sconstest.skip' in filenames: continue - # Slurp in any tests in exclude lists + # Gather up the data from any exclude lists excludes = [] if ".exclude_tests" in filenames: excludefile = Path(dirpath, ".exclude_tests").resolve() @@ -648,8 +643,7 @@ def find_e2e_tests(directory): return sorted(result) -# initial selection: -# if we have a testlist file read that, else hunt for tests. +# Initial test selection: unittests = [] endtests = [] if args.testlistfile: @@ -668,7 +662,7 @@ def find_e2e_tests(directory): # Clean up path removing leading ./ or .\ name = str(path) if name.startswith('.') and name[1] in (os.sep, os.altsep): - path = path.with_name(tn[2:]) + path = path.with_name(name[2:]) if path.exists(): if path.is_dir(): @@ -676,7 +670,8 @@ def find_e2e_tests(directory): unittests.extend(find_unit_tests(path)) elif path.parts[0] == 'test': endtests.extend(find_e2e_tests(path)) - # else: TODO: what if user pointed to a dir outside scons tree? + elif args.external and 'test' in path.parts: + endtests.extend(find_e2e_tests(path)) else: if path.match("*Tests.py"): unittests.append(path) @@ -703,7 +698,7 @@ def find_e2e_tests(directory): """) sys.exit(1) -# ---[ test processing ]----------------------------------- +# ---[ Test Processing ]----------------------------------- tests = [Test(t) for t in tests] if args.list_only: @@ -826,10 +821,11 @@ def run_test(t, io_lock=None, run_async=True): class RunTest(threading.Thread): - """ Test Runner class. + """Test Runner thread. - One instance will be created for each job thread in multi-job mode + One will be created for each job in multi-job mode """ + def __init__(self, queue=None, io_lock=None, group=None, target=None, name=None): super().__init__(group=group, target=target, name=name) self.queue = queue diff --git a/test/runtest/external_tests.py b/test/runtest/external_tests.py new file mode 100644 index 0000000000..f851d79e53 --- /dev/null +++ b/test/runtest/external_tests.py @@ -0,0 +1,91 @@ +#!/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. + +""" +Test that external subdirs are searched if --external is given: + + python runtest.py --external ext/test/subdir + +""" + +import os + +import TestRuntest + +test = TestRuntest.TestRuntest() +test.subdir('ext', ['ext', 'test'], ['ext', 'test', 'subdir']) + +pythonstring = TestRuntest.pythonstring +pythonflags = TestRuntest.pythonflags + +one = os.path.join('ext', 'test', 'subdir', 'test_one.py') +two = os.path.join('ext', 'test', 'subdir', 'two.py') +three = os.path.join('ext', 'test', 'test_three.py') + +test.write_passing_test(['ext', 'test', 'subdir', 'test_one.py']) +test.write_passing_test(['ext', 'test', 'subdir', 'two.py']) +test.write_passing_test(['ext', 'test', 'test_three.py']) + +expect_stderr_noarg = """\ +usage: runtest.py [OPTIONS] [TEST ...] + +error: no tests matching the specification were found. + See "Test selection options" in the help for details on + how to specify and/or exclude tests. +""" + +expect_stdout = f"""\ +{pythonstring}{pythonflags} {one} +PASSING TEST STDOUT +{pythonstring}{pythonflags} {two} +PASSING TEST STDOUT +""" + +expect_stderr = """\ +PASSING TEST STDERR +PASSING TEST STDERR +""" + +test.run( + arguments='--no-progress ext/test/subdir', + status=1, + stdout=None, + stderr=expect_stderr_noarg, +) + +test.run( + arguments='--no-progress --external ext/test/subdir', + status=0, + stdout=expect_stdout, + stderr=expect_stderr, +) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: From 0b7d5e9251362a7a711a764c6ffccc7101c1c7f9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 2 Apr 2025 21:05:34 -0700 Subject: [PATCH 292/386] Simplified paths and usage of passing test paths --- test/runtest/external_tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/runtest/external_tests.py b/test/runtest/external_tests.py index f851d79e53..622bfbd6e5 100644 --- a/test/runtest/external_tests.py +++ b/test/runtest/external_tests.py @@ -35,7 +35,7 @@ import TestRuntest test = TestRuntest.TestRuntest() -test.subdir('ext', ['ext', 'test'], ['ext', 'test', 'subdir']) +test.subdir('ext/test/subdir') pythonstring = TestRuntest.pythonstring pythonflags = TestRuntest.pythonflags @@ -44,9 +44,9 @@ two = os.path.join('ext', 'test', 'subdir', 'two.py') three = os.path.join('ext', 'test', 'test_three.py') -test.write_passing_test(['ext', 'test', 'subdir', 'test_one.py']) -test.write_passing_test(['ext', 'test', 'subdir', 'two.py']) -test.write_passing_test(['ext', 'test', 'test_three.py']) +test.write_passing_test(one) +test.write_passing_test(two) +test.write_passing_test(three) expect_stderr_noarg = """\ usage: runtest.py [OPTIONS] [TEST ...] From 541edc4120e1700921e1e0e8a0ff55759b8272bf Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 2 Apr 2025 21:06:49 -0700 Subject: [PATCH 293/386] Added RELEASE.txt blurb --- RELEASE.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE.txt b/RELEASE.txt index eecbe364f3..aae805d928 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -57,7 +57,8 @@ DOCUMENTATION DEVELOPMENT ----------- -- List visible changes in the way SCons is developed +- runtest.py once again finds "external" tests, such as the tests for + tools in scons-contrib. An earlier rework had broken this. Fixes #4699. Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== From 54b843ab7d3e9d6e3e4b429b87d4e0d44f05f962 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 5 Apr 2025 12:53:17 -0700 Subject: [PATCH 294/386] updated tests to use f-string and skip str()'ing node --- test/implicit/changed-node.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/implicit/changed-node.py b/test/implicit/changed-node.py index 7b52302e0e..99ea01b0d7 100644 --- a/test/implicit/changed-node.py +++ b/test/implicit/changed-node.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 we don't throw an exception if a stored implicit @@ -51,7 +50,7 @@ def lister(target, source, env): for l in os.listdir(source[0]): ofp.write(l + '\\n') else: - ofp.write(str(source[0]) + '\\n') + ofp.write(f'{source[0]}\\n') builder = Builder(action=lister, source_factory=Dir, @@ -76,6 +75,7 @@ def lister(target, source, env): test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) SetOption('implicit_cache', 1) SetOption('max_drift', 1) @@ -86,7 +86,7 @@ def lister(target, source, env): for l in os.listdir(source[0]): ofp.write(l + '\\n') else: - ofp.write(str(source[0]) + '\\n') + ofp.write(f'{source[0]}\\n') builder = Builder(action=lister, source_factory=File) From 414f920fe8c9ddbfebbd63956f0949404b243dc5 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 5 Apr 2025 17:21:35 -0700 Subject: [PATCH 295/386] updated docstring to indicate using both C and C++ compilers --- test/CXX/SHCXXFLAGS-live.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CXX/SHCXXFLAGS-live.py b/test/CXX/SHCXXFLAGS-live.py index 3ec873c84f..8ffb97ec81 100644 --- a/test/CXX/SHCXXFLAGS-live.py +++ b/test/CXX/SHCXXFLAGS-live.py @@ -26,7 +26,7 @@ """ Verify that $SHCXXFLAGS settings are used to build shared object files. -This is a live test, uses the detected C compiler. +This is a live test, uses the detected C and C++ compilers. """ import os From 88ce29c0d765c63706422aafc562b4b025b3f8b8 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 6 Apr 2025 14:39:50 -0700 Subject: [PATCH 296/386] [ci skip] Simplify and hopefully clarify a bit --- SCons/Environment.xml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/SCons/Environment.xml b/SCons/Environment.xml index ab514b7985..be49e03c9d 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -354,9 +354,6 @@ to be performed after the specified target has been built. -target -may be a string or Node object, -or a list of strings or Node objects. action may be an Action object, or anything that can be converted into an Action object. @@ -378,7 +375,7 @@ AddPostAction(foo, Chmod('$TARGET', "a-x"))
-If a target is an Alias target, +If a target is an Alias, action is associated with the action of the alias, if specified. @@ -398,9 +395,6 @@ to be performed before the specified target is built. -target -may be a string or Node object, -or a list of strings or Node objects. action may be an Action object, or anything that can be converted into an Action object. @@ -446,7 +440,7 @@ gcc -o foo foo.o -If a target is an Alias target, +If a target is an Alias, action is associated with the action of the alias, if specified. @@ -460,7 +454,7 @@ action of the alias, if specified. -Create an Alias target that +Create an Alias node that can be used as a reference to zero or more other targets, specified by the optional source parameter. Aliases provide a way to give a shorter or more descriptive From 1304e48ae6ccdaeb2283820653078ae66de5ad99 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 6 Apr 2025 14:50:28 -0700 Subject: [PATCH 297/386] [ci skip] Use Alias entity --- SCons/Environment.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Environment.xml b/SCons/Environment.xml index be49e03c9d..d50d650b00 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -375,7 +375,7 @@ AddPostAction(foo, Chmod('$TARGET', "a-x")) -If a target is an Alias, +If a target is an &f-Alias, action is associated with the action of the alias, if specified. @@ -440,7 +440,7 @@ gcc -o foo foo.o -If a target is an Alias, +If a target is an &f-Alias, action is associated with the action of the alias, if specified. From 9d9188ebeb78d1ed903450f0e20eb40b5c79aa22 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 6 Apr 2025 14:51:43 -0700 Subject: [PATCH 298/386] [ci skip] Fix Alias entity usage --- SCons/Environment.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Environment.xml b/SCons/Environment.xml index d50d650b00..296a890b8a 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -375,7 +375,7 @@ AddPostAction(foo, Chmod('$TARGET', "a-x")) -If a target is an &f-Alias, +If a target is an &f-Alias;, action is associated with the action of the alias, if specified. @@ -440,7 +440,7 @@ gcc -o foo foo.o -If a target is an &f-Alias, +If a target is an &f-Alias;, action is associated with the action of the alias, if specified. From 125a1ac0730952759807bab6193dfd17c0cb35d9 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 8 Apr 2025 14:26:04 -0600 Subject: [PATCH 299/386] Update docs for SConsDoc and SConsExample Both now have docstrings that render reasonably with Sphinx, in anticipation of possibly adding these files into the "API Docs" build, since they're used by developers, even if not part of SCons code itself. Signed-off-by: Mats Wichmann --- CHANGES.txt | 4 + bin/SConsDoc.py | 32 ++++-- bin/SConsExamples.py | 232 ++++++++++++++++++++++++++----------------- 3 files changed, 164 insertions(+), 104 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ca52cc4a7b..e60c172672 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -27,6 +27,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - runtest.py once again finds "external" tests, such as the tests for tools in scons-contrib. An earlier rework had broken this. Fixes #4699. - Clarify how pre/post actions on an alias work. + - Tweak the two doc-generation modules. The significant change is + turning the introductory comment in bin/SConsExamples into a docstring + that can be rendered by Sphinx, and updating that text. The rest is + minor fiddling like switching to f-strings small doc changes. RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/bin/SConsDoc.py b/bin/SConsDoc.py index bac1bca1a7..dbf2b82b5b 100644 --- a/bin/SConsDoc.py +++ b/bin/SConsDoc.py @@ -3,7 +3,9 @@ # SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org) # SPDX-License-Identifier: MIT -"""Module for handling SCons documentation processing. +""" +SCons Documentation Processing module +===================================== This module parses home-brew XML files that document important SCons components. Currently it handles Builders, Environment functions/methods, @@ -18,6 +20,8 @@ Builder example: +.. code-block:: xml + This is the summary description of an SCons Builder. @@ -29,7 +33,7 @@ interpolated by specifying the &b-link-BUILDER; element. - Unlike normal XML, blank lines are significant in these + Unlike vanilla DocBook, blank lines are significant in these descriptions and serve to separate paragraphs. They'll get replaced in DocBook output with appropriate tags to indicate a new paragraph. @@ -42,6 +46,8 @@ Function example: +.. code-block:: xml + (arg1, arg2, key=value) @@ -74,6 +80,8 @@ Construction variable example: +.. code-block:: xml + This is the summary description of a construction variable. @@ -93,6 +101,8 @@ Tool example: +.. code-block:: xml + This is the summary description of an SCons Tool. @@ -223,7 +233,7 @@ def addEntity(self, name, uri): self.entries.append(DoctypeEntity(name, uri)) def createDoctype(self): - content = '\n' @@ -318,7 +328,7 @@ def prettyPrintFile(fpath): @staticmethod def decorateWithHeader(root): - root.attrib["{"+xsi+"}schemaLocation"] = "%s %s/scons.xsd" % (dbxsd, dbxsd) + root.attrib["{"+xsi+"}schemaLocation"] = f"{dbxsd} {dbxsd}/scons.xsd" return root def newXmlTree(self, root): @@ -340,22 +350,22 @@ def validateXml(fpath, xmlschema_context): try: doc = etree.parse(fpath) except Exception as e: - print("ERROR: %s fails to parse:"%fpath) + print(f"ERROR: {fpath} fails to parse:") print(e) return False doc.xinclude() try: TreeFactory.xmlschema.assertValid(doc) except etree.XMLSchemaValidateError as e: - print("ERROR: %s fails to validate:" % fpath) + print(f"ERROR: {fpath} fails to validate:") print(e) print(e.error_log.last_error.message) - print("In file: [%s]" % e.error_log.last_error.filename) + print(f"In file: [{e.error_log.last_error.filename}]") print("Line : %d" % e.error_log.last_error.line) return False except Exception as e: - print("ERROR: %s fails to validate:" % fpath) + print(f"ERROR: {fpath} fails to validate:") print(e) return False @@ -365,14 +375,14 @@ def validateXml(fpath, xmlschema_context): def findAll(root, tag, ns=None, xp_ctxt=None, nsmap=None): expression = ".//{%s}%s" % (nsmap[ns], tag) if not ns or not nsmap: - expression = ".//%s" % tag + expression = f".//{tag}" return root.findall(expression) @staticmethod def findAllChildrenOf(root, tag, ns=None, xp_ctxt=None, nsmap=None): expression = "./{%s}%s/*" % (nsmap[ns], tag) if not ns or not nsmap: - expression = "./%s/*" % tag + expression = f"./{tag}/*" return root.findall(expression) @staticmethod @@ -495,7 +505,7 @@ def __str__(self): result = [] for m in re.findall(r'([a-zA-Z/_]+|[^a-zA-Z/_]+)', s): if ' ' in m: - m = '"%s"' % m + m = f'"{m}"' result.append(m) return ' '.join(result) def append(self, data): diff --git a/bin/SConsExamples.py b/bin/SConsExamples.py index 6f57085126..90205fef27 100644 --- a/bin/SConsExamples.py +++ b/bin/SConsExamples.py @@ -1,90 +1,136 @@ -# !/usr/bin/env python +#!/usr/bin/env python # -# Copyright (c) 2010 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. +# SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org) +# SPDX-License-Identifier: MIT -# -# -# This script looks for some XML tags that describe SCons example -# configurations and commands to execute in those configurations, and -# uses TestCmd.py to execute the commands and insert the output from -# those commands into the XML that we output. This way, we can run a -# script and update all of our example documentation output without -# a lot of laborious by-hand checking. -# -# An "SCons example" looks like this, and essentially describes a set of -# input files (program source files as well as SConscript files): -# -# -# -# env = Environment() -# env.Program('foo') -# -# -# int main(void) { printf("foo.c\n"); } -# -# -# -# The contents within the tag will get written -# into a temporary directory whenever example output needs to be -# generated. By default, the contents are not inserted into text -# directly, unless you set the "printme" attribute on one or more files, -# in which case they will get inserted within a tag. -# This makes it easy to define the example at the appropriate -# point in the text where you intend to show the SConstruct file. -# -# Note that you should usually give the a "name" -# attribute so that you can refer to the example configuration later to -# run SCons and generate output. -# -# If you just want to show a file's contents without worry about running -# SCons, there's a shorter tag: -# -# -# env = Environment() -# env.Program('foo') -# -# -# This is essentially equivalent to , -# but it's more straightforward. -# -# SCons output is generated from the following sort of tag: -# -# -# scons -Q foo -# scons -Q foo -# -# -# You tell it which example to use with the "example" attribute, and then -# give it a list of tags to execute. You can also -# supply an "os" tag, which specifies the type of operating system this -# example is intended to show; if you omit this, default value is "posix". -# -# The generated XML will show the command line (with the appropriate -# command-line prompt for the operating system), execute the command in -# a temporary directory with the example files, capture the standard -# output from SCons, and insert it into the text as appropriate. -# Error output gets passed through to your error output so you -# can see if there are any problems executing the command. -# +""" +SCons Example Generator +======================= + +Generate example outputs for the SCons documentation (primarily +the User Guide) by processing custom XML tags that describe example SCons +projects. The generator automates tasks that would otherwise require +considerable manual effort: verifying that example projects run correctly, +and capturing their output for documentation. Conceptually this is a +bit like Python ``doctest``, but file-based rather than snippet-based. + +An example consists of three parts: + +#. Project files (``scons_example`` tag) +#. Build commands (``scons_output_command`` tag) +#. Generated output (``scons_output`` tag) + +Here's a minimal example project: + +.. code-block:: xml + + + + env = Environment() + env.Program('foo') + + + int main(void) { printf("foo.c\\n"); } + + + +The example project's ``name`` attribute provides a handle for later +associating the output with this project. + +Each ``file`` tag describes the contents of a file to be +created when setting up the project, and each ``directory`` tag +describes a directory to be created. +Both take a ``name`` attribute. +The optional ``chmod`` attribute can be used if the created file +or directory needs something other than default permissions, +usually to make a file executable. +The ``printme`` attribute indicates whether a file's contents +should be shown in the documentation. +Any file with a ``printme`` value of ``1`` will have contents +generated for inclusion; ``SConstruct`` and other SConscript +files will normally set this. +The default is to not show, since often the contents of the +source files is needed to make the build work, +but is not often not that interesting when illustrating an SCons concept. + +To show just an SConstruct file, use the shorthand ``sconstruct`` tag: + +.. code-block:: xml + + + env = Environment() + env.Program('foo') + + +This is equivalent to: + +.. code-block:: xml + + + + ...contents... + + + +The ``scons_example_file`` tag allows you to display the contents +of a file outside the context of its definition in ``scons_example``. +This looks like: + +.. code-block:: xml + + + +Link an example project to its output using the ``scons_output`` tag: + +.. code-block:: xml + + + scons -Q foo + + +The ``example`` attribute associates the project of that name with the output. +The ``os`` attribute can be used to indicate a specific platform +(for example, to display a suitable shell prompt). The default is ``posix``. +The ``suffix`` attribute allows tracking outputs from multiple +ways of running a project. The optional ``tools`` attribute gives a +non-default tool list for this run. + +An ``scons_output_command`` tag inside an ``scons_output`` contains +the instructions to build the project. There can be several build +commands defined for a given example project: + +.. code-block:: xml + + + scons -Q foo + + + scons -Q --option foo + + +The command's ``environment`` attribute can be used to set environment +variables before the command is run. The ``output`` attribute can be +used to emit commentary in the output display that is not produced +by the command itself, for example: + +.. code-block:: xml + + edit hello.h + +The actual command text has some special recognized values: + +* ``scons`` - to run scons +* ``touch`` - to update the file change time +* ``edit`` - to change a file's contents without changing its behavior (works for C / C++) +* ``ls`` - generate a directory listing +* ``sleep`` - delay for a while + +The generator will: +1. Show the OS-appropriate command prompt +2. Execute the command in a temporary directory +3. Capture SCons standard output for the documentation +4. Pass through error output for troubleshooting +""" import os import re @@ -286,7 +332,7 @@ def ensureExampleOutputsExist(dpath): key + '_' + r.name.replace("/", "_")) # Write file with open(fpath, 'w') as f: - f.write("%s\n" % content) + f.write(f"{content}\n") perc = "%" @@ -320,7 +366,7 @@ def createAllExampleOutputs(dpath): key + '_' + r.name.replace("/", "_")) # Write file with open(fpath, 'w') as f: - f.write("%s\n" % content) + f.write(f"{content}\n") idx += 1 def collectSConsExampleNames(fpath): @@ -345,7 +391,7 @@ def collectSConsExampleNames(fpath): if n not in suffixes: suffixes[n] = [] else: - print("Error: Example in file '%s' is missing a name!" % fpath) + print(f"Error: Example in file '{fpath}' is missing a name.") failed_suffixes = True for o in stf.findAll(t.root, "scons_output", SConsDoc.dbxid, @@ -354,11 +400,11 @@ def collectSConsExampleNames(fpath): if stf.hasAttribute(o, 'example'): n = stf.getAttribute(o, 'example') else: - print("Error: scons_output in file '%s' is missing an example name!" % fpath) + print(f"Error: scons_output in file '{fpath}' is missing an example name.") failed_suffixes = True if n not in suffixes: - print("Error: scons_output in file '%s' is referencing non-existent example '%s'!" % (fpath, n)) + print(f"Error: scons_output in file '{fpath}' is referencing non-existent example '{n}'.") failed_suffixes = True continue @@ -366,13 +412,13 @@ def collectSConsExampleNames(fpath): if stf.hasAttribute(o, 'suffix'): s = stf.getAttribute(o, 'suffix') else: - print("Error: scons_output in file '%s' (example '%s') is missing a suffix!" % (fpath, n)) + print(f"Error: scons_output in file '{fpath}' (example '{n}') is missing a suffix.") failed_suffixes = True if s not in suffixes[n]: suffixes[n].append(s) else: - print("Error: scons_output in file '%s' (example '%s') is using a duplicate suffix '%s'!" % (fpath, n, s)) + print(f"Error: scons_output in file '{fpath}' (example '{n}') is using a duplicate suffix '{s}'.") failed_suffixes = True return names, failed_suffixes @@ -393,7 +439,7 @@ def exampleNamesAreUnique(dpath): unique = False i = allnames.intersection(names) if i: - print("Not unique in %s are: %s" % (fpath, ', '.join(i))) + print(f"Not unique in {fpath} are: {', '.join(i)}") unique = False allnames |= names From bed850317cf52ddbbefdbbdcb4ef3714636e8753 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 9 Apr 2025 15:06:47 -0600 Subject: [PATCH 300/386] Switch tu using Python ternary op Python 2.5 introduced a better approach than "c and x or y" which is less error-prone, and, these days, more familiar to readers. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 +++ RELEASE.txt | 4 ++++ SCons/Builder.py | 2 +- SCons/Script/Main.py | 15 ++++++++++++--- SCons/Tool/linkCommon/SharedLibrary.py | 2 +- SCons/Tool/msvc.py | 8 ++++---- bin/scons-diff.py | 4 +++- bin/update-release-info.py | 2 +- site_scons/BuildCommandLine.py | 2 +- test/Actions/function.py | 2 +- test/SPAWN.py | 17 ++++++++++------- test/option/help-options.py | 6 +++--- testing/framework/TestCmd.py | 2 +- testing/framework/TestCommon.py | 12 ++++++------ 14 files changed, 51 insertions(+), 30 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ca52cc4a7b..2d6a97b921 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -27,6 +27,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - runtest.py once again finds "external" tests, such as the tests for tools in scons-contrib. An earlier rework had broken this. Fixes #4699. - Clarify how pre/post actions on an alias work. + - Replace use of old conditional expression idioms with the official + one from PEP 308 introduced in Python 2.5 (2006). The idiom being + replaced (using and/or) is regarded as error prone. RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 2219bf7866..24d4a698fe 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -31,6 +31,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - Nodes are now treated as PathLike objects. +- Replace use of old conditional expression idioms with the official + one from PEP 308 introduced in Python 2.5 (2006). The idiom being + replaced (using and/or) is regarded as error prone. + FIXES ----- diff --git a/SCons/Builder.py b/SCons/Builder.py index 870596f0c7..0e8d337521 100644 --- a/SCons/Builder.py +++ b/SCons/Builder.py @@ -701,7 +701,7 @@ def set_src_suffix(self, src_suffix) -> None: src_suffix = [] elif not SCons.Util.is_List(src_suffix): src_suffix = [ src_suffix ] - self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix] + self.src_suffix = [suf if callable(suf) else self.adjust_suffix(suf) for suf in src_suffix] def get_src_suffix(self, env): """Get the first src_suffix in the list of src_suffixes.""" diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 79cf8321b2..c1335cc4b9 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -465,19 +465,28 @@ def __init__(self, derived: bool=False, prune: bool=False, status: bool=False, s self.prune = prune self.status = status self.sLineDraw = sLineDraw + def get_all_children(self, node): return node.all_children() + def get_derived_children(self, node): children = node.all_children(None) return [x for x in children if x.has_builder()] + def display(self, t) -> None: if self.derived: func = self.get_derived_children else: func = self.get_all_children - s = self.status and 2 or 0 - SCons.Util.print_tree(t, func, prune=self.prune, showtags=s, lastChild=True, singleLineDraw=self.sLineDraw) - + s = 2 if self.status else 0 + SCons.Util.print_tree( + t, + func, + prune=self.prune, + showtags=s, + lastChild=True, + singleLineDraw=self.sLineDraw, + ) def python_version_string(): return sys.version.split()[0] diff --git a/SCons/Tool/linkCommon/SharedLibrary.py b/SCons/Tool/linkCommon/SharedLibrary.py index 30170f8bb8..04de7fac02 100644 --- a/SCons/Tool/linkCommon/SharedLibrary.py +++ b/SCons/Tool/linkCommon/SharedLibrary.py @@ -202,7 +202,7 @@ def setup_shared_lib_logic(env) -> None: # Note this is gnu style env["SHLIBSONAMEFLAGS"] = "-Wl,-soname=$_SHLIBSONAME" - env["_SHLIBVERSION"] = "${SHLIBVERSION and '.'+SHLIBVERSION or ''}" + env["_SHLIBVERSION"] = "${'.' + SHLIBVERSION if SHLIBVERSION else ''}" env["_SHLIBVERSIONFLAGS"] = "$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLIBSONAME" env["SHLIBEMITTER"] = [lib_emitter, shlib_symlink_emitter] diff --git a/SCons/Tool/msvc.py b/SCons/Tool/msvc.py index b823752b50..e73869bc4e 100644 --- a/SCons/Tool/msvc.py +++ b/SCons/Tool/msvc.py @@ -72,12 +72,12 @@ def msvc_set_PCHPDBFLAGS(env) -> None: if env.get('MSVC_VERSION',False): maj, min = msvc_version_to_maj_min(env['MSVC_VERSION']) if maj < 8: - env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}']) + env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${"/Yd" if PDB else ""}']) else: env['PCHPDBFLAGS'] = '' else: # Default if we can't determine which version of MSVC we're using - env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}']) + env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${"/Yd" if PDB else ""}']) def pch_emitter(target, source, env): @@ -143,7 +143,7 @@ def gen_ccpchflags(env, target, source, for_signature): pch_node = get_pch_node(env, target, source) if not pch_node: return '' - + return SCons.Util.CLVar(["/Yu$PCHSTOP", "/Fp%s" % pch_node]) pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR') @@ -256,7 +256,7 @@ def generate(env) -> None: static_obj.add_emitter(suffix, static_object_emitter) shared_obj.add_emitter(suffix, shared_object_emitter) - env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}']) + env['CCPDBFLAGS'] = SCons.Util.CLVar(['${"/Z7" if PDB else ""}']) env['CCPCHFLAGS'] = gen_ccpchflags env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS' diff --git a/bin/scons-diff.py b/bin/scons-diff.py index b1b2eec4ed..a60337afeb 100644 --- a/bin/scons-diff.py +++ b/bin/scons-diff.py @@ -90,8 +90,10 @@ def simple_diff(a, b, fromfile='', tofile='', output like the simple, unadorned 'diff" command. """ sm = difflib.SequenceMatcher(None, a, b) + def comma(x1, x2): - return x1+1 == x2 and str(x2) or '%s,%s' % (x1+1, x2) + return x1 + 1 == str(x2) if x2 else '%s,%s' % (x1 + 1, x2) + result = [] for op, a1, a2, b1, b2 in sm.get_opcodes(): if op == 'delete': diff --git a/bin/update-release-info.py b/bin/update-release-info.py index c911fca38f..4f7cf40035 100644 --- a/bin/update-release-info.py +++ b/bin/update-release-info.py @@ -173,7 +173,7 @@ def set_new_date(self): Mon, 05 Jun 2010 21:17:15 -0700 NEW DATE WILL BE INSERTED HERE """ - min = (time.daylight and time.altzone or time.timezone) // 60 + min = (time.altzone if time.daylight else time.timezone) // 60 hr = min // 60 min = -(min % 60 + hr * 100) self.new_date = (time.strftime('%a, %d %b %Y %X', self.release_date + (0, 0, 0)) diff --git a/site_scons/BuildCommandLine.py b/site_scons/BuildCommandLine.py index 5d00f6eb8e..41d4cf68c7 100644 --- a/site_scons/BuildCommandLine.py +++ b/site_scons/BuildCommandLine.py @@ -104,7 +104,7 @@ def set_date(self): NEW DATE WILL BE INSERTED HERE """ - min = (time.daylight and time.altzone or time.timezone) // 60 + min = (time.altzone if time.daylight else time.timezone) // 60 hr = min // 60 min = -(min % 60 + hr * 100) # TODO: is it better to take the date of last rev? Externally: diff --git a/test/Actions/function.py b/test/Actions/function.py index fd5d5f0257..9b584d807e 100644 --- a/test/Actions/function.py +++ b/test/Actions/function.py @@ -122,7 +122,7 @@ def foo(b=b): def runtest(arguments, expectedOutFile, expectedRebuild=True, stderr=""): test.run( arguments=arguments, - stdout=expectedRebuild and rebuildstr or nobuildstr, + stdout=rebuildstr if expectedRebuild else nobuildstr, stderr="", ) diff --git a/test/SPAWN.py b/test/SPAWN.py index ca9d3448d0..5939f4fca2 100644 --- a/test/SPAWN.py +++ b/test/SPAWN.py @@ -42,7 +42,7 @@ ofp.write(ifp.read()) """) -test.write('SConstruct', """ +test.write('SConstruct', """\ import subprocess import sys @@ -56,16 +56,19 @@ def my_spawn2(sh, escape, cmd, args, env): cp = subprocess.run(s, shell=True) return cp.returncode -env = Environment(MY_SPAWN1 = my_spawn1, - MY_SPAWN2 = my_spawn2, - COMMAND = r'%(_python_)s cat.py $TARGET $SOURCES') -env1 = env.Clone(SPAWN = my_spawn1) +DefaultEnvironment() +env = Environment( + MY_SPAWN1=my_spawn1, + MY_SPAWN2=my_spawn2, + COMMAND=r'%(_python_)s cat.py $TARGET $SOURCES', +) +env1 = env.Clone(SPAWN=my_spawn1) env1.Command('file1.out', 'file1.in', '$COMMAND') -env2 = env.Clone(SPAWN = '$MY_SPAWN2') +env2 = env.Clone(SPAWN='$MY_SPAWN2') env2.Command('file2.out', 'file2.in', '$COMMAND') -env3 = env.Clone(SPAWN = '${USE_TWO and MY_SPAWN2 or MY_SPAWN1}') +env3 = env.Clone(SPAWN='${MY_SPAWN2 if USE_TWO else MY_SPAWN1}') env3.Command('file3.out', 'file3.in', '$COMMAND', USE_TWO=0) env3.Command('file4.out', 'file4.in', '$COMMAND', USE_TWO=1) """ % locals()) diff --git a/test/option/help-options.py b/test/option/help-options.py index e430572a7f..c84961f83d 100644 --- a/test/option/help-options.py +++ b/test/option/help-options.py @@ -54,9 +54,9 @@ lines = stdout.split('\n') lines = [x for x in lines if x[:3] == ' -'] lines = [x[3:] for x in lines] -lines = [x[0] == '-' and x[1:] or x for x in lines] +lines = [x[1:] if x.startswith('-') else x for x in lines] options = [x.split()[0] for x in lines] -options = [x[-1] == ',' and x[:-1] or x for x in options] +options = [x[:-1] if x.endswith(',') else x for x in options] lowered = [x.lower() for x in options] ordered = sorted(lowered) if lowered != ordered: @@ -65,7 +65,7 @@ test.fail_test() test.pass_test() - + # Local Variables: # tab-width:4 diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 059ad608b6..6ac1275fc3 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -699,7 +699,7 @@ def simple_diff( sm = difflib.SequenceMatcher(None, a, b) def comma(x1, x2): - return x1 + 1 == x2 and str(x2) or f'{x1 + 1},{x2}' + return str(x2) if x1 + 1 == x2 else f'{x1 + 1},{x2}' for op, a1, a2, b1, b2 in sm.get_opcodes(): if op == 'delete': diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index 2cd3c490af..9a0cb8beb4 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -321,7 +321,7 @@ def must_be_writable(self, *files) -> None: them. Exits FAILED if any of the files does not exist or is not writable. """ - flist = [is_List(x) and os.path.join(*x) or x for x in files] + flist = [os.path.join(*x) if is_List(x) else x for x in files] existing, missing = separate_files(flist) unwritable = [x for x in existing if not is_writable(x)] if missing: @@ -531,7 +531,7 @@ def must_exist(self, *files, message: str = "") -> None: pathname will be constructed by concatenating them. Exits FAILED if any of the files does not exist. """ - flist = [is_List(x) and os.path.join(*x) or x for x in files] + flist = [os.path.join(*x) if is_List(x) else x for x in files] missing = [x for x in flist if not os.path.exists(x) and not os.path.islink(x)] if missing: print("Missing files: `%s'" % "', `".join(missing)) @@ -550,7 +550,7 @@ def must_exist_one_of(self, files, message: str = "") -> None: if is_List(x) or is_Tuple(x): xpath = os.path.join(*x) else: - xpath = is_Sequence(x) and os.path.join(x) or x + xpath = os.path.join(x) if is_Sequence(x) else x if glob.glob(xpath): return missing.append(xpath) @@ -669,7 +669,7 @@ def must_not_exist(self, *files) -> None: which case the pathname will be constructed by concatenating them. Exits FAILED if any of the files exists. """ - flist = [is_List(x) and os.path.join(*x) or x for x in files] + flist = [os.path.join(*x) if is_List(x) else x for x in files] existing = [x for x in flist if os.path.exists(x) or os.path.islink(x)] if existing: print("Unexpected files exist: `%s'" % "', `".join(existing)) @@ -688,7 +688,7 @@ def must_not_exist_any_of(self, files) -> None: if is_List(x) or is_Tuple(x): xpath = os.path.join(*x) else: - xpath = is_Sequence(x) and os.path.join(x) or x + xpath = os.path.join(x) if is_Sequence(x) else x if glob.glob(xpath): existing.append(xpath) if existing: @@ -719,7 +719,7 @@ def must_not_be_writable(self, *files) -> None: them. Exits FAILED if any of the files does not exist or is writable. """ - flist = [is_List(x) and os.path.join(*x) or x for x in files] + flist = [os.path.join(*x) if is_List(x) else x for x in files] existing, missing = separate_files(flist) writable = [file for file in existing if is_writable(file)] if missing: From 0ec83af0612eb0da6dd86bf71587d97d4e0cdcd6 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 7 Mar 2025 10:40:42 -0700 Subject: [PATCH 301/386] Update Fortran FLAGS tests Tests contain both are split into a flags-handling-only section using a mocked tool, and a live section using a real compiler, in a new file. General cleanup. Added shared-object-flags tests for two dialects that didn't have one. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 + test/Fortran/F03FLAGS-live.py | 95 ++++++++++++++++++++ test/Fortran/F03FLAGS.py | 111 ++++++++---------------- test/Fortran/F08FLAGS-live.py | 93 ++++++++++++++++++++ test/Fortran/F08FLAGS.py | 101 +++++++-------------- test/Fortran/F77FLAGS-live.py | 93 ++++++++++++++++++++ test/Fortran/F77FLAGS.py | 83 +++++------------- test/Fortran/F90FLAGS-live.py | 89 +++++++++++++++++++ test/Fortran/F90FLAGS.py | 103 ++++++++-------------- test/Fortran/F95FLAGS-live.py | 95 ++++++++++++++++++++ test/Fortran/F95FLAGS.py | 69 +++------------ test/Fortran/FORTRANCOMMONFLAGS-live.py | 90 +++++++++++++++++++ test/Fortran/FORTRANCOMMONFLAGS.py | 54 ++---------- test/Fortran/FORTRANFLAGS-live.py | 96 ++++++++++++++++++++ test/Fortran/FORTRANFLAGS.py | 94 +++++--------------- test/Fortran/SHF03FLAGS-live.py | 93 ++++++++++++++++++++ test/Fortran/SHF03FLAGS.py | 91 +++++++++++++++++++ test/Fortran/SHF08FLAGS-live.py | 93 ++++++++++++++++++++ test/Fortran/SHF08FLAGS.py | 91 +++++++++++++++++++ test/Fortran/SHF77FLAGS-live.py | 95 ++++++++++++++++++++ test/Fortran/SHF77FLAGS.py | 76 ++++------------ test/Fortran/SHF90FLAGS-live.py | 93 ++++++++++++++++++++ test/Fortran/SHF90FLAGS.py | 104 ++++++++-------------- test/Fortran/SHF95FLAGS-live.py | 93 ++++++++++++++++++++ test/Fortran/SHF95FLAGS.py | 72 ++++----------- test/Fortran/SHFORTRANFLAGS-live.py | 91 +++++++++++++++++++ test/Fortran/SHFORTRANFLAGS.py | 88 +++++-------------- 27 files changed, 1651 insertions(+), 697 deletions(-) create mode 100644 test/Fortran/F03FLAGS-live.py create mode 100644 test/Fortran/F08FLAGS-live.py create mode 100644 test/Fortran/F77FLAGS-live.py create mode 100644 test/Fortran/F90FLAGS-live.py create mode 100644 test/Fortran/F95FLAGS-live.py create mode 100644 test/Fortran/FORTRANCOMMONFLAGS-live.py create mode 100644 test/Fortran/FORTRANFLAGS-live.py create mode 100644 test/Fortran/SHF03FLAGS-live.py create mode 100644 test/Fortran/SHF03FLAGS.py create mode 100644 test/Fortran/SHF08FLAGS-live.py create mode 100644 test/Fortran/SHF08FLAGS.py create mode 100644 test/Fortran/SHF77FLAGS-live.py create mode 100644 test/Fortran/SHF90FLAGS-live.py create mode 100644 test/Fortran/SHF95FLAGS-live.py create mode 100644 test/Fortran/SHFORTRANFLAGS-live.py diff --git a/CHANGES.txt b/CHANGES.txt index ca52cc4a7b..8ed5f80ab5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -27,6 +27,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - runtest.py once again finds "external" tests, such as the tests for tools in scons-contrib. An earlier rework had broken this. Fixes #4699. - Clarify how pre/post actions on an alias work. + - Rearrange Fortran e2e tests - "live" tests now live in a separate + test file and don't share with flags-only mocked tests. RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/test/Fortran/F03FLAGS-live.py b/test/Fortran/F03FLAGS-live.py new file mode 100644 index 0000000000..e96e673131 --- /dev/null +++ b/test/Fortran/F03FLAGS-live.py @@ -0,0 +1,95 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable, using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() +_exe = TestSCons._exe + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f03' +if not test.detect_tool(fc): + # gfortran names all variants the same, try it too + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find an f03 tool; skipping test.\n') + +test.subdir('x') +test.write(['x', 'dummy.i'], """\ +# Exists only such that -Ix finds the directory... +""") +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +foo = Environment(F03='%(fc)s') +f03 = foo.Dictionary('F03') +bar = foo.Clone(F03=r'%(_python_)s wrapper.py ' + f03, F03FLAGS='-Ix') +foo.Program(target='foo', source='foo.f03') +bar.Program(target='bar', source='bar.f03') +""" % locals()) + +test.write('foo.f03', r""" + PROGRAM FOO + PRINT *,'foo.f03' + STOP + END +""") + +test.write('bar.f03', r""" + PROGRAM BAR + PRINT *,'bar.f03' + STOP + END +""") + +test.run(arguments='foo' + _exe, stderr=None) +test.run(program=test.workpath('foo'), stdout=" foo.f03\n") +test.must_not_exist('wrapper.out') + +if sys.platform[:5] == 'sunos': + test.run(arguments='bar' + _exe, stderr=None) +else: + test.run(arguments='bar' + _exe) +test.run(program=test.workpath('bar'), stdout=" bar.f03\n") +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/F03FLAGS.py b/test/Fortran/F03FLAGS.py index 63d1fcb59c..f527d4efad 100644 --- a/test/Fortran/F03FLAGS.py +++ b/test/Fortran/F03FLAGS.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,11 @@ # 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__" +""" +Test handling of the dialect-specific FLAGS variable, +using a mocked compiler. +""" import TestSCons @@ -31,40 +35,45 @@ test = TestSCons.TestSCons() _exe = TestSCons._exe +# ref: test/fixture/mylink.py test.file_fixture('mylink.py') +# ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ -env = Environment(LINK = r'%(_python_)s mylink.py', - LINKFLAGS = [], - F03 = r'%(_python_)s myfortran_flags.py g03', - F03FLAGS = '-x', - FORTRAN = r'%(_python_)s myfortran_flags.py fortran', - FORTRANFLAGS = '-y') -env.Program(target = 'test01', source = 'test01.f') -env.Program(target = 'test02', source = 'test02.F') -env.Program(target = 'test03', source = 'test03.for') -env.Program(target = 'test04', source = 'test04.FOR') -env.Program(target = 'test05', source = 'test05.ftn') -env.Program(target = 'test06', source = 'test06.FTN') -env.Program(target = 'test07', source = 'test07.fpp') -env.Program(target = 'test08', source = 'test08.FPP') -env.Program(target = 'test13', source = 'test13.f03') -env.Program(target = 'test14', source = 'test14.F03') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment( + LINK=r"%(_python_)s mylink.py", + LINKFLAGS=[], + F03=r"%(_python_)s myfortran_flags.py g03", + F03FLAGS="-x", + FORTRAN=r"%(_python_)s myfortran_flags.py fortran", + FORTRANFLAGS="-y", +) +env.Program(target="test01", source="test01.f") +env.Program(target="test02", source="test02.F") +env.Program(target="test03", source="test03.for") +env.Program(target="test04", source="test04.FOR") +env.Program(target="test05", source="test05.ftn") +env.Program(target="test06", source="test06.FTN") +env.Program(target="test07", source="test07.fpp") +env.Program(target="test08", source="test08.FPP") +env.Program(target="test09", source="test09.f03") +env.Program(target="test10", source="test10.F03") """ % locals()) -test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") -test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") +test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") test.write('test03.for', "This is a .for file.\n#link\n#fortran\n") test.write('test04.FOR', "This is a .FOR file.\n#link\n#fortran\n") test.write('test05.ftn', "This is a .ftn file.\n#link\n#fortran\n") test.write('test06.FTN', "This is a .FTN file.\n#link\n#fortran\n") test.write('test07.fpp', "This is a .fpp file.\n#link\n#fortran\n") test.write('test08.FPP', "This is a .FPP file.\n#link\n#fortran\n") -test.write('test13.f03', "This is a .f03 file.\n#link\n#g03\n") -test.write('test14.F03', "This is a .F03 file.\n#link\n#g03\n") +test.write('test09.f03', "This is a .f03 file.\n#link\n#g03\n") +test.write('test10.F03', "This is a .F03 file.\n#link\n#g03\n") -test.run(arguments = '.', stderr = None) +test.run(arguments='.', stderr=None) test.must_match('test01' + _exe, " -c -y\nThis is a .f file.\n") test.must_match('test02' + _exe, " -c -y\nThis is a .F file.\n") @@ -74,56 +83,8 @@ test.must_match('test06' + _exe, " -c -y\nThis is a .FTN file.\n") test.must_match('test07' + _exe, " -c -y\nThis is a .fpp file.\n") test.must_match('test08' + _exe, " -c -y\nThis is a .FPP file.\n") -test.must_match('test13' + _exe, " -c -x\nThis is a .f03 file.\n") -test.must_match('test14' + _exe, " -c -x\nThis is a .F03 file.\n") - - -fc = 'f03' -g03 = test.detect_tool(fc) - - -if g03: - - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(F03 = '%(fc)s') -f03 = foo.Dictionary('F03') -bar = foo.Clone(F03 = r'%(_python_)s wrapper.py ' + f03, F03FLAGS = '-Ix') -foo.Program(target = 'foo', source = 'foo.f03') -bar.Program(target = 'bar', source = 'bar.f03') -""" % locals()) - - test.write('foo.f03', r""" - PROGRAM FOO - PRINT *,'foo.f03' - STOP - END -""") - - test.write('bar.f03', r""" - PROGRAM BAR - PRINT *,'bar.f03' - STOP - END -""") - - - test.run(arguments = 'foo' + _exe, stderr = None) - - test.run(program = test.workpath('foo'), stdout = " foo.f03\n") - - test.must_not_exist('wrapper.out') - - import sys - if sys.platform[:5] == 'sunos': - test.run(arguments = 'bar' + _exe, stderr = None) - else: - test.run(arguments = 'bar' + _exe) - - test.run(program = test.workpath('bar'), stdout = " bar.f03\n") - - test.must_match('wrapper.out', "wrapper.py\n") +test.must_match('test09' + _exe, " -c -x\nThis is a .f03 file.\n") +test.must_match('test10' + _exe, " -c -x\nThis is a .F03 file.\n") test.pass_test() diff --git a/test/Fortran/F08FLAGS-live.py b/test/Fortran/F08FLAGS-live.py new file mode 100644 index 0000000000..20b9dc4f3a --- /dev/null +++ b/test/Fortran/F08FLAGS-live.py @@ -0,0 +1,93 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable, using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() +_exe = TestSCons._exe + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f08' +if not test.detect_tool(fc): + # gfortran names all variants the same, try it too + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find an f08 tool; skipping test.\n') + +test.subdir('x') +test.write(['x', 'dummy.i'], """\ +# Exists only such that -Ix finds the directory... +""") +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +foo = Environment(F08='%(fc)s') +f08 = foo.Dictionary('F08') +bar = foo.Clone(F08=r'%(_python_)s wrapper.py ' + f08, F08FLAGS='-Ix') +foo.Program(target='foo', source='foo.f08') +bar.Program(target='bar', source='bar.f08') +""" % locals()) + +test.write('foo.f08', r""" + PROGRAM FOO + PRINT *,'foo.f08' + END PROGRAM FOO +""") + +test.write('bar.f08', r""" + PROGRAM BAR + PRINT *,'bar.f08' + END PROGRAM BAR +""") + +test.run(arguments='foo' + _exe, stderr=None) +test.run(program=test.workpath('foo'), stdout=" foo.f08\n") +test.must_not_exist('wrapper.out') + +if sys.platform[:5] == 'sunos': + test.run(arguments='bar' + _exe, stderr=None) +else: + test.run(arguments='bar' + _exe) +test.run(program=test.workpath('bar'), stdout=" bar.f08\n") +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/F08FLAGS.py b/test/Fortran/F08FLAGS.py index 81ae44174c..a42e163fe9 100644 --- a/test/Fortran/F08FLAGS.py +++ b/test/Fortran/F08FLAGS.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,11 @@ # 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__" +""" +Test handling of the dialect-specific FLAGS variable, +using a mocked compiler. +""" import TestSCons @@ -31,30 +35,35 @@ test = TestSCons.TestSCons() _exe = TestSCons._exe +# ref: test/fixture/mylink.py test.file_fixture('mylink.py') +# ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ -env = Environment(LINK = r'%(_python_)s mylink.py', - LINKFLAGS = [], - F08 = r'%(_python_)s myfortran_flags.py g08', - F08FLAGS = '-x', - FORTRAN = r'%(_python_)s myfortran_flags.py fortran', - FORTRANFLAGS = '-y') -env.Program(target = 'test01', source = 'test01.f') -env.Program(target = 'test02', source = 'test02.F') -env.Program(target = 'test03', source = 'test03.for') -env.Program(target = 'test04', source = 'test04.FOR') -env.Program(target = 'test05', source = 'test05.ftn') -env.Program(target = 'test06', source = 'test06.FTN') -env.Program(target = 'test07', source = 'test07.fpp') -env.Program(target = 'test08', source = 'test08.FPP') -env.Program(target = 'test09', source = 'test09.f08') -env.Program(target = 'test10', source = 'test10.F08') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment( + LINK=r'%(_python_)s mylink.py', + LINKFLAGS=[], + F08=r'%(_python_)s myfortran_flags.py g08', + F08FLAGS='-x', + FORTRAN=r'%(_python_)s myfortran_flags.py fortran', + FORTRANFLAGS='-y', +) +env.Program(target='test01', source='test01.f') +env.Program(target='test02', source='test02.F') +env.Program(target='test03', source='test03.for') +env.Program(target='test04', source='test04.FOR') +env.Program(target='test05', source='test05.ftn') +env.Program(target='test06', source='test06.FTN') +env.Program(target='test07', source='test07.fpp') +env.Program(target='test08', source='test08.FPP') +env.Program(target='test09', source='test09.f08') +env.Program(target='test10', source='test10.F08') """ % locals()) -test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") -test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") +test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") test.write('test03.for', "This is a .for file.\n#link\n#fortran\n") test.write('test04.FOR', "This is a .FOR file.\n#link\n#fortran\n") test.write('test05.ftn', "This is a .ftn file.\n#link\n#fortran\n") @@ -64,7 +73,7 @@ test.write('test09.f08', "This is a .f08 file.\n#link\n#g08\n") test.write('test10.F08', "This is a .F08 file.\n#link\n#g08\n") -test.run(arguments = '.', stderr = None) +test.run(arguments='.', stderr=None) test.must_match('test01' + _exe, " -c -y\nThis is a .f file.\n") test.must_match('test02' + _exe, " -c -y\nThis is a .F file.\n") @@ -77,52 +86,6 @@ test.must_match('test09' + _exe, " -c -x\nThis is a .f08 file.\n") test.must_match('test10' + _exe, " -c -x\nThis is a .F08 file.\n") - -fc = 'f08' -g08 = test.detect_tool(fc) - - -if g08: - - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(F08 = '%(fc)s') -f08 = foo.Dictionary('F08') -bar = foo.Clone(F08 = r'%(_python_)s wrapper.py ' + f08, F08FLAGS = '-Ix') -foo.Program(target = 'foo', source = 'foo.f08') -bar.Program(target = 'bar', source = 'bar.f08') -""" % locals()) - - test.write('foo.f08', r""" - PROGRAM FOO - PRINT *,'foo.f08' - ENDPROGRAM FOO -""") - - test.write('bar.f08', r""" - PROGRAM BAR - PRINT *,'bar.f08' - ENDPROGRAM FOO -""") - - - test.run(arguments = 'foo' + _exe, stderr = None) - - test.run(program = test.workpath('foo'), stdout = " foo.f08\n") - - test.must_not_exist('wrapper.out') - - import sys - if sys.platform[:5] == 'sunos': - test.run(arguments = 'bar' + _exe, stderr = None) - else: - test.run(arguments = 'bar' + _exe) - - test.run(program = test.workpath('bar'), stdout = " bar.f08\n") - - test.must_match('wrapper.out', "wrapper.py\n") - test.pass_test() # Local Variables: diff --git a/test/Fortran/F77FLAGS-live.py b/test/Fortran/F77FLAGS-live.py new file mode 100644 index 0000000000..14cd8c6bf9 --- /dev/null +++ b/test/Fortran/F77FLAGS-live.py @@ -0,0 +1,93 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable, using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() +_exe = TestSCons._exe + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f77' +if not test.detect_tool(fc): + # gfortran names all variants the same, try it too + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find an f77 tool; skipping test.\n') + +directory = 'x' +test.subdir(directory) +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +foo = Environment(F77='%(fc)s', tools=['default', 'f77'], F77FILESUFFIXES=[".f"]) +f77 = foo.Dictionary('F77') +bar = foo.Clone(F77=r'%(_python_)s wrapper.py ' + f77, F77FLAGS='-I%(directory)s') +foo.Program(target='foo', source='foo.f') +bar.Program(target='bar', source='bar.f') +""" % locals()) + +test.write('foo.f', r""" + PROGRAM FOO + PRINT *,'foo.f' + STOP + END +""") + +test.write('bar.f', r""" + PROGRAM BAR + PRINT *,'bar.f' + STOP + END +""") + +test.run(arguments='foo' + _exe, stderr=None) +test.run(program=test.workpath('foo'), stdout=" foo.f\n") +test.must_not_exist('wrapper.out') + +if sys.platform[:5] == 'sunos': + test.run(arguments='bar' + _exe, stderr=None) +else: + test.run(arguments='bar' + _exe) +test.run(program=test.workpath('bar'), stdout=" bar.f\n") +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/F77FLAGS.py b/test/Fortran/F77FLAGS.py index 01bf33b66c..c3c49d9b45 100644 --- a/test/Fortran/F77FLAGS.py +++ b/test/Fortran/F77FLAGS.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,11 @@ # 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__" +""" +Test handling of the dialect-specific FLAGS variable, +using a mocked compiler. +""" import TestSCons @@ -31,76 +35,29 @@ test = TestSCons.TestSCons() _exe = TestSCons._exe +# ref: test/fixture/mylink.py test.file_fixture('mylink.py') +# ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ -env = Environment(LINK = r'%(_python_)s mylink.py', - LINKFLAGS = [], - F77 = r'%(_python_)s myfortran_flags.py g77', - F77FLAGS = '-x') -env.Program(target = 'test09', source = 'test09.f77') -env.Program(target = 'test10', source = 'test10.F77') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment( + LINK=r'%(_python_)s mylink.py', + LINKFLAGS=[], + F77=r'%(_python_)s myfortran_flags.py g77', + F77FLAGS='-x', +) +env.Program(target='test09', source='test09.f77') +env.Program(target='test10', source='test10.F77') """ % locals()) test.write('test09.f77', "This is a .f77 file.\n#link\n#g77\n") test.write('test10.F77', "This is a .F77 file.\n#link\n#g77\n") - -test.run(arguments = '.', stderr = None) - +test.run(arguments='.', stderr=None) test.must_match('test09' + _exe, " -c -x\nThis is a .f77 file.\n") test.must_match('test10' + _exe, " -c -x\nThis is a .F77 file.\n") - -fc = 'f77' -g77 = test.detect_tool(fc) - -if g77: - - directory = 'x' - test.subdir(directory) - - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(F77 = '%(fc)s', tools = ['default', 'f77'], F77FILESUFFIXES = [".f"]) -f77 = foo.Dictionary('F77') -bar = foo.Clone(F77 = r'%(_python_)s wrapper.py ' + f77, F77FLAGS = '-I%(directory)s') -foo.Program(target = 'foo', source = 'foo.f') -bar.Program(target = 'bar', source = 'bar.f') -""" % locals()) - - test.write('foo.f', r""" - PROGRAM FOO - PRINT *,'foo.f' - STOP - END -""") - - test.write('bar.f', r""" - PROGRAM BAR - PRINT *,'bar.f' - STOP - END -""") - - - test.run(arguments = 'foo' + _exe, stderr = None) - - test.run(program = test.workpath('foo'), stdout = " foo.f\n") - - test.must_not_exist('wrapper.out') - - import sys - if sys.platform[:5] == 'sunos': - test.run(arguments = 'bar' + _exe, stderr = None) - else: - test.run(arguments = 'bar' + _exe) - - test.run(program = test.workpath('bar'), stdout = " bar.f\n") - - test.must_match('wrapper.out', "wrapper.py\n") - test.pass_test() # Local Variables: diff --git a/test/Fortran/F90FLAGS-live.py b/test/Fortran/F90FLAGS-live.py new file mode 100644 index 0000000000..f5491bca88 --- /dev/null +++ b/test/Fortran/F90FLAGS-live.py @@ -0,0 +1,89 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable, using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() +_exe = TestSCons._exe + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f90' +if not test.detect_tool(fc): + # gfortran names all variants the same, try it too + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find an f90 tool; skipping test.\n') + +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +foo = Environment(F90='%(fc)s') +f90 = foo.Dictionary('F90') +bar = foo.Clone(F90=r'%(_python_)s wrapper.py ' + f90) +foo.Program(target='foo', source='foo.f90') +bar.Program(target='bar', source='bar.f90') +""" % locals()) + +test.write('foo.f90', r""" + PROGRAM FOO + PRINT *,'foo.f90' + END +""") + +test.write('bar.f90', r""" + PROGRAM BAR + PRINT *,'bar.f90' + END +""") + +test.run(arguments='foo' + _exe, stderr=None) +test.run(program=test.workpath('foo'), stdout=" foo.f90\n") +test.must_not_exist('wrapper.out') + +if sys.platform[:5] == 'sunos': + test.run(arguments='bar' + _exe, stderr=None) +else: + test.run(arguments='bar' + _exe) +test.run(program=test.workpath('bar'), stdout=" bar.f90\n") +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/F90FLAGS.py b/test/Fortran/F90FLAGS.py index 10a2f35a97..5ae1828d6f 100644 --- a/test/Fortran/F90FLAGS.py +++ b/test/Fortran/F90FLAGS.py @@ -23,6 +23,11 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test handling of the dialect-specific FLAGS variable, +using a mocked compiler. +""" + import TestSCons _python_ = TestSCons._python_ @@ -30,40 +35,45 @@ test = TestSCons.TestSCons() _exe = TestSCons._exe +# ref: test/fixture/mylink.py test.file_fixture('mylink.py') +# ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ -env = Environment(LINK = r'%(_python_)s mylink.py', - LINKFLAGS = [], - F90 = r'%(_python_)s myfortran_flags.py g90', - F90FLAGS = '-x', - FORTRAN = r'%(_python_)s myfortran_flags.py fortran', - FORTRANFLAGS = '-y') -env.Program(target = 'test01', source = 'test01.f') -env.Program(target = 'test02', source = 'test02.F') -env.Program(target = 'test03', source = 'test03.for') -env.Program(target = 'test04', source = 'test04.FOR') -env.Program(target = 'test05', source = 'test05.ftn') -env.Program(target = 'test06', source = 'test06.FTN') -env.Program(target = 'test07', source = 'test07.fpp') -env.Program(target = 'test08', source = 'test08.FPP') -env.Program(target = 'test11', source = 'test11.f90') -env.Program(target = 'test12', source = 'test12.F90') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment( + LINK=r'%(_python_)s mylink.py', + LINKFLAGS=[], + F90=r'%(_python_)s myfortran_flags.py g90', + F90FLAGS='-x', + FORTRAN=r'%(_python_)s myfortran_flags.py fortran', + FORTRANFLAGS='-y', +) +env.Program(target='test01', source='test01.f') +env.Program(target='test02', source='test02.F') +env.Program(target='test03', source='test03.for') +env.Program(target='test04', source='test04.FOR') +env.Program(target='test05', source='test05.ftn') +env.Program(target='test06', source='test06.FTN') +env.Program(target='test07', source='test07.fpp') +env.Program(target='test08', source='test08.FPP') +env.Program(target='test09', source='test09.f90') +env.Program(target='test10', source='test10.F90') """ % locals()) -test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") -test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") +test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") test.write('test03.for', "This is a .for file.\n#link\n#fortran\n") test.write('test04.FOR', "This is a .FOR file.\n#link\n#fortran\n") test.write('test05.ftn', "This is a .ftn file.\n#link\n#fortran\n") test.write('test06.FTN', "This is a .FTN file.\n#link\n#fortran\n") test.write('test07.fpp', "This is a .fpp file.\n#link\n#fortran\n") test.write('test08.FPP', "This is a .FPP file.\n#link\n#fortran\n") -test.write('test11.f90', "This is a .f90 file.\n#link\n#g90\n") -test.write('test12.F90', "This is a .F90 file.\n#link\n#g90\n") +test.write('test09.f90', "This is a .f90 file.\n#link\n#g90\n") +test.write('test10.F90', "This is a .F90 file.\n#link\n#g90\n") -test.run(arguments = '.', stderr = None) +test.run(arguments='.', stderr=None) test.must_match('test01' + _exe, " -c -y\nThis is a .f file.\n") test.must_match('test02' + _exe, " -c -y\nThis is a .F file.\n") @@ -73,53 +83,8 @@ test.must_match('test06' + _exe, " -c -y\nThis is a .FTN file.\n") test.must_match('test07' + _exe, " -c -y\nThis is a .fpp file.\n") test.must_match('test08' + _exe, " -c -y\nThis is a .FPP file.\n") -test.must_match('test11' + _exe, " -c -x\nThis is a .f90 file.\n") -test.must_match('test12' + _exe, " -c -x\nThis is a .F90 file.\n") - - - -fc = 'f90' -g90 = test.detect_tool(fc) - -if g90: - - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(F90 = '%(fc)s') -f90 = foo.Dictionary('F90') -bar = foo.Clone(F90 = r'%(_python_)s wrapper.py ' + f90) -foo.Program(target = 'foo', source = 'foo.f90') -bar.Program(target = 'bar', source = 'bar.f90') -""" % locals()) - - test.write('foo.f90', r""" - PROGRAM FOO - PRINT *,'foo.f90' - END -""") - - test.write('bar.f90', r""" - PROGRAM BAR - PRINT *,'bar.f90' - END -""") - - test.run(arguments = 'foo' + _exe, stderr = None) - - test.run(program = test.workpath('foo'), stdout = " foo.f90\n") - - test.must_not_exist('wrapper.out') - - import sys - if sys.platform[:5] == 'sunos': - test.run(arguments = 'bar' + _exe, stderr = None) - else: - test.run(arguments = 'bar' + _exe) - - test.run(program = test.workpath('bar'), stdout = " bar.f90\n") - - test.must_match('wrapper.out', "wrapper.py\n") +test.must_match('test09' + _exe, " -c -x\nThis is a .f90 file.\n") +test.must_match('test10' + _exe, " -c -x\nThis is a .F90 file.\n") test.pass_test() diff --git a/test/Fortran/F95FLAGS-live.py b/test/Fortran/F95FLAGS-live.py new file mode 100644 index 0000000000..5277df93a5 --- /dev/null +++ b/test/Fortran/F95FLAGS-live.py @@ -0,0 +1,95 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable, using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() +_exe = TestSCons._exe + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f95' +if not test.detect_tool(fc): + # gfortran names all variants the same, try it too + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find an f95 tool; skipping test.\n') + +test.subdir('x') +test.write(['x', 'dummy.i'], """\ +# Exists only such that -Ix finds the directory... +""") +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +foo = Environment(F95='%(fc)s') +f95 = foo.Dictionary('F95') +bar = foo.Clone(F95=r'%(_python_)s wrapper.py ' + f95, F95FLAGS='-Ix') +foo.Program(target='foo', source='foo.f95') +bar.Program(target='bar', source='bar.f95') +""" % locals()) + +test.write('foo.f95', r""" + PROGRAM FOO + PRINT *,'foo.f95' + STOP + END +""") + +test.write('bar.f95', r""" + PROGRAM BAR + PRINT *,'bar.f95' + STOP + END +""") + +test.run(arguments='foo' + _exe, stderr=None) +test.run(program=test.workpath('foo'), stdout=" foo.f95\n") +test.must_not_exist('wrapper.out') + +if sys.platform.startswith('sunos'): + test.run(arguments='bar' + _exe, stderr=None) +else: + test.run(arguments='bar' + _exe) +test.run(program=test.workpath('bar'), stdout=" bar.f95\n") +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/F95FLAGS.py b/test/Fortran/F95FLAGS.py index e706264197..6e61052d01 100644 --- a/test/Fortran/F95FLAGS.py +++ b/test/Fortran/F95FLAGS.py @@ -23,6 +23,11 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test handling of the dialect-specific FLAGS variable, +using a mocked compiler. +""" + import TestSCons _python_ = TestSCons._python_ @@ -35,7 +40,8 @@ # ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment( LINK=r'%(_python_)s mylink.py', LINKFLAGS=[], @@ -52,8 +58,8 @@ env.Program(target='test06', source='test06.FTN') env.Program(target='test07', source='test07.fpp') env.Program(target='test08', source='test08.FPP') -env.Program(target='test13', source='test13.f95') -env.Program(target='test14', source='test14.F95') +env.Program(target='test09', source='test09.f95') +env.Program(target='test10', source='test10.F95') """ % locals()) test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") @@ -64,8 +70,8 @@ test.write('test06.FTN', "This is a .FTN file.\n#link\n#fortran\n") test.write('test07.fpp', "This is a .fpp file.\n#link\n#fortran\n") test.write('test08.FPP', "This is a .FPP file.\n#link\n#fortran\n") -test.write('test13.f95', "This is a .f95 file.\n#link\n#g95\n") -test.write('test14.F95', "This is a .F95 file.\n#link\n#g95\n") +test.write('test09.f95', "This is a .f95 file.\n#link\n#g95\n") +test.write('test10.F95', "This is a .F95 file.\n#link\n#g95\n") test.run(arguments = '.', stderr = None) @@ -77,57 +83,8 @@ test.must_match('test06' + _exe, " -c -y\nThis is a .FTN file.\n") test.must_match('test07' + _exe, " -c -y\nThis is a .fpp file.\n") test.must_match('test08' + _exe, " -c -y\nThis is a .FPP file.\n") -test.must_match('test13' + _exe, " -c -x\nThis is a .f95 file.\n") -test.must_match('test14' + _exe, " -c -x\nThis is a .F95 file.\n") - - -fc = 'f95' -g95 = test.detect_tool(fc) -if g95: - test.subdir('x') - test.write(['x','dummy.i'], -""" -# Exists only such that -Ix finds the directory... -""") - - # ref: test/fixture/wrapper.py - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(F95='%(fc)s') -f95 = foo.Dictionary('F95') -bar = foo.Clone(F95=r'%(_python_)s wrapper.py ' + f95, F95FLAGS='-Ix') -foo.Program(target='foo', source='foo.f95') -bar.Program(target='bar', source='bar.f95') -""" % locals()) - - test.write('foo.f95', r""" - PROGRAM FOO - PRINT *,'foo.f95' - STOP - END -""") - - test.write('bar.f95', r""" - PROGRAM BAR - PRINT *,'bar.f95' - STOP - END -""") - - test.run(arguments='foo' + _exe, stderr=None) - test.run(program=test.workpath('foo'), stdout=" foo.f95\n") - test.must_not_exist('wrapper.out') - - import sys - - if sys.platform.startswith('sunos'): - test.run(arguments='bar' + _exe, stderr=None) - else: - test.run(arguments='bar' + _exe) - - test.run(program=test.workpath('bar'), stdout=" bar.f95\n") - test.must_match('wrapper.out', "wrapper.py\n") +test.must_match('test09' + _exe, " -c -x\nThis is a .f95 file.\n") +test.must_match('test10' + _exe, " -c -x\nThis is a .F95 file.\n") test.pass_test() diff --git a/test/Fortran/FORTRANCOMMONFLAGS-live.py b/test/Fortran/FORTRANCOMMONFLAGS-live.py new file mode 100644 index 0000000000..608c2675c7 --- /dev/null +++ b/test/Fortran/FORTRANCOMMONFLAGS-live.py @@ -0,0 +1,90 @@ +#!/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. + +""" +Test that while FORTRANFLAGS is not passed to another dialect, +FORTRANCOMMONFLAGS makes it through. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() +_exe = TestSCons._exe + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f90' +if not test.detect_tool(fc): + # gfortran names all variants the same, try it too + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find an f90 tool; skipping test.\n') + +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +foo = Environment(F90='%(fc)s') +f90 = foo.Dictionary('F90') +bar = foo.Clone(F90=r'%(_python_)s wrapper.py ' + f90) +foo.Program(target='foo', source='foo.f90') +bar.Program(target='bar', source='bar.f90') +""" % locals()) + +test.write('foo.f90', r""" + PROGRAM FOO + PRINT *,'foo.f90' + END +""") + +test.write('bar.f90', r""" + PROGRAM BAR + PRINT *,'bar.f90' + END +""") + +test.run(arguments='foo' + _exe, stderr=None) +test.run(program=test.workpath('foo'), stdout=" foo.f90\n") +test.must_not_exist('wrapper.out') + +if sys.platform[:5] == 'sunos': + test.run(arguments='bar' + _exe, stderr=None) +else: + test.run(arguments='bar' + _exe) +test.run(program=test.workpath('bar'), stdout=" bar.f90\n") +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/FORTRANCOMMONFLAGS.py b/test/Fortran/FORTRANCOMMONFLAGS.py index 90307094fc..3d13afbd5a 100644 --- a/test/Fortran/FORTRANCOMMONFLAGS.py +++ b/test/Fortran/FORTRANCOMMONFLAGS.py @@ -35,10 +35,13 @@ test = TestSCons.TestSCons() _exe = TestSCons._exe +# ref: test/fixture/mylink.py test.file_fixture('mylink.py') +# ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) test.write('SConstruct', """ +DefaultEnvironment(tools=[]) env = Environment( LINK=r'%(_python_)s mylink.py', LINKFLAGS=[], @@ -56,8 +59,8 @@ env.Program(target='test06', source='test06.FTN') env.Program(target='test07', source='test07.fpp') env.Program(target='test08', source='test08.FPP') -env.Program(target='test11', source='test11.f90') -env.Program(target='test12', source='test12.F90') +env.Program(target='test09', source='test09.f90') +env.Program(target='test10', source='test10.F90') """ % locals()) test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") @@ -68,8 +71,8 @@ test.write('test06.FTN', "This is a .FTN file.\n#link\n#fortran\n") test.write('test07.fpp', "This is a .fpp file.\n#link\n#fortran\n") test.write('test08.FPP', "This is a .FPP file.\n#link\n#fortran\n") -test.write('test11.f90', "This is a .f90 file.\n#link\n#g90\n") -test.write('test12.F90', "This is a .F90 file.\n#link\n#g90\n") +test.write('test09.f90', "This is a .f90 file.\n#link\n#g90\n") +test.write('test10.F90', "This is a .F90 file.\n#link\n#g90\n") test.run(arguments = '.', stderr = None) @@ -81,47 +84,8 @@ test.must_match('test06' + _exe, " -c -z -y\nThis is a .FTN file.\n") test.must_match('test07' + _exe, " -c -z -y\nThis is a .fpp file.\n") test.must_match('test08' + _exe, " -c -z -y\nThis is a .FPP file.\n") -test.must_match('test11' + _exe, " -c -z -x\nThis is a .f90 file.\n") -test.must_match('test12' + _exe, " -c -z -x\nThis is a .F90 file.\n") - - -fc = 'f90' -g90 = test.detect_tool(fc) -if g90: - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(F90='%(fc)s') -f90 = foo.Dictionary('F90') -bar = foo.Clone(F90=r'%(_python_)s wrapper.py ' + f90) -foo.Program(target='foo', source='foo.f90') -bar.Program(target='bar', source='bar.f90') -""" % locals()) - - test.write('foo.f90', r""" - PROGRAM FOO - PRINT *,'foo.f90' - END -""") - - test.write('bar.f90', r""" - PROGRAM BAR - PRINT *,'bar.f90' - END -""") - - test.run(arguments='foo' + _exe, stderr=None) - test.run(program=test.workpath('foo'), stdout=" foo.f90\n") - test.must_not_exist('wrapper.out') - - import sys - - if sys.platform[:5] == 'sunos': - test.run(arguments='bar' + _exe, stderr=None) - else: - test.run(arguments='bar' + _exe) - test.run(program=test.workpath('bar'), stdout=" bar.f90\n") - test.must_match('wrapper.out', "wrapper.py\n") +test.must_match('test09' + _exe, " -c -z -x\nThis is a .f90 file.\n") +test.must_match('test10' + _exe, " -c -z -x\nThis is a .F90 file.\n") test.pass_test() diff --git a/test/Fortran/FORTRANFLAGS-live.py b/test/Fortran/FORTRANFLAGS-live.py new file mode 100644 index 0000000000..944ec2ed8e --- /dev/null +++ b/test/Fortran/FORTRANFLAGS-live.py @@ -0,0 +1,96 @@ +#!/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. + +""" +Test handling of the FLAGS variable for the generic dialect, +using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() +_exe = TestSCons._exe + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f77' +if not test.detect_tool(fc): + # gfortran names all variants the same, try it too + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find a fortran tool; skipping test.\n') + +directory = 'x' +test.subdir(directory) +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """ +DefaultEnvironment(tools=[]) +foo = Environment(FORTRAN='%(fc)s') +f77 = foo.Dictionary('FORTRAN') +bar = foo.Clone( + FORTRAN=r'%(_python_)s wrapper.py ' + f77, FORTRANFLAGS='-I%(directory)s' +) +foo.Program(target='foo', source='foo.f') +bar.Program(target='bar', source='bar.f') +""" % locals()) + +test.write('foo.f', r""" + PROGRAM FOO + PRINT *,'foo.f' + STOP + END +""") + +test.write('bar.f', r""" + PROGRAM BAR + PRINT *,'bar.f' + STOP + END +""") + +test.run(arguments='foo' + _exe, stderr=None) +test.run(program=test.workpath('foo'), stdout=" foo.f\n") +test.must_not_exist('wrapper.out') + +if sys.platform[:5] == 'sunos': + test.run(arguments='bar' + _exe, stderr=None) +else: + test.run(arguments='bar' + _exe) +test.run(program=test.workpath('bar'), stdout=" bar.f\n") +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/FORTRANFLAGS.py b/test/Fortran/FORTRANFLAGS.py index 218c95f9ef..02d5b99505 100644 --- a/test/Fortran/FORTRANFLAGS.py +++ b/test/Fortran/FORTRANFLAGS.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__" import TestSCons @@ -31,26 +30,31 @@ test = TestSCons.TestSCons() _exe = TestSCons._exe +# ref: test/fixture/mylink.py test.file_fixture('mylink.py') +# ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ -env = Environment(LINK = r'%(_python_)s mylink.py', - LINKFLAGS = [], - FORTRAN = r'%(_python_)s myfortran_flags.py fortran', - FORTRANFLAGS = '-x') -env.Program(target = 'test01', source = 'test01.f') -env.Program(target = 'test02', source = 'test02.F') -env.Program(target = 'test03', source = 'test03.for') -env.Program(target = 'test04', source = 'test04.FOR') -env.Program(target = 'test05', source = 'test05.ftn') -env.Program(target = 'test06', source = 'test06.FTN') -env.Program(target = 'test07', source = 'test07.fpp') -env.Program(target = 'test08', source = 'test08.FPP') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment( + LINK=r'%(_python_)s mylink.py', + LINKFLAGS=[], + FORTRAN=r'%(_python_)s myfortran_flags.py fortran', + FORTRANFLAGS='-x', +) +env.Program(target='test01', source='test01.f') +env.Program(target='test02', source='test02.F') +env.Program(target='test03', source='test03.for') +env.Program(target='test04', source='test04.FOR') +env.Program(target='test05', source='test05.ftn') +env.Program(target='test06', source='test06.FTN') +env.Program(target='test07', source='test07.fpp') +env.Program(target='test08', source='test08.FPP') """ % locals()) -test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") -test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") +test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") test.write('test03.for', "This is a .for file.\n#link\n#fortran\n") test.write('test04.FOR', "This is a .FOR file.\n#link\n#fortran\n") test.write('test05.ftn', "This is a .ftn file.\n#link\n#fortran\n") @@ -58,7 +62,7 @@ test.write('test07.fpp', "This is a .fpp file.\n#link\n#fortran\n") test.write('test08.FPP', "This is a .FPP file.\n#link\n#fortran\n") -test.run(arguments = '.', stderr = None) +test.run(arguments='.', stderr=None) test.must_match('test01' + _exe, " -c -x\nThis is a .f file.\n") test.must_match('test02' + _exe, " -c -x\nThis is a .F file.\n") @@ -69,56 +73,6 @@ test.must_match('test07' + _exe, " -c -x\nThis is a .fpp file.\n") test.must_match('test08' + _exe, " -c -x\nThis is a .FPP file.\n") - -fc = 'f77' -g77 = test.detect_tool(fc) - -if g77: - - directory = 'x' - test.subdir(directory) - - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(FORTRAN = '%(fc)s') -f77 = foo.Dictionary('FORTRAN') -bar = foo.Clone(FORTRAN = r'%(_python_)s wrapper.py ' + f77, FORTRANFLAGS = '-I%(directory)s') -foo.Program(target = 'foo', source = 'foo.f') -bar.Program(target = 'bar', source = 'bar.f') -""" % locals()) - - test.write('foo.f', r""" - PROGRAM FOO - PRINT *,'foo.f' - STOP - END -""") - - test.write('bar.f', r""" - PROGRAM BAR - PRINT *,'bar.f' - STOP - END -""") - - - test.run(arguments = 'foo' + _exe, stderr = None) - - test.run(program = test.workpath('foo'), stdout = " foo.f\n") - - test.must_not_exist('wrapper.out') - - import sys - if sys.platform[:5] == 'sunos': - test.run(arguments = 'bar' + _exe, stderr = None) - else: - test.run(arguments = 'bar' + _exe) - - test.run(program = test.workpath('bar'), stdout = " bar.f\n") - - test.must_match('wrapper.out', "wrapper.py\n") - test.pass_test() # Local Variables: diff --git a/test/Fortran/SHF03FLAGS-live.py b/test/Fortran/SHF03FLAGS-live.py new file mode 100644 index 0000000000..ac17e57eb6 --- /dev/null +++ b/test/Fortran/SHF03FLAGS-live.py @@ -0,0 +1,93 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +test = TestSCons.TestSCons() + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f03' +if not test.detect_tool(fc): + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find a f03 tool; skipping test.\n') + +test.subdir('x') +test.write(['x', 'dummy.i'], """\ +# Exists only such that -Ix finds the directory... +""") +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +# foo = Environment(SHF03='%(fc)s') +foo = Environment() +shf03 = foo.Dictionary('SHF03') +bar = foo.Clone(SHF03=r'%(_python_)s wrapper.py ' + shf03) +bar.Append(SHF03FLAGS='-Ix') +foo.SharedLibrary(target='foo/foo', source='foo.f03') +bar.SharedLibrary(target='bar/bar', source='bar.f03') +""" % locals()) + +test.write('foo.f03', r""" + PROGRAM FOO + PRINT *,'foo.f03' + STOP + END +""") + +test.write('bar.f03', r""" + PROGRAM BAR + PRINT *,'bar.f03' + STOP + END +""") + +test.run(arguments='foo', stderr=None) +test.must_not_exist('wrapper.out') + +if sys.platform.startswith('sunos'): + test.run(arguments='bar', stderr=None) +else: + test.run(arguments='bar') +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/SHF03FLAGS.py b/test/Fortran/SHF03FLAGS.py new file mode 100644 index 0000000000..e9bf6632eb --- /dev/null +++ b/test/Fortran/SHF03FLAGS.py @@ -0,0 +1,91 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a mocked compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +_obj = TestSCons._shobj +obj_ = TestSCons.shobj_ + +test = TestSCons.TestSCons() +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment( + SHF03=r'%(_python_)s myfortran_flags.py g03', + SHFORTRAN=r'%(_python_)s myfortran_flags.py fortran', +) +env.Append(SHF03FLAGS='-x', SHFORTRANFLAGS='-y') +env.SharedObject(target='test01', source='test01.f') +env.SharedObject(target='test02', source='test02.F') +env.SharedObject(target='test03', source='test03.for') +env.SharedObject(target='test04', source='test04.FOR') +env.SharedObject(target='test05', source='test05.ftn') +env.SharedObject(target='test06', source='test06.FTN') +env.SharedObject(target='test07', source='test07.fpp') +env.SharedObject(target='test08', source='test08.FPP') +env.SharedObject(target='test09', source='test09.f03') +env.SharedObject(target='test10', source='test10.F03') +""" % locals()) + +test.write('test01.f', "This is a .f file.\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#fortran\n") +test.write('test03.for', "This is a .for file.\n#fortran\n") +test.write('test04.FOR', "This is a .FOR file.\n#fortran\n") +test.write('test05.ftn', "This is a .ftn file.\n#fortran\n") +test.write('test06.FTN', "This is a .FTN file.\n#fortran\n") +test.write('test07.fpp', "This is a .fpp file.\n#fortran\n") +test.write('test08.FPP', "This is a .FPP file.\n#fortran\n") +test.write('test09.f03', "This is a .f03 file.\n#g03\n") +test.write('test10.F03', "This is a .F03 file.\n#g03\n") + +test.run(arguments='.', stderr=None) +test.must_match(obj_ + 'test01' + _obj, " -c -y\nThis is a .f file.\n") +test.must_match(obj_ + 'test02' + _obj, " -c -y\nThis is a .F file.\n") +test.must_match(obj_ + 'test03' + _obj, " -c -y\nThis is a .for file.\n") +test.must_match(obj_ + 'test04' + _obj, " -c -y\nThis is a .FOR file.\n") +test.must_match(obj_ + 'test05' + _obj, " -c -y\nThis is a .ftn file.\n") +test.must_match(obj_ + 'test06' + _obj, " -c -y\nThis is a .FTN file.\n") +test.must_match(obj_ + 'test07' + _obj, " -c -y\nThis is a .fpp file.\n") +test.must_match(obj_ + 'test08' + _obj, " -c -y\nThis is a .FPP file.\n") +test.must_match(obj_ + 'test09' + _obj, " -c -x\nThis is a .f03 file.\n") +test.must_match(obj_ + 'test10' + _obj, " -c -x\nThis is a .F03 file.\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/SHF08FLAGS-live.py b/test/Fortran/SHF08FLAGS-live.py new file mode 100644 index 0000000000..162d736ce2 --- /dev/null +++ b/test/Fortran/SHF08FLAGS-live.py @@ -0,0 +1,93 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +test = TestSCons.TestSCons() + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f08' +if not test.detect_tool(fc): + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find a f08 tool; skipping test.\n') + +test.subdir('x') +test.write(['x', 'dummy.i'], """\ +# Exists only such that -Ix finds the directory... +""") +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +# foo = Environment(SHF08='%(fc)s') +foo = Environment() +shf08 = foo.Dictionary('SHF08') +bar = foo.Clone(SHF08=r'%(_python_)s wrapper.py ' + shf08) +bar.Append(SHF08FLAGS='-Ix') +foo.SharedLibrary(target='foo/foo', source='foo.f08') +bar.SharedLibrary(target='bar/bar', source='bar.f08') +""" % locals()) + +test.write('foo.f08', r""" + PROGRAM FOO + PRINT *,'foo.f08' + STOP + END +""") + +test.write('bar.f08', r""" + PROGRAM BAR + PRINT *,'bar.f08' + STOP + END +""") + +test.run(arguments='foo', stderr=None) +test.must_not_exist('wrapper.out') + +if sys.platform.startswith('sunos'): + test.run(arguments='bar', stderr=None) +else: + test.run(arguments='bar') +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/SHF08FLAGS.py b/test/Fortran/SHF08FLAGS.py new file mode 100644 index 0000000000..cc9f5370bd --- /dev/null +++ b/test/Fortran/SHF08FLAGS.py @@ -0,0 +1,91 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a mocked compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +_obj = TestSCons._shobj +obj_ = TestSCons.shobj_ + +test = TestSCons.TestSCons() +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment( + SHF08=r'%(_python_)s myfortran_flags.py g08', + SHFORTRAN=r'%(_python_)s myfortran_flags.py fortran', +) +env.Append(SHF08FLAGS='-x', SHFORTRANFLAGS='-y') +env.SharedObject(target='test01', source='test01.f') +env.SharedObject(target='test02', source='test02.F') +env.SharedObject(target='test03', source='test03.for') +env.SharedObject(target='test04', source='test04.FOR') +env.SharedObject(target='test05', source='test05.ftn') +env.SharedObject(target='test06', source='test06.FTN') +env.SharedObject(target='test07', source='test07.fpp') +env.SharedObject(target='test08', source='test08.FPP') +env.SharedObject(target='test09', source='test09.f08') +env.SharedObject(target='test10', source='test10.F08') +""" % locals()) + +test.write('test01.f', "This is a .f file.\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#fortran\n") +test.write('test03.for', "This is a .for file.\n#fortran\n") +test.write('test04.FOR', "This is a .FOR file.\n#fortran\n") +test.write('test05.ftn', "This is a .ftn file.\n#fortran\n") +test.write('test06.FTN', "This is a .FTN file.\n#fortran\n") +test.write('test07.fpp', "This is a .fpp file.\n#fortran\n") +test.write('test08.FPP', "This is a .FPP file.\n#fortran\n") +test.write('test09.f08', "This is a .f08 file.\n#g08\n") +test.write('test10.F08', "This is a .F08 file.\n#g08\n") + +test.run(arguments='.', stderr=None) +test.must_match(obj_ + 'test01' + _obj, " -c -y\nThis is a .f file.\n") +test.must_match(obj_ + 'test02' + _obj, " -c -y\nThis is a .F file.\n") +test.must_match(obj_ + 'test03' + _obj, " -c -y\nThis is a .for file.\n") +test.must_match(obj_ + 'test04' + _obj, " -c -y\nThis is a .FOR file.\n") +test.must_match(obj_ + 'test05' + _obj, " -c -y\nThis is a .ftn file.\n") +test.must_match(obj_ + 'test06' + _obj, " -c -y\nThis is a .FTN file.\n") +test.must_match(obj_ + 'test07' + _obj, " -c -y\nThis is a .fpp file.\n") +test.must_match(obj_ + 'test08' + _obj, " -c -y\nThis is a .FPP file.\n") +test.must_match(obj_ + 'test09' + _obj, " -c -x\nThis is a .f08 file.\n") +test.must_match(obj_ + 'test10' + _obj, " -c -x\nThis is a .F08 file.\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/SHF77FLAGS-live.py b/test/Fortran/SHF77FLAGS-live.py new file mode 100644 index 0000000000..b3ec4aa3c7 --- /dev/null +++ b/test/Fortran/SHF77FLAGS-live.py @@ -0,0 +1,95 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +test = TestSCons.TestSCons() + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f77' +if not test.detect_tool(fc): + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find a f77 tool; skipping test.\n') + +directory = 'x' +test.subdir(directory) +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +# foo = Environment(SHF77='%(fc)s') +foo = Environment() +shf77 = foo.Dictionary('SHF77') +bar = foo.Clone( + SHF77=r'%(_python_)s wrapper.py ' + shf77, + tools=["default", 'f77'], + F77FILESUFFIXES=[".f"], +) +bar.Append(SHF77FLAGS='-I%(directory)s') +foo.SharedLibrary(target='foo/foo', source='foo.f') +bar.SharedLibrary(target='bar/bar', source='bar.f') +""" % locals()) + +test.write('foo.f', r""" + PROGRAM FOO + PRINT *,'foo.f' + STOP + END +""") + +test.write('bar.f', r""" + PROGRAM BAR + PRINT *,'bar.f' + STOP + END +""") + +test.run(arguments='foo', stderr=None) +test.must_not_exist('wrapper.out') + +if sys.platform[:5] == 'sunos': + test.run(arguments='bar', stderr=None) +else: + test.run(arguments='bar') +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/SHF77FLAGS.py b/test/Fortran/SHF77FLAGS.py index 3a7e6b2d9a..5b15616943 100644 --- a/test/Fortran/SHF77FLAGS.py +++ b/test/Fortran/SHF77FLAGS.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,83 +22,39 @@ # 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__" +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a mocked compiler. +""" + +import sys import TestSCons _python_ = TestSCons._python_ - _obj = TestSCons._shobj obj_ = TestSCons.shobj_ test = TestSCons.TestSCons() +# ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ -env = Environment(SHF77 = r'%(_python_)s myfortran_flags.py g77') -env.Append(SHF77FLAGS = '-x') -env.SharedObject(target = 'test09', source = 'test09.f77') -env.SharedObject(target = 'test10', source = 'test10.F77') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(SHF77=r'%(_python_)s myfortran_flags.py g77') +env.Append(SHF77FLAGS='-x') +env.SharedObject(target='test09', source='test09.f77') +env.SharedObject(target='test10', source='test10.F77') """ % locals()) test.write('test09.f77', "This is a .f77 file.\n#g77\n") test.write('test10.F77', "This is a .F77 file.\n#g77\n") -test.run(arguments = '.', stderr = None) - +test.run(arguments='.', stderr=None) test.must_match(obj_ + 'test09' + _obj, " -c -x\nThis is a .f77 file.\n") test.must_match(obj_ + 'test10' + _obj, " -c -x\nThis is a .F77 file.\n") - -fc = 'f77' -g77 = test.detect_tool(fc) - -if g77: - - directory = 'x' - test.subdir(directory) - - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(SHF77 = '%(fc)s') -shf77 = foo.Dictionary('SHF77') -bar = foo.Clone(SHF77 = r'%(_python_)s wrapper.py ' + shf77, - tools = ["default", 'f77'], F77FILESUFFIXES = [".f"]) -bar.Append(SHF77FLAGS = '-I%(directory)s') -foo.SharedLibrary(target = 'foo/foo', source = 'foo.f') -bar.SharedLibrary(target = 'bar/bar', source = 'bar.f') -""" % locals()) - - test.write('foo.f', r""" - PROGRAM FOO - PRINT *,'foo.f' - STOP - END -""") - - test.write('bar.f', r""" - PROGRAM BAR - PRINT *,'bar.f' - STOP - END -""") - - - test.run(arguments = 'foo', stderr = None) - - test.must_not_exist('wrapper.out') - - import sys - if sys.platform[:5] == 'sunos': - test.run(arguments = 'bar', stderr = None) - else: - test.run(arguments = 'bar') - - test.must_match('wrapper.out', "wrapper.py\n") - test.pass_test() # Local Variables: diff --git a/test/Fortran/SHF90FLAGS-live.py b/test/Fortran/SHF90FLAGS-live.py new file mode 100644 index 0000000000..08a12a3b68 --- /dev/null +++ b/test/Fortran/SHF90FLAGS-live.py @@ -0,0 +1,93 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +test = TestSCons.TestSCons() + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f90' +if not test.detect_tool(fc): + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find a f90 tool; skipping test.\n') + +test.subdir('x') +test.write(['x', 'dummy.i'], """\ +# Exists only such that -Ix finds the directory... +""") +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +# foo = Environment(SHF90='%(fc)s') +foo = Environment() +shf90 = foo.Dictionary('SHF90') +bar = foo.Clone(SHF90=r'%(_python_)s wrapper.py ' + shf90) +bar.Append(SHF90FLAGS='-Ix') +foo.SharedLibrary(target='foo/foo', source='foo.f90') +bar.SharedLibrary(target='bar/bar', source='bar.f90') +""" % locals()) + +test.write('foo.f90', r""" + PROGRAM FOO + PRINT *,'foo.f90' + STOP + END +""") + +test.write('bar.f90', r""" + PROGRAM BAR + PRINT *,'bar.f90' + STOP + END +""") + +test.run(arguments='foo', stderr=None) +test.must_not_exist('wrapper.out') + +if sys.platform.startswith('sunos'): + test.run(arguments='bar', stderr=None) +else: + test.run(arguments='bar') +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/SHF90FLAGS.py b/test/Fortran/SHF90FLAGS.py index 900acbccab..fbc591f956 100644 --- a/test/Fortran/SHF90FLAGS.py +++ b/test/Fortran/SHF90FLAGS.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,50 +22,55 @@ # 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__" +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a mocked compiler. +""" + +import sys import TestSCons _python_ = TestSCons._python_ - _obj = TestSCons._shobj obj_ = TestSCons.shobj_ test = TestSCons.TestSCons() +# ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ -env = Environment(SHF90 = r'%(_python_)s myfortran_flags.py g90', - SHFORTRAN = r'%(_python_)s myfortran_flags.py fortran') -env.Append(SHF90FLAGS = '-x', - SHFORTRANFLAGS = '-y') -env.SharedObject(target = 'test01', source = 'test01.f') -env.SharedObject(target = 'test02', source = 'test02.F') -env.SharedObject(target = 'test03', source = 'test03.for') -env.SharedObject(target = 'test04', source = 'test04.FOR') -env.SharedObject(target = 'test05', source = 'test05.ftn') -env.SharedObject(target = 'test06', source = 'test06.FTN') -env.SharedObject(target = 'test07', source = 'test07.fpp') -env.SharedObject(target = 'test08', source = 'test08.FPP') -env.SharedObject(target = 'test11', source = 'test11.f90') -env.SharedObject(target = 'test12', source = 'test12.F90') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment( + SHF90=r'%(_python_)s myfortran_flags.py g90', + SHFORTRAN=r'%(_python_)s myfortran_flags.py fortran', +) +env.Append(SHF90FLAGS='-x', SHFORTRANFLAGS='-y') +env.SharedObject(target='test01', source='test01.f') +env.SharedObject(target='test02', source='test02.F') +env.SharedObject(target='test03', source='test03.for') +env.SharedObject(target='test04', source='test04.FOR') +env.SharedObject(target='test05', source='test05.ftn') +env.SharedObject(target='test06', source='test06.FTN') +env.SharedObject(target='test07', source='test07.fpp') +env.SharedObject(target='test08', source='test08.FPP') +env.SharedObject(target='test09', source='test09.f90') +env.SharedObject(target='test10', source='test10.F90') """ % locals()) -test.write('test01.f', "This is a .f file.\n#fortran\n") -test.write('test02.F', "This is a .F file.\n#fortran\n") +test.write('test01.f', "This is a .f file.\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#fortran\n") test.write('test03.for', "This is a .for file.\n#fortran\n") test.write('test04.FOR', "This is a .FOR file.\n#fortran\n") test.write('test05.ftn', "This is a .ftn file.\n#fortran\n") test.write('test06.FTN', "This is a .FTN file.\n#fortran\n") test.write('test07.fpp', "This is a .fpp file.\n#fortran\n") test.write('test08.FPP', "This is a .FPP file.\n#fortran\n") -test.write('test11.f90', "This is a .f90 file.\n#g90\n") -test.write('test12.F90', "This is a .F90 file.\n#g90\n") - -test.run(arguments = '.', stderr = None) +test.write('test09.f90', "This is a .f90 file.\n#g90\n") +test.write('test10.F90', "This is a .F90 file.\n#g90\n") +test.run(arguments='.', stderr=None) test.must_match(obj_ + 'test01' + _obj, " -c -y\nThis is a .f file.\n") test.must_match(obj_ + 'test02' + _obj, " -c -y\nThis is a .F file.\n") test.must_match(obj_ + 'test03' + _obj, " -c -y\nThis is a .for file.\n") @@ -72,51 +79,8 @@ test.must_match(obj_ + 'test06' + _obj, " -c -y\nThis is a .FTN file.\n") test.must_match(obj_ + 'test07' + _obj, " -c -y\nThis is a .fpp file.\n") test.must_match(obj_ + 'test08' + _obj, " -c -y\nThis is a .FPP file.\n") -test.must_match(obj_ + 'test11' + _obj, " -c -x\nThis is a .f90 file.\n") -test.must_match(obj_ + 'test12' + _obj, " -c -x\nThis is a .F90 file.\n") - -fc = 'f90' -g90 = test.detect_tool(fc) - -if g90: - - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(SHF90 = '%(fc)s') -shf90 = foo.Dictionary('SHF90') -bar = foo.Clone(SHF90 = r'%(_python_)s wrapper.py ' + shf90) -bar.Append(SHF90FLAGS = '-Ix') -foo.SharedLibrary(target = 'foo/foo', source = 'foo.f90') -bar.SharedLibrary(target = 'bar/bar', source = 'bar.f90') -""" % locals()) - - test.write('foo.f90', r""" - PROGRAM FOO - PRINT *,'foo.f90' - STOP - END -""") - - test.write('bar.f90', r""" - PROGRAM BAR - PRINT *,'bar.f90' - STOP - END -""") - - - test.run(arguments = 'foo', stderr = None) - - test.must_not_exist('wrapper.out') - - import sys - if sys.platform[:5] == 'sunos': - test.run(arguments = 'bar', stderr = None) - else: - test.run(arguments = 'bar') - - test.must_match('wrapper.out', "wrapper.py\n") +test.must_match(obj_ + 'test09' + _obj, " -c -x\nThis is a .f90 file.\n") +test.must_match(obj_ + 'test10' + _obj, " -c -x\nThis is a .F90 file.\n") test.pass_test() diff --git a/test/Fortran/SHF95FLAGS-live.py b/test/Fortran/SHF95FLAGS-live.py new file mode 100644 index 0000000000..aaaf981cbb --- /dev/null +++ b/test/Fortran/SHF95FLAGS-live.py @@ -0,0 +1,93 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +test = TestSCons.TestSCons() + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f95' +if not test.detect_tool(fc): + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find a f95 tool; skipping test.\n') + +test.subdir('x') +test.write(['x', 'dummy.i'], """\ +# Exists only such that -Ix finds the directory... +""") +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +# foo = Environment(SHF95='%(fc)s') +foo = Environment() +shf95 = foo.Dictionary('SHF95') +bar = foo.Clone(SHF95=r'%(_python_)s wrapper.py ' + shf95) +bar.Append(SHF95FLAGS='-Ix') +foo.SharedLibrary(target='foo/foo', source='foo.f95') +bar.SharedLibrary(target='bar/bar', source='bar.f95') +""" % locals()) + +test.write('foo.f95', r""" + PROGRAM FOO + PRINT *,'foo.f95' + STOP + END +""") + +test.write('bar.f95', r""" + PROGRAM BAR + PRINT *,'bar.f95' + STOP + END +""") + +test.run(arguments='foo', stderr=None) +test.must_not_exist('wrapper.out') + +if sys.platform.startswith('sunos'): + test.run(arguments='bar', stderr=None) +else: + test.run(arguments='bar') +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/SHF95FLAGS.py b/test/Fortran/SHF95FLAGS.py index dcec49bf7d..f8a80d4b20 100644 --- a/test/Fortran/SHF95FLAGS.py +++ b/test/Fortran/SHF95FLAGS.py @@ -23,10 +23,16 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a mocked compiler. +""" + +import sys + import TestSCons _python_ = TestSCons._python_ - _obj = TestSCons._shobj obj_ = TestSCons.shobj_ @@ -34,7 +40,8 @@ # ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) env = Environment( SHF95=r'%(_python_)s myfortran_flags.py g95', SHFORTRAN=r'%(_python_)s myfortran_flags.py fortran', @@ -48,20 +55,20 @@ env.SharedObject(target='test06', source='test06.FTN') env.SharedObject(target='test07', source='test07.fpp') env.SharedObject(target='test08', source='test08.FPP') -env.SharedObject(target='test13', source='test13.f95') -env.SharedObject(target='test14', source='test14.F95') +env.SharedObject(target='test09', source='test09.f95') +env.SharedObject(target='test10', source='test10.F95') """ % locals()) -test.write('test01.f', "This is a .f file.\n#fortran\n") -test.write('test02.F', "This is a .F file.\n#fortran\n") +test.write('test01.f', "This is a .f file.\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#fortran\n") test.write('test03.for', "This is a .for file.\n#fortran\n") test.write('test04.FOR', "This is a .FOR file.\n#fortran\n") test.write('test05.ftn', "This is a .ftn file.\n#fortran\n") test.write('test06.FTN', "This is a .FTN file.\n#fortran\n") test.write('test07.fpp', "This is a .fpp file.\n#fortran\n") test.write('test08.FPP', "This is a .FPP file.\n#fortran\n") -test.write('test13.f95', "This is a .f95 file.\n#g95\n") -test.write('test14.F95', "This is a .F95 file.\n#g95\n") +test.write('test09.f95', "This is a .f95 file.\n#g95\n") +test.write('test10.F95', "This is a .F95 file.\n#g95\n") test.run(arguments='.', stderr=None) test.must_match(obj_ + 'test01' + _obj, " -c -y\nThis is a .f file.\n") @@ -72,53 +79,8 @@ test.must_match(obj_ + 'test06' + _obj, " -c -y\nThis is a .FTN file.\n") test.must_match(obj_ + 'test07' + _obj, " -c -y\nThis is a .fpp file.\n") test.must_match(obj_ + 'test08' + _obj, " -c -y\nThis is a .FPP file.\n") -test.must_match(obj_ + 'test13' + _obj, " -c -x\nThis is a .f95 file.\n") -test.must_match(obj_ + 'test14' + _obj, " -c -x\nThis is a .F95 file.\n") - -fc = 'f95' -g95 = test.detect_tool(fc) -if g95: - test.subdir('x') - test.write(['x','dummy.i'], -""" -# Exists only such that -Ix finds the directory... -""") - - # ref: test/fixture/wrapper.py - test.file_fixture('wrapper.py') - test.write('SConstruct', """ -foo = Environment(SHF95='%(fc)s') -shf95 = foo.Dictionary('SHF95') -bar = foo.Clone(SHF95=r'%(_python_)s wrapper.py ' + shf95) -bar.Append(SHF95FLAGS='-Ix') -foo.SharedLibrary(target='foo/foo', source='foo.f95') -bar.SharedLibrary(target='bar/bar', source='bar.f95') -""" % locals()) - - test.write('foo.f95', r""" - PROGRAM FOO - PRINT *,'foo.f95' - STOP - END -""") - - test.write('bar.f95', r""" - PROGRAM BAR - PRINT *,'bar.f95' - STOP - END -""") - - test.run(arguments='foo', stderr=None) - test.must_not_exist('wrapper.out') - - import sys - - if sys.platform.startswith('sunos'): - test.run(arguments='bar', stderr=None) - else: - test.run(arguments='bar') - test.must_match('wrapper.out', "wrapper.py\n") +test.must_match(obj_ + 'test09' + _obj, " -c -x\nThis is a .f95 file.\n") +test.must_match(obj_ + 'test10' + _obj, " -c -x\nThis is a .F95 file.\n") test.pass_test() diff --git a/test/Fortran/SHFORTRANFLAGS-live.py b/test/Fortran/SHFORTRANFLAGS-live.py new file mode 100644 index 0000000000..a36cfd1afa --- /dev/null +++ b/test/Fortran/SHFORTRANFLAGS-live.py @@ -0,0 +1,91 @@ +#!/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. + +""" +Test handling of the dialect-specific FLAGS variable for shared objects, +using a live compiler. +""" + +import sys + +import TestSCons + +_python_ = TestSCons._python_ +test = TestSCons.TestSCons() + +# ref: test/Fortran/fixture/myfortran_flags.py +test.file_fixture(['fixture', 'myfortran_flags.py']) + +fc = 'f77' +if not test.detect_tool(fc): + fc = 'gfortran' + if not test.detect_tool(fc): + test.skip_test('Could not find a fortran tool; skipping test.\n') + +directory = 'x' +test.subdir(directory) +# ref: test/fixture/wrapper.py +test.file_fixture('wrapper.py') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +# foo = Environment(SHFORTRAN='%(fc)s') +foo = Environment() +shfortran = foo.Dictionary('SHFORTRAN') +bar = foo.Clone(SHFORTRAN=r'%(_python_)s wrapper.py ' + shfortran) +bar.Append(SHFORTRANFLAGS='-I%(directory)s') +foo.SharedLibrary(target='foo/foo', source='foo.f') +bar.SharedLibrary(target='bar/bar', source='bar.f') +""" % locals()) + +test.write('foo.f', r""" + PROGRAM FOO + PRINT *,'foo.f' + STOP + END +""") + +test.write('bar.f', r""" + PROGRAM BAR + PRINT *,'bar.f' + STOP + END +""") + +test.run(arguments='foo', stderr=None) +test.must_not_exist('wrapper.out') + +if sys.platform[:5] == 'sunos': + test.run(arguments='bar', stderr=None) +else: + test.run(arguments='bar') +test.must_match('wrapper.out', "wrapper.py\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Fortran/SHFORTRANFLAGS.py b/test/Fortran/SHFORTRANFLAGS.py index 6a4692bb2c..0954738aa5 100644 --- a/test/Fortran/SHFORTRANFLAGS.py +++ b/test/Fortran/SHFORTRANFLAGS.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,34 +22,35 @@ # 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__" +import sys import TestSCons _python_ = TestSCons._python_ -_obj = TestSCons._shobj -obj_ = TestSCons.shobj_ +_obj = TestSCons._shobj +obj_ = TestSCons.shobj_ test = TestSCons.TestSCons() +# ref: test/Fortran/fixture/myfortran_flags.py test.file_fixture(['fixture', 'myfortran_flags.py']) -test.write('SConstruct', """ -env = Environment(SHFORTRAN = r'%(_python_)s myfortran_flags.py fortran') -env.Append(SHFORTRANFLAGS = '-x') -env.SharedObject(target = 'test01', source = 'test01.f') -env.SharedObject(target = 'test02', source = 'test02.F') -env.SharedObject(target = 'test03', source = 'test03.for') -env.SharedObject(target = 'test04', source = 'test04.FOR') -env.SharedObject(target = 'test05', source = 'test05.ftn') -env.SharedObject(target = 'test06', source = 'test06.FTN') -env.SharedObject(target = 'test07', source = 'test07.fpp') -env.SharedObject(target = 'test08', source = 'test08.FPP') +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +env = Environment(SHFORTRAN=r'%(_python_)s myfortran_flags.py fortran') +env.Append(SHFORTRANFLAGS='-x') +env.SharedObject(target='test01', source='test01.f') +env.SharedObject(target='test02', source='test02.F') +env.SharedObject(target='test03', source='test03.for') +env.SharedObject(target='test04', source='test04.FOR') +env.SharedObject(target='test05', source='test05.ftn') +env.SharedObject(target='test06', source='test06.FTN') +env.SharedObject(target='test07', source='test07.fpp') +env.SharedObject(target='test08', source='test08.FPP') """ % locals()) -test.write('test01.f', "This is a .f file.\n#fortran\n") -test.write('test02.F', "This is a .F file.\n#fortran\n") +test.write('test01.f', "This is a .f file.\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#fortran\n") test.write('test03.for', "This is a .for file.\n#fortran\n") test.write('test04.FOR', "This is a .FOR file.\n#fortran\n") test.write('test05.ftn', "This is a .ftn file.\n#fortran\n") @@ -55,8 +58,7 @@ test.write('test07.fpp', "This is a .fpp file.\n#fortran\n") test.write('test08.FPP', "This is a .FPP file.\n#fortran\n") -test.run(arguments = '.', stderr = None) - +test.run(arguments='.', stderr=None) test.must_match(obj_ + 'test01' + _obj, " -c -x\nThis is a .f file.\n") test.must_match(obj_ + 'test02' + _obj, " -c -x\nThis is a .F file.\n") test.must_match(obj_ + 'test03' + _obj, " -c -x\nThis is a .for file.\n") @@ -66,52 +68,6 @@ test.must_match(obj_ + 'test07' + _obj, " -c -x\nThis is a .fpp file.\n") test.must_match(obj_ + 'test08' + _obj, " -c -x\nThis is a .FPP file.\n") -fc = 'f77' -fortran = test.detect_tool(fc) - -if fortran: - - directory = 'x' - test.subdir(directory) - - test.file_fixture('wrapper.py') - - test.write('SConstruct', """ -foo = Environment(SHFORTRAN = '%(fc)s') -shfortran = foo.Dictionary('SHFORTRAN') -bar = foo.Clone(SHFORTRAN = r'%(_python_)s wrapper.py ' + shfortran) -bar.Append(SHFORTRANFLAGS = '-I%(directory)s') -foo.SharedLibrary(target = 'foo/foo', source = 'foo.f') -bar.SharedLibrary(target = 'bar/bar', source = 'bar.f') -""" % locals()) - - test.write('foo.f', r""" - PROGRAM FOO - PRINT *,'foo.f' - STOP - END -""") - - test.write('bar.f', r""" - PROGRAM BAR - PRINT *,'bar.f' - STOP - END -""") - - - test.run(arguments = 'foo', stderr = None) - - test.must_not_exist('wrapper.out') - - import sys - if sys.platform[:5] == 'sunos': - test.run(arguments = 'bar', stderr = None) - else: - test.run(arguments = 'bar') - - test.must_match('wrapper.out', "wrapper.py\n") - test.pass_test() # Local Variables: From 3b61ed1379bfcbb22589d948f702cd7baaff0309 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 9 Mar 2025 08:00:06 -0600 Subject: [PATCH 302/386] Clean up the C/C++ compiler variable tests Tests now don't combine flags-only cases (don't use a real compiler) with live cases (do require a compiler) in the same test file. Signed-off-by: Mats Wichmann --- RELEASE.txt | 5 ++ test/CC/CC-live.py | 67 ++++++++++++++++++++++++ test/CC/CC.py | 55 ++++++-------------- test/CC/SHCC-live.py | 63 +++++++++++++++++++++++ test/{CC/SHCC.py => CXX/CXX-live.py} | 38 +++++++------- test/CXX/CXX.py | 76 +++++++--------------------- test/CXX/{SHCXX.py => SHCXX-live.py} | 27 +++++----- 7 files changed, 204 insertions(+), 127 deletions(-) create mode 100644 test/CC/CC-live.py create mode 100644 test/CC/SHCC-live.py rename test/{CC/SHCC.py => CXX/CXX-live.py} (74%) rename test/CXX/{SHCXX.py => SHCXX-live.py} (81%) diff --git a/RELEASE.txt b/RELEASE.txt index 2219bf7866..fb07053b7f 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -61,9 +61,14 @@ DOCUMENTATION DEVELOPMENT ----------- +- List visible changes in the way SCons is developed + - runtest.py once again finds "external" tests, such as the tests for tools in scons-contrib. An earlier rework had broken this. Fixes #4699. +- Clean up C and C++ FLAGS tests. Tests which use a real compiler + are now more clearly distinguished (-live.py suffix and docstring). + Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text diff --git a/test/CC/CC-live.py b/test/CC/CC-live.py new file mode 100644 index 0000000000..4848b0f5f8 --- /dev/null +++ b/test/CC/CC-live.py @@ -0,0 +1,67 @@ +#!/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. + +""" +Test the C compiler name variable $CC. +This is a live test, calling the detected C compiler via a wrapper. +""" + +import os +import sys +import TestSCons + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +test.dir_fixture('CC-fixture') +test.file_fixture('wrapper.py') + +test.write('SConstruct', f"""\ +DefaultEnvironment(tools=[]) +foo = Environment() +bar = Environment() + +bar['CC'] = r'{_python_} wrapper.py ' + foo['CC'] +foo.Program(target='foo', source='foo.c') +bar.Program(target='bar', source='bar.c') +""") + +test.run(arguments='foo' + _exe) +test.must_not_exist(test.workpath('wrapper.out')) +test.up_to_date(arguments='foo' + _exe) + +test.run(arguments='bar' + _exe) +test.must_match('wrapper.out', "wrapper.py\n", mode='r') +test.up_to_date(arguments='bar' + _exe) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/CC.py b/test/CC/CC.py index d43106dd5a..ecf126e416 100644 --- a/test/CC/CC.py +++ b/test/CC/CC.py @@ -23,6 +23,10 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test the C compiler name variable $CC, calling a mocked compiler. +""" + import os import sys import TestSCons @@ -35,61 +39,34 @@ test.dir_fixture('CC-fixture') test.file_fixture('mylink.py') -test.write('SConstruct', """ +test.write('SConstruct', f"""\ DefaultEnvironment(tools=[]) env = Environment( - tools=['link','cc'], - LINK=r'%(_python_)s mylink.py', + tools=['link', 'cc'], + LINK=r'{_python_} mylink.py', LINKFLAGS=[], - CC=r'%(_python_)s mycc.py', + CC=r'{_python_} mycc.py', ) env.Program(target='test1', source='test1.c') -""" % locals()) - -test.run(arguments = '.', stderr = None) +""") +test.run(arguments='.', stderr=None) test.must_match('test1' + _exe, "This is a .c file.\n", mode='r') if os.path.normcase('.c') == os.path.normcase('.C'): - - test.write('SConstruct', """ + test.write('SConstruct2', f""" DefaultEnvironment(tools=[]) - env = Environment( - tools=['link','cc'], - LINK=r'%(_python_)s mylink.py', - CC=r'%(_python_)s mycc.py', + tools=['link', 'cc'], + LINK=r'{_python_} mylink.py', + CC=r'{_python_} mycc.py', ) env.Program(target='test2', source='test2.C') -""" % locals()) +""") - test.run(arguments = '.', stderr = None) + test.run(arguments=['-f', 'SConstruct2', '.'], stderr=None) test.must_match('test2' + _exe, "This is a .C file.\n", mode='r') -test.file_fixture('wrapper.py') - -test.write('SConstruct', """ -DefaultEnvironment(tools=[]) - -foo = Environment() -bar = Environment() -bar['CC'] = r'%(_python_)s wrapper.py ' + foo['CC'] -foo.Program(target='foo', source='foo.c') -bar.Program(target='bar', source='bar.c') -""" % locals()) - -test.run(arguments = 'foo' + _exe) - -test.must_not_exist(test.workpath('wrapper.out')) - -test.up_to_date(arguments = 'foo' + _exe) - -test.run(arguments = 'bar' + _exe) - -test.must_match('wrapper.out', "wrapper.py\n", mode='r') - -test.up_to_date(arguments = 'bar' + _exe) - test.pass_test() # Local Variables: diff --git a/test/CC/SHCC-live.py b/test/CC/SHCC-live.py new file mode 100644 index 0000000000..9da11d07b7 --- /dev/null +++ b/test/CC/SHCC-live.py @@ -0,0 +1,63 @@ +#!/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. + +""" +Test the C shared-object compiler name variable $SHCC. +This is a live test, calling the detected C compiler via a wrapper. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.file_fixture('wrapper.py') +test.file_fixture('CC-fixture/foo.c') +test.file_fixture('CC-fixture/bar.c') + +test.write('SConstruct', f"""\ +DefaultEnvironment(tools=[]) +foo = Environment() + +shcc = foo['SHCC'] +bar = Environment(SHCC=r'{_python_} wrapper.py ' + shcc) +foo.SharedObject(target='foo/foo', source='foo.c') +bar.SharedObject(target='bar/bar', source='bar.c') +""") + +test.run(arguments='foo') +test.must_not_exist(test.workpath('wrapper.out')) + +test.run(arguments='bar') +test.must_match('wrapper.out', "wrapper.py\n", mode='r') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/CC/SHCC.py b/test/CXX/CXX-live.py similarity index 74% rename from test/CC/SHCC.py rename to test/CXX/CXX-live.py index 3645c2dd96..9f72b78e74 100644 --- a/test/CC/SHCC.py +++ b/test/CXX/CXX-live.py @@ -23,57 +23,59 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test the C compiler name variable $CC. +This is a live test, calling the detected C compiler via a wrapper. +""" +import sys import TestSCons _python_ = TestSCons._python_ +_exe = TestSCons._exe test = TestSCons.TestSCons() test.file_fixture('wrapper.py') -test.write('SConstruct', """ +test.write('SConstruct', f"""\ DefaultEnvironment(tools=[]) foo = Environment() -shcc = foo['SHCC'] -bar = Environment(SHCC = r'%(_python_)s wrapper.py ' + shcc) -foo.SharedObject(target = 'foo/foo', source = 'foo.c') -bar.SharedObject(target = 'bar/bar', source = 'bar.c') + +cxx = foo.Dictionary('CXX') +bar = Environment(CXX=r'{_python_} wrapper.py ' + cxx) +foo.Program(target='foo', source='foo.cxx') +bar.Program(target='bar', source='bar.cxx') """ % locals()) -test.write('foo.c', r""" +test.write('foo.cxx', r""" #include #include - int main(int argc, char *argv[]) { - argv[argc++] = "--"; - printf("foo.c\n"); + argv[argc++] = (char *)"--"; + printf("foo.cxx\n"); exit (0); } """) -test.write('bar.c', r""" +test.write('bar.cxx', r""" #include #include - int main(int argc, char *argv[]) { - argv[argc++] = "--"; - printf("foo.c\n"); + argv[argc++] = (char *)"--"; + printf("foo.cxx\n"); exit (0); } """) - -test.run(arguments = 'foo') - +test.run(arguments='foo' + _exe) test.must_not_exist(test.workpath('wrapper.out')) -test.run(arguments = 'bar') - +test.run(arguments='bar' + _exe) test.must_match('wrapper.out', "wrapper.py\n", mode='r') test.pass_test() diff --git a/test/CXX/CXX.py b/test/CXX/CXX.py index ad00b55bf0..baf2c3150a 100644 --- a/test/CXX/CXX.py +++ b/test/CXX/CXX.py @@ -23,6 +23,10 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Test the C++ compiler name variable $CXX, calling a mocked compiler. +""" + import sys import TestSCons @@ -34,19 +38,21 @@ test.file_fixture('mylink.py') test.dir_fixture('CXX-fixture') -test.write('SConstruct', """ +test.write('SConstruct', f"""\ +DefaultEnvironment(tools=[]) env = Environment( - LINK=r'%(_python_)s mylink.py', + LINK=r'{_python_} mylink.py', LINKFLAGS=[], - CXX=r'%(_python_)s myc++.py', + CXX=r'{_python_} myc++.py', CXXFLAGS=[], ) + env.Program(target='test1', source='test1.cc') env.Program(target='test2', source='test2.cpp') env.Program(target='test3', source='test3.cxx') env.Program(target='test4', source='test4.c++') env.Program(target='test5', source='test5.C++') -""" % locals()) +""") test.write('test1.cc', r"""This is a .cc file. /*c++*/ @@ -73,80 +79,34 @@ #link """) -test.run(arguments = '.', stderr = None) - +test.run(arguments='.', stderr=None) test.must_match('test1' + _exe, "This is a .cc file.\n", mode='r') - test.must_match('test2' + _exe, "This is a .cpp file.\n", mode='r') - test.must_match('test3' + _exe, "This is a .cxx file.\n", mode='r') - test.must_match('test4' + _exe, "This is a .c++ file.\n", mode='r') - test.must_match('test5' + _exe, "This is a .C++ file.\n", mode='r') if TestSCons.case_sensitive_suffixes('.c', '.C'): - test.write('SConstruct', """ + test.write('SConstruct2', f"""\ +DefaultEnvironment(tools=[]) env = Environment( - LINK=r'%(_python_)s mylink.py', + LINK=r'{_python_} mylink.py', LINKFLAGS=[], - CXX=r'%(_python_)s myc++.py', + CXX=r'{_python_} myc++.py', CXXFLAGS=[], ) + env.Program(target='test6', source='test6.C') -""" % locals()) +""") test.write('test6.C', r"""This is a .C file. /*c++*/ #link """) - test.run(arguments = '.', stderr = None) + test.run(arguments=['-f', 'SConstruct2', '.'], stderr=None) test.must_match('test6' + _exe, "This is a .C file.\n", mode='r') -test.file_fixture('wrapper.py') - -test.write('SConstruct', """ -foo = Environment() -cxx = foo.Dictionary('CXX') -bar = Environment(CXX=r'%(_python_)s wrapper.py ' + cxx) -foo.Program(target='foo', source='foo.cxx') -bar.Program(target='bar', source='bar.cxx') -""" % locals()) - -test.write('foo.cxx', r""" -#include -#include -int -main(int argc, char *argv[]) -{ - argv[argc++] = (char *)"--"; - printf("foo.cxx\n"); - exit (0); -} -""") - -test.write('bar.cxx', r""" -#include -#include -int -main(int argc, char *argv[]) -{ - argv[argc++] = (char *)"--"; - printf("foo.cxx\n"); - exit (0); -} -""") - - -test.run(arguments = 'foo' + _exe) - -test.must_not_exist(test.workpath('wrapper.out')) - -test.run(arguments = 'bar' + _exe) - -test.must_match('wrapper.out', "wrapper.py\n", mode='r') - test.pass_test() # Local Variables: diff --git a/test/CXX/SHCXX.py b/test/CXX/SHCXX-live.py similarity index 81% rename from test/CXX/SHCXX.py rename to test/CXX/SHCXX-live.py index 0d1ad3669d..b7318d4b17 100644 --- a/test/CXX/SHCXX.py +++ b/test/CXX/SHCXX-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,11 @@ # 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__" +""" +Test the C++ shared-object compiler name variable $SHCXX. +This is a live test, calling the detected C++ compiler via a wrapper. +""" import os @@ -34,12 +38,14 @@ test.file_fixture('wrapper.py') -test.write('SConstruct', """ +test.write('SConstruct', f"""\ +DefaultEnvironment(tools=[]) foo = Environment() + shcxx = foo.Dictionary('SHCXX') -bar = Environment(SHCXX = r'%(_python_)s wrapper.py ' + shcxx) -foo.SharedObject(target = 'foo/foo', source = 'foo.cpp') -bar.SharedObject(target = 'bar/bar', source = 'bar.cpp') +bar = Environment(SHCXX=r'{_python_} wrapper.py ' + shcxx) +foo.SharedObject(target='foo/foo', source='foo.cpp') +bar.SharedObject(target='bar/bar', source='bar.cpp') """ % locals()) test.write('foo.cpp', r""" @@ -66,13 +72,10 @@ } """) - -test.run(arguments = 'foo') - +test.run(arguments='foo') test.fail_test(os.path.exists(test.workpath('wrapper.out'))) -test.run(arguments = 'bar') - +test.run(arguments='bar') test.must_match('wrapper.out', "wrapper.py\n", mode='r') # test.fail_test(test.read('wrapper.out') != "wrapper.py\n") From 395a3702e6758f185f5da1c09dfe40db87c1106a Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 10 Apr 2025 15:43:43 -0600 Subject: [PATCH 303/386] Fix tests for use with Python 3.14 As with every Python release, a few tests have to be fiddled to work with that release. SCons/ActionTests is always one of those, as it has dictionaries mapping (major, minor) version to corresponding bytecode strings. The only other outstanding one this go-round was an expection of the exception messages for a divide-by-zero, which has changed slightly again. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 +++ RELEASE.txt | 4 ++++ SCons/ActionTests.py | 15 ++++++++++++++- SCons/Taskmaster/TaskmasterTests.py | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index ca52cc4a7b..a4969f2761 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -27,6 +27,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - runtest.py once again finds "external" tests, such as the tests for tools in scons-contrib. An earlier rework had broken this. Fixes #4699. - Clarify how pre/post actions on an alias work. + - Fix a couple of unit tests to not fail with Python 3.14. These involve + bytecode and error message contents, and there was no problem with + SCons itself using 3.14 in its current (just-before-freeze) state. RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 2219bf7866..394dd05850 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -64,6 +64,10 @@ DEVELOPMENT - runtest.py once again finds "external" tests, such as the tests for tools in scons-contrib. An earlier rework had broken this. Fixes #4699. +- Fix a couple of unit tests to not fail with Python 3.14. These involve + expectations for bytecode and error message contents; there was no problem + with SCons itself using 3.14 in its current (just-before-freeze) state. + Thanks to the following contributors listed below for their contributions to this release. ========================================================================================== .. code-block:: text diff --git a/SCons/ActionTests.py b/SCons/ActionTests.py index ed87e031d0..64a413722d 100644 --- a/SCons/ActionTests.py +++ b/SCons/ActionTests.py @@ -1552,6 +1552,7 @@ def LocalFunc() -> None: (3, 11): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'), (3, 12): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00y\x00),(),()'), (3, 13): bytearray(b'0, 0, 0, 0,(),(),(\x95\x00g\x00),(),()'), + (3, 14): bytearray(b'0, 0, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()'), } meth_matches = [ @@ -1732,6 +1733,7 @@ def LocalFunc() -> None: (3, 11): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'), (3, 12): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00y\x00),(),()'), (3, 13): bytearray(b'0, 0, 0, 0,(),(),(\x95\x00g\x00),(),()'), + (3, 14): bytearray(b'0, 0, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()'), } @@ -1743,6 +1745,7 @@ def LocalFunc() -> None: (3, 11): bytearray(b'1, 1, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'), (3, 12): bytearray(b'1, 1, 0, 0,(),(),(\x97\x00y\x00),(),()'), (3, 13): bytearray(b'1, 1, 0, 0,(),(),(\x95\x00g\x00),(),()'), + (3, 14): bytearray(b'1, 1, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()'), } def factory(act, **kw): @@ -1983,6 +1986,7 @@ def LocalFunc() -> None: (3, 11): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'), (3, 12): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00y\x00),(),()'), (3, 13): bytearray(b'0, 0, 0, 0,(),(),(\x95\x00g\x00),(),()'), + (3, 14): bytearray(b'0, 0, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()'), } meth_matches = [ @@ -2045,6 +2049,7 @@ def LocalFunc() -> None: (3, 11): b'\x97\x00d\x00S\x00', (3, 12): b'\x97\x00y\x00', (3, 13): b'\x95\x00g\x00', + (3, 14): b'\x80\x00P\x00"\x00', } with self.subTest(): @@ -2250,6 +2255,7 @@ def func1(a, b, c): (3, 11): (bytearray(b'3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()'),), (3, 12): (bytearray(b'3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()'),), (3, 13): (bytearray(b'3, 3, 0, 0,(),(),(\x95\x00U\x00$\x00),(),()'),), + (3, 14): (bytearray(b'3, 3, 0, 0,(),(),(\x80\x00R\x00"\x00),(),()'),), } c = SCons.Action._function_contents(func1) @@ -2288,9 +2294,13 @@ def test_object_contents(self) -> None: (3, 13): bytearray( b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x95\x00S\x01U\x00l\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x02U\x00l\x01\x00\x00\x00\x00\x00\x00\x00\x00g\x00),(),(),2, 2, 0, 0,(),(),(\x95\x00g\x00),(),()}}{{{a=a,b=b}}}" ), + (3, 14): bytearray( + b'{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x80\x00P\x00R\x00j\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x01R\x00j\x01\x00\x00\x00\x00\x00\x00\x00\x00P\x02"\x00),(),(),2, 2, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()}}{{{a=a,b=b}}}' + ), } - self.assertEqual(c, expected[sys.version_info[:2]]) + # self.assertEqual(c, expected[sys.version_info[:2]]) + assert c == expected[sys.version_info[:2]], c def test_code_contents(self) -> None: """Test that Action._code_contents works""" @@ -2321,6 +2331,9 @@ def test_code_contents(self) -> None: (3, 13): bytearray( b'0, 0, 0, 0,(Hello, World!),(print),(\x95\x00\\\x00"\x00S\x005\x01\x00\x00\x00\x00\x00\x00 \x00g\x01)' ), + (3, 14): bytearray( + b'0, 0, 0, 0,(Hello, World!),(print),(\x80\x00Y\x00 \x00P\x002\x01\x00\x00\x00\x00\x00\x00\x1e\x00P\x01"\x00)' + ), } self.assertEqual(c, expected[sys.version_info[:2]]) diff --git a/SCons/Taskmaster/TaskmasterTests.py b/SCons/Taskmaster/TaskmasterTests.py index f279f05eb4..9e52557c28 100644 --- a/SCons/Taskmaster/TaskmasterTests.py +++ b/SCons/Taskmaster/TaskmasterTests.py @@ -1161,6 +1161,7 @@ def test_exception(self) -> None: "integer division or modulo", "integer division or modulo by zero", "integer division by zero", # PyPy2 + "division by zero", # Python 3.14+ ] assert str(exc_value) in exception_values, exc_value From cd1742b3830a4068c427c6d0bf9a9ef680780b6c Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 11 Apr 2025 10:02:15 -0600 Subject: [PATCH 304/386] Python 3.14 fix for Windows Apparently, some of the bytecode strings now have system-specific differences - tiny ones, but enough to fail ActionTests. We dont have CI set up runnon Pyton 3.14 yet, neither Linux nor Windows, but I have tested on a Windows 11 system with 3.14.0a7 Signed-off-by: Mats Wichmann --- SCons/ActionTests.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/SCons/ActionTests.py b/SCons/ActionTests.py index 64a413722d..77614aa5a2 100644 --- a/SCons/ActionTests.py +++ b/SCons/ActionTests.py @@ -2255,7 +2255,11 @@ def func1(a, b, c): (3, 11): (bytearray(b'3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()'),), (3, 12): (bytearray(b'3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()'),), (3, 13): (bytearray(b'3, 3, 0, 0,(),(),(\x95\x00U\x00$\x00),(),()'),), - (3, 14): (bytearray(b'3, 3, 0, 0,(),(),(\x80\x00R\x00"\x00),(),()'),), + (3, 14): (bytearray( + b'3, 3, 0, 0,(),(),(\x80\x00T\x00"\x00),(),()' + if sys.platform == 'win32' + else b'3, 3, 0, 0,(),(),(\x80\x00R\x00"\x00),(),()' + ),), } c = SCons.Action._function_contents(func1) @@ -2295,12 +2299,12 @@ def test_object_contents(self) -> None: b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x95\x00S\x01U\x00l\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x02U\x00l\x01\x00\x00\x00\x00\x00\x00\x00\x00g\x00),(),(),2, 2, 0, 0,(),(),(\x95\x00g\x00),(),()}}{{{a=a,b=b}}}" ), (3, 14): bytearray( - b'{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x80\x00P\x00R\x00j\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x01R\x00j\x01\x00\x00\x00\x00\x00\x00\x00\x00P\x02"\x00),(),(),2, 2, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()}}{{{a=a,b=b}}}' + b'{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x80\x00P\x00T\x00l\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x01T\x00l\x01\x00\x00\x00\x00\x00\x00\x00\x00P\x02"\x00),(),(),2, 2, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()}}{{{a=a,b=b}}}' + if sys.platform == 'win32' + else b'{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x80\x00P\x00R\x00j\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x01R\x00j\x01\x00\x00\x00\x00\x00\x00\x00\x00P\x02"\x00),(),(),2, 2, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()}}{{{a=a,b=b}}}' ), } - - # self.assertEqual(c, expected[sys.version_info[:2]]) - assert c == expected[sys.version_info[:2]], c + self.assertEqual(c, expected[sys.version_info[:2]]) def test_code_contents(self) -> None: """Test that Action._code_contents works""" @@ -2332,7 +2336,9 @@ def test_code_contents(self) -> None: b'0, 0, 0, 0,(Hello, World!),(print),(\x95\x00\\\x00"\x00S\x005\x01\x00\x00\x00\x00\x00\x00 \x00g\x01)' ), (3, 14): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(\x80\x00Y\x00 \x00P\x002\x01\x00\x00\x00\x00\x00\x00\x1e\x00P\x01"\x00)' + b'0, 0, 0, 0,(Hello, World!),(print),(\x80\x00[\x00 \x00P\x002\x01\x00\x00\x00\x00\x00\x00\x1e\x00P\x01"\x00)' + if sys.platform == 'win32' + else b'0, 0, 0, 0,(Hello, World!),(print),(\x80\x00Y\x00 \x00P\x002\x01\x00\x00\x00\x00\x00\x00\x1e\x00P\x01"\x00)' ), } From 283a2133008543da2dcddcf4ba8c67bd9af739f8 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Mon, 31 Mar 2025 09:11:35 -0600 Subject: [PATCH 305/386] Tweak PackageVariable docs again Trying to find the right angle for this slightly odd variable. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + RELEASE.txt | 2 ++ doc/man/scons.xml | 68 ++++++++++++++++++++++++++++------------------- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index eac44f9954..41d20fec11 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -39,6 +39,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Replace use of old conditional expression idioms with the official one from PEP 308 introduced in Python 2.5 (2006). The idiom being replaced (using and/or) is regarded as error prone. + - Improve the description of PackageVariable. RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index ae6e01db2f..2012fbe0d2 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -62,6 +62,8 @@ DOCUMENTATION - Clarify how pre/post actions on an alias work. +- Improve the description of PackageVariable. + DEVELOPMENT ----------- diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 4d3c7d46e5..594b70552a 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -5391,16 +5391,18 @@ the validator parameter. Set up a variable named key -to help control a build component, +to help control the use of a build component, such as a software package. -The variable can be specified to disable, enable, -or enable with a custom path. -The resulting &consvar; will have a value of -True, False, or a path string. -Interpretation of this value is up to the consumer, -but a path string must refer to an existing filesystem entry -or the PackageVariable validator -will raise an exception. +Similar to a +BoolVariable, +but can also specify a path string +to provide additional information to the build, +for example the path to a configuration file describing the package, +or a directory containing the package headers and libraries. +The variable will have a default value of +default, +and the help parameter +will be used as the descriptive part of the help text. @@ -5410,30 +5412,42 @@ Any of the (case-insensitive) strings true, on, enable -and +or search -can be used -to indicate the package is "enabled", -and the (case-insensitive) strings +can be used to indicate the package is "enabled", +and can also be used as the value of default. +A value of boolean True is +produced except for the special case described +for a path string. + + + +Any of the (case-insensitive) strings 0, no, false, off -and +or disable -to indicate the package is "disabled". - - - -The default parameter -can be either a path string or one of the enabling or disabling strings. -default is produced if the variable is not specified, -or if it is specified with one of the enabling strings, -except that if default is one -of the enabling strings, the boolean literal True -is produced instead of the string. -The help parameter -specifies the descriptive part of the help text. +can be used to indicate the package is "disabled", +and can also be used as the value of default. +A value of boolean False is +produced. + + + +A string which is neither an enabling or disabling +string is considered a "path string". +A path string can be given when specifying the variable, +and can also be used as the value of default. +A path string must refer to an existing filesystem path, +but any further meaning is left to the build system to decide. +The path string is produced. +As a special case, +if default is a path string, +and the variable is specified with an enabling string, +the default path string is produced, +rather than True. From aaf05ece3cf0a26cc9f1cca403f582acae65a1bd Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 13 Apr 2025 14:04:51 -0700 Subject: [PATCH 306/386] [ci skip] Simplify the text a bit --- doc/man/scons.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 594b70552a..b97580e33e 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -5391,7 +5391,7 @@ the validator parameter. Set up a variable named key -to help control the use of a build component, +to help control a build component, such as a software package. Similar to a BoolVariable, From 7dfe38f0ce5fa547a0344b7d815c34ebe9054c50 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 13 Apr 2025 14:51:51 -0700 Subject: [PATCH 307/386] Removed x if a else y from golden values for now --- SCons/ActionTests.py | 82 ++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/SCons/ActionTests.py b/SCons/ActionTests.py index 77614aa5a2..02d4f2ebf4 100644 --- a/SCons/ActionTests.py +++ b/SCons/ActionTests.py @@ -2245,21 +2245,24 @@ def func1(a, b, c): # we need different expected results per version # Note unlike the others, this result is a tuple, use assertIn expected = { - (3, 7): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),), - (3, 8): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),), - (3, 9): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),), + (3, 7): (bytearray(b"3, 3, 0, 0,(),(),(|\x00S\x00),(),()"),), + (3, 8): (bytearray(b"3, 3, 0, 0,(),(),(|\x00S\x00),(),()"),), + (3, 9): (bytearray(b"3, 3, 0, 0,(),(),(|\x00S\x00),(),()"),), (3, 10): ( # 3.10.1, 3.10.2 - bytearray(b'3, 3, 0, 0,(N.),(),(|\x00S\x00),(),()'), - bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'), + bytearray(b"3, 3, 0, 0,(N.),(),(|\x00S\x00),(),()"), + bytearray(b"3, 3, 0, 0,(),(),(|\x00S\x00),(),()"), + ), + (3, 11): (bytearray(b"3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()"),), + (3, 12): (bytearray(b"3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()"),), + (3, 13): (bytearray(b"3, 3, 0, 0,(),(),(\x95\x00U\x00$\x00),(),()"),), + (3, 14): ( + bytearray( + b'3, 3, 0, 0,(),(),(\x80\x00T\x00"\x00),(),()' + ), # win32 has different bytecodes + bytearray( + b'3, 3, 0, 0,(),(),(\x80\x00R\x00"\x00),(),()' + ), # every other OS? ), - (3, 11): (bytearray(b'3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()'),), - (3, 12): (bytearray(b'3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()'),), - (3, 13): (bytearray(b'3, 3, 0, 0,(),(),(\x95\x00U\x00$\x00),(),()'),), - (3, 14): (bytearray( - b'3, 3, 0, 0,(),(),(\x80\x00T\x00"\x00),(),()' - if sys.platform == 'win32' - else b'3, 3, 0, 0,(),(),(\x80\x00R\x00"\x00),(),()' - ),), } c = SCons.Action._function_contents(func1) @@ -2278,33 +2281,36 @@ def test_object_contents(self) -> None: # we need different expected results per version expected = { (3, 7): bytearray( - b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" ), (3, 8): bytearray( - b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" ), (3, 9): bytearray( - b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" ), (3, 10): bytearray( - b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}" ), (3, 11): bytearray( - b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x97\x00d\x01|\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x02|\x00_\x01\x00\x00\x00\x00\x00\x00\x00\x00d\x00S\x00),(),(),2, 2, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()}}{{{a=a,b=b}}}" + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x97\x00d\x01|\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x02|\x00_\x01\x00\x00\x00\x00\x00\x00\x00\x00d\x00S\x00),(),(),2, 2, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()}}{{{a=a,b=b}}}" ), (3, 12): bytearray( - b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x97\x00d\x01|\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x02|\x00_\x01\x00\x00\x00\x00\x00\x00\x00\x00y\x00),(),(),2, 2, 0, 0,(),(),(\x97\x00y\x00),(),()}}{{{a=a,b=b}}}" + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x97\x00d\x01|\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x02|\x00_\x01\x00\x00\x00\x00\x00\x00\x00\x00y\x00),(),(),2, 2, 0, 0,(),(),(\x97\x00y\x00),(),()}}{{{a=a,b=b}}}" ), (3, 13): bytearray( - b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x95\x00S\x01U\x00l\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x02U\x00l\x01\x00\x00\x00\x00\x00\x00\x00\x00g\x00),(),(),2, 2, 0, 0,(),(),(\x95\x00g\x00),(),()}}{{{a=a,b=b}}}" + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x95\x00S\x01U\x00l\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x02U\x00l\x01\x00\x00\x00\x00\x00\x00\x00\x00g\x00),(),(),2, 2, 0, 0,(),(),(\x95\x00g\x00),(),()}}{{{a=a,b=b}}}" ), - (3, 14): bytearray( - b'{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x80\x00P\x00T\x00l\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x01T\x00l\x01\x00\x00\x00\x00\x00\x00\x00\x00P\x02"\x00),(),(),2, 2, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()}}{{{a=a,b=b}}}' - if sys.platform == 'win32' - else b'{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x80\x00P\x00R\x00j\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x01R\x00j\x01\x00\x00\x00\x00\x00\x00\x00\x00P\x02"\x00),(),(),2, 2, 0, 0,(),(),(\x80\x00P\x00"\x00),(),()}}{{{a=a,b=b}}}' + (3, 14): ( + bytearray( + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x80\x00P\x00T\x00l\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x01T\x00l\x01\x00\x00\x00\x00\x00\x00\x00\x00P\x02\"\x00),(),(),2, 2, 0, 0,(),(),(\x80\x00P\x00\"\x00),(),()}}{{{a=a,b=b}}}" + ), # win32 + bytearray( + b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x80\x00P\x00R\x00j\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x01R\x00j\x01\x00\x00\x00\x00\x00\x00\x00\x00P\x02\"\x00),(),(),2, 2, 0, 0,(),(),(\x80\x00P\x00\"\x00),(),()}}{{{a=a,b=b}}}" + ), ), } - self.assertEqual(c, expected[sys.version_info[:2]]) + self.assertTrue(c in expected[sys.version_info[:2]]) def test_code_contents(self) -> None: """Test that Action._code_contents works""" @@ -2315,34 +2321,37 @@ def test_code_contents(self) -> None: # Since the python bytecode has per version differences, we need different expected results per version expected = { (3, 7): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)' + b"0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)" ), (3, 8): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)' + b"0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)" ), (3, 9): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)' + b"0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)" ), (3, 10): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)' + b"0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)" ), (3, 11): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(\x97\x00\x02\x00e\x00d\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00d\x01S\x00)' + b"0, 0, 0, 0,(Hello, World!),(print),(\x97\x00\x02\x00e\x00d\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00d\x01S\x00)" ), (3, 12): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(\x97\x00\x02\x00e\x00d\x00\xab\x01\x00\x00\x00\x00\x00\x00\x01\x00y\x01)' + b"0, 0, 0, 0,(Hello, World!),(print),(\x97\x00\x02\x00e\x00d\x00\xab\x01\x00\x00\x00\x00\x00\x00\x01\x00y\x01)" ), (3, 13): bytearray( b'0, 0, 0, 0,(Hello, World!),(print),(\x95\x00\\\x00"\x00S\x005\x01\x00\x00\x00\x00\x00\x00 \x00g\x01)' ), - (3, 14): bytearray( - b'0, 0, 0, 0,(Hello, World!),(print),(\x80\x00[\x00 \x00P\x002\x01\x00\x00\x00\x00\x00\x00\x1e\x00P\x01"\x00)' - if sys.platform == 'win32' - else b'0, 0, 0, 0,(Hello, World!),(print),(\x80\x00Y\x00 \x00P\x002\x01\x00\x00\x00\x00\x00\x00\x1e\x00P\x01"\x00)' + (3, 14): ( + bytearray( + b'0, 0, 0, 0,(Hello, World!),(print),(\x80\x00[\x00 \x00P\x002\x01\x00\x00\x00\x00\x00\x00\x1e\x00P\x01"\x00)' + ), # win32 + bytearray( + b'0, 0, 0, 0,(Hello, World!),(print),(\x80\x00Y\x00 \x00P\x002\x01\x00\x00\x00\x00\x00\x00\x1e\x00P\x01"\x00)' + ), ), } - self.assertEqual(c, expected[sys.version_info[:2]]) + self.assertTrue(c in expected[sys.version_info[:2]],f"\nExpected:{expected[sys.version_info[:2]]}\nGot :{c}") def test_uncaught_exception_bubbles(self): """Test that scons_subproc_run bubbles uncaught exceptions""" @@ -2356,7 +2365,6 @@ def test_uncaught_exception_bubbles(self): raise Exception("expected a non-EnvironmentError exception") - def mock_subprocess_run(*args, **kwargs): """Replacement subprocess.run: return kwargs for checking.""" kwargs.pop("env") # the value of env isn't interesting here From 21b559a9d5840bf73fe81ec7717523d7afc28d21 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Tue, 22 Apr 2025 12:57:56 -0700 Subject: [PATCH 308/386] fix typo --- doc/user/misc.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/misc.xml b/doc/user/misc.xml index f78fb35da5..f1c21f111a 100644 --- a/doc/user/misc.xml +++ b/doc/user/misc.xml @@ -673,7 +673,7 @@ env.Command('directory_build_info', variables to all created construction environment env['ENV'], and modifies SCons PATH appropriately to prefer virtualenv's executables. Setting environment variable SCONS_ENABLE_VIRTUALENV=1 - will have same effect. If virtualenv support is enabled system-vide + will have same effect. If virtualenv support is enabled system-wide by the environment variable, it may be suppressed with the option. From 5259838f65017f92b2cdf1271355cb3502bb78ac Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 25 Apr 2025 11:41:50 -0600 Subject: [PATCH 309/386] Fix some minor docstring Sphinx complaints Signed-off-by: Mats Wichmann --- SCons/Environment.py | 6 +++--- SCons/Script/Main.py | 2 +- SCons/Variables/ListVariable.py | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/SCons/Environment.py b/SCons/Environment.py index c18808db5b..ff9309a6fe 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -649,7 +649,7 @@ def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw): args - filename strings or nodes to convert; nodes are just added to the list without further processing. node_factory - optional factory to create the nodes; if not - specified, will use this environment's ``fs.File method. + specified, will use this environment's ``fs.File`` method. lookup_list - optional list of lookup functions to call to attempt to find the file referenced by each *args*. kw - keyword arguments that represent additional nodes to add. @@ -1755,8 +1755,8 @@ def Dump(self, *key: str, format: str = 'pretty') -> str: (pretty-print) or ``<>`` (JSON). Args: - key: if omitted, format the whole dict of variables, - else format *key*(s) with the corresponding values. + key: variables to format together with their values. + If omitted, format the whole dict of variables, format: specify the format to serialize to. ``"pretty"`` generates a pretty-printed string, ``"json"`` a JSON-formatted string. diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index c1335cc4b9..98f1f345a9 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -528,7 +528,7 @@ def AddOption(*args, **kw) -> SConsOption: """Add a local option to the option parser - Public API. If the SCons-specific *settable* kwarg is true (default ``False``), - the option will allow calling :func:``SetOption`. + the option will allow calling :func:`SetOption`. .. versionchanged:: 4.8.0 The *settable* parameter added to allow including the new option diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index 880496f706..90f563b56c 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -25,8 +25,8 @@ A list variable allows selecting one or more from a supplied set of allowable values, as well as from an optional mapping of alternate names -(such as aliases and abbreviations) and the special names ``'all'`` and -``'none'``. Specified values are converted during processing into values +(such as aliases and abbreviations) and the special names "all" and +"none". Specified values are converted during processing into values only from the allowable values set. Usage example:: @@ -123,7 +123,7 @@ def _converter(val, allowedElems, mapdict) -> _ListVariable: for a :class:`Variables` converter: the lambda in the :func:`ListVariable` function arranges for us to be called correctly. - Incoming values ``all`` and ``none`` are recognized and converted + Incoming values "all" and "none" are recognized and converted into their expanded form. """ if val == 'none': @@ -189,8 +189,8 @@ def ListVariable( """Return a tuple describing a list variable. A List Variable is an abstraction that allows choosing one or more - values from a provided list of possibilities (*names). The special terms - ``all`` and ``none`` are also provided to help make the selection. + values from a provided list of possibilities (*names*). The special terms + "all" and "none" are also provided to help make the selection. Arguments: key: the name of the list variable. @@ -198,14 +198,14 @@ def ListVariable( the allowed values (not including any extra names from *map*). default: the default value(s) for the list variable. Can be given as string (use commas to -separated multiple values), or as a list - of strings. ``all`` or ``none`` are allowed as *default*. - A must-specify ListVariable can be simulated by giving a value + of strings. "all" or "none" are allowed as *default*. + A must-specify list variable can be simulated by giving a value that is not part of *names*, which will cause validation to fail - if the variable is not given in the input sources. + if the variable is not supplied in the input sources. names: the values to choose from. Must be a list of strings. map: optional dictionary to map alternative names to the ones in - *names*, providing a form of alias. The converter will make - the replacement, names from *map* are not stored and will + *names*, providing a form of alias. The converter will perform + the replacement. Names from *map* are not stored, and will not appear in the help message. validator: optional callback to validate supplied values. The default validator is used if not specified. From 7037f478af44972e5001854a594b90c9f421e362 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 29 Apr 2025 14:24:55 -0600 Subject: [PATCH 310/386] Tweak the API Docs build (Sphinx) configuration [skip appveyor] Some one-file modules were reported as duplicated, this is fixed. SConsDoc and SConsExample are now included - their API is interesting to developers working on SCons (needed to write docs), even if not part of "The SCons API" itself. Reworded the API Docs intro section a bit. Signed-off-by: Mats Wichmann --- CHANGES.txt | 6 +++++ RELEASE.txt | 5 +++++ doc/sphinx/SCons.rst | 39 ++++++++++++++++++++------------- doc/sphinx/conf.py | 3 +++ doc/sphinx/index.rst | 52 ++++++++++++++++++++++++-------------------- 5 files changed, 66 insertions(+), 39 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 41d20fec11..9b6f0236b7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -40,6 +40,12 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER one from PEP 308 introduced in Python 2.5 (2006). The idiom being replaced (using and/or) is regarded as error prone. - Improve the description of PackageVariable. + - Tweak the "API Docs" build (Sphinx) configuration a bit. Some + one-file modules were reported as duplicated, this is fixed. + SConsDoc and SConsExample are now included - their API is + interesting to developers working on SCons (needed to write docs), + even if not part of "The SCons API" itself. + Reworded the API Docs intro sectios a bit. RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 2012fbe0d2..3c7bff54c6 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -64,6 +64,11 @@ DOCUMENTATION - Improve the description of PackageVariable. +- The "API Docs" build (Sphinx) configuration is improved, and + SConsDoc and SConsExample are now included - their API is + interesting to developers working on SCons (needed to write docs), + even if not part of "The SCons API" itself. + DEVELOPMENT ----------- diff --git a/doc/sphinx/SCons.rst b/doc/sphinx/SCons.rst index 9ab44b020e..951504d403 100644 --- a/doc/sphinx/SCons.rst +++ b/doc/sphinx/SCons.rst @@ -9,21 +9,6 @@ Module contents :undoc-members: :show-inheritance: -Subpackages ------------ - -.. toctree:: - - SCons.Node - SCons.Platform - SCons.Scanner - SCons.Script - SCons.Taskmaster - SCons.Tool - SCons.Util - SCons.Variables - SCons.compat - Submodules ---------- @@ -174,3 +159,27 @@ SCons.exitfuncs module :members: :undoc-members: :show-inheritance: + +SConsDoc documentation module +----------------------------- + +This module is NOT part of the SCons build tool itself. +It is supporting tooling, invoked by tools used to build +documentation components. + +.. automodule:: bin.SConsDoc + :members: + :undoc-members: + :show-inheritance: + +SConsExamples documentation module +---------------------------------- + +This module is NOT part of the SCons build tool itself. +It is supporting tooling, invoked by tools used to build +documentation components. + +.. automodule:: bin.SConsExamples + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py index 8bfdc419e5..7e5456213d 100644 --- a/doc/sphinx/conf.py +++ b/doc/sphinx/conf.py @@ -18,6 +18,9 @@ # import os import sys + +sys.path.insert(0, os.path.abspath('../../testing/framework')) +sys.path.insert(0, os.path.abspath('../../bin')) sys.path.insert(0, os.path.abspath('../../')) # -- General configuration ------------------------------------------------ diff --git a/doc/sphinx/index.rst b/doc/sphinx/index.rst index 1b810eb8ce..f79069c448 100644 --- a/doc/sphinx/index.rst +++ b/doc/sphinx/index.rst @@ -1,5 +1,4 @@ -.. SCons documentation master file, originally created by - sphinx-quickstart on Mon Apr 30 09:36:53 2018. +.. SCons documentation master file, originally created by sphinx-quickstart on Mon Apr 30 09:36:53 2018. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. @@ -7,38 +6,44 @@ SCons API Documentation ======================= .. Attention:: - This is the **internal** API Documentation for SCons (aka - "everything"). It is generated automatically from code docstrings using - the `Sphinx `_ documentation generator. - Any missing/incomplete information is due to shortcomings in the - docstrings in the code. To not be too flippant about it, filling - in all the docstrings has not always been a priority across the - two-plus decades SCons has been in existence (contributions on this - front are welcomed). Additionally, for SCons classes which inherit - from Python standard library classes (such as ``UserList``, - ``UserDict``, ``UserString``), the generated pages will show methods - that are inherited, sometimes with no information at all, sometimes - with a signature/description that seems mangled: Python upstream has - similar limitations as to the quality of dosctrings vs the current - standards Sphinx expects. Inherited interfaces from outside SCons - code can be identified by the lack of a ``[source]`` button to the - right of the method signature. - - If you are looking for the Public API - the interfaces that have + This is the **internal** API Documentation for SCons. + It is generated automatically from code docstrings using + the `Sphinx `_ documentation generator, + and covers much more than the Public API. + If you were looking for the Public API - the interfaces that have long-term consistency guarantees, which you can reliably use when writing a build system for a project - see the `SCons Reference Manual `_. Note that - what is Public API and what is not is not clearly delineated in these - API Docs. + what is Public API and what is not is often not clearly delineated in + these API Docs. - The target audience is both developers contributing to SCons itself, + The target audience is developers contributing to SCons itself, and those writing external Tools, Builders, and other related functionality for their project, who may need to reach beyond the Public API to accomplish their tasks. Reaching into internals is fine, but comes with the usual risks of "things here could change, it's up to you to keep your code working". + Any missing/incomplete information is due to shortcomings in the + docstrings in the code. Without being flippant, filling + in all the docstrings has not always been a priority across the + two-plus decades SCons has been in existence. Contributions + improving the docstring front are welcome. It is often useful when + making some larger change to fill in docstrings and suitable + type annotations in the code being modified, "leaving the world + a better place", as it were. + + Note that the Sphinx configuration is limited, still a work + in progress. SCons classes which inherit from Python standard + library classes (e.g. ``UserList``, ``UserDict``, ``UserString``), + may be allowed to show inherited mmembers; the Python standard library + occasionally has little to no helpful docstring information. + Inherited interfaces from outside SCons code can be identified by the + lack of a ``[source]`` button to the right of the method signature. + Such classes should be evaluated case-by-case and possibly + switched to not show inherited members, depending on which way + seems to provide the more useful result. .. toctree:: :maxdepth: 2 @@ -55,7 +60,6 @@ SCons API Documentation SCons.Util SCons.Variables - Indices and Tables ================== From 74670429c93aa5bcc3e17f4a0407d11c8983b6a0 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 7 May 2025 13:11:39 -0400 Subject: [PATCH 311/386] Initial fix for vcpkg run time in windows runner. Changes for msvc batch file invocation environment: * Add Powershell 7 to PATH * Add PSModulePath for Powershell 7 and 5 * Import VCPKG_DISABLE_METRICS if defined * Import VCPKG_ROOT if defined --- SCons/Tool/MSCommon/common.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index 5f011052bd..812ae795cc 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -342,6 +342,19 @@ def normalize_env(env, keys, force: bool=False): if sys32_wbem_dir not in normenv['PATH']: normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_wbem_dir + # ProgramFiles for PowerShell 7 Path and PSModulePath + progfiles_dir = os.environ.get("ProgramFiles") + if not progfiles_dir: + sysroot_drive, _ = os.path.splitdrive(sys32_dir) + sysroot_path = sysroot_drive + os.sep + progfiles_dir = os.path.join(sysroot_path, "Program Files") + + # Powershell 7 + progfiles_ps_dir = os.path.join(progfiles_dir, r"PowerShell\7") + if progfiles_ps_dir not in normenv["PATH"]: + normenv["PATH"] = normenv["PATH"] + os.pathsep + progfiles_ps_dir + + # Powershell 5 # Without Powershell in PATH, an internal call to a telemetry # function (starting with a VS2019 update) can fail # Note can also set VSCMD_SKIP_SENDTELEMETRY to avoid this. @@ -350,6 +363,22 @@ def normalize_env(env, keys, force: bool=False): normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_ps_dir debug("PATH: %s", normenv['PATH']) + + if sys32_dir not in normenv["PATH"]: + normenv["PATH"] = normenv["PATH"] + os.pathsep + sys32_dir + + psmodulepath_dirs = [ + # Powershell 7 paths + os.path.join(progfiles_dir, r"PowerShell\Modules"), + os.path.join(progfiles_dir, r"PowerShell\7\Modules"), + # Powershell 5 paths + os.path.join(progfiles_dir, r"WindowsPowerShell\Modules"), + os.path.join(sys32_dir, r"WindowsPowerShell\v1.0\Modules"), + ] + + normenv["PSModulePath"] = os.pathsep.join(psmodulepath_dirs) + + debug("PSModulePath: %s", normenv['PSModulePath']) return normenv @@ -386,6 +415,8 @@ def get_output(vcbat, args=None, env=None, skip_sendtelemetry=False): 'VS71COMNTOOLS', 'VSCOMNTOOLS', 'MSDevDir', + 'VCPKG_DISABLE_METRICS', + 'VCPKG_ROOT', 'VSCMD_DEBUG', # enable logging and other debug aids 'VSCMD_SKIP_SENDTELEMETRY', 'windir', # windows directory (SystemRoot not available in 95/98/ME) @@ -395,6 +426,8 @@ def get_output(vcbat, args=None, env=None, skip_sendtelemetry=False): if skip_sendtelemetry: _force_vscmd_skip_sendtelemetry(env) + # debug("ENV=%r", env['ENV']) + if args: debug("Calling '%s %s'", vcbat, args) cmd_str = '"%s" %s & set' % (vcbat, args) From 39b49371d68822a1ae4931a8fb6d77b373a8dcfd Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 7 May 2025 13:22:50 -0400 Subject: [PATCH 312/386] Fix: remove inadvertent code copy and move debug logging for PATH to function exit. --- SCons/Tool/MSCommon/common.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index 812ae795cc..2748c45dc8 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -362,11 +362,6 @@ def normalize_env(env, keys, force: bool=False): if sys32_ps_dir not in normenv['PATH']: normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_ps_dir - debug("PATH: %s", normenv['PATH']) - - if sys32_dir not in normenv["PATH"]: - normenv["PATH"] = normenv["PATH"] + os.pathsep + sys32_dir - psmodulepath_dirs = [ # Powershell 7 paths os.path.join(progfiles_dir, r"PowerShell\Modules"), @@ -378,6 +373,7 @@ def normalize_env(env, keys, force: bool=False): normenv["PSModulePath"] = os.pathsep.join(psmodulepath_dirs) + debug("PATH: %s", normenv['PATH']) debug("PSModulePath: %s", normenv['PSModulePath']) return normenv From 7ba488ec828e591e4fa79ceb53b6a53aee4c2734 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 8 May 2025 09:12:54 -0400 Subject: [PATCH 313/386] Remove PSModulePath from msvc environment. --- SCons/Tool/MSCommon/common.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index 2748c45dc8..64a9a1fecb 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -354,7 +354,6 @@ def normalize_env(env, keys, force: bool=False): if progfiles_ps_dir not in normenv["PATH"]: normenv["PATH"] = normenv["PATH"] + os.pathsep + progfiles_ps_dir - # Powershell 5 # Without Powershell in PATH, an internal call to a telemetry # function (starting with a VS2019 update) can fail # Note can also set VSCMD_SKIP_SENDTELEMETRY to avoid this. @@ -362,19 +361,7 @@ def normalize_env(env, keys, force: bool=False): if sys32_ps_dir not in normenv['PATH']: normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_ps_dir - psmodulepath_dirs = [ - # Powershell 7 paths - os.path.join(progfiles_dir, r"PowerShell\Modules"), - os.path.join(progfiles_dir, r"PowerShell\7\Modules"), - # Powershell 5 paths - os.path.join(progfiles_dir, r"WindowsPowerShell\Modules"), - os.path.join(sys32_dir, r"WindowsPowerShell\v1.0\Modules"), - ] - - normenv["PSModulePath"] = os.pathsep.join(psmodulepath_dirs) - debug("PATH: %s", normenv['PATH']) - debug("PSModulePath: %s", normenv['PSModulePath']) return normenv @@ -411,11 +398,11 @@ def get_output(vcbat, args=None, env=None, skip_sendtelemetry=False): 'VS71COMNTOOLS', 'VSCOMNTOOLS', 'MSDevDir', - 'VCPKG_DISABLE_METRICS', - 'VCPKG_ROOT', 'VSCMD_DEBUG', # enable logging and other debug aids 'VSCMD_SKIP_SENDTELEMETRY', 'windir', # windows directory (SystemRoot not available in 95/98/ME) + 'VCPKG_DISABLE_METRICS', + 'VCPKG_ROOT', ] env['ENV'] = normalize_env(env['ENV'], vs_vc_vars, force=False) From 0f8efc2522a50e88079bada52562e5cd95834c85 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 8 May 2025 08:06:19 -0600 Subject: [PATCH 314/386] Add manpage .1 files to doc tarball [skip appveyor] The three generated roff-style (.1) manpages are included in the `scons-doc` tarball which is generated at release time and uploaded to `scons.org/doc`. This provides a more natural place to grab them from if someone wants to download this tarball (its existence is not currently advertised, though one distro packager is known to use id; normally it is used to populate the documenations matrix for each version. No scons changes involved, and not user-visible, since you have to grab the tarball explicitly. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ RELEASE.txt | 7 +++++++ doc/SConscript | 3 +++ 3 files changed, 12 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 9b6f0236b7..c5a1542b90 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -46,6 +46,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER interesting to developers working on SCons (needed to write docs), even if not part of "The SCons API" itself. Reworded the API Docs intro sectios a bit. + - Include the roff (.1) manpages in the scons-doc tarball as a better + long-term home than in the sdist. RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 3c7bff54c6..f78c8c732f 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -53,6 +53,13 @@ PACKAGING - List changes in the way SCons is packaged and/or released +- The generated roff (.1) manpages are now included in the + scons-doc tarball that is built at part of the release process, + in addition to the html and txt versions. For distribution + packaging, the manpages can be extracted from here (downloadable + from https://scons.org/doc/ using a a version-specific URL, + e.g. https://scons.org/doc/4.9.1/scons-doc-4.9.1.tar.gz). + DOCUMENTATION ------------- diff --git a/doc/SConscript b/doc/SConscript index a15c71e87f..57ce85c0a9 100644 --- a/doc/SConscript +++ b/doc/SConscript @@ -582,6 +582,9 @@ else: if 'man' in targets: for m in man_page_list: + manpage = os.path.join(build, 'man', m) + tar_deps.append(manpage) + tar_list.append(manpage) man, _1 = os.path.splitext(m) pdf = os.path.join(build, 'PDF', f'{man}-man.pdf') From 0d24e9aa7fe45a81437dfab00760fcdde3217903 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 25 Apr 2025 08:24:29 -0600 Subject: [PATCH 315/386] Update virtualenv platform/tests Tweak the check for "virtualenv running" to be more modern, preferrring the current build-in scheme which has been standard since Python 3.3. Leave comments about dropping the "old approach". In unit test, mocking should always set up sys.base_prefix. Add entry for Virtualenv function to manpage and update User Guide doc. Add docstrings, reformat tests and switch to f-strings. Signed-off-by: Mats Wichmann --- SCons/Platform/virtualenv.py | 76 +++++--- SCons/Platform/virtualenv.xml | 39 ++++ SCons/Platform/virtualenvTests.py | 180 ++++++++++++------ doc/user/misc.xml | 143 ++++++++------ .../activated/option/enable-virtualenv.py | 52 +++-- .../activated/option/ignore-virtualenv.py | 48 +++-- .../activated/virtualenv_activated_python.py | 55 +++--- .../activated/virtualenv_detect_virtualenv.py | 14 +- .../always/virtualenv_global_function.py | 33 ++-- .../virtualenv_detect_regularenv.py | 14 +- .../virtualenv_unactivated_python.py | 55 +++--- 11 files changed, 450 insertions(+), 259 deletions(-) create mode 100644 SCons/Platform/virtualenv.xml diff --git a/SCons/Platform/virtualenv.py b/SCons/Platform/virtualenv.py index df7ad574d7..9e4db47f1d 100644 --- a/SCons/Platform/virtualenv.py +++ b/SCons/Platform/virtualenv.py @@ -21,7 +21,10 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -"""'Platform" support for a Python virtualenv.""" +"""Platform support for a Python virtualenv. + +This is support code, not a loadable Platform module. +""" import os import sys @@ -41,70 +44,85 @@ def _ignore_virtualenv_default(): enable_virtualenv = _enable_virtualenv_default() ignore_virtualenv = _ignore_virtualenv_default() -virtualenv_variables = ['VIRTUAL_ENV', 'PIPENV_ACTIVE'] - - -def _running_in_virtualenv(): - """Returns True if scons is executed within a virtualenv""" - # see https://stackoverflow.com/a/42580137 - return (hasattr(sys, 'real_prefix') or - (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)) - -def _is_path_in(path, base) -> bool: - """Returns true if **path** is located under the **base** directory.""" - if not path or not base: # empty path may happen, base too +# Variables to export: +# - Python docs: +# When a virtual environment has been activated, the VIRTUAL_ENV environment +# variable is set to the path of the environment. Since explicitly +# activating a virtual environment is not required to use it, VIRTUAL_ENV +# cannot be relied upon to determine whether a virtual environment is being +# used. +# - pipenv: shell sets PIPENV_ACTIVE, cannot find it documented. +# Any others we should include? +VIRTUALENV_VARIABLES = ['VIRTUAL_ENV', 'PIPENV_ACTIVE'] + + +def _running_in_virtualenv() -> bool: + """Check whether scons is running in a virtualenv.""" + # TODO: the virtualenv command used to inject a sys.real_prefix before + # Python started officially tracking virtualenvs with the venv module. + # All Pythons since 3.3 use sys.base_prefix for tracking (PEP 405); + # virtualenv has retired their old behavior and now only makes + # venv-style virtualenvs. We're now using the detection suggested in + # PEP 668, and should be able to drop the real_prefix check soon. + return sys.base_prefix != sys.prefix or hasattr(sys, 'real_prefix') + + +def _is_path_in(path: str, base: str) -> bool: + """Check if *path* is located under the *base* directory.""" + if not path or not base: # empty path or base are possible return False rp = os.path.relpath(path, base) return (not rp.startswith(os.path.pardir)) and (not rp == os.path.curdir) def _inject_venv_variables(env) -> None: + """Copy any set virtualenv variables from ``os.environ`` to *env*.""" if 'ENV' not in env: env['ENV'] = {} ENV = env['ENV'] - for name in virtualenv_variables: + for name in VIRTUALENV_VARIABLES: try: ENV[name] = os.environ[name] except KeyError: pass def _inject_venv_path(env, path_list=None) -> None: - """Modify environment such that SCons will take into account its virtualenv - when running external tools.""" + """Insert virtualenv-related paths from ``os.environe`` to *env*.""" if path_list is None: path_list = os.getenv('PATH') env.PrependENVPath('PATH', select_paths_in_venv(path_list)) -def select_paths_in_venv(path_list): - """Returns a list of paths from **path_list** which are under virtualenv's - home directory.""" +def select_paths_in_venv(path_list: str | list[str]) -> list[str]: + """Filter *path_list*, returning values under the virtualenv.""" if SCons.Util.is_String(path_list): path_list = path_list.split(os.path.pathsep) - # Find in path_list the paths under the virtualenv's home return [path for path in path_list if IsInVirtualenv(path)] def ImportVirtualenv(env) -> None: - """Copies virtualenv-related environment variables from OS environment - to ``env['ENV']`` and prepends virtualenv's PATH to ``env['ENV']['PATH']``. - """ + """Add virtualenv information to *env*.""" _inject_venv_variables(env) _inject_venv_path(env) -def Virtualenv(): - """Returns path to the virtualenv home if scons is executing within a - virtualenv or None, if not.""" +def Virtualenv() -> str | None: + """Return whether operating in a virtualenv. + + Returns the path to the virtualenv home if scons is executing + within a virtualenv, else ``None``. + """ if _running_in_virtualenv(): return sys.prefix return None -def IsInVirtualenv(path): - """Returns True, if **path** is under virtualenv's home directory. If not, - or if we don't use virtualenv, returns False.""" +def IsInVirtualenv(path: str) -> bool: + """Check whether *path* is under the virtualenv's directory. + + Returns ``False`` if not using a virtualenv. + """ return _is_path_in(path, Virtualenv()) diff --git a/SCons/Platform/virtualenv.xml b/SCons/Platform/virtualenv.xml new file mode 100644 index 0000000000..766277e141 --- /dev/null +++ b/SCons/Platform/virtualenv.xml @@ -0,0 +1,39 @@ + + + + +%scons; + +%builders-mod; + +%functions-mod; + +%tools-mod; + +%variables-mod; +]> + + + + +() + + +If the &SCons; process is running inside a &Python; +virtual environment, +return the path to the directory where that environment is stored. +Otherwise, return None. + + + + + diff --git a/SCons/Platform/virtualenvTests.py b/SCons/Platform/virtualenvTests.py index e2565205ee..5b002adb5b 100644 --- a/SCons/Platform/virtualenvTests.py +++ b/SCons/Platform/virtualenvTests.py @@ -27,6 +27,7 @@ # This happens in this unittest, since it's the script path. Remove # it before the stdlib imports. Better way to handle this problem? import sys + if 'Platform' in sys.path[0]: platpath = sys.path.pop(0) # pylint: disable=wrong-import-position @@ -40,13 +41,19 @@ import SCons.Util # pylint: enable=wrong-import-position +# TODO: this test does a lot of fiddling with obsolete sys.real_prefix. +# The 'virtualenv' tool used to force this in, but now uses venv-style +# to follow Python since 3.3. Simplify once we think it's safe enough. + class Environment(collections.UserDict): + """Mock environment for testing.""" + def Detect(self, cmd): return cmd def AppendENVPath(self, key, value) -> None: if SCons.Util.is_List(value): - value = os.path.pathsep.join(value) + value = os.path.pathsep.join(value) if 'ENV' not in self: self['ENV'] = {} current = self['ENV'].get(key) @@ -57,7 +64,7 @@ def AppendENVPath(self, key, value) -> None: def PrependENVPath(self, key, value) -> None: if SCons.Util.is_List(value): - value = os.path.pathsep.join(value) + value = os.path.pathsep.join(value) if 'ENV' not in self: self['ENV'] = {} current = self['ENV'].get(key) @@ -66,8 +73,10 @@ def PrependENVPath(self, key, value) -> None: else: self['ENV'][key] = os.path.pathsep.join([value, current]) + class SysPrefixes: - """Used to temporarily mock sys.prefix, sys.real_prefix and sys.base_prefix""" + """Context manager to mock/restore sys.{prefix,real_prefix.base_prefix}.""" + def __init__(self, prefix, real_prefix=None, base_prefix=None) -> None: self._prefix = prefix self._real_prefix = real_prefix @@ -83,7 +92,10 @@ def start(self) -> None: sys.real_prefix = self._real_prefix if self._base_prefix is None: if hasattr(sys, 'base_prefix'): - del sys.base_prefix + # Since 3.3, python always sets base_prefix. We used to + # delete it, but now we need it to behave like Python: + # if not pretending to be in a venv, should match sys.prefix. + sys.base_prefix = sys.prefix else: sys.base_prefix = self._base_prefix @@ -117,77 +129,104 @@ def _restore(self) -> None: sys.prefix = s['prefix'] del self._stored + def _p(p): - """Converts path string **p** from posix format to os-specific format.""" + """Convert path string *p* from posix format to os-specific format.""" drive = [] if p.startswith('/') and sys.platform == 'win32': - drive = ['C:'] + drive = ['C:'] pieces = p.split('/') return os.path.sep.join(drive + pieces) class _is_path_in_TestCase(unittest.TestCase): def test_false(self) -> None: - for args in [ ('',''), - ('', _p('/foo/bar')), - (_p('/foo/bar'), ''), - (_p('/foo/bar'), _p('/foo/bar')), - (_p('/foo/bar'), _p('/foo/bar/geez')), - (_p('/'), _p('/foo')), - (_p('foo'), _p('foo/bar')) ]: - assert SCons.Platform.virtualenv._is_path_in(*args) is False, "_is_path_in(%r, %r) should be False" % args + for args in [ + ('', ''), + ('', _p('/foo/bar')), + (_p('/foo/bar'), ''), + (_p('/foo/bar'), _p('/foo/bar')), + (_p('/foo/bar'), _p('/foo/bar/geez')), + (_p('/'), _p('/foo')), + (_p('foo'), _p('foo/bar')), + ]: + with self.subTest(): + assert SCons.Platform.virtualenv._is_path_in(*args) is False, ( + "_is_path_in(%r, %r) should be False" % args + ) def test__true(self) -> None: - for args in [ (_p('/foo'), _p('/')), - (_p('/foo/bar'), _p('/foo')), - (_p('/foo/bar/geez'), _p('/foo/bar')), - (_p('/foo//bar//geez'), _p('/foo/bar')), - (_p('/foo/bar/geez'), _p('/foo//bar')), - (_p('/foo/bar/geez'), _p('//foo//bar')) ]: - assert SCons.Platform.virtualenv._is_path_in(*args) is True, "_is_path_in(%r, %r) should be True" % args + for args in [ + (_p('/foo'), _p('/')), + (_p('/foo/bar'), _p('/foo')), + (_p('/foo/bar/geez'), _p('/foo/bar')), + (_p('/foo//bar//geez'), _p('/foo/bar')), + (_p('/foo/bar/geez'), _p('/foo//bar')), + (_p('/foo/bar/geez'), _p('//foo//bar')), + ]: + with self.subTest(): + assert SCons.Platform.virtualenv._is_path_in(*args) is True, ( + "_is_path_in(%r, %r) should be True" % args + ) + class IsInVirtualenvTestCase(unittest.TestCase): def test_false(self) -> None: - # "without wirtualenv" - always false + # "without virtualenv" - always false with SysPrefixes(_p('/prefix')): - for p in [ _p(''), - _p('/foo'), - _p('/prefix'), - _p('/prefix/foo') ]: - assert SCons.Platform.virtualenv.IsInVirtualenv(p) is False, "IsInVirtualenv(%r) should be False" % p + for p in [_p(''), _p('/foo'), _p('/prefix'), _p('/prefix/foo')]: + with self.subTest(): + assert SCons.Platform.virtualenv.IsInVirtualenv(p) is False, ( + f"IsInVirtualenv({p!r}) should be False" + ) # "with virtualenv" with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')): - for p in [ _p(''), - _p('/real/prefix/foo'), - _p('/virtualenv/prefix'), - _p('/virtualenv/prefix/bar/..'), - _p('/virtualenv/prefix/bar/../../bleah'), - _p('/virtualenv/bleah') ]: - assert SCons.Platform.virtualenv.IsInVirtualenv(p) is False, "IsInVirtualenv(%r) should be False" % p + for p in [ + _p(''), + _p('/real/prefix/foo'), + _p('/virtualenv/prefix'), + _p('/virtualenv/prefix/bar/..'), + _p('/virtualenv/prefix/bar/../../bleah'), + _p('/virtualenv/bleah'), + ]: + with self.subTest(): + assert SCons.Platform.virtualenv.IsInVirtualenv(p) is False, ( + f"IsInVirtualenv({p!r}) should be False" + ) # "with venv" with SysPrefixes(_p('/virtualenv/prefix'), base_prefix=_p('/base/prefix')): - for p in [ _p(''), - _p('/base/prefix/foo'), - _p('/virtualenv/prefix'), - _p('/virtualenv/prefix/bar/..'), - _p('/virtualenv/prefix/bar/../../bleah'), - _p('/virtualenv/bleah') ]: - assert SCons.Platform.virtualenv.IsInVirtualenv(p) is False, "IsInVirtualenv(%r) should be False" % p + for p in [ + _p(''), + _p('/base/prefix/foo'), + _p('/virtualenv/prefix'), + _p('/virtualenv/prefix/bar/..'), + _p('/virtualenv/prefix/bar/../../bleah'), + _p('/virtualenv/bleah'), + ]: + with self.subTest(): + assert SCons.Platform.virtualenv.IsInVirtualenv(p) is False, ( + f"IsInVirtualenv({p!r}) should be False" + ) def test_true(self) -> None: # "with virtualenv" with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')): - for p in [ _p('/virtualenv/prefix/foo'), - _p('/virtualenv/prefix/foo/bar') ]: - assert SCons.Platform.virtualenv.IsInVirtualenv(p) is True, "IsInVirtualenv(%r) should be True" % p + for p in [_p('/virtualenv/prefix/foo'), _p('/virtualenv/prefix/foo/bar')]: + with self.subTest(): + assert SCons.Platform.virtualenv.IsInVirtualenv(p) is True, ( + f"IsInVirtualenv({p!r}) should be True" + ) # "with venv" with SysPrefixes(_p('/virtualenv/prefix'), base_prefix=_p('/base/prefix')): - for p in [ _p('/virtualenv/prefix/foo'), - _p('/virtualenv/prefix/foo/bar') ]: - assert SCons.Platform.virtualenv.IsInVirtualenv(p) is True, "IsInVirtualenv(%r) should be True" % p + for p in [_p('/virtualenv/prefix/foo'), _p('/virtualenv/prefix/foo/bar')]: + with self.subTest(): + assert SCons.Platform.virtualenv.IsInVirtualenv(p) is True, ( + f"IsInVirtualenv({p!r}) should be True" + ) + class _inject_venv_pathTestCase(unittest.TestCase): def path_list(self): @@ -197,46 +236,65 @@ def path_list(self): _p('/virtualenv/prefix/../bar'), _p('/home/user/.local/bin'), _p('/usr/bin'), - _p('/opt/bin') + _p('/opt/bin'), ] + def test_with_path_string(self) -> None: env = Environment() path_string = os.path.pathsep.join(self.path_list()) - with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')): + with self.subTest(), SysPrefixes( + _p('/virtualenv/prefix'), real_prefix=_p('/real/prefix') + ): SCons.Platform.virtualenv._inject_venv_path(env, path_string) - assert env['ENV']['PATH'] == _p('/virtualenv/prefix/bin'), env['ENV']['PATH'] + assert env['ENV']['PATH'] == _p('/virtualenv/prefix/bin'), env['ENV'][ + 'PATH' + ] def test_with_path_list(self) -> None: env = Environment() - with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')): + with self.subTest(), SysPrefixes( + _p('/virtualenv/prefix'), real_prefix=_p('/real/prefix') + ): SCons.Platform.virtualenv._inject_venv_path(env, self.path_list()) - assert env['ENV']['PATH'] == _p('/virtualenv/prefix/bin'), env['ENV']['PATH'] + assert env['ENV']['PATH'] == _p('/virtualenv/prefix/bin'), env['ENV'][ + 'PATH' + ] + class VirtualenvTestCase(unittest.TestCase): def test_none(self) -> None: def _msg(given) -> str: - return "Virtualenv() should be None, not %s" % repr(given) + return f"Virtualenv() should be None, not {given!r}" - with SysPrefixes(_p('/prefix')): + with self.subTest(), SysPrefixes(_p('/prefix')): ve = SCons.Platform.virtualenv.Virtualenv() - assert ve is None , _msg(ve) - with SysPrefixes(_p('/base/prefix'), base_prefix=_p('/base/prefix')): + assert ve is None, _msg(ve) + + with self.subTest(), SysPrefixes( + _p('/base/prefix'), base_prefix=_p('/base/prefix') + ): ve = SCons.Platform.virtualenv.Virtualenv() assert ve is None, _msg(ve) def test_not_none(self) -> None: def _msg(expected, given) -> str: - return "Virtualenv() should == %r, not %s" % (_p(expected), repr(given)) + return f"Virtualenv() should == {_p(expected)!r}, not {repr(given)}" - with SysPrefixes(_p('/virtualenv/prefix'), real_prefix=_p('/real/prefix')): + with self.subTest(), SysPrefixes( + _p('/virtualenv/prefix'), real_prefix=_p('/real/prefix') + ): ve = SCons.Platform.virtualenv.Virtualenv() assert ve == _p('/virtualenv/prefix'), _msg('/virtualenv/prefix', ve) - with SysPrefixes(_p('/same/prefix'), real_prefix=_p('/same/prefix')): + + with self.subTest(), SysPrefixes( + _p('/same/prefix'), real_prefix=_p('/same/prefix') + ): ve = SCons.Platform.virtualenv.Virtualenv() - assert ve == _p('/same/prefix'), _msg('/same/prefix', ve) + assert ve == _p('/same/prefix'), _msg('/same/prefix', ve) + with SysPrefixes(_p('/virtualenv/prefix'), base_prefix=_p('/base/prefix')): ve = SCons.Platform.virtualenv.Virtualenv() - assert ve == _p('/virtualenv/prefix'), _msg('/virtualenv/prefix', ve) + assert ve == _p('/virtualenv/prefix'), _msg('/virtualenv/prefix', ve) if __name__ == "__main__": diff --git a/doc/user/misc.xml b/doc/user/misc.xml index f1c21f111a..b7e05d2bd0 100644 --- a/doc/user/misc.xml +++ b/doc/user/misc.xml @@ -35,24 +35,25 @@ This file is processed by the bin/SConsDoc.py module. -
+
Verifying the Python Version: the &EnsurePythonVersion; Function Although the &SCons; code itself will run - on any 2.x Python version 2.7 or later, - you are perfectly free to make use of - Python syntax and modules from later versions + on any 3.x Python version + (check the release notes for the precise minimum supported release), + you are free to make use of + &Python; syntax and modules from later versions when writing your &SConscript; files or your own local modules. If you do this, it's usually helpful to configure &SCons; to exit gracefully with an error message - if it's being run with a version of Python + if it's being run with a version of &Python; that simply won't work with your code. This is especially true if you're going to use &SCons; to build source code that you plan to distribute publicly, - where you can't be sure of the Python version + where you can't be sure of the &Python; version that an anonymous remote user might use to try to build your software. @@ -62,7 +63,7 @@ This file is processed by the bin/SConsDoc.py module. &SCons; provides an &EnsurePythonVersion; function for this. You simply pass it the major and minor versions - numbers of the version of Python you require: + numbers of the version of &Python; you require: @@ -74,21 +75,21 @@ This file is processed by the bin/SConsDoc.py module. -EnsurePythonVersion(2, 5) +EnsurePythonVersion(3, 8) --> -EnsurePythonVersion(2, 5) +EnsurePythonVersion(3, 8) And then &SCons; will exit with the following error message when a user runs it with an unsupported - earlier version of Python: + earlier version of &Python;: @@ -106,12 +107,12 @@ EnsurePythonVersion(2, 5) % scons -Q -Python 2.5 or greater required, but you have Python 2.3.6 +Python 3.7 or greater required, but you have Python 3.6.5
-
+
Verifying the SCons Version: the &EnsureSConsVersion; Function @@ -129,7 +130,7 @@ Python 2.5 or greater required, but you have Python 2.3.6 that verifies the version of &SCons; in the same the &EnsurePythonVersion; function - verifies the version of Python, + verifies the version of &Python;, by passing in the major and minor versions numbers of the version of SCons you require: @@ -180,7 +181,7 @@ SCons 1.0 or greater required, but you have SCons 0.98.5
-
+
Accessing SCons Version: the &GetSConsVersion; Function @@ -206,7 +207,7 @@ else:
-
+
Explicitly Terminating &SCons; While Reading &SConscript; Files: the &Exit; Function @@ -250,18 +251,18 @@ hello.c Note that the &Exit; function - is equivalent to calling the Python + is equivalent to calling the &Python; sys.exit function (which it actually calls), but because &Exit; is a &SCons; function, - you don't have to import the Python + you don't have to import the &Python; sys module to use it.
-
+
Searching for Files: the &FindFile; Function @@ -446,13 +447,13 @@ leaf
-
+
Handling Nested Lists: the &Flatten; Function &SCons; supports a &Flatten; function - which takes an input Python sequence + which takes an input &Python; sequence (list or tuple) and returns a flattened list containing just the individual elements of @@ -589,7 +590,7 @@ cc -o prog1 prog1.o prog2.o
-
+
Finding the Invocation Directory: the &GetLaunchDir; Function @@ -613,7 +614,7 @@ env.Command('directory_build_info', Because &SCons; is usually invoked from the top-level directory in which the &SConstruct; file lives, - the Python os.getcwd() + the &Python; os.getcwd() is often equivalent. However, the &SCons; -u, @@ -638,54 +639,90 @@ env.Command('directory_build_info', -
- Virtual environments (virtualenvs) +
+ Using Python Virtual Environments - Virtualenv is a tool to create isolated Python environments. - A python application (such as SCons) may be executed within - an activated virtualenv. The activation of virtualenv modifies - current environment by defining some virtualenv-specific variables - and modifying search PATH, such that executables installed within - virtualenv's home directory are preferred over the ones installed - outside of it. + &Python; supports lightweight "virtual environments" (usually + abbreviated virtualenv) which allow + encapsulation / isolation of package dependencies for a project. + When a &Python; program is executed in the context of a + virtualenv, the paths for package imports are amended so the + modules in the virtualenv are preferred. Depending on how the + virtualenv was configured, system paths may or may not be used + as a fallback (the default is not). + &SCons; itself works as expected when executed within a virtualenv. + However, there may be issues if the project needs + to build using external commands written in &Python; + which are installed in the virtualenv, + or calls the &Python; interpreter to run a script. + &SCons; launches command actions using a special restricted + PATH setting which the new process uses + to find executables. + This path is part of the execution environment + (see ), + and by default, + does not contain any information about the virtualenv. + The result can be commands not found, + or scripts executed with the system default copy of &Python; + rather than the virtualenv one, possibly causing incorrect imports. + If you encounter this problem, &SCons; provides a mechanism + to more fully integrate with a virtualenv. + - Normally, SCons uses hard-coded PATH when searching for external - executables, so it always picks-up executables from these pre-defined - locations. This applies also to python interpreter, which is invoked - by some custom SCons tools or test suites. This means, when running - SCons in a virtualenv, an eventual invocation of python interpreter from - SCons script will most probably jump out of virtualenv and execute - python executable found in hard-coded SCons PATH, not the one which is - executing SCons. Some users may consider this as an inconsistency. + + Use the + to import virtualenv-related environment variables to the + execution environment (&cv-link-ENV;) + and to modify the execution environment's PATH + appropriately to prefer the virtualenv executables and + &Python; interpreter. + + + To make this setting permanent, you can either: + + + Add it to the SCONSFLAGS environment variable , or + + + Set SCONS_ENABLE_VIRTUALENV=1 in your environment. + + + - This issue may be overcome by using the - - option. The option automatically imports virtualenv-related environment - variables to all created construction environment env['ENV'], - and modifies SCons PATH appropriately to prefer virtualenv's executables. - Setting environment variable SCONS_ENABLE_VIRTUALENV=1 - will have same effect. If virtualenv support is enabled system-wide - by the environment variable, it may be suppressed with the - option. + SCONSFLAGS is the preferred approach, + as it's easier to manage a single variable controlling &SCons; + behavior than multiples. + If enabled by environment variable, + the special virtualenv behavior can be disabled for the current + run using the option. - Inside of &SConscript;, a global function Virtualenv is - available. It returns a path to virtualenv's home directory, or - None if &scons; is not running from virtualenv. Note - that this function returns a path even if &scons; is run from an - unactivated virtualenv. + You can query the state at runtime by calling + the &f-link-Virtualenv; global function. + It returns either a path to the virtualenv's home directory, + or None if &SCons; is not running in a virtualenv. + + &f-Virtualenv; returns a path even if &SCons; + is run from an unactivated virtualenv. + A virtualenv does not have to be activated to be used, + you only need to use the path to its &Python; interpreter, + but only an activated virtualenv makes available the + suitable PATH elements for &SCons; to + copy in when is used. + +
diff --git a/test/virtualenv/activated/option/enable-virtualenv.py b/test/virtualenv/activated/option/enable-virtualenv.py index ff0658371b..0ec2c92242 100644 --- a/test/virtualenv/activated/option/enable-virtualenv.py +++ b/test/virtualenv/activated/option/enable-virtualenv.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__" """ Ensure that the --enable-virtualenv flag works. @@ -36,50 +35,61 @@ test = TestSCons.TestSCons() if SCons.Platform.virtualenv.virtualenv_enabled_by_default: - test.skip_test("Virtualenv support enabled by default, the option --enable-virtualenv is unavailable, skipping\n") + test.skip_test( + "Virtualenv support enabled by default, " + "the option --enable-virtualenv is unavailable, skipping\n" + ) if not SCons.Platform.virtualenv.Virtualenv(): test.skip_test("No virtualenv detected, skipping\n") -if not SCons.Platform.virtualenv.select_paths_in_venv(os.getenv('PATH','')): - test.skip_test("Virtualenv detected but looks like unactivated, skipping\n") +if not SCons.Platform.virtualenv.select_paths_in_venv(os.getenv('PATH', '')): + test.skip_test("Virtualenv detected but looks unactivated, skipping\n") -test.write('SConstruct', """ +test.write('SConstruct', """\ import sys import SCons.Platform.virtualenv + env = DefaultEnvironment(tools=[]) -print("sys.executable: %r" % sys.executable) -print("env.WhereIs('python'): %r" % env.WhereIs('python')) +print(f"sys.executable: {sys.executable!r}") +print(f"env.WhereIs('python'): {env.WhereIs('python')!r}") """) test.run(['-Q', '--enable-virtualenv']) s = test.stdout() m = re.search(r"""^sys\.executable:\s*(?P["'][^"']+["'])\s*$""", s, re.MULTILINE) if not m: - test.fail_test(message="""\ + test.fail_test( + message=f"""\ can't determine sys.executable from stdout: ========= STDOUT ========= -%s +{s} ========================== -""" % s) +""") interpreter = eval(m.group('py')) -m = re.search(r"""^\s*env.WhereIs\('python'\):\s*(?P["']?[^"']+["']?)\s*$""", s, re.MULTILINE) +m = re.search( + r"""^\s*env.WhereIs\('python'\):\s*(?P["']?[^"']+["']?)\s*$""", s, re.MULTILINE +) if not m: - test.fail_test(message=""" + test.fail_test(message=f""" can't determine env.WhereIs('python') from stdout: ========= STDOUT ========= -%s +{s} ========================== -""" % s) +""") python = eval(m.group('py')) -test.fail_test(not SCons.Platform.virtualenv.IsInVirtualenv(interpreter), - message="sys.executable points outside of virtualenv") -test.fail_test(not SCons.Platform.virtualenv.IsInVirtualenv(python), - message="env.WhereIs('python') points to virtualenv") +test.fail_test( + not SCons.Platform.virtualenv.IsInVirtualenv(interpreter), + message="sys.executable points outside of virtualenv", +) +test.fail_test( + not SCons.Platform.virtualenv.IsInVirtualenv(python), + message="env.WhereIs('python') points to virtualenv", +) test.pass_test() diff --git a/test/virtualenv/activated/option/ignore-virtualenv.py b/test/virtualenv/activated/option/ignore-virtualenv.py index b0d482e523..d769ed20be 100644 --- a/test/virtualenv/activated/option/ignore-virtualenv.py +++ b/test/virtualenv/activated/option/ignore-virtualenv.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__" """ Ensure that the --ignore-virtualenv flag works. @@ -38,15 +37,16 @@ if not SCons.Platform.virtualenv.Virtualenv(): test.skip_test("No virtualenv detected, skipping\n") -if not SCons.Platform.virtualenv.select_paths_in_venv(os.getenv('PATH','')): - test.skip_test("Virtualenv detected but looks like unactivated, skipping\n") +if not SCons.Platform.virtualenv.select_paths_in_venv(os.getenv('PATH', '')): + test.skip_test("Virtualenv detected but looks unactivated, skipping\n") -test.write('SConstruct', """ +test.write('SConstruct', """\ import sys import SCons.Platform.virtualenv + env = DefaultEnvironment(tools=[]) -print("sys.executable: %s" % repr(sys.executable)) -print("env.WhereIs('python'): %s" % repr(env.WhereIs('python'))) +print(f"sys.executable: {sys.executable!r}") +print(f"env.WhereIs('python'): {env.WhereIs('python')!r}") """) os.environ['SCONS_ENABLE_VIRTUALENV'] = '1' @@ -55,30 +55,36 @@ s = test.stdout() m = re.search(r"""^sys\.executable:\s*(?P["']?[^"']+["']?)\s*$""", s, re.MULTILINE) if not m: - test.fail_test(message="""\ -can't determine sys.executable from stdout: + test.fail_test( + message=f"""can't determine sys.executable from stdout: ========= STDOUT ========= -%s +{s} ========================== -""" % s) +""") interpreter = eval(m.group('py')) -m = re.search(r"""^\s*env.WhereIs\('python'\):\s*(?P["']?[^"']+["']?)\s*$""", s, re.MULTILINE) +m = re.search( + r"""\s*env.WhereIs\('python'\):\s*(?P["']?[^"']+["']?)\s*$""", s, re.MULTILINE +) if not m: - test.fail_test(message=""" + test.fail_test(message=f""" can't determine env.WhereIs('python') from stdout: ========= STDOUT ========= -%s +{s} ========================== -""" % s) +""") python = eval(m.group('py')) -test.fail_test(not SCons.Platform.virtualenv.IsInVirtualenv(interpreter), - message="sys.executable points outside of virtualenv") -test.fail_test(SCons.Platform.virtualenv.IsInVirtualenv(python), - message="env.WhereIs('python') points to virtualenv") +test.fail_test( + not SCons.Platform.virtualenv.IsInVirtualenv(interpreter), + message="sys.executable points outside of virtualenv", +) +test.fail_test( + SCons.Platform.virtualenv.IsInVirtualenv(python), + message="env.WhereIs('python') points to virtualenv", +) test.pass_test() diff --git a/test/virtualenv/activated/virtualenv_activated_python.py b/test/virtualenv/activated/virtualenv_activated_python.py index 4e793daf0b..f685c723d9 100644 --- a/test/virtualenv/activated/virtualenv_activated_python.py +++ b/test/virtualenv/activated/virtualenv_activated_python.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,15 +22,12 @@ # 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__" """ Check which python executable is running scons and which python executable -would be used by scons, when we run under activated virtualenv (i.e. PATH -contains the virtualenv's bin path). This test is skipped when ran in regular -environment or in unactivated virtualenv. +would be used by scons when we run under activated virtualenv (i.e., PATH +contains the virtualenv's bin path). This test is skipped when run in a +regular environment or in an unactivated virtualenv. """ import TestSCons @@ -42,14 +41,15 @@ test.skip_test("No virtualenv detected, skipping\n") if not SCons.Platform.virtualenv.select_paths_in_venv(os.getenv('PATH')): - test.skip_test("Virtualenv detected but looks like unactivated, skipping\n") + test.skip_test("Virtualenv detected but looks unactivated, skipping\n") -test.write('SConstruct', """ +test.write('SConstruct', """\ import sys + env = DefaultEnvironment(tools=[]) -print("sys.executable: %s" % repr(sys.executable)) -print("env.WhereIs('python'): %s" % repr(env.WhereIs('python'))) +print(f"sys.executable: {sys.executable!r}") +print(f"env.WhereIs('python'): {env.WhereIs('python')!r}") """) if SCons.Platform.virtualenv.virtualenv_enabled_by_default: @@ -60,31 +60,40 @@ s = test.stdout() m = re.search(r"""^sys\.executable:\s*(?P["']?[^"']+["']?)\s*$""", s, re.MULTILINE) if not m: - test.fail_test(message="""\ + test.fail_test( + message=f"""\ can't determine sys.executable from stdout: ========= STDOUT ========= -%s +{s} ========================== -""" % s) +""") interpreter = eval(m.group('py')) -m = re.search(r"""^\s*env\.WhereIs\('python'\):\s*(?P["'][^"']+["'])\s*$""", s, re.MULTILINE) +m = re.search( + r"""^\s*env\.WhereIs\('python'\):\s*(?P["'][^"']+["'])\s*$""", s, re.MULTILINE +) if not m: - test.fail_test(message=""" + test.fail_test( + message=f""" can't determine env.WhereIs('python') from stdout: ========= STDOUT ========= -%s +{s} ========================== -""" % s) +""") python = eval(m.group('py')) -# runing in activated virtualenv (after "activate") - PATH includes virtualenv's bin directory -test.fail_test(not SCons.Platform.virtualenv.IsInVirtualenv(interpreter), - message="sys.executable points outside of virtualenv") -test.fail_test(not SCons.Platform.virtualenv.IsInVirtualenv(python), - message="env.WhereIs('python') points outside of virtualenv") +# running in an activated virtualenv (after "activate") - +# PATH includes the virtualenv bin directory +test.fail_test( + not SCons.Platform.virtualenv.IsInVirtualenv(interpreter), + message="sys.executable points outside of virtualenv", +) +test.fail_test( + not SCons.Platform.virtualenv.IsInVirtualenv(python), + message="env.WhereIs('python') points outside of virtualenv", +) test.pass_test() diff --git a/test/virtualenv/activated/virtualenv_detect_virtualenv.py b/test/virtualenv/activated/virtualenv_detect_virtualenv.py index e7b7cb0faa..c7b5be5c31 100644 --- a/test/virtualenv/activated/virtualenv_detect_virtualenv.py +++ b/test/virtualenv/activated/virtualenv_detect_virtualenv.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__" """ Check if SCons.Platform.virtualenv.Virtualenv() works in SConscripts. @@ -37,8 +36,9 @@ if not ve: test.skip_test("Virtualenv is not active, skipping\n") -test.write('SConstruct', """ -print("virtualenv: %r" % Virtualenv()) +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +print(f"virtualenv: {Virtualenv()!r}") """) if SCons.Platform.virtualenv.virtualenv_enabled_by_default: @@ -46,7 +46,7 @@ else: test.run(['-Q', '--enable-virtualenv']) -test.must_contain_all_lines(test.stdout(), ['virtualenv: %r' % ve]) +test.must_contain_all_lines(test.stdout(), [f'virtualenv: {ve!r}']) test.pass_test() diff --git a/test/virtualenv/always/virtualenv_global_function.py b/test/virtualenv/always/virtualenv_global_function.py index 8f2c291c9d..537aa98caf 100644 --- a/test/virtualenv/always/virtualenv_global_function.py +++ b/test/virtualenv/always/virtualenv_global_function.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,13 +22,10 @@ # 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__" """ Check which python executable is running scons and which python executable -would be used by scons, when we run under activated virtualenv (i.e. PATH +would be used by scons when we run in an activated virtualenv (i.e., PATH contains the virtualenv's bin path). """ @@ -36,28 +35,32 @@ test = TestSCons.TestSCons() -test.write('SConstruct', """ -print("Virtualenv(): %r" % Virtualenv()) -""") +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +print(f"Virtualenv(): {Virtualenv()!r}") +""", +) test.run(['-Q']) s = test.stdout() m = re.search(r"^Virtualenv\(\):\s*(?P.+\S)\s*$", s, re.MULTILINE) if not m: - test.fail_test(message="""\ + test.fail_test(message=f"""\ can't determine Virtualenv() result from stdout: ========= STDOUT ========= -%s +{s} ========================== -""" % s) +""") scons_ve = m.group('ve') -our_ve = "%r" % SCons.Platform.virtualenv.Virtualenv() +our_ve = f"{SCons.Platform.virtualenv.Virtualenv()!r}" -# runing in activated virtualenv (after "activate") - PATH includes virtualenv's bin directory -test.fail_test(scons_ve != our_ve, - message="Virtualenv() from SCons != Virtualenv() from caller script (%r != %r)" % (scons_ve, our_ve)) +# running in activated virtualenv (after "activate") - PATH includes virtualenv's bin directory +test.fail_test( + scons_ve != our_ve, + message=f"Virtualenv() from SCons != Virtualenv() from caller script ({scons_ve!r} != {our_ve!r})", +) test.pass_test() diff --git a/test/virtualenv/regularenv/virtualenv_detect_regularenv.py b/test/virtualenv/regularenv/virtualenv_detect_regularenv.py index 6856838c93..66892faf23 100644 --- a/test/virtualenv/regularenv/virtualenv_detect_regularenv.py +++ b/test/virtualenv/regularenv/virtualenv_detect_regularenv.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__" """ Check if SCons.Platform.virtualenv.Virtualenv() works in SConscript. @@ -36,8 +35,9 @@ if SCons.Platform.virtualenv.Virtualenv(): test.skip_test("Virtualenv is active, skipping\n") -test.write('SConstruct', """ -print("virtualenv: %r" % Virtualenv()) +test.write('SConstruct', """\ +DefaultEnvironment(tools=[]) +print(f"virtualenv: {Virtualenv()!r}") """) if SCons.Platform.virtualenv.virtualenv_enabled_by_default: @@ -45,7 +45,7 @@ else: test.run(['-Q', '--enable-virtualenv']) -test.must_contain_all_lines(test.stdout(), ['virtualenv: %r' % None]) +test.must_contain_all_lines(test.stdout(), ["virtualenv: None"]) test.pass_test() diff --git a/test/virtualenv/unactivated/virtualenv_unactivated_python.py b/test/virtualenv/unactivated/virtualenv_unactivated_python.py index 38d5329874..266156c33b 100644 --- a/test/virtualenv/unactivated/virtualenv_unactivated_python.py +++ b/test/virtualenv/unactivated/virtualenv_unactivated_python.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,21 +22,19 @@ # 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__" """ Check which python executable is running scons and which python executable -would be used by scons, when we run under an unactivated virtualenv (i.e. PATH +would be used by scons when we run under an unactivated virtualenv (i.e., PATH does not contain virtualenv's bin path). This test is skipped if ran in a regular environment or in an activated virtualenv. """ -import TestSCons -import SCons.Platform.virtualenv import os import re +import SCons.Platform.virtualenv + +import TestSCons test = TestSCons.TestSCons() @@ -42,13 +42,14 @@ test.skip_test("No virtualenv detected, skipping\n") if SCons.Platform.virtualenv.select_paths_in_venv(os.getenv('PATH')): - test.skip_test("Virtualenv detected and it looks like activated, skipping\n") + test.skip_test("Virtualenv detected and it looks activated, skipping\n") -test.write('SConstruct', """ +test.write('SConstruct', """\ import sys + env = DefaultEnvironment(tools=[]) -print("sys.executable: %s" % repr(sys.executable)) -print("env.WhereIs('python'): %s" % repr(env.WhereIs('python'))) +print(f"sys.executable: {sys.executable!r}") +print(f"env.WhereIs('python'): {env.WhereIs('python')!r}") """) if SCons.Platform.virtualenv.virtualenv_enabled_by_default: @@ -59,31 +60,41 @@ s = test.stdout() m = re.search(r"""^sys\.executable:\s*(?P["']?[^\"']+["']?)\s*$""", s, re.MULTILINE) if not m: - test.fail_test(message="""\ + test.fail_test( + message=f"""\ can't determine sys.executable from stdout: ========= STDOUT ========= -%s +{s} ========================== -""" % s) +""") interpreter = eval(m.group('py')) -m = re.search(r"""^\s*env\.WhereIs\('python'\):\s*(?P["']?[^"']+[\"']?)\s*$""", s, re.MULTILINE) +m = re.search( + r"""^\s*env\.WhereIs\('python'\):\s*(?P["']?[^"']+[\"']?)\s*$""", + s, + re.MULTILINE, +) if not m: - test.fail_test(message=""" + test.fail_test( + message=f"""\ can't determine env.WhereIs('python') from stdout: ========= STDOUT ========= -%s +{s} ========================== -""" % s) +""") python = eval(m.group('py')) # running without activating virtualenv (by just /path/to/virtualenv/bin/python runtest.py ...). -test.fail_test(not SCons.Platform.virtualenv.IsInVirtualenv(interpreter), - message="sys.executable points outside of virtualenv") -test.fail_test(SCons.Platform.virtualenv.IsInVirtualenv(python), - message="env.WhereIs('python') points to virtualenv") +test.fail_test( + not SCons.Platform.virtualenv.IsInVirtualenv(interpreter), + message="sys.executable points outside of virtualenv", +) +test.fail_test( + SCons.Platform.virtualenv.IsInVirtualenv(python), + message="env.WhereIs('python') points to virtualenv", +) test.pass_test() From bd57eb809a599c4499e4cd78bb9b443dbdac7cd6 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Thu, 8 May 2025 11:43:06 -0600 Subject: [PATCH 316/386] Flip Virtualenv() to return only strings. Return an empty string from Virtualen() if not running in a virtualenv, instead of returning None. This can still be evaluated boolean-style, which all test cases and internal uses did (e.g. if Virtualenv():). Documentation now reflects this. Signed-off-by: Mats Wichmann --- CHANGES.txt | 7 ++ RELEASE.txt | 10 +++ SCons/Platform/virtualenv.py | 6 +- SCons/Platform/virtualenv.xml | 5 +- SCons/Platform/virtualenvTests.py | 14 ++-- doc/generated/functions.gen | 66 +++++++++++++------ doc/generated/functions.mod | 4 ++ doc/user/misc.xml | 4 +- .../virtualenv_detect_regularenv.py | 2 +- 9 files changed, 83 insertions(+), 35 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c5a1542b90..3057d0db0f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -48,6 +48,13 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Reworded the API Docs intro sectios a bit. - Include the roff (.1) manpages in the scons-doc tarball as a better long-term home than in the sdist. + - Virtualenv support module modernized: previously looked first for an + obsolete mechanism the external virtualenv tool used to use, now checks + first for the official approach introduced in PEP 405. + - Add missing manpage entry for Virtualenv(). Return type is documented + as an empty string in case of a negative result (rather than None), + and the code adjusted. All internal usage, including tests, + was dont boolean-style anyway ("if Virtualenv():"). RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index f78c8c732f..d2eea967c8 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -48,6 +48,11 @@ IMPROVEMENTS documentation: performance improvements (describe the circumstances under which they would be observed), or major code cleanups +- Virtualenv support module modernized: previously looked first for an + unofficial approach from before venv support was made part of Python + in 3.3; now looks for the official approach first. This in an internal + detail, the API is unchanged. + PACKAGING --------- @@ -76,6 +81,11 @@ DOCUMENTATION interesting to developers working on SCons (needed to write docs), even if not part of "The SCons API" itself. +- Missing documentation for the Virtualen() function is added. + Note that the User Guide previously described a negative outcomee + as returning None. It is now explicit that the path is returned if + running in a virtualenv, and an empty (falsy) string if not. + DEVELOPMENT ----------- diff --git a/SCons/Platform/virtualenv.py b/SCons/Platform/virtualenv.py index 9e4db47f1d..bb6645109d 100644 --- a/SCons/Platform/virtualenv.py +++ b/SCons/Platform/virtualenv.py @@ -107,15 +107,15 @@ def ImportVirtualenv(env) -> None: _inject_venv_path(env) -def Virtualenv() -> str | None: +def Virtualenv() -> str: """Return whether operating in a virtualenv. Returns the path to the virtualenv home if scons is executing - within a virtualenv, else ``None``. + within a virtualenv, else and empty string. """ if _running_in_virtualenv(): return sys.prefix - return None + return "" def IsInVirtualenv(path: str) -> bool: diff --git a/SCons/Platform/virtualenv.xml b/SCons/Platform/virtualenv.xml index 766277e141..0e4a50a53a 100644 --- a/SCons/Platform/virtualenv.xml +++ b/SCons/Platform/virtualenv.xml @@ -30,8 +30,9 @@ This file is processed by the bin/SConsDoc.py module. If the &SCons; process is running inside a &Python; virtual environment, -return the path to the directory where that environment is stored. -Otherwise, return None. +return the path to the directory where that environment is stored, +else an empty string. +The result can be treated as a boolean value if the path is unneeded.
diff --git a/SCons/Platform/virtualenvTests.py b/SCons/Platform/virtualenvTests.py index 5b002adb5b..cc8565db6e 100644 --- a/SCons/Platform/virtualenvTests.py +++ b/SCons/Platform/virtualenvTests.py @@ -262,23 +262,25 @@ def test_with_path_list(self) -> None: class VirtualenvTestCase(unittest.TestCase): - def test_none(self) -> None: + """Test the Virtualenv() function.""" + + def test_no_venv(self) -> None: def _msg(given) -> str: - return f"Virtualenv() should be None, not {given!r}" + return f"Virtualenv() should be empty, not {given!r}" with self.subTest(), SysPrefixes(_p('/prefix')): ve = SCons.Platform.virtualenv.Virtualenv() - assert ve is None, _msg(ve) + self.assertEqual(ve, "", msg=_msg(ve)) with self.subTest(), SysPrefixes( _p('/base/prefix'), base_prefix=_p('/base/prefix') ): ve = SCons.Platform.virtualenv.Virtualenv() - assert ve is None, _msg(ve) + self.assertEqual(ve, "", msg=_msg(ve)) - def test_not_none(self) -> None: + def test_virtualenv(self) -> None: def _msg(expected, given) -> str: - return f"Virtualenv() should == {_p(expected)!r}, not {repr(given)}" + return f"Virtualenv() should == {_p(expected)!r}, not {given!r}" with self.subTest(), SysPrefixes( _p('/virtualenv/prefix'), real_prefix=_p('/real/prefix') diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index 26f21c7f67..552e37c361 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -251,7 +251,7 @@ Future versions of &SCons; will likely forbid such usage. AddPostAction(target, action) env.AddPostAction(target, action) -Arranges for the specified +Arrange for the specified action to be performed after the specified @@ -277,13 +277,19 @@ foo = Program('foo.c') AddPostAction(foo, Chmod('$TARGET', "a-x")) + +If a target is an &f-Alias;, +action is associated with the +action of the alias, if specified. + + AddPreAction(target, action) env.AddPreAction(target, action) -Arranges for the specified +Arrange for the specified action to be performed before the specified @@ -306,38 +312,46 @@ one or more targets in the list. Note that if any of the targets are built in multiple steps, the action will be invoked just -before the "final" action that specifically +before the action step that specifically generates the specified target(s). -For example, when building an executable program -from a specified source -.c -file via an intermediate object file: +It may not always be obvious +if the process is multi-step - for example, +if you use the &Program; builder to +construct an executable program from a +.c source file, +&scons; builds an intermediate object file first; +the pre-action is invoked after this step +and just before the link command to +generate the executable program binary. +Example: foo = Program('foo.c') -AddPreAction(foo, 'pre_action') +AddPreAction(foo, 'echo "Running pre-action"') + +$ scons -Q +gcc -o foo.o -c foo.c +echo "Running pre-action" +Running pre-action +gcc -o foo foo.o + + -The specified -pre_action -would be executed before -&scons; -calls the link command that actually -generates the executable program binary -foo, -not before compiling the -foo.c -file into an object file. +If a target is an &f-Alias;, +action is associated with the +action of the alias, if specified. + Alias(alias, [source, [action]]) env.Alias(alias, [source, [action]]) -Creates an alias target that +Create an Alias node that can be used as a reference to zero or more other targets, specified by the optional source parameter. Aliases provide a way to give a shorter or more descriptive @@ -1139,7 +1153,7 @@ env.Command( import os def rename(env, target, source): - os.rename('.tmp', str(target[0])) + os.rename('.tmp', target[0]) env.Command( @@ -4859,7 +4873,7 @@ def create(target, source, env): Writes 'prefix=$SOURCE' into the file name given as $TARGET. """ - with open(str(target[0]), 'wb') as f: + with open(target[0], 'wb') as f: f.write(b'prefix=' + source[0].get_contents() + b'\n') # Fetch the prefix= argument, if any, from the command line. @@ -5000,6 +5014,16 @@ SConscript(dirs=['build/src','build/doc']) SConscript(dirs='src', variant_dir='build/src', duplicate=0) SConscript(dirs='doc', variant_dir='build/doc', duplicate=0) + + + + Virtualenv() + +If the &SCons; process is running inside a &Python; +virtual environment, +return the path to the directory where that environment is stored. +Otherwise, return None. + diff --git a/doc/generated/functions.mod b/doc/generated/functions.mod index 0fb4a354ee..f75ba297dc 100644 --- a/doc/generated/functions.mod +++ b/doc/generated/functions.mod @@ -89,6 +89,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. ValidateOptions"> Value"> VariantDir"> +Virtualenv"> WhereIs"> env.Action"> @@ -172,6 +173,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. env.ValidateOptions"> env.Value"> env.VariantDir"> +env.Virtualenv"> env.WhereIs">