Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update msvc detection #2164

Merged
merged 9 commits into from
May 18, 2022
2 changes: 1 addition & 1 deletion .azure/ci-windows-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ jobs:
timeoutInMinutes: 90
displayName: 'Windows CI'
pool:
vmImage: 'windows-2019'
vmImage: 'windows-2022'
strategy:
matrix:
Py37_x86:
Expand Down
63 changes: 29 additions & 34 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
macSetLoaderNames, \
getVcsRev, runcmd, textfile_open, getSipFiles, \
getVisCVersion, getToolsPlatformName, updateLicenseFiles, \
TemporaryDirectory
TemporaryDirectory, getMSVCInfo
from buildtools.wxpysip import sip_runner

import buildtools.version as version
Expand Down Expand Up @@ -785,37 +785,34 @@ def uploadTree(srcPath, destPath, options, days=30):

def checkCompiler(quiet=False):
if isWindows:
# Make sure that the compiler that Python wants to use can be found.
# It will terminate if the compiler is not found or other exceptions
# are raised.
cmd = "import setuptools, distutils.msvc9compiler as msvc; " \
"mc = msvc.MSVCCompiler(); " \
"mc.initialize(); " \
"print(mc.cc)"
CC = runcmd('"%s" -c "%s"' % (PYTHON, cmd), getOutput=True, echoCmd=False)
# Set up the PATH and other environment variables so the proper version
# of MSVC will be used. The setuptools package is used to find the
# needed info, so the target python shoudl have a recent version of
# setuptools installed.

arch = 'x64' if PYTHON_ARCH == '64bit' else 'x86'
info = getMSVCInfo(PYTHON, arch, set_env=True)

# Make sure there is now a cl.exe on the PATH
CL = 'NOT FOUND'
for d in os.environ['PATH'].split(os.pathsep):
p = pathlib.Path(d, 'cl.exe')
if p.exists():
CL = p
break
if not quiet:
msg("MSVC: %s" % CC)

# Now get the environment variables which that compiler needs from
# its vcvarsall.bat command and load them into this process's
# environment.
cmd = "import setuptools, distutils.msvc9compiler as msvc; " \
"arch = msvc.PLAT_TO_VCVARS[msvc.get_platform()]; " \
"env = msvc.query_vcvarsall(msvc.VERSION, arch); " \
"print(env)"
env = eval(runcmd('"%s" -c "%s"' % (PYTHON, cmd), getOutput=True, echoCmd=False))

def _b(v):
return str(v)
#if PY2:
# return bytes(v)
#else:
# return bytes(v, 'utf8')

os.environ['PATH'] = _b(env['path'])
os.environ['INCLUDE'] = _b(env['include'])
os.environ['LIB'] = _b(env['lib'])
os.environ['LIBPATH'] = _b(env['libpath'])
msg(f"CL.exe: {CL}")

# Just needed for debugging
# msg('include: ' + info.include)
# msg('lib: ' + info.lib)
# msg('libpath: ' + info.libpath)
# for d in info.include.split(os.pathsep):
# p = pathlib.Path(d, 'tchar.h')
# if p.exists():
# msg('tchar.h: ' + str(p))
# break


# NOTE: SIP is now generating code with scoped-enums. Older linux
# platforms like what we're using for builds, and also TravisCI for
Expand Down Expand Up @@ -1674,8 +1671,6 @@ def _getWxCompiler(flag, compName, flagName):
build_options.append('--msvc_arch=x86')
if not isWindows:
build_options.append('--wx_config=%s' % WX_CONFIG)
if options.verbose:
build_options.append('--verbose')
if options.jobs:
build_options.append('--jobs=%s' % options.jobs)
if options.relwithdebug:
Expand Down Expand Up @@ -1726,7 +1721,7 @@ def _onWafError():
wafBuildDir = posixjoin(wafBuildBase, 'release')
build_options.append('--out=%s' % wafBuildDir)
cmd = '"%s" %s %s configure build %s' % (PYTHON, waf, ' '.join(build_options), options.extra_waf)
runcmd(cmd)
runcmd(cmd, onError=_onWafError)

copyWxDlls(options)
cmd_build_others(options, args)
Expand Down
15 changes: 1 addition & 14 deletions buildtools/build_wxwidgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import subprocess

from buildtools import builder
from buildtools.config import getVisCVersion

PY3 = sys.version_info[0] == 3

Expand Down Expand Up @@ -63,20 +64,6 @@ def getXcodePaths():
return [base, base+"/Platforms/MacOSX.platform/Developer"]


def getVisCVersion():
text = getoutput("cl.exe")
if 'Version 13' in text:
return '71'
if 'Version 15' in text:
return '90'
if 'Version 16' in text:
return '100'
if 'Version 19' in text:
return '140'
# TODO: Add more tests to get the other versions...
else:
return 'FIXME'


def exitIfError(code, msg):
if code != 0:
Expand Down
79 changes: 66 additions & 13 deletions buildtools/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

import distutils.sysconfig

from attrdict import AttrDict

runSilently = False

#----------------------------------------------------------------------
Expand Down Expand Up @@ -951,20 +953,71 @@ def getSipFiles(names):


def getVisCVersion():
text = runcmd("cl.exe", getOutput=True, echoCmd=False)
if 'Version 13' in text:
return '71'
if 'Version 15' in text:
return '90'
if 'Version 16' in text:
return '100'
if 'Version 18' in text:
return '120'
if 'Version 19' in text:
return '140'
# TODO: Add more tests to get the other versions...
if MSVCinfo is None:
raise RuntimeError('getMSVCInfo has not been called yet.')
# Convert a float like 14.28 to 140, for historical reasons
# TODO: decide on switching to 142, 143, etc.??
ver = str(int(MSVCinfo.vc_ver)) + '0'
return ver


def getExpectedVisCVersion():
"""
Returns the Visual C version that Python is expecting, based on the usual
version that stock Python was built with.
(Not currently used, we're just selecting the latest available compiler
>= 14.0 for now...)
"""
if MSVCinfo is None:
raise RuntimeError('getMSVCInfo has not been called yet.')
py_ver = MSVCinfo.py_ver
if py_ver in ((3, 5), (3, 6), (3, 7), (3, 8)):
min_ver = 14.0
elif py_ver in ((3, 9), (3, 10)):
min_ver = 14.2
else:
return 'FIXME'
raise RuntimeError('This library does not support python version %d.%d' % py_version)
return min_ver


MSVCinfo = None
def getMSVCInfo(PYTHON, arch, set_env=False):
"""
Fetch info from the system about MSVC, such as versions, paths, etc.
"""
global MSVCinfo
if MSVCinfo is not None:
return MSVCinfo

# Note that it starts with a monkey-patch in setuptools.msvc to
# workaround this issue: pypa/setuptools#1902
cmd = \
"import os, sys, setuptools.msvc; " \
"setuptools.msvc.isfile = lambda path: path is not None and os.path.isfile(path); " \
"ei = setuptools.msvc.EnvironmentInfo('{}', vc_min_ver=14.0); " \
"env = ei.return_env(); " \
"env['vc_ver'] = ei.vc_ver; " \
"env['vs_ver'] = ei.vs_ver; " \
"env['arch'] = ei.pi.arch; " \
"env['py_ver'] = sys.version_info[:2]; " \
"print(env)"
cmd = cmd.format(arch)
env = eval(runcmd('"%s" -c "%s"' % (PYTHON, cmd), getOutput=True, echoCmd=False))
info = AttrDict(env)

if set_env:
os.environ['PATH'] = info.path
os.environ['INCLUDE'] = info.include
os.environ['LIB'] = info.lib
os.environ['LIBPATH'] = info.libpath

# We already have everything we need, tell distutils to not go hunting
# for it all again if it happens to be called.
os.environ['DISTUTILS_USE_SDK'] = "1"
os.environ['MSSdk'] = "1"

MSVCinfo = info
return info


_haveObjDump = None
Expand Down
Binary file modified packaging/msw-vcredist/x64/Microsoft.VC140.CRT/msvcp140.dll
Binary file not shown.
Binary file modified packaging/msw-vcredist/x64/Microsoft.VC140.CRT/vcruntime140.dll
Binary file not shown.
Binary file not shown.
Binary file modified packaging/msw-vcredist/x86/Microsoft.VC140.CRT/msvcp140.dll
Binary file not shown.
Binary file not shown.
Binary file modified packaging/msw-vcredist/x86/Microsoft.VC140.CRT/vcruntime140.dll
Binary file not shown.
1 change: 1 addition & 0 deletions requirements/devel.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ sphinx==2.2.0 ; python_version >= '3.0'
sphinx==1.8.5 ; python_version < '3.0'
doc2dash==2.3.0
beautifulsoup4
attrdict3
45 changes: 15 additions & 30 deletions wscript
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ try:
except ImportError:
from buildtools.backports.textwrap3 import indent

from buildtools.config import Config, runcmd, msg
from buildtools.config import Config, runcmd, msg, getMSVCInfo
cfg = Config(True)

#-----------------------------------------------------------------------------
Expand Down Expand Up @@ -66,43 +66,28 @@ def options(opt):

def configure(conf):
if isWindows:
# For now simply choose the compiler version based on the Python
# version. We have a chicken-egg problem here. The compiler needs to
# be selected before the Python stuff can be configured, but we need
# Python to know what version of the compiler to use.
import distutils.msvc9compiler
msvc_version = str( distutils.msvc9compiler.get_build_version() )

# When building for Python 3.7 the msvc_version returned will be
# "14.1" as that is the version of the BasePlatformToolkit that stock
# Python 3.7 was built with, a.k.a v141, which is the default in
# Visual Studio 2017. However, waf is using "msvc 15.0" to designate
# that version rather than "14.1" so we'll need to catch that case and
# fix up the msvc_version accordingly.
if msvc_version in ["14.1", "14.2"] and sys.version_info >= (3,7):
##msvc_version = '15.0'

# On the other hand, microsoft says that v141 and v140 (Visual
# Studio 2015) are binary compatible, so for now let's just drop
# it back to "14.0" until I get all the details worked out for
# using VS 2017+ everywhere for Python 3.7+.
msvc_version = '14.0'

# In some cases (Azure DevOps at least) we're getting "14.1" for Python
# 3.6 too. Smash it down to '14.0'
if msvc_version == "14.1" and sys.version_info[:2] == (3,6):
msvc_version = '14.0'

conf.env['MSVC_VERSIONS'] = ['msvc ' + msvc_version]
# Set up the MSVC compiler info for wxPython's build

PYTHON = conf.options.python if conf.options.python else sys.executable
info = getMSVCInfo(PYTHON, conf.options.msvc_arch, set_env=True)

# WAF uses the VisualStudio version to select the compiler, rather than
# the compiler version like we see elsewhere. Luckily we've got that
# value in the MSVC info.
msvc_version = f"msvc {info.vs_ver}"

conf.env['MSVC_VERSIONS'] = [msvc_version]
conf.env['MSVC_TARGETS'] = [conf.options.msvc_arch]
conf.load('msvc')
else:
# Otherwise, use WAF's default setup for the C and C++ compiler
conf.load('compiler_c compiler_cxx')

# Set up Python
if conf.options.python:
conf.env.PYTHON = conf.options.python
conf.load('python')
conf.check_python_version(minver=(3,6,0))
conf.check_python_version(minver=(3,7,0))
if isWindows:
# Search for the Python headers without doing some stuff that could
# incorrectly fail on Windows. See my_check_python_headers below.
Expand Down