Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/c-analyzer/distutils/_msvccompiler.py
12 views
1
"""distutils._msvccompiler
2
3
Contains MSVCCompiler, an implementation of the abstract CCompiler class
4
for Microsoft Visual Studio 2015.
5
6
The module is compatible with VS 2015 and later. You can find legacy support
7
for older versions in distutils.msvc9compiler and distutils.msvccompiler.
8
"""
9
10
# Written by Perry Stoll
11
# hacked by Robin Becker and Thomas Heller to do a better job of
12
# finding DevStudio (through the registry)
13
# ported to VS 2005 and VS 2008 by Christian Heimes
14
# ported to VS 2015 by Steve Dower
15
16
import os
17
import subprocess
18
import winreg
19
20
from distutils.errors import DistutilsPlatformError
21
from distutils.ccompiler import CCompiler
22
from distutils import log
23
24
from itertools import count
25
26
def _find_vc2015():
27
try:
28
key = winreg.OpenKeyEx(
29
winreg.HKEY_LOCAL_MACHINE,
30
r"Software\Microsoft\VisualStudio\SxS\VC7",
31
access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
32
)
33
except OSError:
34
log.debug("Visual C++ is not registered")
35
return None, None
36
37
best_version = 0
38
best_dir = None
39
with key:
40
for i in count():
41
try:
42
v, vc_dir, vt = winreg.EnumValue(key, i)
43
except OSError:
44
break
45
if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
46
try:
47
version = int(float(v))
48
except (ValueError, TypeError):
49
continue
50
if version >= 14 and version > best_version:
51
best_version, best_dir = version, vc_dir
52
return best_version, best_dir
53
54
def _find_vc2017():
55
"""Returns "15, path" based on the result of invoking vswhere.exe
56
If no install is found, returns "None, None"
57
58
The version is returned to avoid unnecessarily changing the function
59
result. It may be ignored when the path is not None.
60
61
If vswhere.exe is not available, by definition, VS 2017 is not
62
installed.
63
"""
64
root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
65
if not root:
66
return None, None
67
68
try:
69
path = subprocess.check_output([
70
os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
71
"-latest",
72
"-prerelease",
73
"-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
74
"-property", "installationPath",
75
"-products", "*",
76
], encoding="mbcs", errors="strict").strip()
77
except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
78
return None, None
79
80
path = os.path.join(path, "VC", "Auxiliary", "Build")
81
if os.path.isdir(path):
82
return 15, path
83
84
return None, None
85
86
PLAT_SPEC_TO_RUNTIME = {
87
'x86' : 'x86',
88
'x86_amd64' : 'x64',
89
'x86_arm' : 'arm',
90
'x86_arm64' : 'arm64'
91
}
92
93
def _find_vcvarsall(plat_spec):
94
# bpo-38597: Removed vcruntime return value
95
_, best_dir = _find_vc2017()
96
97
if not best_dir:
98
best_version, best_dir = _find_vc2015()
99
100
if not best_dir:
101
log.debug("No suitable Visual C++ version found")
102
return None, None
103
104
vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
105
if not os.path.isfile(vcvarsall):
106
log.debug("%s cannot be found", vcvarsall)
107
return None, None
108
109
return vcvarsall, None
110
111
def _get_vc_env(plat_spec):
112
if os.getenv("DISTUTILS_USE_SDK"):
113
return {
114
key.lower(): value
115
for key, value in os.environ.items()
116
}
117
118
vcvarsall, _ = _find_vcvarsall(plat_spec)
119
if not vcvarsall:
120
raise DistutilsPlatformError("Unable to find vcvarsall.bat")
121
122
try:
123
out = subprocess.check_output(
124
'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
125
stderr=subprocess.STDOUT,
126
).decode('utf-16le', errors='replace')
127
except subprocess.CalledProcessError as exc:
128
log.error(exc.output)
129
raise DistutilsPlatformError("Error executing {}"
130
.format(exc.cmd))
131
132
env = {
133
key.lower(): value
134
for key, _, value in
135
(line.partition('=') for line in out.splitlines())
136
if key and value
137
}
138
139
return env
140
141
def _find_exe(exe, paths=None):
142
"""Return path to an MSVC executable program.
143
144
Tries to find the program in several places: first, one of the
145
MSVC program search paths from the registry; next, the directories
146
in the PATH environment variable. If any of those work, return an
147
absolute path that is known to exist. If none of them work, just
148
return the original program name, 'exe'.
149
"""
150
if not paths:
151
paths = os.getenv('path').split(os.pathsep)
152
for p in paths:
153
fn = os.path.join(os.path.abspath(p), exe)
154
if os.path.isfile(fn):
155
return fn
156
return exe
157
158
# A map keyed by get_platform() return values to values accepted by
159
# 'vcvarsall.bat'. Always cross-compile from x86 to work with the
160
# lighter-weight MSVC installs that do not include native 64-bit tools.
161
PLAT_TO_VCVARS = {
162
'win32' : 'x86',
163
'win-amd64' : 'x86_amd64',
164
'win-arm32' : 'x86_arm',
165
'win-arm64' : 'x86_arm64'
166
}
167
168
class MSVCCompiler(CCompiler) :
169
"""Concrete class that implements an interface to Microsoft Visual C++,
170
as defined by the CCompiler abstract class."""
171
172
compiler_type = 'msvc'
173
174
# Just set this so CCompiler's constructor doesn't barf. We currently
175
# don't use the 'set_executables()' bureaucracy provided by CCompiler,
176
# as it really isn't necessary for this sort of single-compiler class.
177
# Would be nice to have a consistent interface with UnixCCompiler,
178
# though, so it's worth thinking about.
179
executables = {}
180
181
# Private class data (need to distinguish C from C++ source for compiler)
182
_c_extensions = ['.c']
183
_cpp_extensions = ['.cc', '.cpp', '.cxx']
184
_rc_extensions = ['.rc']
185
_mc_extensions = ['.mc']
186
187
# Needed for the filename generation methods provided by the
188
# base class, CCompiler.
189
src_extensions = (_c_extensions + _cpp_extensions +
190
_rc_extensions + _mc_extensions)
191
res_extension = '.res'
192
obj_extension = '.obj'
193
static_lib_extension = '.lib'
194
shared_lib_extension = '.dll'
195
static_lib_format = shared_lib_format = '%s%s'
196
exe_extension = '.exe'
197
198
199
def __init__(self, verbose=0, dry_run=0, force=0):
200
CCompiler.__init__ (self, verbose, dry_run, force)
201
# target platform (.plat_name is consistent with 'bdist')
202
self.plat_name = None
203
self.initialized = False
204
205