summaryrefslogtreecommitdiffstats
path: root/src/engine
diff options
context:
space:
mode:
authorDaniel <dmoody256@gmail.com>2019-01-30 18:32:45 (GMT)
committerDaniel <dmoody256@gmail.com>2019-01-30 18:32:45 (GMT)
commitdd340d59e070ff8fcff8a893b350a0dea5b4859b (patch)
treeebf5f2499a9c14046a072573322dda3e9dd5322a /src/engine
parentb68e505d30e5b2f4ed366e6db10cdd14f14b8a80 (diff)
parent43d40021b7581c40c7dbd87e021a742f2a915102 (diff)
downloadSCons-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__.py34
-rw-r--r--src/engine/SCons/Platform/__init__.xml24
-rw-r--r--src/engine/SCons/Tool/MSCommon/common.py4
-rw-r--r--src/engine/SCons/Tool/MSCommon/vc.py290
-rw-r--r--src/engine/SCons/Tool/MSCommon/vs.py15
-rw-r--r--src/engine/SCons/Tool/gcc.py38
-rw-r--r--src/engine/SCons/Tool/midl.py2
-rw-r--r--src/engine/SCons/Tool/mslib.py2
-rw-r--r--src/engine/SCons/Tool/mslink.py2
-rw-r--r--src/engine/SCons/Tool/msvc.py2
-rw-r--r--src/engine/SCons/Tool/msvc.xml7
-rw-r--r--src/engine/SCons/Tool/msvs.py22
-rw-r--r--src/engine/SCons/Tool/msvsTests.py5
-rw-r--r--src/engine/SCons/Tool/swig.py20
-rw-r--r--src/engine/SCons/cpp.py9
-rw-r--r--src/engine/SCons/cppTests.py107
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