blob: c7ac3f049ebf227e26c3932381c31c955dd28d28 [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
Steve Dowerfcbe1df2015-09-08 21:39:01 -070017import shutil
18import stat
Steve Dowerfd3664b2015-05-23 09:02:50 -070019import subprocess
Steve Dower05f01d82017-09-07 11:49:23 -070020import winreg
Steve Dowerfd3664b2015-05-23 09:02:50 -070021
22from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
23 CompileError, LibError, LinkError
24from distutils.ccompiler import CCompiler, gen_lib_options
25from distutils import log
26from distutils.util import get_platform
27
Steve Dowerfd3664b2015-05-23 09:02:50 -070028from itertools import count
29
Steve Dower05f01d82017-09-07 11:49:23 -070030def _find_vc2015():
Steve Dowerf0ccf022015-10-05 10:35:00 -070031 try:
32 key = winreg.OpenKeyEx(
33 winreg.HKEY_LOCAL_MACHINE,
34 r"Software\Microsoft\VisualStudio\SxS\VC7",
35 access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
36 )
37 except OSError:
38 log.debug("Visual C++ is not registered")
39 return None, None
Steve Dowerfd3664b2015-05-23 09:02:50 -070040
Steve Dower05f01d82017-09-07 11:49:23 -070041 best_version = 0
42 best_dir = None
Steve Dowerf0ccf022015-10-05 10:35:00 -070043 with key:
Steve Dowerfd3664b2015-05-23 09:02:50 -070044 for i in count():
45 try:
46 v, vc_dir, vt = winreg.EnumValue(key, i)
47 except OSError:
48 break
49 if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
50 try:
51 version = int(float(v))
52 except (ValueError, TypeError):
53 continue
54 if version >= 14 and version > best_version:
55 best_version, best_dir = version, vc_dir
Steve Dower05f01d82017-09-07 11:49:23 -070056 return best_version, best_dir
Steve Dowerfd3664b2015-05-23 09:02:50 -070057
Steve Dower05f01d82017-09-07 11:49:23 -070058def _find_vc2017():
Steve Dower53125a52018-10-27 16:48:33 -040059 """Returns "15, path" based on the result of invoking vswhere.exe
60 If no install is found, returns "None, None"
Steve Dowerfd3664b2015-05-23 09:02:50 -070061
Steve Dower53125a52018-10-27 16:48:33 -040062 The version is returned to avoid unnecessarily changing the function
63 result. It may be ignored when the path is not None.
Steve Dower05f01d82017-09-07 11:49:23 -070064
Steve Dower53125a52018-10-27 16:48:33 -040065 If vswhere.exe is not available, by definition, VS 2017 is not
66 installed.
67 """
68 import json
Steve Dower05f01d82017-09-07 11:49:23 -070069
Steve Dower53125a52018-10-27 16:48:33 -040070 root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
71 if not root:
72 return None, None
73
Steve Dower05f01d82017-09-07 11:49:23 -070074 try:
Steve Dower53125a52018-10-27 16:48:33 -040075 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",
Marc Schlaichb2dc4a32019-01-20 19:47:42 +010081 "-products", "*",
Steve Dower53125a52018-10-27 16:48:33 -040082 ], 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
Steve Dower05f01d82017-09-07 11:49:23 -070091
Paul Monson62dfd7d2019-04-25 11:36:45 -070092PLAT_SPEC_TO_RUNTIME = {
93 'x86' : 'x86',
94 'x86_amd64' : 'x64',
95 'x86_arm' : 'arm',
96}
97
Steve Dower05f01d82017-09-07 11:49:23 -070098def _find_vcvarsall(plat_spec):
Steve Dower53125a52018-10-27 16:48:33 -040099 _, best_dir = _find_vc2017()
Steve Dower05f01d82017-09-07 11:49:23 -0700100 vcruntime = None
Paul Monson62dfd7d2019-04-25 11:36:45 -0700101
102 if plat_spec in PLAT_SPEC_TO_RUNTIME:
103 vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec]
104 else:
105 vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
106
Steve Dower53125a52018-10-27 16:48:33 -0400107 if best_dir:
Steve Dower05f01d82017-09-07 11:49:23 -0700108 vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**",
Paul Monson62dfd7d2019-04-25 11:36:45 -0700109 vcruntime_plat, "Microsoft.VC141.CRT", "vcruntime140.dll")
Steve Dower05f01d82017-09-07 11:49:23 -0700110 try:
111 import glob
112 vcruntime = glob.glob(vcredist, recursive=True)[-1]
113 except (ImportError, OSError, LookupError):
114 vcruntime = None
115
Steve Dower53125a52018-10-27 16:48:33 -0400116 if not best_dir:
Steve Dower05f01d82017-09-07 11:49:23 -0700117 best_version, best_dir = _find_vc2015()
118 if best_version:
119 vcruntime = os.path.join(best_dir, 'redist', vcruntime_plat,
120 "Microsoft.VC140.CRT", "vcruntime140.dll")
121
Steve Dower53125a52018-10-27 16:48:33 -0400122 if not best_dir:
Steve Dower05f01d82017-09-07 11:49:23 -0700123 log.debug("No suitable Visual C++ version found")
124 return None, None
125
126 vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
127 if not os.path.isfile(vcvarsall):
128 log.debug("%s cannot be found", vcvarsall)
129 return None, None
130
131 if not vcruntime or not os.path.isfile(vcruntime):
132 log.debug("%s cannot be found", vcruntime)
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700133 vcruntime = None
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700134
Steve Dower05f01d82017-09-07 11:49:23 -0700135 return vcvarsall, vcruntime
Steve Dowerfd3664b2015-05-23 09:02:50 -0700136
137def _get_vc_env(plat_spec):
138 if os.getenv("DISTUTILS_USE_SDK"):
139 return {
140 key.lower(): value
141 for key, value in os.environ.items()
142 }
143
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700144 vcvarsall, vcruntime = _find_vcvarsall(plat_spec)
Steve Dowerfd3664b2015-05-23 09:02:50 -0700145 if not vcvarsall:
146 raise DistutilsPlatformError("Unable to find vcvarsall.bat")
147
148 try:
149 out = subprocess.check_output(
Steve Dower08bb8a42016-06-17 09:32:38 -0700150 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
Steve Dowerfd3664b2015-05-23 09:02:50 -0700151 stderr=subprocess.STDOUT,
Steve Dower08bb8a42016-06-17 09:32:38 -0700152 ).decode('utf-16le', errors='replace')
Steve Dowerfd3664b2015-05-23 09:02:50 -0700153 except subprocess.CalledProcessError as exc:
154 log.error(exc.output)
155 raise DistutilsPlatformError("Error executing {}"
156 .format(exc.cmd))
157
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700158 env = {
Steve Dowerfd3664b2015-05-23 09:02:50 -0700159 key.lower(): value
160 for key, _, value in
161 (line.partition('=') for line in out.splitlines())
162 if key and value
163 }
Larry Hastings52e40cd2015-09-09 06:54:57 -0700164
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700165 if vcruntime:
166 env['py_vcruntime_redist'] = vcruntime
167 return env
Steve Dowerfd3664b2015-05-23 09:02:50 -0700168
169def _find_exe(exe, paths=None):
170 """Return path to an MSVC executable program.
171
172 Tries to find the program in several places: first, one of the
173 MSVC program search paths from the registry; next, the directories
174 in the PATH environment variable. If any of those work, return an
175 absolute path that is known to exist. If none of them work, just
176 return the original program name, 'exe'.
177 """
178 if not paths:
179 paths = os.getenv('path').split(os.pathsep)
180 for p in paths:
181 fn = os.path.join(os.path.abspath(p), exe)
182 if os.path.isfile(fn):
183 return fn
184 return exe
185
186# A map keyed by get_platform() return values to values accepted by
Steve Dower1d329412016-01-16 12:39:10 -0800187# 'vcvarsall.bat'. Always cross-compile from x86 to work with the
188# lighter-weight MSVC installs that do not include native 64-bit tools.
Steve Dowerfd3664b2015-05-23 09:02:50 -0700189PLAT_TO_VCVARS = {
190 'win32' : 'x86',
Steve Dower1d329412016-01-16 12:39:10 -0800191 'win-amd64' : 'x86_amd64',
Paul Monson62dfd7d2019-04-25 11:36:45 -0700192 'win-arm32' : 'x86_arm',
Steve Dowerfd3664b2015-05-23 09:02:50 -0700193}
194
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700195# A set containing the DLLs that are guaranteed to be available for
196# all micro versions of this Python version. Known extension
197# dependencies that are not in this set will be copied to the output
198# path.
199_BUNDLED_DLLS = frozenset(['vcruntime140.dll'])
200
Steve Dowerfd3664b2015-05-23 09:02:50 -0700201class MSVCCompiler(CCompiler) :
202 """Concrete class that implements an interface to Microsoft Visual C++,
203 as defined by the CCompiler abstract class."""
204
205 compiler_type = 'msvc'
206
207 # Just set this so CCompiler's constructor doesn't barf. We currently
208 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
209 # as it really isn't necessary for this sort of single-compiler class.
210 # Would be nice to have a consistent interface with UnixCCompiler,
211 # though, so it's worth thinking about.
212 executables = {}
213
214 # Private class data (need to distinguish C from C++ source for compiler)
215 _c_extensions = ['.c']
216 _cpp_extensions = ['.cc', '.cpp', '.cxx']
217 _rc_extensions = ['.rc']
218 _mc_extensions = ['.mc']
219
220 # Needed for the filename generation methods provided by the
221 # base class, CCompiler.
222 src_extensions = (_c_extensions + _cpp_extensions +
223 _rc_extensions + _mc_extensions)
224 res_extension = '.res'
225 obj_extension = '.obj'
226 static_lib_extension = '.lib'
227 shared_lib_extension = '.dll'
228 static_lib_format = shared_lib_format = '%s%s'
229 exe_extension = '.exe'
230
231
232 def __init__(self, verbose=0, dry_run=0, force=0):
233 CCompiler.__init__ (self, verbose, dry_run, force)
234 # target platform (.plat_name is consistent with 'bdist')
235 self.plat_name = None
236 self.initialized = False
237
238 def initialize(self, plat_name=None):
239 # multi-init means we would need to check platform same each time...
240 assert not self.initialized, "don't init multiple times"
241 if plat_name is None:
242 plat_name = get_platform()
243 # sanity check for platforms to prevent obscure errors later.
244 if plat_name not in PLAT_TO_VCVARS:
245 raise DistutilsPlatformError("--plat-name must be one of {}"
246 .format(tuple(PLAT_TO_VCVARS)))
247
Steve Dower1d329412016-01-16 12:39:10 -0800248 # Get the vcvarsall.bat spec for the requested platform.
249 plat_spec = PLAT_TO_VCVARS[plat_name]
Steve Dowerfd3664b2015-05-23 09:02:50 -0700250
251 vc_env = _get_vc_env(plat_spec)
252 if not vc_env:
253 raise DistutilsPlatformError("Unable to find a compatible "
254 "Visual Studio installation.")
255
Steve Dower31202ea2015-08-05 11:39:19 -0700256 self._paths = vc_env.get('path', '')
257 paths = self._paths.split(os.pathsep)
Steve Dowerfd3664b2015-05-23 09:02:50 -0700258 self.cc = _find_exe("cl.exe", paths)
259 self.linker = _find_exe("link.exe", paths)
260 self.lib = _find_exe("lib.exe", paths)
261 self.rc = _find_exe("rc.exe", paths) # resource compiler
262 self.mc = _find_exe("mc.exe", paths) # message compiler
263 self.mt = _find_exe("mt.exe", paths) # message compiler
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700264 self._vcruntime_redist = vc_env.get('py_vcruntime_redist', '')
Steve Dowerfd3664b2015-05-23 09:02:50 -0700265
266 for dir in vc_env.get('include', '').split(os.pathsep):
267 if dir:
Steve Dower5473f062018-07-26 04:23:10 -0700268 self.add_include_dir(dir.rstrip(os.sep))
Steve Dowerfd3664b2015-05-23 09:02:50 -0700269
270 for dir in vc_env.get('lib', '').split(os.pathsep):
271 if dir:
Steve Dower5473f062018-07-26 04:23:10 -0700272 self.add_library_dir(dir.rstrip(os.sep))
Steve Dowerfd3664b2015-05-23 09:02:50 -0700273
274 self.preprocess_options = None
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700275 # If vcruntime_redist is available, link against it dynamically. Otherwise,
276 # use /MT[d] to build statically, then switch from libucrt[d].lib to ucrt[d].lib
Steve Dower31202ea2015-08-05 11:39:19 -0700277 # later to dynamically link to ucrtbase but not vcruntime.
Steve Dowerfd3664b2015-05-23 09:02:50 -0700278 self.compile_options = [
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700279 '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG'
Steve Dowerfd3664b2015-05-23 09:02:50 -0700280 ]
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700281 self.compile_options.append('/MD' if self._vcruntime_redist else '/MT')
Larry Hastings52e40cd2015-09-09 06:54:57 -0700282
Steve Dowerfd3664b2015-05-23 09:02:50 -0700283 self.compile_options_debug = [
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700284 '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'
Steve Dowerfd3664b2015-05-23 09:02:50 -0700285 ]
286
Steve Dower31202ea2015-08-05 11:39:19 -0700287 ldflags = [
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700288 '/nologo', '/INCREMENTAL:NO', '/LTCG'
Steve Dowerfd3664b2015-05-23 09:02:50 -0700289 ]
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700290 if not self._vcruntime_redist:
291 ldflags.extend(('/nodefaultlib:libucrt.lib', 'ucrt.lib'))
292
Steve Dower31202ea2015-08-05 11:39:19 -0700293 ldflags_debug = [
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700294 '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL'
Steve Dowerfd3664b2015-05-23 09:02:50 -0700295 ]
Steve Dower31202ea2015-08-05 11:39:19 -0700296
297 self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
298 self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
299 self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
300 self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
301 self.ldflags_static = [*ldflags]
302 self.ldflags_static_debug = [*ldflags_debug]
303
304 self._ldflags = {
305 (CCompiler.EXECUTABLE, None): self.ldflags_exe,
306 (CCompiler.EXECUTABLE, False): self.ldflags_exe,
307 (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug,
308 (CCompiler.SHARED_OBJECT, None): self.ldflags_shared,
309 (CCompiler.SHARED_OBJECT, False): self.ldflags_shared,
310 (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
311 (CCompiler.SHARED_LIBRARY, None): self.ldflags_static,
312 (CCompiler.SHARED_LIBRARY, False): self.ldflags_static,
313 (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
314 }
Steve Dowerfd3664b2015-05-23 09:02:50 -0700315
316 self.initialized = True
317
318 # -- Worker methods ------------------------------------------------
319
320 def object_filenames(self,
321 source_filenames,
322 strip_dir=0,
323 output_dir=''):
Steve Dower31202ea2015-08-05 11:39:19 -0700324 ext_map = {
325 **{ext: self.obj_extension for ext in self.src_extensions},
326 **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions},
327 }
328
329 output_dir = output_dir or ''
Steve Dowerfd3664b2015-05-23 09:02:50 -0700330
331 def make_out_path(p):
332 base, ext = os.path.splitext(p)
333 if strip_dir:
334 base = os.path.basename(base)
335 else:
336 _, base = os.path.splitdrive(base)
337 if base.startswith((os.path.sep, os.path.altsep)):
338 base = base[1:]
339 try:
Steve Dower31202ea2015-08-05 11:39:19 -0700340 # XXX: This may produce absurdly long paths. We should check
341 # the length of the result and trim base until we fit within
342 # 260 characters.
343 return os.path.join(output_dir, base + ext_map[ext])
Steve Dowerfd3664b2015-05-23 09:02:50 -0700344 except LookupError:
345 # Better to raise an exception instead of silently continuing
346 # and later complain about sources and targets having
347 # different lengths
348 raise CompileError("Don't know how to compile {}".format(p))
349
Steve Dower31202ea2015-08-05 11:39:19 -0700350 return list(map(make_out_path, source_filenames))
Steve Dowerfd3664b2015-05-23 09:02:50 -0700351
352
353 def compile(self, sources,
354 output_dir=None, macros=None, include_dirs=None, debug=0,
355 extra_preargs=None, extra_postargs=None, depends=None):
356
357 if not self.initialized:
358 self.initialize()
359 compile_info = self._setup_compile(output_dir, macros, include_dirs,
360 sources, depends, extra_postargs)
361 macros, objects, extra_postargs, pp_opts, build = compile_info
362
363 compile_opts = extra_preargs or []
364 compile_opts.append('/c')
365 if debug:
366 compile_opts.extend(self.compile_options_debug)
367 else:
368 compile_opts.extend(self.compile_options)
369
370
371 add_cpp_opts = False
372
373 for obj in objects:
374 try:
375 src, ext = build[obj]
376 except KeyError:
377 continue
378 if debug:
379 # pass the full pathname to MSVC in debug mode,
380 # this allows the debugger to find the source file
381 # without asking the user to browse for it
382 src = os.path.abspath(src)
383
384 if ext in self._c_extensions:
385 input_opt = "/Tc" + src
386 elif ext in self._cpp_extensions:
387 input_opt = "/Tp" + src
388 add_cpp_opts = True
389 elif ext in self._rc_extensions:
390 # compile .RC to .RES file
391 input_opt = src
392 output_opt = "/fo" + obj
393 try:
394 self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
395 except DistutilsExecError as msg:
396 raise CompileError(msg)
397 continue
398 elif ext in self._mc_extensions:
399 # Compile .MC to .RC file to .RES file.
400 # * '-h dir' specifies the directory for the
401 # generated include file
402 # * '-r dir' specifies the target directory of the
403 # generated RC file and the binary message resource
404 # it includes
405 #
406 # For now (since there are no options to change this),
407 # we use the source-directory for the include file and
408 # the build directory for the RC file and message
409 # resources. This works at least for win32all.
410 h_dir = os.path.dirname(src)
411 rc_dir = os.path.dirname(obj)
412 try:
413 # first compile .MC to .RC and .H file
414 self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
415 base, _ = os.path.splitext(os.path.basename (src))
416 rc_file = os.path.join(rc_dir, base + '.rc')
417 # then compile .RC to .RES file
418 self.spawn([self.rc, "/fo" + obj, rc_file])
419
420 except DistutilsExecError as msg:
421 raise CompileError(msg)
422 continue
423 else:
424 # how to handle this file?
425 raise CompileError("Don't know how to compile {} to {}"
426 .format(src, obj))
427
428 args = [self.cc] + compile_opts + pp_opts
429 if add_cpp_opts:
430 args.append('/EHsc')
431 args.append(input_opt)
432 args.append("/Fo" + obj)
433 args.extend(extra_postargs)
434
435 try:
436 self.spawn(args)
437 except DistutilsExecError as msg:
438 raise CompileError(msg)
439
440 return objects
441
442
443 def create_static_lib(self,
444 objects,
445 output_libname,
446 output_dir=None,
447 debug=0,
448 target_lang=None):
449
450 if not self.initialized:
451 self.initialize()
452 objects, output_dir = self._fix_object_args(objects, output_dir)
453 output_filename = self.library_filename(output_libname,
454 output_dir=output_dir)
455
456 if self._need_link(objects, output_filename):
457 lib_args = objects + ['/OUT:' + output_filename]
458 if debug:
459 pass # XXX what goes here?
460 try:
Steve Dower31202ea2015-08-05 11:39:19 -0700461 log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
Steve Dowerfd3664b2015-05-23 09:02:50 -0700462 self.spawn([self.lib] + lib_args)
463 except DistutilsExecError as msg:
464 raise LibError(msg)
465 else:
466 log.debug("skipping %s (up-to-date)", output_filename)
467
468
469 def link(self,
470 target_desc,
471 objects,
472 output_filename,
473 output_dir=None,
474 libraries=None,
475 library_dirs=None,
476 runtime_library_dirs=None,
477 export_symbols=None,
478 debug=0,
479 extra_preargs=None,
480 extra_postargs=None,
481 build_temp=None,
482 target_lang=None):
483
484 if not self.initialized:
485 self.initialize()
486 objects, output_dir = self._fix_object_args(objects, output_dir)
487 fixed_args = self._fix_lib_args(libraries, library_dirs,
488 runtime_library_dirs)
489 libraries, library_dirs, runtime_library_dirs = fixed_args
490
491 if runtime_library_dirs:
492 self.warn("I don't know what to do with 'runtime_library_dirs': "
493 + str(runtime_library_dirs))
494
495 lib_opts = gen_lib_options(self,
496 library_dirs, runtime_library_dirs,
497 libraries)
498 if output_dir is not None:
499 output_filename = os.path.join(output_dir, output_filename)
500
501 if self._need_link(objects, output_filename):
Steve Dower31202ea2015-08-05 11:39:19 -0700502 ldflags = self._ldflags[target_desc, debug]
Steve Dowerfd3664b2015-05-23 09:02:50 -0700503
Steve Dower31202ea2015-08-05 11:39:19 -0700504 export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
Steve Dowerfd3664b2015-05-23 09:02:50 -0700505
506 ld_args = (ldflags + lib_opts + export_opts +
507 objects + ['/OUT:' + output_filename])
508
509 # The MSVC linker generates .lib and .exp files, which cannot be
510 # suppressed by any linker switches. The .lib files may even be
511 # needed! Make sure they are generated in the temporary build
512 # directory. Since they have different names for debug and release
513 # builds, they can go into the same directory.
514 build_temp = os.path.dirname(objects[0])
515 if export_symbols is not None:
516 (dll_name, dll_ext) = os.path.splitext(
517 os.path.basename(output_filename))
518 implib_file = os.path.join(
519 build_temp,
520 self.library_filename(dll_name))
521 ld_args.append ('/IMPLIB:' + implib_file)
522
Steve Dowerfd3664b2015-05-23 09:02:50 -0700523 if extra_preargs:
524 ld_args[:0] = extra_preargs
525 if extra_postargs:
526 ld_args.extend(extra_postargs)
527
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700528 output_dir = os.path.dirname(os.path.abspath(output_filename))
529 self.mkpath(output_dir)
Steve Dowerfd3664b2015-05-23 09:02:50 -0700530 try:
Steve Dower31202ea2015-08-05 11:39:19 -0700531 log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
Steve Dowerfd3664b2015-05-23 09:02:50 -0700532 self.spawn([self.linker] + ld_args)
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700533 self._copy_vcruntime(output_dir)
Steve Dowerfd3664b2015-05-23 09:02:50 -0700534 except DistutilsExecError as msg:
535 raise LinkError(msg)
Steve Dowerfd3664b2015-05-23 09:02:50 -0700536 else:
537 log.debug("skipping %s (up-to-date)", output_filename)
538
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700539 def _copy_vcruntime(self, output_dir):
540 vcruntime = self._vcruntime_redist
541 if not vcruntime or not os.path.isfile(vcruntime):
542 return
543
544 if os.path.basename(vcruntime).lower() in _BUNDLED_DLLS:
545 return
546
547 log.debug('Copying "%s"', vcruntime)
548 vcruntime = shutil.copy(vcruntime, output_dir)
549 os.chmod(vcruntime, stat.S_IWRITE)
550
Steve Dower31202ea2015-08-05 11:39:19 -0700551 def spawn(self, cmd):
552 old_path = os.getenv('path')
Steve Dowerfd3664b2015-05-23 09:02:50 -0700553 try:
Steve Dower31202ea2015-08-05 11:39:19 -0700554 os.environ['path'] = self._paths
555 return super().spawn(cmd)
556 finally:
557 os.environ['path'] = old_path
Steve Dowerfd3664b2015-05-23 09:02:50 -0700558
559 # -- Miscellaneous methods -----------------------------------------
560 # These are all used by the 'gen_lib_options() function, in
561 # ccompiler.py.
562
563 def library_dir_option(self, dir):
564 return "/LIBPATH:" + dir
565
566 def runtime_library_dir_option(self, dir):
567 raise DistutilsPlatformError(
568 "don't know how to set runtime library search path for MSVC")
569
570 def library_option(self, lib):
571 return self.library_filename(lib)
572
573 def find_library_file(self, dirs, lib, debug=0):
574 # Prefer a debugging library if found (and requested), but deal
575 # with it if we don't have one.
576 if debug:
577 try_names = [lib + "_d", lib]
578 else:
579 try_names = [lib]
580 for dir in dirs:
581 for name in try_names:
582 libfile = os.path.join(dir, self.library_filename(name))
583 if os.path.isfile(libfile):
584 return libfile
585 else:
586 # Oops, didn't find it in *any* of 'dirs'
587 return None