blob: b2b52e56c8ea5e5c9583da02c2f9faa5a7022712 [file] [log] [blame]
Christian Heimescbf3b5c2007-12-03 21:02:03 +00001"""distutils.msvc9compiler
2
3Contains MSVCCompiler, an implementation of the abstract CCompiler class
4for the Microsoft Visual Studio 2008.
5
6The module is compatible with VS 2005 and VS 2008. You can find legacy support
7for older versions of VS in 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 VS2005 and VS 2008 by Christian Heimes
14
Christian Heimescbf3b5c2007-12-03 21:02:03 +000015import os
16import subprocess
17import sys
Martin v. Löwis1679ea82009-12-03 20:57:49 +000018import re
Tarek Ziadé63b64c02009-03-07 00:51:53 +000019
Tarek Ziadé36797272010-07-22 12:50:05 +000020from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
21 CompileError, LibError, LinkError
Victor Stinnere488e302020-04-30 11:28:09 +020022from distutils.ccompiler import CCompiler, gen_lib_options
Christian Heimescbf3b5c2007-12-03 21:02:03 +000023from distutils import log
Tarek Ziadé8b441d02010-01-29 11:46:31 +000024from distutils.util import get_platform
Christian Heimescbf3b5c2007-12-03 21:02:03 +000025
Tarek Ziadé8b441d02010-01-29 11:46:31 +000026import winreg
Tarek Ziadéedacea32010-01-29 11:41:03 +000027
Georg Brandl38feaf02008-05-25 07:45:51 +000028RegOpenKeyEx = winreg.OpenKeyEx
29RegEnumKey = winreg.EnumKey
30RegEnumValue = winreg.EnumValue
31RegError = winreg.error
Christian Heimescbf3b5c2007-12-03 21:02:03 +000032
Georg Brandl38feaf02008-05-25 07:45:51 +000033HKEYS = (winreg.HKEY_USERS,
34 winreg.HKEY_CURRENT_USER,
35 winreg.HKEY_LOCAL_MACHINE,
36 winreg.HKEY_CLASSES_ROOT)
Christian Heimescbf3b5c2007-12-03 21:02:03 +000037
Benjamin Peterson31b16a52010-06-21 15:37:16 +000038NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)
39if NATIVE_WIN64:
40 # Visual C++ is a 32-bit application, so we need to look in
41 # the corresponding registry branch, if we're running a
42 # 64-bit Python on Win64
43 VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"
44 WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"
45 NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"
46else:
47 VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
48 WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
49 NET_BASE = r"Software\Microsoft\.NETFramework"
Christian Heimescbf3b5c2007-12-03 21:02:03 +000050
Christian Heimes5e696852008-04-09 08:37:03 +000051# A map keyed by get_platform() return values to values accepted by
52# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
Martin Panter46f50722016-05-26 05:35:26 +000053# the param to cross-compile on x86 targeting amd64.)
Christian Heimes5e696852008-04-09 08:37:03 +000054PLAT_TO_VCVARS = {
55 'win32' : 'x86',
56 'win-amd64' : 'amd64',
Adrian Vladucb7bc762021-03-04 18:59:12 +020057 'win-arm64' : 'arm64',
Christian Heimes5e696852008-04-09 08:37:03 +000058}
Christian Heimescbf3b5c2007-12-03 21:02:03 +000059
60class Reg:
61 """Helper class to read values from the registry
62 """
63
Christian Heimescbf3b5c2007-12-03 21:02:03 +000064 def get_value(cls, path, key):
65 for base in HKEYS:
66 d = cls.read_values(base, path)
67 if d and key in d:
68 return d[key]
69 raise KeyError(key)
Tarek Ziadé63b64c02009-03-07 00:51:53 +000070 get_value = classmethod(get_value)
Christian Heimescbf3b5c2007-12-03 21:02:03 +000071
Christian Heimescbf3b5c2007-12-03 21:02:03 +000072 def read_keys(cls, base, key):
73 """Return list of registry keys."""
74 try:
75 handle = RegOpenKeyEx(base, key)
76 except RegError:
77 return None
78 L = []
79 i = 0
80 while True:
81 try:
82 k = RegEnumKey(handle, i)
83 except RegError:
84 break
85 L.append(k)
86 i += 1
87 return L
Tarek Ziadé63b64c02009-03-07 00:51:53 +000088 read_keys = classmethod(read_keys)
Christian Heimescbf3b5c2007-12-03 21:02:03 +000089
Christian Heimescbf3b5c2007-12-03 21:02:03 +000090 def read_values(cls, base, key):
91 """Return dict of registry keys and values.
92
93 All names are converted to lowercase.
94 """
95 try:
96 handle = RegOpenKeyEx(base, key)
97 except RegError:
98 return None
99 d = {}
100 i = 0
101 while True:
102 try:
103 name, value, type = RegEnumValue(handle, i)
104 except RegError:
105 break
106 name = name.lower()
107 d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
108 i += 1
109 return d
Tarek Ziadé63b64c02009-03-07 00:51:53 +0000110 read_values = classmethod(read_values)
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000111
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000112 def convert_mbcs(s):
113 dec = getattr(s, "decode", None)
114 if dec is not None:
115 try:
116 s = dec("mbcs")
117 except UnicodeError:
118 pass
119 return s
Tarek Ziadé63b64c02009-03-07 00:51:53 +0000120 convert_mbcs = staticmethod(convert_mbcs)
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000121
122class MacroExpander:
123
124 def __init__(self, version):
125 self.macros = {}
126 self.vsbase = VS_BASE % version
127 self.load_macros(version)
128
129 def set_macro(self, macro, path, key):
130 self.macros["$(%s)" % macro] = Reg.get_value(path, key)
131
132 def load_macros(self, version):
133 self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
134 self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
135 self.set_macro("FrameworkDir", NET_BASE, "installroot")
136 try:
137 if version >= 8.0:
138 self.set_macro("FrameworkSDKDir", NET_BASE,
139 "sdkinstallrootv2.0")
140 else:
141 raise KeyError("sdkinstallrootv2.0")
Tarek Ziadé63b64c02009-03-07 00:51:53 +0000142 except KeyError:
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000143 raise DistutilsPlatformError(
144 """Python was built with Visual Studio 2008;
145extensions must be built with a compiler than can generate compatible binaries.
146Visual Studio 2008 was not found on this system. If you have Cygwin installed,
147you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
148
149 if version >= 9.0:
150 self.set_macro("FrameworkVersion", self.vsbase, "clr version")
151 self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
152 else:
153 p = r"Software\Microsoft\NET Framework Setup\Product"
154 for base in HKEYS:
155 try:
156 h = RegOpenKeyEx(base, p)
157 except RegError:
158 continue
159 key = RegEnumKey(h, 0)
160 d = Reg.get_value(base, r"%s\%s" % (p, key))
161 self.macros["$(FrameworkVersion)"] = d["version"]
162
163 def sub(self, s):
164 for k, v in self.macros.items():
165 s = s.replace(k, v)
166 return s
167
168def get_build_version():
169 """Return the version of MSVC that was used to build Python.
170
171 For Python 2.3 and up, the version number is included in
172 sys.version. For earlier versions, assume the compiler is MSVC 6.
173 """
174 prefix = "MSC v."
175 i = sys.version.find(prefix)
176 if i == -1:
177 return 6
178 i = i + len(prefix)
179 s, rest = sys.version[i:].split(" ", 1)
180 majorVersion = int(s[:-2]) - 6
Steve Dower65e4cb12014-11-22 12:54:57 -0800181 if majorVersion >= 13:
182 # v13 was skipped and should be v14
183 majorVersion += 1
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000184 minorVersion = int(s[2:3]) / 10.0
185 # I don't think paths are affected by minor version in version 6
186 if majorVersion == 6:
187 minorVersion = 0
188 if majorVersion >= 6:
189 return majorVersion + minorVersion
190 # else we don't know what version of the compiler this is
191 return None
192
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000193def normalize_and_reduce_paths(paths):
194 """Return a list of normalized paths with duplicates removed.
195
196 The current order of paths is maintained.
197 """
198 # Paths are normalized so things like: /a and /a/ aren't both preserved.
199 reduced_paths = []
200 for p in paths:
201 np = os.path.normpath(p)
202 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
203 if np not in reduced_paths:
204 reduced_paths.append(np)
205 return reduced_paths
206
Amaury Forgeot d'Arcd8976f12008-09-02 23:22:56 +0000207def removeDuplicates(variable):
208 """Remove duplicate values of an environment variable.
209 """
210 oldList = variable.split(os.pathsep)
211 newList = []
212 for i in oldList:
213 if i not in newList:
214 newList.append(i)
215 newVariable = os.pathsep.join(newList)
216 return newVariable
217
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000218def find_vcvarsall(version):
219 """Find the vcvarsall.bat file
220
221 At first it tries to find the productdir of VS 2008 in the registry. If
222 that fails it falls back to the VS90COMNTOOLS env var.
223 """
224 vsbase = VS_BASE % version
225 try:
226 productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
227 "productdir")
228 except KeyError:
229 log.debug("Unable to find productdir in registry")
230 productdir = None
231
232 if not productdir or not os.path.isdir(productdir):
233 toolskey = "VS%0.f0COMNTOOLS" % version
234 toolsdir = os.environ.get(toolskey, None)
235
236 if toolsdir and os.path.isdir(toolsdir):
237 productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
238 productdir = os.path.abspath(productdir)
239 if not os.path.isdir(productdir):
240 log.debug("%s is not a valid directory" % productdir)
241 return None
242 else:
243 log.debug("Env var %s is not set or invalid" % toolskey)
244 if not productdir:
245 log.debug("No productdir found")
246 return None
247 vcvarsall = os.path.join(productdir, "vcvarsall.bat")
248 if os.path.isfile(vcvarsall):
249 return vcvarsall
250 log.debug("Unable to find vcvarsall.bat")
251 return None
252
253def query_vcvarsall(version, arch="x86"):
254 """Launch vcvarsall.bat and read the settings from its environment
255 """
256 vcvarsall = find_vcvarsall(version)
Jon Dufresne39726282017-05-18 07:35:54 -0700257 interesting = {"include", "lib", "libpath", "path"}
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000258 result = {}
259
260 if vcvarsall is None:
Tarek Ziadé9df8ce32008-12-30 23:09:20 +0000261 raise DistutilsPlatformError("Unable to find vcvarsall.bat")
Christian Heimes5e696852008-04-09 08:37:03 +0000262 log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000263 popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
264 stdout=subprocess.PIPE,
265 stderr=subprocess.PIPE)
Éric Araujo5ac6d802010-11-06 02:10:32 +0000266 try:
267 stdout, stderr = popen.communicate()
268 if popen.wait() != 0:
269 raise DistutilsPlatformError(stderr.decode("mbcs"))
Christian Heimesb9eccbf2007-12-05 20:18:38 +0000270
Éric Araujo8bdbe9c2010-11-06 15:57:52 +0000271 stdout = stdout.decode("mbcs")
272 for line in stdout.split("\n"):
273 line = Reg.convert_mbcs(line)
274 if '=' not in line:
275 continue
276 line = line.strip()
277 key, value = line.split('=', 1)
278 key = key.lower()
279 if key in interesting:
280 if value.endswith(os.pathsep):
281 value = value[:-1]
282 result[key] = removeDuplicates(value)
283
284 finally:
Éric Araujo8bdbe9c2010-11-06 15:57:52 +0000285 popen.stdout.close()
286 popen.stderr.close()
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000287
288 if len(result) != len(interesting):
289 raise ValueError(str(list(result.keys())))
290
291 return result
292
293# More globals
294VERSION = get_build_version()
295if VERSION < 8.0:
296 raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000297# MACROS = MacroExpander(VERSION)
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000298
299class MSVCCompiler(CCompiler) :
300 """Concrete class that implements an interface to Microsoft Visual C++,
301 as defined by the CCompiler abstract class."""
302
303 compiler_type = 'msvc'
304
305 # Just set this so CCompiler's constructor doesn't barf. We currently
306 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
307 # as it really isn't necessary for this sort of single-compiler class.
308 # Would be nice to have a consistent interface with UnixCCompiler,
309 # though, so it's worth thinking about.
310 executables = {}
311
312 # Private class data (need to distinguish C from C++ source for compiler)
313 _c_extensions = ['.c']
314 _cpp_extensions = ['.cc', '.cpp', '.cxx']
315 _rc_extensions = ['.rc']
316 _mc_extensions = ['.mc']
317
318 # Needed for the filename generation methods provided by the
319 # base class, CCompiler.
320 src_extensions = (_c_extensions + _cpp_extensions +
321 _rc_extensions + _mc_extensions)
322 res_extension = '.res'
323 obj_extension = '.obj'
324 static_lib_extension = '.lib'
325 shared_lib_extension = '.dll'
326 static_lib_format = shared_lib_format = '%s%s'
327 exe_extension = '.exe'
328
329 def __init__(self, verbose=0, dry_run=0, force=0):
330 CCompiler.__init__ (self, verbose, dry_run, force)
331 self.__version = VERSION
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000332 self.__root = r"Software\Microsoft\VisualStudio"
333 # self.__macros = MACROS
Christian Heimes94e07722008-11-28 11:05:17 +0000334 self.__paths = []
Christian Heimes5e696852008-04-09 08:37:03 +0000335 # target platform (.plat_name is consistent with 'bdist')
336 self.plat_name = None
337 self.__arch = None # deprecated name
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000338 self.initialized = False
339
Christian Heimes5e696852008-04-09 08:37:03 +0000340 def initialize(self, plat_name=None):
341 # multi-init means we would need to check platform same each time...
342 assert not self.initialized, "don't init multiple times"
343 if plat_name is None:
Tarek Ziadé8b441d02010-01-29 11:46:31 +0000344 plat_name = get_platform()
Christian Heimes5e696852008-04-09 08:37:03 +0000345 # sanity check for platforms to prevent obscure errors later.
Adrian Vladucb7bc762021-03-04 18:59:12 +0200346 ok_plats = 'win32', 'win-amd64', 'win-arm64'
Christian Heimes5e696852008-04-09 08:37:03 +0000347 if plat_name not in ok_plats:
348 raise DistutilsPlatformError("--plat-name must be one of %s" %
349 (ok_plats,))
350
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000351 if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
352 # Assume that the SDK set up everything alright; don't try to be
353 # smarter
354 self.cc = "cl.exe"
355 self.linker = "link.exe"
356 self.lib = "lib.exe"
357 self.rc = "rc.exe"
358 self.mc = "mc.exe"
359 else:
Christian Heimes5e696852008-04-09 08:37:03 +0000360 # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
361 # to cross compile, you use 'x86_amd64'.
362 # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
363 # compile use 'x86' (ie, it runs the x86 compiler directly)
Tarek Ziadé8b441d02010-01-29 11:46:31 +0000364 if plat_name == get_platform() or plat_name == 'win32':
Christian Heimes5e696852008-04-09 08:37:03 +0000365 # native build or cross-compile to win32
366 plat_spec = PLAT_TO_VCVARS[plat_name]
367 else:
368 # cross compile from win32 -> some 64bit
Tarek Ziadé8b441d02010-01-29 11:46:31 +0000369 plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \
Christian Heimes5e696852008-04-09 08:37:03 +0000370 PLAT_TO_VCVARS[plat_name]
371
372 vc_env = query_vcvarsall(VERSION, plat_spec)
373
374 self.__paths = vc_env['path'].split(os.pathsep)
Adrian Vladucb7bc762021-03-04 18:59:12 +0200375 if plat_name == 'win-arm64':
376 self.__paths = (
377 vc_env['path'].replace('HostX64', 'HostX86').split(os.pathsep))
Christian Heimes5e696852008-04-09 08:37:03 +0000378 os.environ['lib'] = vc_env['lib']
379 os.environ['include'] = vc_env['include']
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000380
381 if len(self.__paths) == 0:
382 raise DistutilsPlatformError("Python was built with %s, "
383 "and extensions need to be built with the same "
384 "version of the compiler, but it isn't installed."
385 % self.__product)
386
387 self.cc = self.find_exe("cl.exe")
388 self.linker = self.find_exe("link.exe")
389 self.lib = self.find_exe("lib.exe")
390 self.rc = self.find_exe("rc.exe") # resource compiler
391 self.mc = self.find_exe("mc.exe") # message compiler
Adrian Vladucb7bc762021-03-04 18:59:12 +0200392 if plat_name == 'win-arm64':
393 self.cc = self.cc.replace('HostX64', 'Hostx86')
394 self.linker = self.linker.replace('HostX64', 'Hostx86')
395 self.lib = self.lib.replace('HostX64', 'Hostx86')
396 self.rc = self.rc.replace('x64', 'arm64')
397 self.mc = self.mc.replace('x64', 'arm64')
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000398 #self.set_path_env_var('lib')
399 #self.set_path_env_var('include')
400
401 # extend the MSVC path with the current path
402 try:
403 for p in os.environ['path'].split(';'):
404 self.__paths.append(p)
405 except KeyError:
406 pass
407 self.__paths = normalize_and_reduce_paths(self.__paths)
408 os.environ['path'] = ";".join(self.__paths)
409
410 self.preprocess_options = None
411 if self.__arch == "x86":
412 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
413 '/DNDEBUG']
414 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
415 '/Z7', '/D_DEBUG']
416 else:
417 # Win64
418 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
419 '/DNDEBUG']
420 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
421 '/Z7', '/D_DEBUG']
422
423 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
424 if self.__version >= 7:
425 self.ldflags_shared_debug = [
Steve Dower3a7ffa72015-08-07 19:48:03 -0700426 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000427 ]
428 self.ldflags_static = [ '/nologo']
429
430 self.initialized = True
431
432 # -- Worker methods ------------------------------------------------
433
434 def object_filenames(self,
435 source_filenames,
436 strip_dir=0,
437 output_dir=''):
438 # Copied from ccompiler.py, extended to return .res as 'object'-file
439 # for .rc input file
440 if output_dir is None: output_dir = ''
441 obj_names = []
442 for src_name in source_filenames:
443 (base, ext) = os.path.splitext (src_name)
444 base = os.path.splitdrive(base)[1] # Chop off the drive
445 base = base[os.path.isabs(base):] # If abs, chop off leading /
446 if ext not in self.src_extensions:
447 # Better to raise an exception instead of silently continuing
448 # and later complain about sources and targets having
449 # different lengths
450 raise CompileError ("Don't know how to compile %s" % src_name)
451 if strip_dir:
452 base = os.path.basename (base)
453 if ext in self._rc_extensions:
454 obj_names.append (os.path.join (output_dir,
455 base + self.res_extension))
456 elif ext in self._mc_extensions:
457 obj_names.append (os.path.join (output_dir,
458 base + self.res_extension))
459 else:
460 obj_names.append (os.path.join (output_dir,
461 base + self.obj_extension))
462 return obj_names
463
464
465 def compile(self, sources,
466 output_dir=None, macros=None, include_dirs=None, debug=0,
467 extra_preargs=None, extra_postargs=None, depends=None):
468
469 if not self.initialized:
470 self.initialize()
471 compile_info = self._setup_compile(output_dir, macros, include_dirs,
472 sources, depends, extra_postargs)
473 macros, objects, extra_postargs, pp_opts, build = compile_info
474
475 compile_opts = extra_preargs or []
476 compile_opts.append ('/c')
477 if debug:
478 compile_opts.extend(self.compile_options_debug)
479 else:
480 compile_opts.extend(self.compile_options)
481
482 for obj in objects:
483 try:
484 src, ext = build[obj]
485 except KeyError:
486 continue
487 if debug:
488 # pass the full pathname to MSVC in debug mode,
489 # this allows the debugger to find the source file
490 # without asking the user to browse for it
491 src = os.path.abspath(src)
492
493 if ext in self._c_extensions:
494 input_opt = "/Tc" + src
495 elif ext in self._cpp_extensions:
496 input_opt = "/Tp" + src
497 elif ext in self._rc_extensions:
498 # compile .RC to .RES file
499 input_opt = src
500 output_opt = "/fo" + obj
501 try:
502 self.spawn([self.rc] + pp_opts +
503 [output_opt] + [input_opt])
Tarek Ziadéb3c6ed52009-03-07 01:12:09 +0000504 except DistutilsExecError as msg:
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000505 raise CompileError(msg)
506 continue
507 elif ext in self._mc_extensions:
508 # Compile .MC to .RC file to .RES file.
509 # * '-h dir' specifies the directory for the
510 # generated include file
511 # * '-r dir' specifies the target directory of the
512 # generated RC file and the binary message resource
513 # it includes
514 #
515 # For now (since there are no options to change this),
516 # we use the source-directory for the include file and
517 # the build directory for the RC file and message
518 # resources. This works at least for win32all.
519 h_dir = os.path.dirname(src)
520 rc_dir = os.path.dirname(obj)
521 try:
522 # first compile .MC to .RC and .H file
523 self.spawn([self.mc] +
524 ['-h', h_dir, '-r', rc_dir] + [src])
525 base, _ = os.path.splitext (os.path.basename (src))
526 rc_file = os.path.join (rc_dir, base + '.rc')
527 # then compile .RC to .RES file
528 self.spawn([self.rc] +
529 ["/fo" + obj] + [rc_file])
530
Tarek Ziadéb3c6ed52009-03-07 01:12:09 +0000531 except DistutilsExecError as msg:
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000532 raise CompileError(msg)
533 continue
534 else:
535 # how to handle this file?
536 raise CompileError("Don't know how to compile %s to %s"
537 % (src, obj))
538
539 output_opt = "/Fo" + obj
540 try:
541 self.spawn([self.cc] + compile_opts + pp_opts +
542 [input_opt, output_opt] +
543 extra_postargs)
Tarek Ziadéb3c6ed52009-03-07 01:12:09 +0000544 except DistutilsExecError as msg:
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000545 raise CompileError(msg)
546
547 return objects
548
549
550 def create_static_lib(self,
551 objects,
552 output_libname,
553 output_dir=None,
554 debug=0,
555 target_lang=None):
556
557 if not self.initialized:
558 self.initialize()
559 (objects, output_dir) = self._fix_object_args(objects, output_dir)
560 output_filename = self.library_filename(output_libname,
561 output_dir=output_dir)
562
563 if self._need_link(objects, output_filename):
564 lib_args = objects + ['/OUT:' + output_filename]
565 if debug:
566 pass # XXX what goes here?
567 try:
568 self.spawn([self.lib] + lib_args)
Tarek Ziadéb3c6ed52009-03-07 01:12:09 +0000569 except DistutilsExecError as msg:
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000570 raise LibError(msg)
571 else:
572 log.debug("skipping %s (up-to-date)", output_filename)
573
574
575 def link(self,
576 target_desc,
577 objects,
578 output_filename,
579 output_dir=None,
580 libraries=None,
581 library_dirs=None,
582 runtime_library_dirs=None,
583 export_symbols=None,
584 debug=0,
585 extra_preargs=None,
586 extra_postargs=None,
587 build_temp=None,
588 target_lang=None):
589
590 if not self.initialized:
591 self.initialize()
592 (objects, output_dir) = self._fix_object_args(objects, output_dir)
593 fixed_args = self._fix_lib_args(libraries, library_dirs,
594 runtime_library_dirs)
595 (libraries, library_dirs, runtime_library_dirs) = fixed_args
596
597 if runtime_library_dirs:
598 self.warn ("I don't know what to do with 'runtime_library_dirs': "
599 + str (runtime_library_dirs))
600
601 lib_opts = gen_lib_options(self,
602 library_dirs, runtime_library_dirs,
603 libraries)
604 if output_dir is not None:
605 output_filename = os.path.join(output_dir, output_filename)
606
607 if self._need_link(objects, output_filename):
608 if target_desc == CCompiler.EXECUTABLE:
609 if debug:
610 ldflags = self.ldflags_shared_debug[1:]
611 else:
612 ldflags = self.ldflags_shared[1:]
613 else:
614 if debug:
615 ldflags = self.ldflags_shared_debug
616 else:
617 ldflags = self.ldflags_shared
618
619 export_opts = []
620 for sym in (export_symbols or []):
621 export_opts.append("/EXPORT:" + sym)
622
623 ld_args = (ldflags + lib_opts + export_opts +
624 objects + ['/OUT:' + output_filename])
625
626 # The MSVC linker generates .lib and .exp files, which cannot be
627 # suppressed by any linker switches. The .lib files may even be
628 # needed! Make sure they are generated in the temporary build
629 # directory. Since they have different names for debug and release
630 # builds, they can go into the same directory.
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000631 build_temp = os.path.dirname(objects[0])
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000632 if export_symbols is not None:
633 (dll_name, dll_ext) = os.path.splitext(
634 os.path.basename(output_filename))
635 implib_file = os.path.join(
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000636 build_temp,
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000637 self.library_filename(dll_name))
638 ld_args.append ('/IMPLIB:' + implib_file)
639
Mark Hammond6c58b282011-10-17 11:05:57 +1100640 self.manifest_setup_ldargs(output_filename, build_temp, ld_args)
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000641
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000642 if extra_preargs:
643 ld_args[:0] = extra_preargs
644 if extra_postargs:
645 ld_args.extend(extra_postargs)
646
Adrian Vladucb7bc762021-03-04 18:59:12 +0200647 if get_platform() == 'win-arm64':
648 ld_args_arm = []
649 for ld_arg in ld_args:
650 # VS tries to use the x86 linker
651 ld_arg_arm = ld_arg.replace(r'\um\x86', r'\um\arm64')
652 # A larger memory address is required on ARM64
653 ld_arg_arm = ld_arg_arm.replace("0x1", "0x10")
654 ld_args_arm += [ld_arg_arm]
655
656 ld_args = list(ld_args_arm)
657
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000658 self.mkpath(os.path.dirname(output_filename))
659 try:
660 self.spawn([self.linker] + ld_args)
Tarek Ziadéb3c6ed52009-03-07 01:12:09 +0000661 except DistutilsExecError as msg:
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000662 raise LinkError(msg)
663
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000664 # embed the manifest
665 # XXX - this is somewhat fragile - if mt.exe fails, distutils
666 # will still consider the DLL up-to-date, but it will not have a
667 # manifest. Maybe we should link to a temp file? OTOH, that
668 # implies a build environment error that shouldn't go undetected.
Mark Hammond6c58b282011-10-17 11:05:57 +1100669 mfinfo = self.manifest_get_embed_info(target_desc, ld_args)
670 if mfinfo is not None:
671 mffilename, mfid = mfinfo
672 out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
673 try:
674 self.spawn(['mt.exe', '-nologo', '-manifest',
675 mffilename, out_arg])
676 except DistutilsExecError as msg:
677 raise LinkError(msg)
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000678 else:
679 log.debug("skipping %s (up-to-date)", output_filename)
680
Mark Hammond6c58b282011-10-17 11:05:57 +1100681 def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
682 # If we need a manifest at all, an embedded manifest is recommended.
683 # See MSDN article titled
684 # "How to: Embed a Manifest Inside a C/C++ Application"
685 # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
686 # Ask the linker to generate the manifest in the temp dir, so
687 # we can check it, and possibly embed it, later.
688 temp_manifest = os.path.join(
689 build_temp,
690 os.path.basename(output_filename) + ".manifest")
691 ld_args.append('/MANIFESTFILE:' + temp_manifest)
692
693 def manifest_get_embed_info(self, target_desc, ld_args):
694 # If a manifest should be embedded, return a tuple of
695 # (manifest_filename, resource_id). Returns None if no manifest
696 # should be embedded. See http://bugs.python.org/issue7833 for why
697 # we want to avoid any manifest for extension modules if we can)
698 for arg in ld_args:
699 if arg.startswith("/MANIFESTFILE:"):
700 temp_manifest = arg.split(":", 1)[1]
701 break
702 else:
703 # no /MANIFESTFILE so nothing to do.
704 return None
705 if target_desc == CCompiler.EXECUTABLE:
706 # by default, executables always get the manifest with the
707 # CRT referenced.
708 mfid = 1
709 else:
710 # Extension modules try and avoid any manifest if possible.
711 mfid = 2
712 temp_manifest = self._remove_visual_c_ref(temp_manifest)
713 if temp_manifest is None:
714 return None
715 return temp_manifest, mfid
716
Tarek Ziadéc60ea322009-12-21 23:12:41 +0000717 def _remove_visual_c_ref(self, manifest_file):
718 try:
719 # Remove references to the Visual C runtime, so they will
720 # fall through to the Visual C dependency of Python.exe.
721 # This way, when installed for a restricted user (e.g.
722 # runtimes are not in WinSxS folder, but in Python's own
723 # folder), the runtimes do not need to be in every folder
724 # with .pyd's.
Mark Hammond6c58b282011-10-17 11:05:57 +1100725 # Returns either the filename of the modified manifest or
726 # None if no manifest should be embedded.
Tarek Ziadéc60ea322009-12-21 23:12:41 +0000727 manifest_f = open(manifest_file)
728 try:
729 manifest_buf = manifest_f.read()
730 finally:
731 manifest_f.close()
732 pattern = re.compile(
733 r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
734 r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
735 re.DOTALL)
736 manifest_buf = re.sub(pattern, "", manifest_buf)
R David Murray44b548d2016-09-08 13:59:53 -0400737 pattern = r"<dependentAssembly>\s*</dependentAssembly>"
Tarek Ziadéc60ea322009-12-21 23:12:41 +0000738 manifest_buf = re.sub(pattern, "", manifest_buf)
Mark Hammond53e4a9a2011-10-17 11:35:31 +1100739 # Now see if any other assemblies are referenced - if not, we
Mark Hammond6c58b282011-10-17 11:05:57 +1100740 # don't want a manifest embedded.
741 pattern = re.compile(
742 r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
743 r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL)
744 if re.search(pattern, manifest_buf) is None:
745 return None
746
Tarek Ziadéc60ea322009-12-21 23:12:41 +0000747 manifest_f = open(manifest_file, 'w')
748 try:
749 manifest_f.write(manifest_buf)
Mark Hammond6c58b282011-10-17 11:05:57 +1100750 return manifest_file
Tarek Ziadéc60ea322009-12-21 23:12:41 +0000751 finally:
752 manifest_f.close()
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200753 except OSError:
Tarek Ziadéc60ea322009-12-21 23:12:41 +0000754 pass
Christian Heimescbf3b5c2007-12-03 21:02:03 +0000755
756 # -- Miscellaneous methods -----------------------------------------
757 # These are all used by the 'gen_lib_options() function, in
758 # ccompiler.py.
759
760 def library_dir_option(self, dir):
761 return "/LIBPATH:" + dir
762
763 def runtime_library_dir_option(self, dir):
764 raise DistutilsPlatformError(
765 "don't know how to set runtime library search path for MSVC++")
766
767 def library_option(self, lib):
768 return self.library_filename(lib)
769
770
771 def find_library_file(self, dirs, lib, debug=0):
772 # Prefer a debugging library if found (and requested), but deal
773 # with it if we don't have one.
774 if debug:
775 try_names = [lib + "_d", lib]
776 else:
777 try_names = [lib]
778 for dir in dirs:
779 for name in try_names:
780 libfile = os.path.join(dir, self.library_filename (name))
781 if os.path.exists(libfile):
782 return libfile
783 else:
784 # Oops, didn't find it in *any* of 'dirs'
785 return None
786
787 # Helper methods for using the MSVC registry settings
788
789 def find_exe(self, exe):
790 """Return path to an MSVC executable program.
791
792 Tries to find the program in several places: first, one of the
793 MSVC program search paths from the registry; next, the directories
794 in the PATH environment variable. If any of those work, return an
795 absolute path that is known to exist. If none of them work, just
796 return the original program name, 'exe'.
797 """
798 for p in self.__paths:
799 fn = os.path.join(os.path.abspath(p), exe)
800 if os.path.isfile(fn):
801 return fn
802
803 # didn't find it; try existing path
804 for p in os.environ['Path'].split(';'):
805 fn = os.path.join(os.path.abspath(p),exe)
806 if os.path.isfile(fn):
807 return fn
808
809 return exe