diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2019-07-24 09:57:09 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2019-07-24 09:57:09 +0200 |
commit | c7665433b2004d2b404d6fb9d6fd064998486f63 (patch) | |
tree | 8525ef6d24f7c6ceb238945ebb2cc997c7afc905 /src/engine/SCons/Tool/MSCommon | |
parent | e48d2727885efda8369c7edbc2e3929a59532adc (diff) | |
parent | 6e228c305122f0564eda1e67d56651f8386d24d7 (diff) |
Merge branch 'release/debian/3.1.0+repack-1'debian/3.1.0+repack-1
Diffstat (limited to 'src/engine/SCons/Tool/MSCommon')
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/__init__.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/arch.py | 12 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/common.py | 17 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/netframework.py | 6 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/sdk.py | 14 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/vc.py | 375 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/vcTests.py | 180 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/vs.py | 32 |
8 files changed, 533 insertions, 107 deletions
diff --git a/src/engine/SCons/Tool/MSCommon/__init__.py b/src/engine/SCons/Tool/MSCommon/__init__.py index 989ab08..e3c4471 100644 --- a/src/engine/SCons/Tool/MSCommon/__init__.py +++ b/src/engine/SCons/Tool/MSCommon/__init__.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/MSCommon/__init__.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """ Common functions for Microsoft Visual Studio and Visual C/C++. diff --git a/src/engine/SCons/Tool/MSCommon/arch.py b/src/engine/SCons/Tool/MSCommon/arch.py index 96f05a8..6312541 100644 --- a/src/engine/SCons/Tool/MSCommon/arch.py +++ b/src/engine/SCons/Tool/MSCommon/arch.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/MSCommon/arch.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/arch.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """Module to define supported Windows chip architectures. """ @@ -37,22 +37,22 @@ class ArchDefinition(object): self.synonyms = synonyms SupportedArchitectureList = [ - ArchitectureDefinition( + ArchDefinition( 'x86', ['i386', 'i486', 'i586', 'i686'], ), - ArchitectureDefinition( + ArchDefinition( 'x86_64', ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], ), - ArchitectureDefinition( + ArchDefinition( 'ia64', ['IA64'], ), - ArchitectureDefinition( + ArchDefinition( 'arm', ['ARM'], ), diff --git a/src/engine/SCons/Tool/MSCommon/common.py b/src/engine/SCons/Tool/MSCommon/common.py index fcf623f..8353944 100644 --- a/src/engine/SCons/Tool/MSCommon/common.py +++ b/src/engine/SCons/Tool/MSCommon/common.py @@ -2,7 +2,7 @@ Common helper functions for working with the Microsoft tool chain. """ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -25,7 +25,7 @@ Common helper functions for working with the Microsoft tool chain. # from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/MSCommon/common.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/common.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import copy import os @@ -34,7 +34,6 @@ import re import SCons.Util - LOGFILE = os.environ.get('SCONS_MSCOMMON_DEBUG') if LOGFILE == '-': def debug(message): @@ -46,7 +45,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 @@ -117,7 +116,7 @@ def normalize_env(env, keys, force=False): normenv[k] = copy.deepcopy(env[k]) for k in keys: - if k in os.environ and (force or not k in normenv): + if k in os.environ and (force or k not in normenv): normenv[k] = os.environ[k] # This shouldn't be necessary, since the default environment should include system32, @@ -188,8 +187,10 @@ def get_output(vcbat, args = None, env = None): # Use the .stdout and .stderr attributes directly because the # .communicate() method uses the threading module on Windows # and won't work under Pythons not built with threading. - stdout = popen.stdout.read() - stderr = popen.stderr.read() + with popen.stdout: + stdout = popen.stdout.read() + with popen.stderr: + stderr = popen.stderr.read() # Extra debug logic, uncomment if necessary # debug('get_output():stdout:%s'%stdout) @@ -206,7 +207,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/netframework.py b/src/engine/SCons/Tool/MSCommon/netframework.py index b769c44..edd9fd9 100644 --- a/src/engine/SCons/Tool/MSCommon/netframework.py +++ b/src/engine/SCons/Tool/MSCommon/netframework.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 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,7 +20,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. -__revision__ = "src/engine/SCons/Tool/MSCommon/netframework.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/netframework.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """ """ @@ -68,7 +68,7 @@ def query_versions(): # sequence comparison in python is lexicographical # which is exactly what we want. # Note we sort backwards so the highest version is first. - return cmp(bbl,aal) + return (aal > bbl) - (aal < bbl) versions.sort(versrt) else: diff --git a/src/engine/SCons/Tool/MSCommon/sdk.py b/src/engine/SCons/Tool/MSCommon/sdk.py index 8bc9830..e664a30 100644 --- a/src/engine/SCons/Tool/MSCommon/sdk.py +++ b/src/engine/SCons/Tool/MSCommon/sdk.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -__revision__ = "src/engine/SCons/Tool/MSCommon/sdk.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/sdk.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """Module to detect the Platform/Windows SDK @@ -178,6 +178,16 @@ SDK100VCSetupScripts = {'x86' : r'bin\vcvars32.bat', # # If you update this list, update the documentation in Tool/mssdk.xml. SupportedSDKList = [ + WindowsSDK('10.0A', + sanity_check_file=r'bin\SetEnv.Cmd', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + vc_setup_scripts = SDK70VCSetupScripts, + ), WindowsSDK('10.0', sanity_check_file=r'bin\SetEnv.Cmd', include_subdir='include', diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py index fba86af..fa7be96 100644 --- a/src/engine/SCons/Tool/MSCommon/vc.py +++ b/src/engine/SCons/Tool/MSCommon/vc.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -30,7 +30,7 @@ # * test on 64 bits XP + VS 2005 (and VS 6 if possible) # * SDK # * Assembly -__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """Module for Visual C/C++ detection and configuration. """ @@ -43,6 +43,7 @@ import platform from string import digits as string_digits import SCons.Warnings +from SCons.Tool import find_program_path from . import common @@ -59,7 +60,10 @@ class VisualCException(Exception): class UnsupportedVersion(VisualCException): pass -class UnsupportedArch(VisualCException): +class MSVCUnsupportedHostArch(VisualCException): + pass + +class MSVCUnsupportedTargetArch(VisualCException): pass class MissingConfiguration(VisualCException): @@ -79,15 +83,41 @@ _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", +} + +_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"), } -# Given a (host, target) tuple, return the argument for the bat file. Both host -# and targets should be canonalized. +# 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. _HOST_TARGET_ARCH_TO_BAT_ARCH = { ("x86", "x86"): "x86", ("x86", "amd64"): "x86_amd64", @@ -95,9 +125,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()') @@ -122,25 +175,33 @@ def get_host_target(env): try: host = _ARCH_TO_CANONICAL[host_platform.lower()] - except KeyError as e: + except KeyError: msg = "Unrecognized host architecture %s" - raise ValueError(msg % repr(host_platform)) + raise MSVCUnsupportedHostArch(msg % repr(host_platform)) try: target = _ARCH_TO_CANONICAL[target_platform.lower()] - except KeyError as e: + except KeyError: all_archs = str(list(_ARCH_TO_CANONICAL.keys())) - raise ValueError("Unrecognized target architecture %s\n\tValid architectures: %s" % (target_platform, all_archs)) + raise MSVCUnsupportedTargetArch("Unrecognized target architecture %s\n\tValid architectures: %s" % (target_platform, all_archs)) return (host, target,req_target_platform) # If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the # MSVC_VERSION documentation in Tool/msvc.xml. -_VCVER = ["14.1", "14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] +_VCVER = ["14.2", "14.1", "14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] + +# if using vswhere, a further mapping is needed +_VCVER_TO_VSWHERE_VER = { + '14.2' : '[16.0, 17.0)', + '14.1' : '[15.0, 16.0)', +} _VCVER_TO_PRODUCT_DIR = { + '14.2' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # VS 2019 doesn't set this key '14.1' : [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # Visual Studio 2017 doesn't set this registry key anymore + (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # VS 2017 doesn't set this key '14.0' : [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], '14.0Exp' : [ @@ -188,7 +249,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: @@ -201,53 +262,73 @@ 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 (host, target) pair is supported for a VC version. + + :note: only checks whether a given version *may* support the given (host, + target), not that the toolchain is actually present on the machine. + :param tuple host_target: canonalized host-targets pair, e.g. + ("x86", "amd64") for cross compilation from 32 bit Windows to 64 bits. + :param str msvc_version: Visual C++ version (major.minor), e.g. "10.0" + :returns: True or False """ # We assume that any Visual Studio version supports x86 as a target if host_target[1] != "x86": maj, min = msvc_version_to_maj_min(msvc_version) if maj < 8: return False - return True def find_vc_pdir_vswhere(msvc_version): """ - 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 + Find the MSVC product directory using the vswhere program. + + :param msvc_version: MSVC version to search for + :return: MSVC install dir or None + :raises UnsupportedVersion: if the version is not known by this file """ - vswhere_path = os.path.join( - 'C:\\', - 'Program Files (x86)', - 'Microsoft Visual Studio', - 'Installer', - 'vswhere.exe' - ) - vswhere_cmd = [vswhere_path, '-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') + + try: + vswhere_version = _VCVER_TO_VSWHERE_VER[msvc_version] + except KeyError: + debug("Unknown version of MSVC: %s" % msvc_version) + raise UnsupportedVersion("Unknown version %s" % msvc_version) + + # For bug 3333 - support default location of vswhere for both 64 and 32 bit windows + # installs. + for pf in ['Program Files (x86)', 'Program Files']: + vswhere_path = os.path.join( + 'C:\\', + pf, + 'Microsoft Visual Studio', + 'Installer', + 'vswhere.exe' + ) + if os.path.exists(vswhere_path): + # If we found vswhere, then use it. + break + else: + # No vswhere on system, no install info available + return None + + vswhere_cmd = [vswhere_path, + '-products', '*', + '-version', vswhere_version, + '-property', 'installationPath'] + + #TODO PY27 cannot use Popen as context manager + # try putting it back to the old way for now + sp = subprocess.Popen(vswhere_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + vsdir, err = sp.communicate() + if vsdir: + 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 @@ -255,13 +336,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 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. - Note - ---- - If for some reason the requested version could not be found, an - exception which inherits from VisualCException will be raised.""" + Args: + msvc_version: str + msvc version (major.minor, e.g. 10.0) + + 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] @@ -275,8 +368,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: @@ -308,10 +403,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") @@ -342,26 +437,146 @@ def find_batch_file(env,msvc_version,host_arch,target_arch): __INSTALLED_VCS_RUN = None +_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): + """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: + + """ -def cached_get_installed_vcs(): + # 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 for version %s' % (host_platform, target_platform, msvc_version)) + + 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, _VC_TOOLS_VERSION_FILE) + 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 host_trgt_dir is None: + debug('_check_cl_exists_in_vc_dir(): unsupported host/target platform combo: (%s,%s)'%(host_platform, target_platform)) + return False + + cl_path = os.path.join(vc_dir, 'Tools','MSVC', vc_specific_version, 'bin', host_trgt_dir[0], host_trgt_dir[1], _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: + + # Set default value to be -1 as "" which is the value for x86/x86 yields true when tested + # if not host_trgt_dir + host_trgt_dir = _HOST_TARGET_TO_CL_DIR.get((host_platform, target_platform), None) + if host_trgt_dir is None: + 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) + + # Set default value to be -1 as "" which is the value for x86/x86 yields true when tested + # if not host_trgt_dir + host_trgt_dir = _HOST_TARGET_TO_CL_DIR.get(('x86', target_platform), None) + if host_trgt_dir is None: + 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: - if find_vc_pdir(ver): + VC_DIR = find_vc_pdir(ver) + if VC_DIR: debug('found VC %s' % ver) - installed_versions.append(ver) + if _check_cl_exists_in_vc_dir(env, VC_DIR, ver): + installed_versions.append(ver) + else: + debug('find_vc_pdir no compiler found %s' % ver) else: debug('find_vc_pdir return None for ver %s' % ver) + except (MSVCUnsupportedTargetArch, MSVCUnsupportedHostArch): + # Allow this exception to propagate further as it should cause + # SCons to exit with an error code + raise except VisualCException as e: debug('did not find VC %s: caught exception %s' % (ver, str(e))) return installed_versions @@ -416,7 +631,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' @@ -443,16 +658,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 @@ -474,7 +690,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+ @@ -493,15 +709,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 @@ -510,6 +728,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 @@ -517,7 +736,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 @@ -566,8 +785,14 @@ def msvc_setup_env(env): debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v)) env.PrependENVPath(k, v, delete_existing=True) -def msvc_exists(version=None): - vcs = cached_get_installed_vcs() + # 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', it may need to be installed separately with Visual Studio") + +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/vcTests.py b/src/engine/SCons/Tool/MSCommon/vcTests.py new file mode 100644 index 0000000..25537f3 --- /dev/null +++ b/src/engine/SCons/Tool/MSCommon/vcTests.py @@ -0,0 +1,180 @@ +# +# Copyright (c) 2001 - 2019 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# 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 typing import Dict, Any + +__revision__ = "src/engine/SCons/Tool/MSCommon/vcTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" + +import os +import os.path +import unittest + +import SCons.Node.FS +import SCons.Warnings +import SCons.Tool.MSCommon.vc + +import TestCmd + +original = os.getcwd() + +test = TestCmd.TestCmd(workdir='') + +os.chdir(test.workpath('')) + +MSVCUnsupportedHostArch = SCons.Tool.MSCommon.vc.MSVCUnsupportedHostArch +MSVCUnsupportedTargetArch = SCons.Tool.MSCommon.vc.MSVCUnsupportedTargetArch + +MS_TOOLS_VERSION='1.1.1' + +class MSVcTestCase(unittest.TestCase): + + @staticmethod + def _createDummyCl(path, add_bin=True): + """ + Creates a dummy cl.ex in the correct directory. + It will create all missing parent directories as well + + Args: + path: Relative path to cl.exe for the version about to be tested. + """ + + # print("PATH:%s"%path) + + path = path.replace('\\', os.sep) + if add_bin: + create_path = os.path.join(path,'bin') + else: + create_path = path + if create_path and not os.path.isdir(create_path): + os.makedirs(create_path) + + create_this = os.path.join(create_path,'cl.exe') + + # print("Creating: %s"%create_this) + with open(create_this,'w') as ct: + ct.write('created') + + + + + def runTest(self): + """ + 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() + """ + + check = SCons.Tool.MSCommon.vc._check_cl_exists_in_vc_dir + + env={'TARGET_ARCH':'x86'} + p = SCons.Tool.MSCommon.vc._HOST_TARGET_TO_CL_DIR[('x86','x86')] + MSVcTestCase._createDummyCl(p) + + # print("retval:%s"%check(env, '.', '8.0')) + + + # Setup for VC 14+ tests + + # Create the VC minor/major version file + tools_version_file = SCons.Tool.MSCommon.vc._VC_TOOLS_VERSION_FILE + tools_dir = os.path.dirname(tools_version_file) + if not os.path.isdir(tools_dir): + os.makedirs(tools_dir) + try: + with open(tools_version_file, 'w') as tf: + tf.write(MS_TOOLS_VERSION) + except IOError as e: + print("Failed trying to write :%s :%s"%(tools_version_file, e)) + + + # Now walk all the valid combinations of host/target for VC 14 + + vc_gt_14_map = SCons.Tool.MSCommon.vc._HOST_TARGET_TO_CL_DIR_GREATER_THAN_14 + + for key, value in vc_gt_14_map.items(): + # print("GT 14 Got: %s -> %s"%(key,value)) + + env={'TARGET_ARCH':key[1], 'HOST_ARCH':key[0]} + path = os.path.join('.','Tools','MSVC', MS_TOOLS_VERSION, 'bin', value[0], value[1]) + MSVcTestCase._createDummyCl(path, add_bin=False) + result=check(env, '.', '14.1') + # print("for:%s got :%s"%(key[1], result)) + self.assertTrue(result, "Checking host: %s target: %s"%(value[0], value[1])) + + # 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"%(value[0], value[1])) + 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"%(value[0], value[1])) + except MSVCUnsupportedTargetArch: + pass + else: + self.fail('Did not fail when HOST_ARCH specified as: %s'%env['TARGET_ARCH']) + + # Test >8 < 14 VC versions + vc_map = SCons.Tool.MSCommon.vc._HOST_TARGET_TO_CL_DIR + for key,value in vc_map.items(): + # print("LT 14 Got: %s -> %s"%(key,value)) + env={'TARGET_ARCH':key[1], 'HOST_ARCH':key[0]} + path = os.path.join('.', 'bin', value ) + MSVcTestCase._createDummyCl(path, add_bin=False) + result=check(env, '.', '9.0') + # print("for:%s got :%s"%(key[1], result)) + self.assertTrue(result, "Checking host: %s target: %s"%(key[0], key[1])) + + # 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 HOST_ARCH specified as: %s'%env['TARGET_ARCH']) + + + +if __name__ == "__main__": + unittest.main() diff --git a/src/engine/SCons/Tool/MSCommon/vs.py b/src/engine/SCons/Tool/MSCommon/vs.py index dafdbaf..731f328 100644 --- a/src/engine/SCons/Tool/MSCommon/vs.py +++ b/src/engine/SCons/Tool/MSCommon/vs.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/MSCommon/vs.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/vs.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """Module to detect Visual Studio and/or Visual C/C++ """ @@ -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() @@ -199,6 +198,17 @@ class VisualStudio(object): # Tool/MSCommon/vc.py, and the MSVC_VERSION documentation in Tool/msvc.xml. SupportedVSList = [ + # Visual Studio 2019 + VisualStudio('14.2', + vc_version='14.2', + sdk_version='10.0A', + hkeys=[], + common_tools_var='VS160COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'VC\Auxiliary\Build\vsvars32.bat', + supported_arch=['x86', 'amd64', "arm"], + ), + # Visual Studio 2017 VisualStudio('14.1', vc_version='14.1', @@ -521,7 +531,7 @@ def get_default_arch(env): if not msvs: arch = 'x86' - elif not arch in msvs.get_supported_arch(): + elif arch not in msvs.get_supported_arch(): fmt = "Visual Studio version %s does not support architecture %s" raise SCons.Errors.UserError(fmt % (env['MSVS_VERSION'], arch)) |