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