Commit 42eb813d authored by Jason R. Coombs's avatar Jason R. Coombs Committed by GitHub

Merge pull request #1904 from mayeut/msvc-python3.5

Use CPython 3.8.0 mechanism to find msvc 14+
parents ccbe2dad 65fa9209
......@@ -7,6 +7,15 @@ environment:
CODECOV_ENV: APPVEYOR_JOB_NAME
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
APPVEYOR_JOB_NAME: "python35-x64-vs2015"
PYTHON: "C:\\Python35-x64"
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
APPVEYOR_JOB_NAME: "python35-x64-vs2017"
PYTHON: "C:\\Python35-x64"
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
APPVEYOR_JOB_NAME: "python35-x64-vs2019"
PYTHON: "C:\\Python35-x64"
- APPVEYOR_JOB_NAME: "python36-x64"
PYTHON: "C:\\Python36-x64"
- APPVEYOR_JOB_NAME: "python37-x64"
......
Update msvc.py to use CPython 3.8.0 mechanism to find msvc 14+
......@@ -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
......
# -*- coding: utf-8 -*-
"""
Tests for msvc support module (msvc14 unit tests).
"""
import os
from distutils.errors import DistutilsPlatformError
import pytest
import sys
@pytest.mark.skipif(sys.platform != "win32",
reason="These tests are only for win32")
class TestMSVC14:
"""Python 3.8 "distutils/tests/test_msvccompiler.py" backport"""
def test_no_compiler(self):
import setuptools.msvc as _msvccompiler
# makes sure query_vcvarsall raises
# a DistutilsPlatformError if the compiler
# is not found
def _find_vcvarsall(plat_spec):
return None, None
old_find_vcvarsall = _msvccompiler._msvc14_find_vcvarsall
_msvccompiler._msvc14_find_vcvarsall = _find_vcvarsall
try:
pytest.raises(DistutilsPlatformError,
_msvccompiler._msvc14_get_vc_env,
'wont find this version')
finally:
_msvccompiler._msvc14_find_vcvarsall = old_find_vcvarsall
@pytest.mark.skipif(sys.version_info[0] < 3,
reason="Unicode requires encode/decode on Python 2")
def test_get_vc_env_unicode(self):
import setuptools.msvc as _msvccompiler
test_var = 'ṰḖṤṪ┅ṼẨṜ'
test_value = '₃⁴₅'
# Ensure we don't early exit from _get_vc_env
old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None)
os.environ[test_var] = test_value
try:
env = _msvccompiler._msvc14_get_vc_env('x86')
assert test_var.lower() in env
assert test_value == env[test_var.lower()]
finally:
os.environ.pop(test_var)
if old_distutils_use_sdk:
os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk
def test_get_vc2017(self):
import setuptools.msvc as _msvccompiler
# This function cannot be mocked, so pass it if we find VS 2017
# and mark it skipped if we do not.
version, path = _msvccompiler._msvc14_find_vc2017()
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') in [
'Visual Studio 2017'
]:
assert version
if version:
assert version >= 15
assert os.path.isdir(path)
else:
pytest.skip("VS 2017 is not installed")
def test_get_vc2015(self):
import setuptools.msvc as _msvccompiler
# This function cannot be mocked, so pass it if we find VS 2015
# and mark it skipped if we do not.
version, path = _msvccompiler._msvc14_find_vc2015()
if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') in [
'Visual Studio 2015', 'Visual Studio 2017'
]:
assert version
if version:
assert version >= 14
assert os.path.isdir(path)
else:
pytest.skip("VS 2015 is not installed")
......@@ -22,7 +22,7 @@ setenv =
COVERAGE_FILE={toxworkdir}/.coverage.{envname}
# TODO: The passed environment variables came from copying other tox.ini files
# These should probably be individually annotated to explain what needs them.
passenv=APPDATA HOMEDRIVE HOMEPATH windir APPVEYOR APPVEYOR_* CI CODECOV_* TRAVIS TRAVIS_* NETWORK_REQUIRED
passenv=APPDATA HOMEDRIVE HOMEPATH windir Program* CommonProgram* VS* APPVEYOR APPVEYOR_* CI CODECOV_* TRAVIS TRAVIS_* NETWORK_REQUIRED
commands=pytest --cov-config={toxinidir}/tox.ini --cov-report= {posargs}
usedevelop=True
extras =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment