From cadd91697cead064b4908b349c90c9a6e29d36b6 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Sat, 14 May 2022 20:31:11 -0700 Subject: [PATCH] Switch to setuptools.msvc for getting setup info for the Windows compiler, reducing dependence on distutils. --- build.py | 53 ++++++++++++--------------- buildtools/build_wxwidgets.py | 15 +------- buildtools/config.py | 68 ++++++++++++++++++++++++++++------- requirements/devel.txt | 1 + 4 files changed, 80 insertions(+), 57 deletions(-) diff --git a/build.py b/build.py index ad5bc4a3..8bb77fde 100755 --- a/build.py +++ b/build.py @@ -45,7 +45,7 @@ from buildtools.config import Config, msg, opj, posixjoin, loadETG, etg2sip, fi macSetLoaderNames, \ getVcsRev, runcmd, textfile_open, getSipFiles, \ getVisCVersion, getToolsPlatformName, updateLicenseFiles, \ - TemporaryDirectory + TemporaryDirectory, getMSVCInfo from buildtools.wxpysip import sip_runner import buildtools.version as version @@ -785,37 +785,30 @@ 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) + # 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. Note that we execute these checks using the target Python in + # case the one running this script is different. (TODO: It's probably + # about time to do away with this...) + + arch = 'x64' if PYTHON_ARCH == '64bit' else 'x86' + info = getMSVCInfo(PYTHON, arch) + + os.environ['PATH'] = info.path + os.environ['INCLUDE'] = info.include + os.environ['LIB'] = info.lib + os.environ['LIBPATH'] = info.libpath + + # 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) + msg(f"CL.exe: {CL}") - # 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']) # NOTE: SIP is now generating code with scoped-enums. Older linux # platforms like what we're using for builds, and also TravisCI for diff --git a/buildtools/build_wxwidgets.py b/buildtools/build_wxwidgets.py index fe6defe1..95acb603 100644 --- a/buildtools/build_wxwidgets.py +++ b/buildtools/build_wxwidgets.py @@ -16,6 +16,7 @@ import types import subprocess from buildtools import builder +from buildtools.config import getVisCVersion PY3 = sys.version_info[0] == 3 @@ -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: diff --git a/buildtools/config.py b/buildtools/config.py index fb707c7d..3f593893 100644 --- a/buildtools/config.py +++ b/buildtools/config.py @@ -27,6 +27,8 @@ from distutils.dep_util import newer import distutils.sysconfig +from attrdict import AttrDict + runSilently = False #---------------------------------------------------------------------- @@ -951,20 +953,60 @@ 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. + """ + if MSVCinfo is None: + raise RuntimeError('getMSVCInfo has not been called yet.') + py_ver = MSVCinfo.py_ver + if py_ver in ((3, 4),): + min_ver = 10.0 + elif 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): + """ + 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('{}'); " \ + "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) + + MSVCinfo = info + return info _haveObjDump = None diff --git a/requirements/devel.txt b/requirements/devel.txt index 18164bdc..d5fd32bf 100644 --- a/requirements/devel.txt +++ b/requirements/devel.txt @@ -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