blob: 896d9d927fa6069150355a214e6c9b7a54293d19 [file] [log] [blame]
Steve Dowerfd3664b2015-05-23 09:02:50 -07001"""distutils._msvccompiler
2
3Contains MSVCCompiler, an implementation of the abstract CCompiler class
4for Microsoft Visual Studio 2015.
5
6The module is compatible with VS 2015 and later. You can find legacy support
7for 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
16import os
17import subprocess
18import re
19
20from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
21 CompileError, LibError, LinkError
22from distutils.ccompiler import CCompiler, gen_lib_options
23from distutils import log
24from distutils.util import get_platform
25
26import winreg
27from itertools import count
28
29def _find_vcvarsall():
30 with winreg.OpenKeyEx(
31 winreg.HKEY_LOCAL_MACHINE,
32 r"Software\Microsoft\VisualStudio\SxS\VC7",
33 access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
34 ) as key:
35 if not key:
36 log.debug("Visual C++ is not registered")
37 return None
38
39 best_version = 0
40 best_dir = None
41 for i in count():
42 try:
43 v, vc_dir, vt = winreg.EnumValue(key, i)
44 except OSError:
45 break
46 if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
47 try:
48 version = int(float(v))
49 except (ValueError, TypeError):
50 continue
51 if version >= 14 and version > best_version:
52 best_version, best_dir = version, vc_dir
53 if not best_version:
54 log.debug("No suitable Visual C++ version found")
55 return None
56
57 vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
58 if not os.path.isfile(vcvarsall):
59 log.debug("%s cannot be found", vcvarsall)
60 return None
61
62 return vcvarsall
63
64def _get_vc_env(plat_spec):
65 if os.getenv("DISTUTILS_USE_SDK"):
66 return {
67 key.lower(): value
68 for key, value in os.environ.items()
69 }
70
71 vcvarsall = _find_vcvarsall()
72 if not vcvarsall:
73 raise DistutilsPlatformError("Unable to find vcvarsall.bat")
74
75 try:
76 out = subprocess.check_output(
77 '"{}" {} && set'.format(vcvarsall, plat_spec),
78 shell=True,
79 stderr=subprocess.STDOUT,
80 universal_newlines=True,
81 )
82 except subprocess.CalledProcessError as exc:
83 log.error(exc.output)
84 raise DistutilsPlatformError("Error executing {}"
85 .format(exc.cmd))
86
87 return {
88 key.lower(): value
89 for key, _, value in
90 (line.partition('=') for line in out.splitlines())
91 if key and value
92 }
93
94def _find_exe(exe, paths=None):
95 """Return path to an MSVC executable program.
96
97 Tries to find the program in several places: first, one of the
98 MSVC program search paths from the registry; next, the directories
99 in the PATH environment variable. If any of those work, return an
100 absolute path that is known to exist. If none of them work, just
101 return the original program name, 'exe'.
102 """
103 if not paths:
104 paths = os.getenv('path').split(os.pathsep)
105 for p in paths:
106 fn = os.path.join(os.path.abspath(p), exe)
107 if os.path.isfile(fn):
108 return fn
109 return exe
110
111# A map keyed by get_platform() return values to values accepted by
112# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
113# the param to cross-compile on x86 targetting amd64.)
114PLAT_TO_VCVARS = {
115 'win32' : 'x86',
116 'win-amd64' : 'amd64',
117}
118
119class MSVCCompiler(CCompiler) :
120 """Concrete class that implements an interface to Microsoft Visual C++,
121 as defined by the CCompiler abstract class."""
122
123 compiler_type = 'msvc'
124
125 # Just set this so CCompiler's constructor doesn't barf. We currently
126 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
127 # as it really isn't necessary for this sort of single-compiler class.
128 # Would be nice to have a consistent interface with UnixCCompiler,
129 # though, so it's worth thinking about.
130 executables = {}
131
132 # Private class data (need to distinguish C from C++ source for compiler)
133 _c_extensions = ['.c']
134 _cpp_extensions = ['.cc', '.cpp', '.cxx']
135 _rc_extensions = ['.rc']
136 _mc_extensions = ['.mc']
137
138 # Needed for the filename generation methods provided by the
139 # base class, CCompiler.
140 src_extensions = (_c_extensions + _cpp_extensions +
141 _rc_extensions + _mc_extensions)
142 res_extension = '.res'
143 obj_extension = '.obj'
144 static_lib_extension = '.lib'
145 shared_lib_extension = '.dll'
146 static_lib_format = shared_lib_format = '%s%s'
147 exe_extension = '.exe'
148
149
150 def __init__(self, verbose=0, dry_run=0, force=0):
151 CCompiler.__init__ (self, verbose, dry_run, force)
152 # target platform (.plat_name is consistent with 'bdist')
153 self.plat_name = None
154 self.initialized = False
155
156 def initialize(self, plat_name=None):
157 # multi-init means we would need to check platform same each time...
158 assert not self.initialized, "don't init multiple times"
159 if plat_name is None:
160 plat_name = get_platform()
161 # sanity check for platforms to prevent obscure errors later.
162 if plat_name not in PLAT_TO_VCVARS:
163 raise DistutilsPlatformError("--plat-name must be one of {}"
164 .format(tuple(PLAT_TO_VCVARS)))
165
166 # On x86, 'vcvarsall.bat amd64' creates an env that doesn't work;
167 # to cross compile, you use 'x86_amd64'.
168 # On AMD64, 'vcvarsall.bat amd64' is a native build env; to cross
169 # compile use 'x86' (ie, it runs the x86 compiler directly)
170 if plat_name == get_platform() or plat_name == 'win32':
171 # native build or cross-compile to win32
172 plat_spec = PLAT_TO_VCVARS[plat_name]
173 else:
174 # cross compile from win32 -> some 64bit
175 plat_spec = '{}_{}'.format(
176 PLAT_TO_VCVARS[get_platform()],
177 PLAT_TO_VCVARS[plat_name]
178 )
179
180 vc_env = _get_vc_env(plat_spec)
181 if not vc_env:
182 raise DistutilsPlatformError("Unable to find a compatible "
183 "Visual Studio installation.")
184
185 paths = vc_env.get('path', '').split(os.pathsep)
186 self.cc = _find_exe("cl.exe", paths)
187 self.linker = _find_exe("link.exe", paths)
188 self.lib = _find_exe("lib.exe", paths)
189 self.rc = _find_exe("rc.exe", paths) # resource compiler
190 self.mc = _find_exe("mc.exe", paths) # message compiler
191 self.mt = _find_exe("mt.exe", paths) # message compiler
192
193 for dir in vc_env.get('include', '').split(os.pathsep):
194 if dir:
195 self.add_include_dir(dir)
196
197 for dir in vc_env.get('lib', '').split(os.pathsep):
198 if dir:
199 self.add_library_dir(dir)
200
201 self.preprocess_options = None
202 self.compile_options = [
203 '/nologo', '/Ox', '/MD', '/W3', '/GL', '/DNDEBUG'
204 ]
205 self.compile_options_debug = [
206 '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'
207 ]
208
209 self.ldflags_shared = [
210 '/nologo', '/DLL', '/INCREMENTAL:NO'
211 ]
212 self.ldflags_shared_debug = [
213 '/nologo', '/DLL', '/INCREMENTAL:no', '/DEBUG:FULL'
214 ]
215 self.ldflags_static = [
216 '/nologo'
217 ]
218
219 self.initialized = True
220
221 # -- Worker methods ------------------------------------------------
222
223 def object_filenames(self,
224 source_filenames,
225 strip_dir=0,
226 output_dir=''):
227 ext_map = {ext: self.obj_extension for ext in self.src_extensions}
228 ext_map.update((ext, self.res_extension)
229 for ext in self._rc_extensions + self._mc_extensions)
230
231 def make_out_path(p):
232 base, ext = os.path.splitext(p)
233 if strip_dir:
234 base = os.path.basename(base)
235 else:
236 _, base = os.path.splitdrive(base)
237 if base.startswith((os.path.sep, os.path.altsep)):
238 base = base[1:]
239 try:
240 return base + ext_map[ext]
241 except LookupError:
242 # Better to raise an exception instead of silently continuing
243 # and later complain about sources and targets having
244 # different lengths
245 raise CompileError("Don't know how to compile {}".format(p))
246
247 output_dir = output_dir or ''
248 return [
249 os.path.join(output_dir, make_out_path(src_name))
250 for src_name in source_filenames
251 ]
252
253
254 def compile(self, sources,
255 output_dir=None, macros=None, include_dirs=None, debug=0,
256 extra_preargs=None, extra_postargs=None, depends=None):
257
258 if not self.initialized:
259 self.initialize()
260 compile_info = self._setup_compile(output_dir, macros, include_dirs,
261 sources, depends, extra_postargs)
262 macros, objects, extra_postargs, pp_opts, build = compile_info
263
264 compile_opts = extra_preargs or []
265 compile_opts.append('/c')
266 if debug:
267 compile_opts.extend(self.compile_options_debug)
268 else:
269 compile_opts.extend(self.compile_options)
270
271
272 add_cpp_opts = False
273
274 for obj in objects:
275 try:
276 src, ext = build[obj]
277 except KeyError:
278 continue
279 if debug:
280 # pass the full pathname to MSVC in debug mode,
281 # this allows the debugger to find the source file
282 # without asking the user to browse for it
283 src = os.path.abspath(src)
284
285 if ext in self._c_extensions:
286 input_opt = "/Tc" + src
287 elif ext in self._cpp_extensions:
288 input_opt = "/Tp" + src
289 add_cpp_opts = True
290 elif ext in self._rc_extensions:
291 # compile .RC to .RES file
292 input_opt = src
293 output_opt = "/fo" + obj
294 try:
295 self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
296 except DistutilsExecError as msg:
297 raise CompileError(msg)
298 continue
299 elif ext in self._mc_extensions:
300 # Compile .MC to .RC file to .RES file.
301 # * '-h dir' specifies the directory for the
302 # generated include file
303 # * '-r dir' specifies the target directory of the
304 # generated RC file and the binary message resource
305 # it includes
306 #
307 # For now (since there are no options to change this),
308 # we use the source-directory for the include file and
309 # the build directory for the RC file and message
310 # resources. This works at least for win32all.
311 h_dir = os.path.dirname(src)
312 rc_dir = os.path.dirname(obj)
313 try:
314 # first compile .MC to .RC and .H file
315 self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
316 base, _ = os.path.splitext(os.path.basename (src))
317 rc_file = os.path.join(rc_dir, base + '.rc')
318 # then compile .RC to .RES file
319 self.spawn([self.rc, "/fo" + obj, rc_file])
320
321 except DistutilsExecError as msg:
322 raise CompileError(msg)
323 continue
324 else:
325 # how to handle this file?
326 raise CompileError("Don't know how to compile {} to {}"
327 .format(src, obj))
328
329 args = [self.cc] + compile_opts + pp_opts
330 if add_cpp_opts:
331 args.append('/EHsc')
332 args.append(input_opt)
333 args.append("/Fo" + obj)
334 args.extend(extra_postargs)
335
336 try:
337 self.spawn(args)
338 except DistutilsExecError as msg:
339 raise CompileError(msg)
340
341 return objects
342
343
344 def create_static_lib(self,
345 objects,
346 output_libname,
347 output_dir=None,
348 debug=0,
349 target_lang=None):
350
351 if not self.initialized:
352 self.initialize()
353 objects, output_dir = self._fix_object_args(objects, output_dir)
354 output_filename = self.library_filename(output_libname,
355 output_dir=output_dir)
356
357 if self._need_link(objects, output_filename):
358 lib_args = objects + ['/OUT:' + output_filename]
359 if debug:
360 pass # XXX what goes here?
361 try:
362 self.spawn([self.lib] + lib_args)
363 except DistutilsExecError as msg:
364 raise LibError(msg)
365 else:
366 log.debug("skipping %s (up-to-date)", output_filename)
367
368
369 def link(self,
370 target_desc,
371 objects,
372 output_filename,
373 output_dir=None,
374 libraries=None,
375 library_dirs=None,
376 runtime_library_dirs=None,
377 export_symbols=None,
378 debug=0,
379 extra_preargs=None,
380 extra_postargs=None,
381 build_temp=None,
382 target_lang=None):
383
384 if not self.initialized:
385 self.initialize()
386 objects, output_dir = self._fix_object_args(objects, output_dir)
387 fixed_args = self._fix_lib_args(libraries, library_dirs,
388 runtime_library_dirs)
389 libraries, library_dirs, runtime_library_dirs = fixed_args
390
391 if runtime_library_dirs:
392 self.warn("I don't know what to do with 'runtime_library_dirs': "
393 + str(runtime_library_dirs))
394
395 lib_opts = gen_lib_options(self,
396 library_dirs, runtime_library_dirs,
397 libraries)
398 if output_dir is not None:
399 output_filename = os.path.join(output_dir, output_filename)
400
401 if self._need_link(objects, output_filename):
402 ldflags = (self.ldflags_shared_debug if debug
403 else self.ldflags_shared)
404 if target_desc == CCompiler.EXECUTABLE:
405 ldflags = ldflags[1:]
406
407 export_opts = []
408 for sym in (export_symbols or []):
409 export_opts.append("/EXPORT:" + sym)
410
411 ld_args = (ldflags + lib_opts + export_opts +
412 objects + ['/OUT:' + output_filename])
413
414 # The MSVC linker generates .lib and .exp files, which cannot be
415 # suppressed by any linker switches. The .lib files may even be
416 # needed! Make sure they are generated in the temporary build
417 # directory. Since they have different names for debug and release
418 # builds, they can go into the same directory.
419 build_temp = os.path.dirname(objects[0])
420 if export_symbols is not None:
421 (dll_name, dll_ext) = os.path.splitext(
422 os.path.basename(output_filename))
423 implib_file = os.path.join(
424 build_temp,
425 self.library_filename(dll_name))
426 ld_args.append ('/IMPLIB:' + implib_file)
427
428 self.manifest_setup_ldargs(output_filename, build_temp, ld_args)
429
430 if extra_preargs:
431 ld_args[:0] = extra_preargs
432 if extra_postargs:
433 ld_args.extend(extra_postargs)
434
435 self.mkpath(os.path.dirname(output_filename))
436 try:
437 self.spawn([self.linker] + ld_args)
438 except DistutilsExecError as msg:
439 raise LinkError(msg)
440
441 # embed the manifest
442 # XXX - this is somewhat fragile - if mt.exe fails, distutils
443 # will still consider the DLL up-to-date, but it will not have a
444 # manifest. Maybe we should link to a temp file? OTOH, that
445 # implies a build environment error that shouldn't go undetected.
446 mfinfo = self.manifest_get_embed_info(target_desc, ld_args)
447 if mfinfo is not None:
448 mffilename, mfid = mfinfo
449 out_arg = '-outputresource:{};{}'.format(output_filename, mfid)
450 try:
451 self.spawn([self.mt, '-nologo', '-manifest',
452 mffilename, out_arg])
453 except DistutilsExecError as msg:
454 raise LinkError(msg)
455 else:
456 log.debug("skipping %s (up-to-date)", output_filename)
457
458 def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
459 # If we need a manifest at all, an embedded manifest is recommended.
460 # See MSDN article titled
461 # "How to: Embed a Manifest Inside a C/C++ Application"
462 # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
463 # Ask the linker to generate the manifest in the temp dir, so
464 # we can check it, and possibly embed it, later.
465 temp_manifest = os.path.join(
466 build_temp,
467 os.path.basename(output_filename) + ".manifest")
468 ld_args.append('/MANIFESTFILE:' + temp_manifest)
469
470 def manifest_get_embed_info(self, target_desc, ld_args):
471 # If a manifest should be embedded, return a tuple of
472 # (manifest_filename, resource_id). Returns None if no manifest
473 # should be embedded. See http://bugs.python.org/issue7833 for why
474 # we want to avoid any manifest for extension modules if we can)
475 for arg in ld_args:
476 if arg.startswith("/MANIFESTFILE:"):
477 temp_manifest = arg.split(":", 1)[1]
478 break
479 else:
480 # no /MANIFESTFILE so nothing to do.
481 return None
482 if target_desc == CCompiler.EXECUTABLE:
483 # by default, executables always get the manifest with the
484 # CRT referenced.
485 mfid = 1
486 else:
487 # Extension modules try and avoid any manifest if possible.
488 mfid = 2
489 temp_manifest = self._remove_visual_c_ref(temp_manifest)
490 if temp_manifest is None:
491 return None
492 return temp_manifest, mfid
493
494 def _remove_visual_c_ref(self, manifest_file):
495 try:
496 # Remove references to the Visual C runtime, so they will
497 # fall through to the Visual C dependency of Python.exe.
498 # This way, when installed for a restricted user (e.g.
499 # runtimes are not in WinSxS folder, but in Python's own
500 # folder), the runtimes do not need to be in every folder
501 # with .pyd's.
502 # Returns either the filename of the modified manifest or
503 # None if no manifest should be embedded.
504 manifest_f = open(manifest_file)
505 try:
506 manifest_buf = manifest_f.read()
507 finally:
508 manifest_f.close()
509 pattern = re.compile(
510 r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
511 r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
512 re.DOTALL)
513 manifest_buf = re.sub(pattern, "", manifest_buf)
514 pattern = "<dependentAssembly>\s*</dependentAssembly>"
515 manifest_buf = re.sub(pattern, "", manifest_buf)
516 # Now see if any other assemblies are referenced - if not, we
517 # don't want a manifest embedded.
518 pattern = re.compile(
519 r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
520 r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL)
521 if re.search(pattern, manifest_buf) is None:
522 return None
523
524 manifest_f = open(manifest_file, 'w')
525 try:
526 manifest_f.write(manifest_buf)
527 return manifest_file
528 finally:
529 manifest_f.close()
530 except OSError:
531 pass
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