diff options
author | Daniel <dmoody256@gmail.com> | 2019-01-30 18:32:45 (GMT) |
---|---|---|
committer | Daniel <dmoody256@gmail.com> | 2019-01-30 18:32:45 (GMT) |
commit | dd340d59e070ff8fcff8a893b350a0dea5b4859b (patch) | |
tree | ebf5f2499a9c14046a072573322dda3e9dd5322a /src/engine | |
parent | b68e505d30e5b2f4ed366e6db10cdd14f14b8a80 (diff) | |
parent | 43d40021b7581c40c7dbd87e021a742f2a915102 (diff) | |
download | SCons-dd340d59e070ff8fcff8a893b350a0dea5b4859b.zip SCons-dd340d59e070ff8fcff8a893b350a0dea5b4859b.tar.gz SCons-dd340d59e070ff8fcff8a893b350a0dea5b4859b.tar.bz2 |
Merge remote-tracking branch 'origin/master' into mingw_link_issue
Diffstat (limited to 'src/engine')
-rw-r--r-- | src/engine/SCons/Platform/__init__.py | 34 | ||||
-rw-r--r-- | src/engine/SCons/Platform/__init__.xml | 24 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/common.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/vc.py | 290 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/vs.py | 15 | ||||
-rw-r--r-- | src/engine/SCons/Tool/gcc.py | 38 | ||||
-rw-r--r-- | src/engine/SCons/Tool/midl.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mslib.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mslink.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Tool/msvc.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Tool/msvc.xml | 7 | ||||
-rw-r--r-- | src/engine/SCons/Tool/msvs.py | 22 | ||||
-rw-r--r-- | src/engine/SCons/Tool/msvsTests.py | 5 | ||||
-rw-r--r-- | src/engine/SCons/Tool/swig.py | 20 | ||||
-rw-r--r-- | src/engine/SCons/cpp.py | 9 | ||||
-rw-r--r-- | src/engine/SCons/cppTests.py | 107 |
16 files changed, 465 insertions, 118 deletions
diff --git a/src/engine/SCons/Platform/__init__.py b/src/engine/SCons/Platform/__init__.py index 8117e60..3168378 100644 --- a/src/engine/SCons/Platform/__init__.py +++ b/src/engine/SCons/Platform/__init__.py @@ -144,15 +144,20 @@ class TempFileMunge(object): line limitation. Example usage: - env["TEMPFILE"] = TempFileMunge - env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES','$LINKCOMSTR')}" + env["TEMPFILE"] = TempFileMunge + env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES','$LINKCOMSTR')}" By default, the name of the temporary file used begins with a - prefix of '@'. This may be configred for other tool chains by - setting '$TEMPFILEPREFIX'. - - env["TEMPFILEPREFIX"] = '-@' # diab compiler - env["TEMPFILEPREFIX"] = '-via' # arm tool chain + prefix of '@'. This may be configured for other tool chains by + setting '$TEMPFILEPREFIX': + env["TEMPFILEPREFIX"] = '-@' # diab compiler + env["TEMPFILEPREFIX"] = '-via' # arm tool chain + env["TEMPFILEPREFIX"] = '' # (the empty string) PC Lint + + You can configure the extension of the temporary file through the + TEMPFILEEXTENSION variable, which defaults to '.lnk' (see comments + in the code below): + env["TEMPFILEEXTENSION"] = '.lnt' # PC Lint """ def __init__(self, cmd, cmdstr = None): self.cmd = cmd @@ -189,21 +194,26 @@ class TempFileMunge(object): node = target[0] if SCons.Util.is_List(target) else target cmdlist = getattr(node.attributes, 'tempfile_cmdlist', None) \ if node is not None else None - if cmdlist is not None : + if cmdlist is not None: return cmdlist # We do a normpath because mktemp() has what appears to be # a bug in Windows that will use a forward slash as a path - # delimiter. Windows's link mistakes that for a command line + # delimiter. Windows' link mistakes that for a command line # switch and barfs. # - # We use the .lnk suffix for the benefit of the Phar Lap + # Default to the .lnk suffix for the benefit of the Phar Lap # linkloc linker, which likes to append an .lnk suffix if # none is given. - (fd, tmp) = tempfile.mkstemp('.lnk', text=True) + if env.has_key('TEMPFILESUFFIX'): + suffix = env.subst('$TEMPFILESUFFIX') + else: + suffix = '.lnk' + + fd, tmp = tempfile.mkstemp(suffix, text=True) native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp)) - if env.get('SHELL',None) == 'sh': + 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'\\\\') diff --git a/src/engine/SCons/Platform/__init__.xml b/src/engine/SCons/Platform/__init__.xml index 0802369..f113278 100644 --- a/src/engine/SCons/Platform/__init__.xml +++ b/src/engine/SCons/Platform/__init__.xml @@ -232,13 +232,29 @@ The suffix used for shared object file names. <summary> <para> The prefix for a temporary file used -to execute lines longer than $MAXLINELENGTH. -The default is '@'. -This may be set for toolchains that use other values, -such as '-@' for the diab compiler +to store lines lines longer than $MAXLINELENGTH +as operations which call out to a shell will fail +if the line is too long, which particularly +impacts linking. +The default is '@', which works for the Microsoft +and GNU toolchains on Windows. +Set this appropriately for other toolchains, +for example '-@' for the diab compiler or '-via' for ARM toolchain. </para> </summary> </cvar> +<cvar name="TEMPFILESUFFIX"> +<summary> +<para> +The suffix used for the temporary file name +used for long command lines. The name should +include the dot ('.') if one is wanted as +it will not be added automatically. +The default is '.lnk'. +</para> +</summary> +</cvar> + </sconsdoc> diff --git a/src/engine/SCons/Tool/MSCommon/common.py b/src/engine/SCons/Tool/MSCommon/common.py index b60cd5b..f7c07b2 100644 --- a/src/engine/SCons/Tool/MSCommon/common.py +++ b/src/engine/SCons/Tool/MSCommon/common.py @@ -46,7 +46,7 @@ elif LOGFILE: debug = lambda message: open(LOGFILE, 'a').write(message + '\n') else: logging.basicConfig(filename=LOGFILE, level=logging.DEBUG) - debug = logging.debug + debug = logging.getLogger(name=__name__).debug else: debug = lambda x: None @@ -206,7 +206,7 @@ def get_output(vcbat, args = None, env = None): output = stdout.decode("mbcs") return output -def parse_output(output, keep=("INCLUDE", "LIB", "LIBPATH", "PATH")): +def parse_output(output, keep=("INCLUDE", "LIB", "LIBPATH", "PATH", 'VSCMD_ARG_app_plat')): """ Parse output from running visual c++/studios vcvarsall.bat and running set To capture the values listed in keep diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py index 32ee96f..c4ba803 100644 --- a/src/engine/SCons/Tool/MSCommon/vc.py +++ b/src/engine/SCons/Tool/MSCommon/vc.py @@ -80,15 +80,43 @@ _ARCH_TO_CANONICAL = { "i486" : "x86", "i586" : "x86", "i686" : "x86", - "ia64" : "ia64", - "itanium" : "ia64", + "ia64" : "ia64", # deprecated + "itanium" : "ia64", # deprecated "x86" : "x86", "x86_64" : "amd64", - "x86_amd64" : "x86_amd64", # Cross compile to 64 bit from 32bits + "arm" : "arm", + "arm64" : "arm64", + "aarch64" : "arm64", +} + +# get path to the cl.exe dir for newer VS versions +# based off a tuple of (host, target) platforms +_HOST_TARGET_TO_CL_DIR_GREATER_THAN_14 = { + ("amd64","amd64") : "Hostx64\\x64", + ("amd64","x86") : "Hostx64\\x86", + ("amd64","arm") : "Hostx64\\arm", + ("amd64","arm64") : "Hostx64\\arm64", + ("x86","amd64") : "Hostx86\\x64", + ("x86","x86") : "Hostx86\\x86", + ("x86","arm") : "Hostx86\\arm", + ("x86","arm64") : "Hostx86\\arm64", +} + +# get path to the cl.exe dir for older VS versions +# based off a tuple of (host, target) platforms +_HOST_TARGET_TO_CL_DIR = { + ("amd64","amd64") : "amd64", + ("amd64","x86") : "amd64_x86", + ("amd64","arm") : "amd64_arm", + ("amd64","arm64") : "amd64_arm64", + ("x86","amd64") : "x86_amd64", + ("x86","x86") : "", + ("x86","arm") : "x86_arm", + ("x86","arm64") : "x86_arm64", } -# Given a (host, target) tuple, return the argument for the bat file. Both host -# and targets should be canonalized. +# Given a (host, target) tuple, return the argument for the bat file. +# Both host and targets should be canonalized. _HOST_TARGET_ARCH_TO_BAT_ARCH = { ("x86", "x86"): "x86", ("x86", "amd64"): "x86_amd64", @@ -96,9 +124,32 @@ _HOST_TARGET_ARCH_TO_BAT_ARCH = { ("amd64", "x86_amd64"): "x86_amd64", # This is present in (at least) VS2012 express ("amd64", "amd64"): "amd64", ("amd64", "x86"): "x86", - ("x86", "ia64"): "x86_ia64" + ("x86", "ia64"): "x86_ia64", # gone since 14.0 + ("arm", "arm"): "arm", # since 14.0, maybe gone 14.1? + ("x86", "arm"): "x86_arm", # since 14.0 + ("x86", "arm64"): "x86_arm64", # since 14.1 + ("amd64", "arm"): "amd64_arm", # since 14.0 + ("amd64", "arm64"): "amd64_arm64", # since 14.1 } +_CL_EXE_NAME = 'cl.exe' + +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' + would get converted to '14.0'. + + Args: + msvc_version: str + string representing the version number, could contain non + digit characters + + Returns: + str: the value converted to a numeric only string + + """ + return ''.join([x for x in msvc_version if x in string_digits + '.']) + def get_host_target(env): debug('vc.py:get_host_target()') @@ -189,7 +240,7 @@ _VCVER_TO_PRODUCT_DIR = { } def msvc_version_to_maj_min(msvc_version): - msvc_version_numeric = ''.join([x for x in msvc_version if x in string_digits + '.']) + msvc_version_numeric = get_msvc_version_numeric(msvc_version) t = msvc_version_numeric.split(".") if not len(t) == 2: @@ -202,21 +253,21 @@ def msvc_version_to_maj_min(msvc_version): raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) def is_host_target_supported(host_target, msvc_version): - """Return True if the given (host, target) tuple is supported given the - msvc version. - - Parameters - ---------- - host_target: tuple - tuple of (canonalized) host-target, e.g. ("x86", "amd64") for cross - compilation from 32 bits windows to 64 bits. - msvc_version: str - msvc version (major.minor, e.g. 10.0) - - Note - ---- - This only check whether a given version *may* support the given (host, - target), not that the toolchain is actually present on the machine. + """Check if the given (host, target) tuple is supported for given version. + + Args: + host_target: tuple + tuple of (canonalized) host-targets, e.g. ("x86", "amd64") + for cross compilation from 32 bit Windows to 64 bits. + msvc_version: str + msvc version (major.minor, e.g. 10.0) + + Returns: + bool: + + Note: + This only checks whether a given version *may* support the given (host, + target), not that the toolchain is actually present on the machine. """ # We assume that any Visual Studio version supports x86 as a target if host_target[1] != "x86": @@ -229,10 +280,11 @@ def is_host_target_supported(host_target, msvc_version): def find_vc_pdir_vswhere(msvc_version): """ - Find the MSVC product directory using vswhere.exe . + Find the MSVC product directory using vswhere.exe. + Run it asking for specified version and get MSVS install location :param msvc_version: - :return: MSVC install dir + :return: MSVC install dir or None """ vswhere_path = os.path.join( 'C:\\', @@ -241,14 +293,17 @@ def find_vc_pdir_vswhere(msvc_version): 'Installer', 'vswhere.exe' ) - vswhere_cmd = [vswhere_path, '-version', msvc_version, '-property', 'installationPath'] + vswhere_cmd = [vswhere_path, '-products', '*', '-version', msvc_version, '-property', 'installationPath'] if os.path.exists(vswhere_path): sp = subprocess.Popen(vswhere_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) vsdir, err = sp.communicate() - vsdir = vsdir.decode("mbcs") - vsdir = vsdir.rstrip() - vc_pdir = os.path.join(vsdir, 'VC') + vsdir = vsdir.decode("mbcs").splitlines() + # vswhere could easily return multiple lines + # 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 for now. + vc_pdir = os.path.join(vsdir[0], 'VC') return vc_pdir else: # No vswhere on system, no install info available @@ -256,13 +311,25 @@ def find_vc_pdir_vswhere(msvc_version): def find_vc_pdir(msvc_version): - """Try to find the product directory for the given - version. + """Find the 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. + + Args: + msvc_version: str + msvc version (major.minor, e.g. 10.0) - Note - ---- - If for some reason the requested version could not be found, an - exception which inherits from VisualCException will be raised.""" + 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. + """ root = 'Software\\' try: hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] @@ -276,8 +343,10 @@ def find_vc_pdir(msvc_version): if not key: comps = find_vc_pdir_vswhere(msvc_version) if not comps: - debug('find_vc_dir(): no VC found via vswhere for version {}'.format(repr(key))) + debug('find_vc_pdir_vswhere(): no VC found for version {}'.format(repr(msvc_version))) raise SCons.Util.WinError + debug('find_vc_pdir_vswhere(): VC found: {}'.format(repr(msvc_version))) + return comps else: if common.is_win64(): try: @@ -309,10 +378,10 @@ def find_batch_file(env,msvc_version,host_arch,target_arch): if pdir is None: raise NoVersionFound("No version of Visual Studio found") - debug('vc.py: find_batch_file() pdir:{}'.format(pdir)) + debug('vc.py: find_batch_file() in {}'.format(pdir)) # filter out e.g. "Exp" from the version name - msvc_ver_numeric = ''.join([x for x in msvc_version if x in string_digits + "."]) + msvc_ver_numeric = get_msvc_version_numeric(msvc_version) vernum = float(msvc_ver_numeric) if 7 <= vernum < 8: pdir = os.path.join(pdir, os.pardir, "Common7", "Tools") @@ -344,29 +413,132 @@ def find_batch_file(env,msvc_version,host_arch,target_arch): __INSTALLED_VCS_RUN = None -def cached_get_installed_vcs(): +def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version): + """Find the cl.exe on the filesystem 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 its None, + which then the native platform is assumed the host and target. + + Args: + env: Environment + a construction environment, usually if this is passed its + because there is a desired TARGET_ARCH to be used when searching + for a cl.exe + vc_dir: str + the path to the VC dir in the MSVC installation + msvc_version: str + msvc version (major.minor, e.g. 10.0) + + Returns: + bool: + + """ + + # determine if there is a specific target platform we want to build for and + # use that to find a list of valid VCs, default is host platform == target platform + # and same for if no env is specified to extract target platform from + if env: + (host_platform, target_platform, req_target_platform) = get_host_target(env) + else: + host_platform = platform.machine().lower() + target_platform = host_platform + + host_platform = _ARCH_TO_CANONICAL[host_platform] + target_platform = _ARCH_TO_CANONICAL[target_platform] + + debug('_check_cl_exists_in_vc_dir(): host platform %s, target platform %s' % (host_platform, target_platform)) + + ver_num = float(get_msvc_version_numeric(msvc_version)) + + # make sure the cl.exe exists meaning the tool is installed + if ver_num > 14: + # 2017 and newer allowed multiple versions of the VC toolset to be installed at the same time. + # Just get the default tool version for now + #TODO: support setting a specific minor VC version + default_toolset_file = os.path.join(vc_dir, r'Auxiliary\Build\Microsoft.VCToolsVersion.default.txt') + try: + with open(default_toolset_file) as f: + vc_specific_version = f.readlines()[0].strip() + except IOError: + debug('_check_cl_exists_in_vc_dir(): failed to read ' + default_toolset_file) + return False + except IndexError: + debug('_check_cl_exists_in_vc_dir(): failed to find MSVC version in ' + default_toolset_file) + return False + + host_trgt_dir = _HOST_TARGET_TO_CL_DIR_GREATER_THAN_14.get((host_platform, target_platform), None) + if not host_trgt_dir: + debug('_check_cl_exists_in_vc_dir(): unsupported host/target platform combo') + return False + + cl_path = os.path.join(vc_dir, r'Tools\MSVC', vc_specific_version, 'bin', host_trgt_dir, _CL_EXE_NAME) + debug('_check_cl_exists_in_vc_dir(): checking for ' + _CL_EXE_NAME + ' at ' + cl_path) + if os.path.exists(cl_path): + debug('_check_cl_exists_in_vc_dir(): found ' + _CL_EXE_NAME + '!') + return True + + elif ver_num <= 14 and ver_num >= 8: + + host_trgt_dir = _HOST_TARGET_TO_CL_DIR.get((host_platform, target_platform), None) + if not host_trgt_dir: + debug('_check_cl_exists_in_vc_dir(): unsupported host/target platform combo') + return False + + cl_path = os.path.join(vc_dir, 'bin', host_trgt_dir, _CL_EXE_NAME) + debug('_check_cl_exists_in_vc_dir(): checking for ' + _CL_EXE_NAME + ' at ' + cl_path) + + cl_path_exists = os.path.exists(cl_path) + if not cl_path_exists and host_platform == 'amd64': + # older versions of visual studio only had x86 binaries, + # so if the host platform is amd64, we need to check cross + # compile options (x86 binary compiles some other target on a 64 bit os) + host_trgt_dir = _HOST_TARGET_TO_CL_DIR.get(('x86', target_platform), None) + if not host_trgt_dir: + return False + + cl_path = os.path.join(vc_dir, 'bin', host_trgt_dir, _CL_EXE_NAME) + debug('_check_cl_exists_in_vc_dir(): checking for ' + _CL_EXE_NAME + ' at ' + cl_path) + cl_path_exists = os.path.exists(cl_path) + + if cl_path_exists: + debug('_check_cl_exists_in_vc_dir(): found ' + _CL_EXE_NAME + '!') + return True + + elif ver_num < 8 and ver_num >= 6: + # not sure about these versions so if a walk the VC dir (could be slow) + for root, _, files in os.walk(vc_dir): + if _CL_EXE_NAME in files: + debug('get_installed_vcs ' + _CL_EXE_NAME + ' found %s' % os.path.join(root, _CL_EXE_NAME)) + return True + return False + else: + # version not support return false + debug('_check_cl_exists_in_vc_dir(): unsupported MSVC version: ' + str(ver_num)) + + return False + +def cached_get_installed_vcs(env=None): global __INSTALLED_VCS_RUN if __INSTALLED_VCS_RUN is None: - ret = get_installed_vcs() + ret = get_installed_vcs(env) __INSTALLED_VCS_RUN = ret return __INSTALLED_VCS_RUN -def get_installed_vcs(): +def get_installed_vcs(env=None): installed_versions = [] + for ver in _VCVER: debug('trying to find VC %s' % ver) try: VC_DIR = find_vc_pdir(ver) if VC_DIR: debug('found VC %s' % ver) - # check to see if the x86 or 64 bit compiler is in the bin dir - if (os.path.exists(os.path.join(VC_DIR, r'bin\cl.exe')) - or os.path.exists(os.path.join(VC_DIR, r'bin\amd64\cl.exe'))): + if _check_cl_exists_in_vc_dir(env, VC_DIR, ver): installed_versions.append(ver) else: - debug('find_vc_pdir no cl.exe found %s' % ver) + debug('find_vc_pdir no compiler found %s' % ver) else: debug('find_vc_pdir return None for ver %s' % ver) except VisualCException as e: @@ -423,7 +595,7 @@ def get_default_version(env): % (msvc_version, msvs_version)) return msvs_version if not msvc_version: - installed_vcs = cached_get_installed_vcs() + installed_vcs = cached_get_installed_vcs(env) debug('installed_vcs:%s' % installed_vcs) if not installed_vcs: #msg = 'No installed VCs' @@ -450,16 +622,17 @@ def msvc_find_valid_batch_script(env,version): debug('vc.py:msvc_find_valid_batch_script()') # Find the host platform, target platform, and if present the requested # target platform - (host_platform, target_platform,req_target_platform) = get_host_target(env) + platforms = get_host_target(env) + debug("vc.py: msvs_find_valid_batch_script(): host_platform %s, target_platform %s req_target_platform:%s" % platforms) + host_platform, target_platform, req_target_platform = platforms try_target_archs = [target_platform] - debug("msvs_find_valid_batch_script(): req_target_platform %s target_platform:%s"%(req_target_platform,target_platform)) # VS2012 has a "cross compile" environment to build 64 bit # with x86_amd64 as the argument to the batch setup script - if req_target_platform in ('amd64','x86_64'): + if req_target_platform in ('amd64', 'x86_64'): try_target_archs.append('x86_amd64') - elif not req_target_platform and target_platform in ['amd64','x86_64']: + elif not req_target_platform and target_platform in ['amd64', 'x86_64']: # There may not be "native" amd64, but maybe "cross" x86_amd64 tools try_target_archs.append('x86_amd64') # If the user hasn't specifically requested a TARGET_ARCH, and @@ -481,7 +654,7 @@ def msvc_find_valid_batch_script(env,version): (host_target, version) SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target] - + # Get just version numbers maj, min = msvc_version_to_maj_min(version) # VS2015+ @@ -500,15 +673,17 @@ def msvc_find_valid_batch_script(env,version): warn_msg = "VC version %s not installed. " + \ "C/C++ compilers are most likely not set correctly.\n" + \ " Installed versions are: %s" - warn_msg = warn_msg % (version, cached_get_installed_vcs()) + warn_msg = warn_msg % (version, cached_get_installed_vcs(env)) SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) continue # Try to use the located batch file for this host/target platform combo debug('vc.py:msvc_find_valid_batch_script() use_script 2 %s, args:%s\n' % (repr(vc_script), arg)) + found = None if vc_script: try: d = script_env(vc_script, args=arg) + found = vc_script except BatchFileExecutionError as e: debug('vc.py:msvc_find_valid_batch_script() use_script 3: failed running VC script %s: %s: Error:%s'%(repr(vc_script),arg,e)) vc_script=None @@ -517,6 +692,7 @@ def msvc_find_valid_batch_script(env,version): debug('vc.py:msvc_find_valid_batch_script() use_script 4: trying sdk script: %s'%(sdk_script)) try: d = script_env(sdk_script) + found = sdk_script except BatchFileExecutionError as e: debug('vc.py:msvc_find_valid_batch_script() use_script 5: failed running SDK script %s: Error:%s'%(repr(sdk_script),e)) continue @@ -524,7 +700,7 @@ def msvc_find_valid_batch_script(env,version): debug('vc.py:msvc_find_valid_batch_script() use_script 6: Neither VC script nor SDK script found') continue - debug("vc.py:msvc_find_valid_batch_script() Found a working script/target: %s %s"%(repr(sdk_script),arg)) + debug("vc.py:msvc_find_valid_batch_script() Found a working script/target: %s/%s"%(repr(found),arg)) break # We've found a working target_platform, so stop looking # If we cannot find a viable installed compiler, reset the TARGET_ARCH @@ -572,15 +748,15 @@ def msvc_setup_env(env): for k, v in d.items(): debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v)) env.PrependENVPath(k, v, delete_existing=True) - + # final check to issue a warning if the compiler is not present msvc_cl = find_program_path(env, 'cl') if not msvc_cl: - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, - "Could not find MSVC compiler 'cl.exe', it may need to be installed separately with Visual Studio") + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, + "Could not find MSVC compiler 'cl', it may need to be installed separately with Visual Studio") -def msvc_exists(version=None): - vcs = cached_get_installed_vcs() +def msvc_exists(env=None, version=None): + vcs = cached_get_installed_vcs(env) if version is None: return len(vcs) > 0 return version in vcs diff --git a/src/engine/SCons/Tool/MSCommon/vs.py b/src/engine/SCons/Tool/MSCommon/vs.py index f9382fb..8fd4ea0 100644 --- a/src/engine/SCons/Tool/MSCommon/vs.py +++ b/src/engine/SCons/Tool/MSCommon/vs.py @@ -68,9 +68,9 @@ class VisualStudio(object): SCons.Tool.MSCommon.vc.get_installed_vcs() dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version) if not dir: - debug('find_vs_dir(): no installed VC %s' % self.vc_version) + debug('find_vs_dir_by_vc(): no installed VC %s' % self.vc_version) return None - return dir + return os.path.abspath(os.path.join(dir, os.pardir)) def find_vs_dir_by_reg(self): root = 'Software\\' @@ -95,12 +95,11 @@ class VisualStudio(object): First try to find by registry, and if that fails find via VC dir """ - - if True: - vs_dir=self.find_vs_dir_by_reg() - return vs_dir - else: - return self.find_vs_dir_by_vc() + vs_dir=self.find_vs_dir_by_reg() + if not vs_dir: + vs_dir = self.find_vs_dir_by_vc() + debug('find_vs_dir(): found VS in ' + str(vs_dir )) + return vs_dir def find_executable(self): vs_dir = self.get_vs_dir() diff --git a/src/engine/SCons/Tool/gcc.py b/src/engine/SCons/Tool/gcc.py index fabcc96..79b64f0 100644 --- a/src/engine/SCons/Tool/gcc.py +++ b/src/engine/SCons/Tool/gcc.py @@ -68,32 +68,40 @@ def exists(env): def detect_version(env, cc): """Return the version of the GNU compiler, or None if it is not a GNU compiler.""" + version = None cc = env.subst(cc) if not cc: - return None - version = None + return version + + # -dumpversion was added in GCC 3.0. As long as we're supporting + # GCC versions older than that, we should use --version and a + # regular expression. # pipe = SCons.Action._subproc(env, SCons.Util.CLVar(cc) + ['-dumpversion'], pipe = SCons.Action._subproc(env, SCons.Util.CLVar(cc) + ['--version'], stdin='devnull', stderr='devnull', stdout=subprocess.PIPE) - # -dumpversion was added in GCC 3.0. As long as we're supporting - # GCC versions older than that, we should use --version and a - # regular expression. - # line = pipe.stdout.read().strip() + if pipe.wait() != 0: + return version + + with pipe.stdout: + # -dumpversion variant: + # line = pipe.stdout.read().strip() + # --version variant: + line = SCons.Util.to_str(pipe.stdout.readline()) + # Non-GNU compiler's output (like AIX xlc's) may exceed the stdout buffer: + # So continue with reading to let the child process actually terminate. + while SCons.Util.to_str(pipe.stdout.readline()): + pass + + # -dumpversion variant: # if line: - # version = line - line = SCons.Util.to_str(pipe.stdout.readline()) + # version = line + # --version variant: match = re.search(r'[0-9]+(\.[0-9]+)+', line) if match: version = match.group(0) - # Non-GNU compiler's output (like AIX xlc's) may exceed the stdout buffer: - # So continue with reading to let the child process actually terminate. - while SCons.Util.to_str(pipe.stdout.readline()): - pass - ret = pipe.wait() - if ret != 0: - return None + return version # Local Variables: diff --git a/src/engine/SCons/Tool/midl.py b/src/engine/SCons/Tool/midl.py index ed9ea94..2757c34 100644 --- a/src/engine/SCons/Tool/midl.py +++ b/src/engine/SCons/Tool/midl.py @@ -79,7 +79,7 @@ def generate(env): env['BUILDERS']['TypeLibrary'] = midl_builder def exists(env): - return msvc_exists() + return msvc_exists(env) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/mslib.py b/src/engine/SCons/Tool/mslib.py index c5a7a32..c901a75 100644 --- a/src/engine/SCons/Tool/mslib.py +++ b/src/engine/SCons/Tool/mslib.py @@ -55,7 +55,7 @@ def generate(env): env['LIBSUFFIX'] = '.lib' def exists(env): - return msvc_exists() + return msvc_exists(env) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index 55cf33f..c8b00d2 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -328,7 +328,7 @@ def generate(env): env['LDMODULECOM'] = compositeLdmodAction def exists(env): - return msvc_exists() + return msvc_exists(env) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index 1412cf7..9f3c1fa 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -289,7 +289,7 @@ def generate(env): env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root() def exists(env): - return msvc_exists() + return msvc_exists(env) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/msvc.xml b/src/engine/SCons/Tool/msvc.xml index d4ffe0d..dacdcba 100644 --- a/src/engine/SCons/Tool/msvc.xml +++ b/src/engine/SCons/Tool/msvc.xml @@ -353,6 +353,7 @@ constructor; setting it later has no effect. <para> Valid values for Windows are +<literal>14.1</literal>, <literal>14.0</literal>, <literal>14.0Exp</literal>, <literal>12.0</literal>, @@ -425,18 +426,24 @@ This variable must be passed as an argument to the Environment() constructor; setting it later has no effect. This is currently only used on Windows, but in the future it will be used on other OSes as well. +If this is set and MSVC_VERSION is not set, this will search for +all installed MSVC's that support the TARGET_ARCH, selecting the +latest version for use. </para> <para> Valid values for Windows are <literal>x86</literal>, +<literal>arm</literal>, <literal>i386</literal> (for 32 bits); <literal>amd64</literal>, +<literal>arm64</literal>, <literal>emt64</literal>, <literal>x86_64</literal> (for 64 bits); and <literal>ia64</literal> (Itanium). + For example, if you want to compile 64-bit binaries, you would set <literal>TARGET_ARCH='x86_64'</literal> in your SCons environment. </para> diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index 62f27f2..60ba278 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -1096,12 +1096,17 @@ V10DSPCommandLine = """\ \t\t<NMakeForcedUsingAssemblies Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'">$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies> """ +V15DSPHeader = """\ +<?xml version="1.0" encoding="%(encoding)s"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +""" + class _GenerateV10DSP(_DSPGenerator, _GenerateV10User): """Generates a Project file for MSVS 2010""" - def __init__(self, dspfile, source, env): + def __init__(self, dspfile, header, source, env): _DSPGenerator.__init__(self, dspfile, source, env) - self.dspheader = V10DSPHeader + self.dspheader = header self.dspconfiguration = V10DSPProjectConfiguration self.dspglobals = V10DSPGlobals @@ -1501,7 +1506,9 @@ class _GenerateV7DSW(_DSWGenerator): def PrintSolution(self): """Writes a solution file""" self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr) - if self.version_num >= 12.0: + if self.version_num > 14.0: + self.file.write('# Visual Studio 15\n') + elif self.version_num >= 12.0: self.file.write('# Visual Studio 14\n') elif self.version_num >= 11.0: self.file.write('# Visual Studio 11\n') @@ -1679,8 +1686,11 @@ def GenerateDSP(dspfile, source, env): version_num = 6.0 if 'MSVS_VERSION' in env: version_num, suite = msvs_parse_version(env['MSVS_VERSION']) - if version_num >= 10.0: - g = _GenerateV10DSP(dspfile, source, env) + if version_num > 14.0: + g = _GenerateV10DSP(dspfile, V15DSPHeader, source, env) + g.Build() + elif version_num >= 10.0: + g = _GenerateV10DSP(dspfile, V10DSPHeader, source, env) g.Build() elif version_num >= 7.0: g = _GenerateV7DSP(dspfile, source, env) @@ -1990,7 +2000,7 @@ def generate(env): env['SCONS_HOME'] = os.environ.get('SCONS_HOME') def exists(env): - return msvc_exists() + return msvc_exists(env) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/msvsTests.py b/src/engine/SCons/Tool/msvsTests.py index bf82114..6adc598 100644 --- a/src/engine/SCons/Tool/msvsTests.py +++ b/src/engine/SCons/Tool/msvsTests.py @@ -540,6 +540,10 @@ def DummyQueryValue(key, value): def DummyExists(path): return 1 +def DummyVsWhere(msvc_version): + # not testing versions with vswhere, so return none + return None + class msvsTestCase(unittest.TestCase): """This test case is run several times with different defaults. See its subclasses below.""" @@ -809,6 +813,7 @@ if __name__ == "__main__": SCons.Util.RegEnumKey = DummyEnumKey SCons.Util.RegEnumValue = DummyEnumValue SCons.Util.RegQueryValueEx = DummyQueryValue + 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/src/engine/SCons/Tool/swig.py b/src/engine/SCons/Tool/swig.py index 08881a0..77cfe1d 100644 --- a/src/engine/SCons/Tool/swig.py +++ b/src/engine/SCons/Tool/swig.py @@ -135,21 +135,31 @@ def _swigEmitter(target, source, env): def _get_swig_version(env, swig): """Run the SWIG command line tool to get and return the version number""" + version = None swig = env.subst(swig) + if not swig: + return version pipe = SCons.Action._subproc(env, SCons.Util.CLVar(swig) + ['-version'], stdin = 'devnull', stderr = 'devnull', stdout = subprocess.PIPE) - if pipe.wait() != 0: return + if pipe.wait() != 0: + return version # MAYBE: out = SCons.Util.to_str (pipe.stdout.read()) - out = SCons.Util.to_str(pipe.stdout.read()) + with pipe.stdout: + out = SCons.Util.to_str(pipe.stdout.read()) + match = re.search('SWIG Version\s+(\S+).*', out, re.MULTILINE) if match: - if verbose: print("Version is:%s"%match.group(1)) - return match.group(1) + version = match.group(1) + if verbose: + print("Version is: %s" % version) else: - if verbose: print("Unable to detect version: [%s]"%out) + if verbose: + print("Unable to detect version: [%s]" % out) + + return version def generate(env): """Add Builders and construction variables for swig to an Environment.""" diff --git a/src/engine/SCons/cpp.py b/src/engine/SCons/cpp.py index d2178a7..cdd3923 100644 --- a/src/engine/SCons/cpp.py +++ b/src/engine/SCons/cpp.py @@ -43,10 +43,13 @@ import re # that we want to fetch, using the regular expressions to which the lists # of preprocessor directives map. cpp_lines_dict = { - # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument, + # Fetch the rest of a #if/#elif as one argument, + # with white space optional. + ('if', 'elif') : '\s*(.+)', + + # Fetch the rest of a #ifdef/#ifndef as one argument, # separated from the keyword by white space. - ('if', 'elif', 'ifdef', 'ifndef',) - : '\s+(.+)', + ('ifdef', 'ifndef',): '\s+(.+)', # Fetch the rest of a #import/#include/#include_next line as one # argument, with white space optional. diff --git a/src/engine/SCons/cppTests.py b/src/engine/SCons/cppTests.py index d496273..ebf7fde 100644 --- a/src/engine/SCons/cppTests.py +++ b/src/engine/SCons/cppTests.py @@ -363,6 +363,62 @@ ifndef_input = """ """ +if_defined_no_space_input = """ +#define DEFINED 0 + +#if(defined DEFINED) +#include "file47-yes" +#endif + +#if(!defined DEFINED) +#include <file48-no> +#elif(!defined DEFINED) +#include <file49-no> +#else +#include <file50-yes> +#endif + +#if!(defined DEFINED) +#include "file51-no" +#elif!(defined DEFINED) +#include <file52-no> +#else +#include "file53-yes" +#endif +""" + +if_no_space_input = """ +#define DEFINED 0 + +#if(DEFINED) +#include "file54-no" +#endif + +#if!(DEFINED) +#include <file55-yes> +#elif!(DEFINED) +#include <file56-no> +#endif + +#if(DEFINED) +#include "file57-no" +#elif(!DEFINED) +#include <file58-yes> +#endif + +#if!( DEFINED) +#include "file59-yes" +#elif!( DEFINED) +#include <file60-no> +#endif + +#if( DEFINED) +#include "file61-no" +#elif(! DEFINED) +#include <file62-yes> +#endif +""" + # pp_class = PreProcessor # #pp_class = DumbPreProcessor @@ -450,6 +506,19 @@ class cppTestCase(unittest.TestCase): result = self.cpp.process_contents(ifndef_input) assert expect == result, (expect, result) + def test_if_defined_no_space(self): + """Test #if(defined, i.e.without space but parenthesis""" + expect = self.if_defined_no_space_expect + result = self.cpp.process_contents(if_defined_no_space_input) + assert expect == result, (expect, result) + + def test_if_no_space(self): + """Test #if(, i.e. without space but parenthesis""" + expect = self.if_no_space_expect + result = self.cpp.process_contents(if_no_space_input) + assert expect == result, (expect, result) + + class cppAllTestCase(cppTestCase): def setUp(self): self.cpp = self.cpp_class(current = ".", @@ -541,7 +610,20 @@ class PreProcessorTestCase(cppAllTestCase): ('include', '"', 'file45-yes'), ('include', '<', 'file46-yes'), ] - + + if_defined_no_space_expect = [ + ('include', '"', 'file47-yes'), + ('include', '<', 'file50-yes'), + ('include', '"', 'file53-yes'), + ] + + if_no_space_expect = [ + ('include', '<', 'file55-yes'), + ('include', '<', 'file58-yes'), + ('include', '"', 'file59-yes'), + ('include', '<', 'file62-yes'), + ] + class DumbPreProcessorTestCase(cppAllTestCase): cpp_class = cpp.DumbPreProcessor @@ -654,7 +736,6 @@ class DumbPreProcessorTestCase(cppAllTestCase): ('include', '"', 'file7-yes') ] - ifndef_expect = [ ('include', '"', 'file45-no'), ('include', '"', 'file45-yes'), @@ -662,6 +743,28 @@ class DumbPreProcessorTestCase(cppAllTestCase): ('include', '<', 'file46-no'), ] + if_defined_no_space_expect = [ + ('include', '"', 'file47-yes'), + ('include', '<', 'file48-no'), + ('include', '<', 'file49-no'), + ('include', '<', 'file50-yes'), + ('include', '"', 'file51-no'), + ('include', '<', 'file52-no'), + ('include', '"', 'file53-yes'), + ] + + if_no_space_expect = [ + ('include', '"', 'file54-no'), + ('include', '<', 'file55-yes'), + ('include', '<', 'file56-no'), + ('include', '"', 'file57-no'), + ('include', '<', 'file58-yes'), + ('include', '"', 'file59-yes'), + ('include', '<', 'file60-no'), + ('include', '"', 'file61-no'), + ('include', '<', 'file62-yes'), + ] + import os import re import shutil |