ยปCore Development>Code coverage>Lib/distutils/_msvccompiler.py

Python code coverage for Lib/distutils/_msvccompiler.py

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