Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/setuptools/_distutils/_msvccompiler.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
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 contextlib | |
19 import warnings | |
20 import unittest.mock | |
21 with contextlib.suppress(ImportError): | |
22 import winreg | |
23 | |
24 from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ | |
25 CompileError, LibError, LinkError | |
26 from distutils.ccompiler import CCompiler, gen_lib_options | |
27 from distutils import log | |
28 from distutils.util import get_platform | |
29 | |
30 from itertools import count | |
31 | |
32 def _find_vc2015(): | |
33 try: | |
34 key = winreg.OpenKeyEx( | |
35 winreg.HKEY_LOCAL_MACHINE, | |
36 r"Software\Microsoft\VisualStudio\SxS\VC7", | |
37 access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY | |
38 ) | |
39 except OSError: | |
40 log.debug("Visual C++ is not registered") | |
41 return None, None | |
42 | |
43 best_version = 0 | |
44 best_dir = None | |
45 with key: | |
46 for i in count(): | |
47 try: | |
48 v, vc_dir, vt = winreg.EnumValue(key, i) | |
49 except OSError: | |
50 break | |
51 if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir): | |
52 try: | |
53 version = int(float(v)) | |
54 except (ValueError, TypeError): | |
55 continue | |
56 if version >= 14 and version > best_version: | |
57 best_version, best_dir = version, vc_dir | |
58 return best_version, best_dir | |
59 | |
60 def _find_vc2017(): | |
61 """Returns "15, path" based on the result of invoking vswhere.exe | |
62 If no install is found, returns "None, None" | |
63 | |
64 The version is returned to avoid unnecessarily changing the function | |
65 result. It may be ignored when the path is not None. | |
66 | |
67 If vswhere.exe is not available, by definition, VS 2017 is not | |
68 installed. | |
69 """ | |
70 root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") | |
71 if not root: | |
72 return None, None | |
73 | |
74 try: | |
75 path = subprocess.check_output([ | |
76 os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), | |
77 "-latest", | |
78 "-prerelease", | |
79 "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", | |
80 "-property", "installationPath", | |
81 "-products", "*", | |
82 ], encoding="mbcs", errors="strict").strip() | |
83 except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): | |
84 return None, None | |
85 | |
86 path = os.path.join(path, "VC", "Auxiliary", "Build") | |
87 if os.path.isdir(path): | |
88 return 15, path | |
89 | |
90 return None, None | |
91 | |
92 PLAT_SPEC_TO_RUNTIME = { | |
93 'x86' : 'x86', | |
94 'x86_amd64' : 'x64', | |
95 'x86_arm' : 'arm', | |
96 'x86_arm64' : 'arm64' | |
97 } | |
98 | |
99 def _find_vcvarsall(plat_spec): | |
100 # bpo-38597: Removed vcruntime return value | |
101 _, best_dir = _find_vc2017() | |
102 | |
103 if not best_dir: | |
104 best_version, best_dir = _find_vc2015() | |
105 | |
106 if not best_dir: | |
107 log.debug("No suitable Visual C++ version found") | |
108 return None, None | |
109 | |
110 vcvarsall = os.path.join(best_dir, "vcvarsall.bat") | |
111 if not os.path.isfile(vcvarsall): | |
112 log.debug("%s cannot be found", vcvarsall) | |
113 return None, None | |
114 | |
115 return vcvarsall, None | |
116 | |
117 def _get_vc_env(plat_spec): | |
118 if os.getenv("DISTUTILS_USE_SDK"): | |
119 return { | |
120 key.lower(): value | |
121 for key, value in os.environ.items() | |
122 } | |
123 | |
124 vcvarsall, _ = _find_vcvarsall(plat_spec) | |
125 if not vcvarsall: | |
126 raise DistutilsPlatformError("Unable to find vcvarsall.bat") | |
127 | |
128 try: | |
129 out = subprocess.check_output( | |
130 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), | |
131 stderr=subprocess.STDOUT, | |
132 ).decode('utf-16le', errors='replace') | |
133 except subprocess.CalledProcessError as exc: | |
134 log.error(exc.output) | |
135 raise DistutilsPlatformError("Error executing {}" | |
136 .format(exc.cmd)) | |
137 | |
138 env = { | |
139 key.lower(): value | |
140 for key, _, value in | |
141 (line.partition('=') for line in out.splitlines()) | |
142 if key and value | |
143 } | |
144 | |
145 return env | |
146 | |
147 def _find_exe(exe, paths=None): | |
148 """Return path to an MSVC executable program. | |
149 | |
150 Tries to find the program in several places: first, one of the | |
151 MSVC program search paths from the registry; next, the directories | |
152 in the PATH environment variable. If any of those work, return an | |
153 absolute path that is known to exist. If none of them work, just | |
154 return the original program name, 'exe'. | |
155 """ | |
156 if not paths: | |
157 paths = os.getenv('path').split(os.pathsep) | |
158 for p in paths: | |
159 fn = os.path.join(os.path.abspath(p), exe) | |
160 if os.path.isfile(fn): | |
161 return fn | |
162 return exe | |
163 | |
164 # A map keyed by get_platform() return values to values accepted by | |
165 # 'vcvarsall.bat'. Always cross-compile from x86 to work with the | |
166 # lighter-weight MSVC installs that do not include native 64-bit tools. | |
167 PLAT_TO_VCVARS = { | |
168 'win32' : 'x86', | |
169 'win-amd64' : 'x86_amd64', | |
170 'win-arm32' : 'x86_arm', | |
171 'win-arm64' : 'x86_arm64' | |
172 } | |
173 | |
174 class MSVCCompiler(CCompiler) : | |
175 """Concrete class that implements an interface to Microsoft Visual C++, | |
176 as defined by the CCompiler abstract class.""" | |
177 | |
178 compiler_type = 'msvc' | |
179 | |
180 # Just set this so CCompiler's constructor doesn't barf. We currently | |
181 # don't use the 'set_executables()' bureaucracy provided by CCompiler, | |
182 # as it really isn't necessary for this sort of single-compiler class. | |
183 # Would be nice to have a consistent interface with UnixCCompiler, | |
184 # though, so it's worth thinking about. | |
185 executables = {} | |
186 | |
187 # Private class data (need to distinguish C from C++ source for compiler) | |
188 _c_extensions = ['.c'] | |
189 _cpp_extensions = ['.cc', '.cpp', '.cxx'] | |
190 _rc_extensions = ['.rc'] | |
191 _mc_extensions = ['.mc'] | |
192 | |
193 # Needed for the filename generation methods provided by the | |
194 # base class, CCompiler. | |
195 src_extensions = (_c_extensions + _cpp_extensions + | |
196 _rc_extensions + _mc_extensions) | |
197 res_extension = '.res' | |
198 obj_extension = '.obj' | |
199 static_lib_extension = '.lib' | |
200 shared_lib_extension = '.dll' | |
201 static_lib_format = shared_lib_format = '%s%s' | |
202 exe_extension = '.exe' | |
203 | |
204 | |
205 def __init__(self, verbose=0, dry_run=0, force=0): | |
206 CCompiler.__init__ (self, verbose, dry_run, force) | |
207 # target platform (.plat_name is consistent with 'bdist') | |
208 self.plat_name = None | |
209 self.initialized = False | |
210 | |
211 def initialize(self, plat_name=None): | |
212 # multi-init means we would need to check platform same each time... | |
213 assert not self.initialized, "don't init multiple times" | |
214 if plat_name is None: | |
215 plat_name = get_platform() | |
216 # sanity check for platforms to prevent obscure errors later. | |
217 if plat_name not in PLAT_TO_VCVARS: | |
218 raise DistutilsPlatformError("--plat-name must be one of {}" | |
219 .format(tuple(PLAT_TO_VCVARS))) | |
220 | |
221 # Get the vcvarsall.bat spec for the requested platform. | |
222 plat_spec = PLAT_TO_VCVARS[plat_name] | |
223 | |
224 vc_env = _get_vc_env(plat_spec) | |
225 if not vc_env: | |
226 raise DistutilsPlatformError("Unable to find a compatible " | |
227 "Visual Studio installation.") | |
228 | |
229 self._paths = vc_env.get('path', '') | |
230 paths = self._paths.split(os.pathsep) | |
231 self.cc = _find_exe("cl.exe", paths) | |
232 self.linker = _find_exe("link.exe", paths) | |
233 self.lib = _find_exe("lib.exe", paths) | |
234 self.rc = _find_exe("rc.exe", paths) # resource compiler | |
235 self.mc = _find_exe("mc.exe", paths) # message compiler | |
236 self.mt = _find_exe("mt.exe", paths) # message compiler | |
237 | |
238 for dir in vc_env.get('include', '').split(os.pathsep): | |
239 if dir: | |
240 self.add_include_dir(dir.rstrip(os.sep)) | |
241 | |
242 for dir in vc_env.get('lib', '').split(os.pathsep): | |
243 if dir: | |
244 self.add_library_dir(dir.rstrip(os.sep)) | |
245 | |
246 self.preprocess_options = None | |
247 # bpo-38597: Always compile with dynamic linking | |
248 # Future releases of Python 3.x will include all past | |
249 # versions of vcruntime*.dll for compatibility. | |
250 self.compile_options = [ | |
251 '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD' | |
252 ] | |
253 | |
254 self.compile_options_debug = [ | |
255 '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' | |
256 ] | |
257 | |
258 ldflags = [ | |
259 '/nologo', '/INCREMENTAL:NO', '/LTCG' | |
260 ] | |
261 | |
262 ldflags_debug = [ | |
263 '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL' | |
264 ] | |
265 | |
266 self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1'] | |
267 self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1'] | |
268 self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] | |
269 self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] | |
270 self.ldflags_static = [*ldflags] | |
271 self.ldflags_static_debug = [*ldflags_debug] | |
272 | |
273 self._ldflags = { | |
274 (CCompiler.EXECUTABLE, None): self.ldflags_exe, | |
275 (CCompiler.EXECUTABLE, False): self.ldflags_exe, | |
276 (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug, | |
277 (CCompiler.SHARED_OBJECT, None): self.ldflags_shared, | |
278 (CCompiler.SHARED_OBJECT, False): self.ldflags_shared, | |
279 (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug, | |
280 (CCompiler.SHARED_LIBRARY, None): self.ldflags_static, | |
281 (CCompiler.SHARED_LIBRARY, False): self.ldflags_static, | |
282 (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug, | |
283 } | |
284 | |
285 self.initialized = True | |
286 | |
287 # -- Worker methods ------------------------------------------------ | |
288 | |
289 def object_filenames(self, | |
290 source_filenames, | |
291 strip_dir=0, | |
292 output_dir=''): | |
293 ext_map = { | |
294 **{ext: self.obj_extension for ext in self.src_extensions}, | |
295 **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions}, | |
296 } | |
297 | |
298 output_dir = output_dir or '' | |
299 | |
300 def make_out_path(p): | |
301 base, ext = os.path.splitext(p) | |
302 if strip_dir: | |
303 base = os.path.basename(base) | |
304 else: | |
305 _, base = os.path.splitdrive(base) | |
306 if base.startswith((os.path.sep, os.path.altsep)): | |
307 base = base[1:] | |
308 try: | |
309 # XXX: This may produce absurdly long paths. We should check | |
310 # the length of the result and trim base until we fit within | |
311 # 260 characters. | |
312 return os.path.join(output_dir, base + ext_map[ext]) | |
313 except LookupError: | |
314 # Better to raise an exception instead of silently continuing | |
315 # and later complain about sources and targets having | |
316 # different lengths | |
317 raise CompileError("Don't know how to compile {}".format(p)) | |
318 | |
319 return list(map(make_out_path, source_filenames)) | |
320 | |
321 | |
322 def compile(self, sources, | |
323 output_dir=None, macros=None, include_dirs=None, debug=0, | |
324 extra_preargs=None, extra_postargs=None, depends=None): | |
325 | |
326 if not self.initialized: | |
327 self.initialize() | |
328 compile_info = self._setup_compile(output_dir, macros, include_dirs, | |
329 sources, depends, extra_postargs) | |
330 macros, objects, extra_postargs, pp_opts, build = compile_info | |
331 | |
332 compile_opts = extra_preargs or [] | |
333 compile_opts.append('/c') | |
334 if debug: | |
335 compile_opts.extend(self.compile_options_debug) | |
336 else: | |
337 compile_opts.extend(self.compile_options) | |
338 | |
339 | |
340 add_cpp_opts = False | |
341 | |
342 for obj in objects: | |
343 try: | |
344 src, ext = build[obj] | |
345 except KeyError: | |
346 continue | |
347 if debug: | |
348 # pass the full pathname to MSVC in debug mode, | |
349 # this allows the debugger to find the source file | |
350 # without asking the user to browse for it | |
351 src = os.path.abspath(src) | |
352 | |
353 if ext in self._c_extensions: | |
354 input_opt = "/Tc" + src | |
355 elif ext in self._cpp_extensions: | |
356 input_opt = "/Tp" + src | |
357 add_cpp_opts = True | |
358 elif ext in self._rc_extensions: | |
359 # compile .RC to .RES file | |
360 input_opt = src | |
361 output_opt = "/fo" + obj | |
362 try: | |
363 self.spawn([self.rc] + pp_opts + [output_opt, input_opt]) | |
364 except DistutilsExecError as msg: | |
365 raise CompileError(msg) | |
366 continue | |
367 elif ext in self._mc_extensions: | |
368 # Compile .MC to .RC file to .RES file. | |
369 # * '-h dir' specifies the directory for the | |
370 # generated include file | |
371 # * '-r dir' specifies the target directory of the | |
372 # generated RC file and the binary message resource | |
373 # it includes | |
374 # | |
375 # For now (since there are no options to change this), | |
376 # we use the source-directory for the include file and | |
377 # the build directory for the RC file and message | |
378 # resources. This works at least for win32all. | |
379 h_dir = os.path.dirname(src) | |
380 rc_dir = os.path.dirname(obj) | |
381 try: | |
382 # first compile .MC to .RC and .H file | |
383 self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src]) | |
384 base, _ = os.path.splitext(os.path.basename (src)) | |
385 rc_file = os.path.join(rc_dir, base + '.rc') | |
386 # then compile .RC to .RES file | |
387 self.spawn([self.rc, "/fo" + obj, rc_file]) | |
388 | |
389 except DistutilsExecError as msg: | |
390 raise CompileError(msg) | |
391 continue | |
392 else: | |
393 # how to handle this file? | |
394 raise CompileError("Don't know how to compile {} to {}" | |
395 .format(src, obj)) | |
396 | |
397 args = [self.cc] + compile_opts + pp_opts | |
398 if add_cpp_opts: | |
399 args.append('/EHsc') | |
400 args.append(input_opt) | |
401 args.append("/Fo" + obj) | |
402 args.extend(extra_postargs) | |
403 | |
404 try: | |
405 self.spawn(args) | |
406 except DistutilsExecError as msg: | |
407 raise CompileError(msg) | |
408 | |
409 return objects | |
410 | |
411 | |
412 def create_static_lib(self, | |
413 objects, | |
414 output_libname, | |
415 output_dir=None, | |
416 debug=0, | |
417 target_lang=None): | |
418 | |
419 if not self.initialized: | |
420 self.initialize() | |
421 objects, output_dir = self._fix_object_args(objects, output_dir) | |
422 output_filename = self.library_filename(output_libname, | |
423 output_dir=output_dir) | |
424 | |
425 if self._need_link(objects, output_filename): | |
426 lib_args = objects + ['/OUT:' + output_filename] | |
427 if debug: | |
428 pass # XXX what goes here? | |
429 try: | |
430 log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args)) | |
431 self.spawn([self.lib] + lib_args) | |
432 except DistutilsExecError as msg: | |
433 raise LibError(msg) | |
434 else: | |
435 log.debug("skipping %s (up-to-date)", output_filename) | |
436 | |
437 | |
438 def link(self, | |
439 target_desc, | |
440 objects, | |
441 output_filename, | |
442 output_dir=None, | |
443 libraries=None, | |
444 library_dirs=None, | |
445 runtime_library_dirs=None, | |
446 export_symbols=None, | |
447 debug=0, | |
448 extra_preargs=None, | |
449 extra_postargs=None, | |
450 build_temp=None, | |
451 target_lang=None): | |
452 | |
453 if not self.initialized: | |
454 self.initialize() | |
455 objects, output_dir = self._fix_object_args(objects, output_dir) | |
456 fixed_args = self._fix_lib_args(libraries, library_dirs, | |
457 runtime_library_dirs) | |
458 libraries, library_dirs, runtime_library_dirs = fixed_args | |
459 | |
460 if runtime_library_dirs: | |
461 self.warn("I don't know what to do with 'runtime_library_dirs': " | |
462 + str(runtime_library_dirs)) | |
463 | |
464 lib_opts = gen_lib_options(self, | |
465 library_dirs, runtime_library_dirs, | |
466 libraries) | |
467 if output_dir is not None: | |
468 output_filename = os.path.join(output_dir, output_filename) | |
469 | |
470 if self._need_link(objects, output_filename): | |
471 ldflags = self._ldflags[target_desc, debug] | |
472 | |
473 export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])] | |
474 | |
475 ld_args = (ldflags + lib_opts + export_opts + | |
476 objects + ['/OUT:' + output_filename]) | |
477 | |
478 # The MSVC linker generates .lib and .exp files, which cannot be | |
479 # suppressed by any linker switches. The .lib files may even be | |
480 # needed! Make sure they are generated in the temporary build | |
481 # directory. Since they have different names for debug and release | |
482 # builds, they can go into the same directory. | |
483 build_temp = os.path.dirname(objects[0]) | |
484 if export_symbols is not None: | |
485 (dll_name, dll_ext) = os.path.splitext( | |
486 os.path.basename(output_filename)) | |
487 implib_file = os.path.join( | |
488 build_temp, | |
489 self.library_filename(dll_name)) | |
490 ld_args.append ('/IMPLIB:' + implib_file) | |
491 | |
492 if extra_preargs: | |
493 ld_args[:0] = extra_preargs | |
494 if extra_postargs: | |
495 ld_args.extend(extra_postargs) | |
496 | |
497 output_dir = os.path.dirname(os.path.abspath(output_filename)) | |
498 self.mkpath(output_dir) | |
499 try: | |
500 log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args)) | |
501 self.spawn([self.linker] + ld_args) | |
502 except DistutilsExecError as msg: | |
503 raise LinkError(msg) | |
504 else: | |
505 log.debug("skipping %s (up-to-date)", output_filename) | |
506 | |
507 def spawn(self, cmd): | |
508 env = dict(os.environ, PATH=self._paths) | |
509 with self._fallback_spawn(cmd, env) as fallback: | |
510 return super().spawn(cmd, env=env) | |
511 return fallback.value | |
512 | |
513 @contextlib.contextmanager | |
514 def _fallback_spawn(self, cmd, env): | |
515 """ | |
516 Discovered in pypa/distutils#15, some tools monkeypatch the compiler, | |
517 so the 'env' kwarg causes a TypeError. Detect this condition and | |
518 restore the legacy, unsafe behavior. | |
519 """ | |
520 bag = type('Bag', (), {})() | |
521 try: | |
522 yield bag | |
523 except TypeError as exc: | |
524 if "unexpected keyword argument 'env'" not in str(exc): | |
525 raise | |
526 else: | |
527 return | |
528 warnings.warn( | |
529 "Fallback spawn triggered. Please update distutils monkeypatch.") | |
530 with unittest.mock.patch('os.environ', env): | |
531 bag.value = super().spawn(cmd) | |
532 | |
533 # -- Miscellaneous methods ----------------------------------------- | |
534 # These are all used by the 'gen_lib_options() function, in | |
535 # ccompiler.py. | |
536 | |
537 def library_dir_option(self, dir): | |
538 return "/LIBPATH:" + dir | |
539 | |
540 def runtime_library_dir_option(self, dir): | |
541 raise DistutilsPlatformError( | |
542 "don't know how to set runtime library search path for MSVC") | |
543 | |
544 def library_option(self, lib): | |
545 return self.library_filename(lib) | |
546 | |
547 def find_library_file(self, dirs, lib, debug=0): | |
548 # Prefer a debugging library if found (and requested), but deal | |
549 # with it if we don't have one. | |
550 if debug: | |
551 try_names = [lib + "_d", lib] | |
552 else: | |
553 try_names = [lib] | |
554 for dir in dirs: | |
555 for name in try_names: | |
556 libfile = os.path.join(dir, self.library_filename(name)) | |
557 if os.path.isfile(libfile): | |
558 return libfile | |
559 else: | |
560 # Oops, didn't find it in *any* of 'dirs' | |
561 return None |