aboutsummaryrefslogtreecommitdiffstats
path: root/setuptools/msvc.py
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools/msvc.py')
-rw-r--r--setuptools/msvc.py178
1 files changed, 162 insertions, 16 deletions
diff --git a/setuptools/msvc.py b/setuptools/msvc.py
index 2ffe1c81..213e39c9 100644
--- a/setuptools/msvc.py
+++ b/setuptools/msvc.py
@@ -26,6 +26,7 @@ from os.path import join, isfile, isdir, dirname
import sys
import platform
import itertools
+import subprocess
import distutils.errors
from setuptools.extern.packaging.version import LegacyVersion
@@ -142,6 +143,154 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
raise
+def _msvc14_find_vc2015():
+ """Python 3.8 "distutils/_msvccompiler.py" backport"""
+ try:
+ key = winreg.OpenKey(
+ winreg.HKEY_LOCAL_MACHINE,
+ r"Software\Microsoft\VisualStudio\SxS\VC7",
+ 0,
+ winreg.KEY_READ | winreg.KEY_WOW64_32KEY
+ )
+ except OSError:
+ return None, None
+
+ best_version = 0
+ best_dir = None
+ with key:
+ for i in itertools.count():
+ try:
+ v, vc_dir, vt = winreg.EnumValue(key, i)
+ except OSError:
+ break
+ if v and vt == winreg.REG_SZ and isdir(vc_dir):
+ try:
+ version = int(float(v))
+ except (ValueError, TypeError):
+ continue
+ if version >= 14 and version > best_version:
+ best_version, best_dir = version, vc_dir
+ return best_version, best_dir
+
+
+def _msvc14_find_vc2017():
+ """Python 3.8 "distutils/_msvccompiler.py" backport
+
+ Returns "15, path" based on the result of invoking vswhere.exe
+ If no install is found, returns "None, None"
+
+ The version is returned to avoid unnecessarily changing the function
+ result. It may be ignored when the path is not None.
+
+ If vswhere.exe is not available, by definition, VS 2017 is not
+ installed.
+ """
+ root = environ.get("ProgramFiles(x86)") or environ.get("ProgramFiles")
+ if not root:
+ return None, None
+
+ try:
+ path = subprocess.check_output([
+ join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
+ "-latest",
+ "-prerelease",
+ "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
+ "-property", "installationPath",
+ "-products", "*",
+ ]).decode(encoding="mbcs", errors="strict").strip()
+ except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
+ return None, None
+
+ path = join(path, "VC", "Auxiliary", "Build")
+ if isdir(path):
+ return 15, path
+
+ return None, None
+
+
+PLAT_SPEC_TO_RUNTIME = {
+ 'x86': 'x86',
+ 'x86_amd64': 'x64',
+ 'x86_arm': 'arm',
+ 'x86_arm64': 'arm64'
+}
+
+
+def _msvc14_find_vcvarsall(plat_spec):
+ """Python 3.8 "distutils/_msvccompiler.py" backport"""
+ _, best_dir = _msvc14_find_vc2017()
+ vcruntime = None
+
+ if plat_spec in PLAT_SPEC_TO_RUNTIME:
+ vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec]
+ else:
+ vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
+
+ if best_dir:
+ vcredist = join(best_dir, "..", "..", "redist", "MSVC", "**",
+ vcruntime_plat, "Microsoft.VC14*.CRT",
+ "vcruntime140.dll")
+ try:
+ import glob
+ vcruntime = glob.glob(vcredist, recursive=True)[-1]
+ except (ImportError, OSError, LookupError):
+ vcruntime = None
+
+ if not best_dir:
+ best_version, best_dir = _msvc14_find_vc2015()
+ if best_version:
+ vcruntime = join(best_dir, 'redist', vcruntime_plat,
+ "Microsoft.VC140.CRT", "vcruntime140.dll")
+
+ if not best_dir:
+ return None, None
+
+ vcvarsall = join(best_dir, "vcvarsall.bat")
+ if not isfile(vcvarsall):
+ return None, None
+
+ if not vcruntime or not isfile(vcruntime):
+ vcruntime = None
+
+ return vcvarsall, vcruntime
+
+
+def _msvc14_get_vc_env(plat_spec):
+ """Python 3.8 "distutils/_msvccompiler.py" backport"""
+ if "DISTUTILS_USE_SDK" in environ:
+ return {
+ key.lower(): value
+ for key, value in environ.items()
+ }
+
+ vcvarsall, vcruntime = _msvc14_find_vcvarsall(plat_spec)
+ if not vcvarsall:
+ raise distutils.errors.DistutilsPlatformError(
+ "Unable to find vcvarsall.bat"
+ )
+
+ try:
+ out = subprocess.check_output(
+ 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
+ stderr=subprocess.STDOUT,
+ ).decode('utf-16le', errors='replace')
+ except subprocess.CalledProcessError as exc:
+ raise distutils.errors.DistutilsPlatformError(
+ "Error executing {}".format(exc.cmd)
+ )
+
+ env = {
+ key.lower(): value
+ for key, _, value in
+ (line.partition('=') for line in out.splitlines())
+ if key and value
+ }
+
+ if vcruntime:
+ env['py_vcruntime_redist'] = vcruntime
+ return env
+
+
def msvc14_get_vc_env(plat_spec):
"""
Patched "distutils._msvccompiler._get_vc_env" for support extra
@@ -159,16 +308,10 @@ def msvc14_get_vc_env(plat_spec):
dict
environment
"""
- # Try to get environment from vcvarsall.bat (Classical way)
- try:
- return get_unpatched(msvc14_get_vc_env)(plat_spec)
- except distutils.errors.DistutilsPlatformError:
- # Pass error Vcvarsall.bat is missing
- pass
- # If error, try to set environment directly
+ # Always use backport from CPython 3.8
try:
- return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env()
+ return _msvc14_get_vc_env(plat_spec)
except distutils.errors.DistutilsPlatformError as exc:
_augment_exception(exc, 14.0)
raise
@@ -544,7 +687,7 @@ class SystemInfo:
# Except for VS15+, VC version is aligned with VS version
self.vs_ver = self.vc_ver = (
- vc_ver or self._find_latest_available_vs_ver())
+ vc_ver or self._find_latest_available_vs_ver())
def _find_latest_available_vs_ver(self):
"""
@@ -1225,7 +1368,7 @@ class EnvironmentInfo:
arch_subdir = self.pi.target_dir(x64=True)
lib = join(self.si.WindowsSdkDir, 'lib')
libver = self._sdk_subdir
- return [join(lib, '%sum%s' % (libver , arch_subdir))]
+ return [join(lib, '%sum%s' % (libver, arch_subdir))]
@property
def OSIncludes(self):
@@ -1274,13 +1417,16 @@ class EnvironmentInfo:
libpath += [
ref,
join(self.si.WindowsSdkDir, 'UnionMetadata'),
- join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
+ join(
+ ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
- join(ref,'Windows.Networking.Connectivity.WwanContract',
- '1.0.0.0'),
- join(self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
- '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
- 'neutral'),
+ join(
+ ref, 'Windows.Networking.Connectivity.WwanContract',
+ '1.0.0.0'),
+ join(
+ self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
+ '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
+ 'neutral'),
]
return libpath