summaryrefslogtreecommitdiff
path: root/src/engine/SCons/Tool/MSCommon
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SCons/Tool/MSCommon')
-rw-r--r--src/engine/SCons/Tool/MSCommon/__init__.py4
-rw-r--r--src/engine/SCons/Tool/MSCommon/arch.py12
-rw-r--r--src/engine/SCons/Tool/MSCommon/common.py17
-rw-r--r--src/engine/SCons/Tool/MSCommon/netframework.py6
-rw-r--r--src/engine/SCons/Tool/MSCommon/sdk.py14
-rw-r--r--src/engine/SCons/Tool/MSCommon/vc.py375
-rw-r--r--src/engine/SCons/Tool/MSCommon/vcTests.py180
-rw-r--r--src/engine/SCons/Tool/MSCommon/vs.py32
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))