blob: 9fe8c744cb99c15778ddf019392eecad7cc66a7d [file] [log] [blame]
Christian Heimes3305c522007-12-03 13:47:29 +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
15__revision__ = "$Id$"
16
17import os
18import subprocess
19import sys
Martin v. Löwiscc0c7fb2009-12-03 20:56:15 +000020import re
Christian Heimes3305c522007-12-03 13:47:29 +000021from distutils.errors import (DistutilsExecError, DistutilsPlatformError,
22 CompileError, LibError, LinkError)
23from distutils.ccompiler import (CCompiler, gen_preprocess_options,
24 gen_lib_options)
25from distutils import log
Mark Hammond495cf992008-04-07 01:53:39 +000026from distutils.util import get_platform
Christian Heimes3305c522007-12-03 13:47:29 +000027
28import _winreg
29
30RegOpenKeyEx = _winreg.OpenKeyEx
31RegEnumKey = _winreg.EnumKey
32RegEnumValue = _winreg.EnumValue
33RegError = _winreg.error
34
35HKEYS = (_winreg.HKEY_USERS,
36 _winreg.HKEY_CURRENT_USER,
37 _winreg.HKEY_LOCAL_MACHINE,
38 _winreg.HKEY_CLASSES_ROOT)
39
Benjamin Petersonaae46dd2010-06-21 15:39:28 +000040NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)
41if NATIVE_WIN64:
42 # Visual C++ is a 32-bit application, so we need to look in
43 # the corresponding registry branch, if we're running a
44 # 64-bit Python on Win64
45 VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"
46 WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"
47 NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"
48else:
49 VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
50 WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
51 NET_BASE = r"Software\Microsoft\.NETFramework"
Christian Heimes3305c522007-12-03 13:47:29 +000052
Mark Hammond495cf992008-04-07 01:53:39 +000053# A map keyed by get_platform() return values to values accepted by
54# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
55# the param to cross-compile on x86 targetting amd64.)
56PLAT_TO_VCVARS = {
57 'win32' : 'x86',
58 'win-amd64' : 'amd64',
59 'win-ia64' : 'ia64',
60}
Christian Heimes3305c522007-12-03 13:47:29 +000061
62class Reg:
63 """Helper class to read values from the registry
64 """
65
66 @classmethod
67 def get_value(cls, path, key):
68 for base in HKEYS:
69 d = cls.read_values(base, path)
70 if d and key in d:
71 return d[key]
72 raise KeyError(key)
73
74 @classmethod
75 def read_keys(cls, base, key):
76 """Return list of registry keys."""
77 try:
78 handle = RegOpenKeyEx(base, key)
79 except RegError:
80 return None
81 L = []
82 i = 0
83 while True:
84 try:
85 k = RegEnumKey(handle, i)
86 except RegError:
87 break
88 L.append(k)
89 i += 1
90 return L
91
92 @classmethod
93 def read_values(cls, base, key):
94 """Return dict of registry keys and values.
95
96 All names are converted to lowercase.
97 """
98 try:
99 handle = RegOpenKeyEx(base, key)
100 except RegError:
101 return None
102 d = {}
103 i = 0
104 while True:
105 try:
106 name, value, type = RegEnumValue(handle, i)
107 except RegError:
108 break
109 name = name.lower()
110 d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
111 i += 1
112 return d
113
114 @staticmethod
115 def convert_mbcs(s):
116 dec = getattr(s, "decode", None)
117 if dec is not None:
118 try:
119 s = dec("mbcs")
120 except UnicodeError:
121 pass
122 return s
123
124class MacroExpander:
125
126 def __init__(self, version):
127 self.macros = {}
128 self.vsbase = VS_BASE % version
129 self.load_macros(version)
130
131 def set_macro(self, macro, path, key):
132 self.macros["$(%s)" % macro] = Reg.get_value(path, key)
133
134 def load_macros(self, version):
135 self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
136 self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
137 self.set_macro("FrameworkDir", NET_BASE, "installroot")
138 try:
139 if version >= 8.0:
140 self.set_macro("FrameworkSDKDir", NET_BASE,
141 "sdkinstallrootv2.0")
142 else:
143 raise KeyError("sdkinstallrootv2.0")
144 except KeyError as exc: #
145 raise DistutilsPlatformError(
146 """Python was built with Visual Studio 2008;
147extensions must be built with a compiler than can generate compatible binaries.
148Visual Studio 2008 was not found on this system. If you have Cygwin installed,
149you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
150
151 if version >= 9.0:
152 self.set_macro("FrameworkVersion", self.vsbase, "clr version")
153 self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
154 else:
155 p = r"Software\Microsoft\NET Framework Setup\Product"
156 for base in HKEYS:
157 try:
158 h = RegOpenKeyEx(base, p)
159 except RegError:
160 continue
161 key = RegEnumKey(h, 0)
162 d = Reg.get_value(base, r"%s\%s" % (p, key))
163 self.macros["$(FrameworkVersion)"] = d["version"]
164
165 def sub(self, s):
166 for k, v in self.macros.items():
167 s = s.replace(k, v)
168 return s
169
170def get_build_version():
171 """Return the version of MSVC that was used to build Python.
172
173 For Python 2.3 and up, the version number is included in
174 sys.version. For earlier versions, assume the compiler is MSVC 6.
175 """
176 prefix = "MSC v."
177 i = sys.version.find(prefix)
178 if i == -1:
179 return 6
180 i = i + len(prefix)
181 s, rest = sys.version[i:].split(" ", 1)
182 majorVersion = int(s[:-2]) - 6
183 minorVersion = int(s[2:3]) / 10.0
184 # I don't think paths are affected by minor version in version 6
185 if majorVersion == 6:
186 minorVersion = 0
187 if majorVersion >= 6:
188 return majorVersion + minorVersion
189 # else we don't know what version of the compiler this is
190 return None
191
Christian Heimes3305c522007-12-03 13:47:29 +0000192def normalize_and_reduce_paths(paths):
193 """Return a list of normalized paths with duplicates removed.
194
195 The current order of paths is maintained.
196 """
197 # Paths are normalized so things like: /a and /a/ aren't both preserved.
198 reduced_paths = []
199 for p in paths:
200 np = os.path.normpath(p)
201 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
202 if np not in reduced_paths:
203 reduced_paths.append(np)
204 return reduced_paths
205
Amaury Forgeot d'Arc293924b2008-09-02 23:19:56 +0000206def removeDuplicates(variable):
207 """Remove duplicate values of an environment variable.
208 """
209 oldList = variable.split(os.pathsep)
210 newList = []
211 for i in oldList:
212 if i not in newList:
213 newList.append(i)
214 newVariable = os.pathsep.join(newList)
215 return newVariable
216
Christian Heimes3305c522007-12-03 13:47:29 +0000217def find_vcvarsall(version):
218 """Find the vcvarsall.bat file
219
220 At first it tries to find the productdir of VS 2008 in the registry. If
221 that fails it falls back to the VS90COMNTOOLS env var.
222 """
223 vsbase = VS_BASE % version
224 try:
225 productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
226 "productdir")
227 except KeyError:
228 log.debug("Unable to find productdir in registry")
229 productdir = None
230
231 if not productdir or not os.path.isdir(productdir):
232 toolskey = "VS%0.f0COMNTOOLS" % version
233 toolsdir = os.environ.get(toolskey, None)
234
235 if toolsdir and os.path.isdir(toolsdir):
236 productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
237 productdir = os.path.abspath(productdir)
238 if not os.path.isdir(productdir):
239 log.debug("%s is not a valid directory" % productdir)
240 return None
241 else:
242 log.debug("Env var %s is not set or invalid" % toolskey)
243 if not productdir:
244 log.debug("No productdir found")
245 return None
246 vcvarsall = os.path.join(productdir, "vcvarsall.bat")
247 if os.path.isfile(vcvarsall):
248 return vcvarsall
249 log.debug("Unable to find vcvarsall.bat")
250 return None
251
252def query_vcvarsall(version, arch="x86"):
253 """Launch vcvarsall.bat and read the settings from its environment
254 """
255 vcvarsall = find_vcvarsall(version)
256 interesting = set(("include", "lib", "libpath", "path"))
257 result = {}
258
259 if vcvarsall is None:
Tarek Ziadée4ea6bc2008-12-30 23:06:46 +0000260 raise DistutilsPlatformError("Unable to find vcvarsall.bat")
Mark Hammond495cf992008-04-07 01:53:39 +0000261 log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
Christian Heimes3305c522007-12-03 13:47:29 +0000262 popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
263 stdout=subprocess.PIPE,
264 stderr=subprocess.PIPE)
Christian Heimes3305c522007-12-03 13:47:29 +0000265
Christian Heimescbdb7052007-12-05 20:10:38 +0000266 stdout, stderr = popen.communicate()
267 if popen.wait() != 0:
Tarek Ziadée4ea6bc2008-12-30 23:06:46 +0000268 raise DistutilsPlatformError(stderr.decode("mbcs"))
Christian Heimescbdb7052007-12-05 20:10:38 +0000269
270 stdout = stdout.decode("mbcs")
271 for line in stdout.split("\n"):
Christian Heimes3305c522007-12-03 13:47:29 +0000272 line = Reg.convert_mbcs(line)
273 if '=' not in line:
274 continue
275 line = line.strip()
Amaury Forgeot d'Arc293924b2008-09-02 23:19:56 +0000276 key, value = line.split('=', 1)
Christian Heimes3305c522007-12-03 13:47:29 +0000277 key = key.lower()
278 if key in interesting:
279 if value.endswith(os.pathsep):
280 value = value[:-1]
Amaury Forgeot d'Arc293924b2008-09-02 23:19:56 +0000281 result[key] = removeDuplicates(value)
Christian Heimes3305c522007-12-03 13:47:29 +0000282
283 if len(result) != len(interesting):
284 raise ValueError(str(list(result.keys())))
285
286 return result
287
288# More globals
289VERSION = get_build_version()
290if VERSION < 8.0:
291 raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
Christian Heimes3305c522007-12-03 13:47:29 +0000292# MACROS = MacroExpander(VERSION)
Christian Heimes3305c522007-12-03 13:47:29 +0000293
294class MSVCCompiler(CCompiler) :
295 """Concrete class that implements an interface to Microsoft Visual C++,
296 as defined by the CCompiler abstract class."""
297
298 compiler_type = 'msvc'
299
300 # Just set this so CCompiler's constructor doesn't barf. We currently
301 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
302 # as it really isn't necessary for this sort of single-compiler class.
303 # Would be nice to have a consistent interface with UnixCCompiler,
304 # though, so it's worth thinking about.
305 executables = {}
306
307 # Private class data (need to distinguish C from C++ source for compiler)
308 _c_extensions = ['.c']
309 _cpp_extensions = ['.cc', '.cpp', '.cxx']
310 _rc_extensions = ['.rc']
311 _mc_extensions = ['.mc']
312
313 # Needed for the filename generation methods provided by the
314 # base class, CCompiler.
315 src_extensions = (_c_extensions + _cpp_extensions +
316 _rc_extensions + _mc_extensions)
317 res_extension = '.res'
318 obj_extension = '.obj'
319 static_lib_extension = '.lib'
320 shared_lib_extension = '.dll'
321 static_lib_format = shared_lib_format = '%s%s'
322 exe_extension = '.exe'
323
324 def __init__(self, verbose=0, dry_run=0, force=0):
325 CCompiler.__init__ (self, verbose, dry_run, force)
326 self.__version = VERSION
Christian Heimes3305c522007-12-03 13:47:29 +0000327 self.__root = r"Software\Microsoft\VisualStudio"
328 # self.__macros = MACROS
Christian Heimes268be912008-11-28 11:03:48 +0000329 self.__paths = []
Mark Hammond495cf992008-04-07 01:53:39 +0000330 # target platform (.plat_name is consistent with 'bdist')
331 self.plat_name = None
332 self.__arch = None # deprecated name
Christian Heimes3305c522007-12-03 13:47:29 +0000333 self.initialized = False
334
Mark Hammond495cf992008-04-07 01:53:39 +0000335 def initialize(self, plat_name=None):
336 # multi-init means we would need to check platform same each time...
337 assert not self.initialized, "don't init multiple times"
338 if plat_name is None:
339 plat_name = get_platform()
340 # sanity check for platforms to prevent obscure errors later.
341 ok_plats = 'win32', 'win-amd64', 'win-ia64'
342 if plat_name not in ok_plats:
343 raise DistutilsPlatformError("--plat-name must be one of %s" %
344 (ok_plats,))
345
Christian Heimes3305c522007-12-03 13:47:29 +0000346 if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
347 # Assume that the SDK set up everything alright; don't try to be
348 # smarter
349 self.cc = "cl.exe"
350 self.linker = "link.exe"
351 self.lib = "lib.exe"
352 self.rc = "rc.exe"
353 self.mc = "mc.exe"
354 else:
Mark Hammond495cf992008-04-07 01:53:39 +0000355 # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
356 # to cross compile, you use 'x86_amd64'.
357 # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
358 # compile use 'x86' (ie, it runs the x86 compiler directly)
359 # No idea how itanium handles this, if at all.
360 if plat_name == get_platform() or plat_name == 'win32':
361 # native build or cross-compile to win32
362 plat_spec = PLAT_TO_VCVARS[plat_name]
363 else:
364 # cross compile from win32 -> some 64bit
365 plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \
366 PLAT_TO_VCVARS[plat_name]
367
368 vc_env = query_vcvarsall(VERSION, plat_spec)
369
Mark Hammond900155f2008-09-18 03:51:46 +0000370 # take care to only use strings in the environment.
371 self.__paths = vc_env['path'].encode('mbcs').split(os.pathsep)
372 os.environ['lib'] = vc_env['lib'].encode('mbcs')
373 os.environ['include'] = vc_env['include'].encode('mbcs')
Christian Heimes3305c522007-12-03 13:47:29 +0000374
375 if len(self.__paths) == 0:
376 raise DistutilsPlatformError("Python was built with %s, "
377 "and extensions need to be built with the same "
378 "version of the compiler, but it isn't installed."
379 % self.__product)
380
381 self.cc = self.find_exe("cl.exe")
382 self.linker = self.find_exe("link.exe")
383 self.lib = self.find_exe("lib.exe")
384 self.rc = self.find_exe("rc.exe") # resource compiler
385 self.mc = self.find_exe("mc.exe") # message compiler
386 #self.set_path_env_var('lib')
387 #self.set_path_env_var('include')
388
389 # extend the MSVC path with the current path
390 try:
391 for p in os.environ['path'].split(';'):
392 self.__paths.append(p)
393 except KeyError:
394 pass
395 self.__paths = normalize_and_reduce_paths(self.__paths)
396 os.environ['path'] = ";".join(self.__paths)
397
398 self.preprocess_options = None
399 if self.__arch == "x86":
400 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
401 '/DNDEBUG']
402 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
403 '/Z7', '/D_DEBUG']
404 else:
405 # Win64
406 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
407 '/DNDEBUG']
408 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
409 '/Z7', '/D_DEBUG']
410
411 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
412 if self.__version >= 7:
413 self.ldflags_shared_debug = [
414 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
415 ]
416 self.ldflags_static = [ '/nologo']
417
418 self.initialized = True
419
420 # -- Worker methods ------------------------------------------------
421
422 def object_filenames(self,
423 source_filenames,
424 strip_dir=0,
425 output_dir=''):
426 # Copied from ccompiler.py, extended to return .res as 'object'-file
427 # for .rc input file
428 if output_dir is None: output_dir = ''
429 obj_names = []
430 for src_name in source_filenames:
431 (base, ext) = os.path.splitext (src_name)
432 base = os.path.splitdrive(base)[1] # Chop off the drive
433 base = base[os.path.isabs(base):] # If abs, chop off leading /
434 if ext not in self.src_extensions:
435 # Better to raise an exception instead of silently continuing
436 # and later complain about sources and targets having
437 # different lengths
438 raise CompileError ("Don't know how to compile %s" % src_name)
439 if strip_dir:
440 base = os.path.basename (base)
441 if ext in self._rc_extensions:
442 obj_names.append (os.path.join (output_dir,
443 base + self.res_extension))
444 elif ext in self._mc_extensions:
445 obj_names.append (os.path.join (output_dir,
446 base + self.res_extension))
447 else:
448 obj_names.append (os.path.join (output_dir,
449 base + self.obj_extension))
450 return obj_names
451
452
453 def compile(self, sources,
454 output_dir=None, macros=None, include_dirs=None, debug=0,
455 extra_preargs=None, extra_postargs=None, depends=None):
456
457 if not self.initialized:
458 self.initialize()
459 compile_info = self._setup_compile(output_dir, macros, include_dirs,
460 sources, depends, extra_postargs)
461 macros, objects, extra_postargs, pp_opts, build = compile_info
462
463 compile_opts = extra_preargs or []
464 compile_opts.append ('/c')
465 if debug:
466 compile_opts.extend(self.compile_options_debug)
467 else:
468 compile_opts.extend(self.compile_options)
469
470 for obj in objects:
471 try:
472 src, ext = build[obj]
473 except KeyError:
474 continue
475 if debug:
476 # pass the full pathname to MSVC in debug mode,
477 # this allows the debugger to find the source file
478 # without asking the user to browse for it
479 src = os.path.abspath(src)
480
481 if ext in self._c_extensions:
482 input_opt = "/Tc" + src
483 elif ext in self._cpp_extensions:
484 input_opt = "/Tp" + src
485 elif ext in self._rc_extensions:
486 # compile .RC to .RES file
487 input_opt = src
488 output_opt = "/fo" + obj
489 try:
490 self.spawn([self.rc] + pp_opts +
491 [output_opt] + [input_opt])
492 except DistutilsExecError as msg:
493 raise CompileError(msg)
494 continue
495 elif ext in self._mc_extensions:
496 # Compile .MC to .RC file to .RES file.
497 # * '-h dir' specifies the directory for the
498 # generated include file
499 # * '-r dir' specifies the target directory of the
500 # generated RC file and the binary message resource
501 # it includes
502 #
503 # For now (since there are no options to change this),
504 # we use the source-directory for the include file and
505 # the build directory for the RC file and message
506 # resources. This works at least for win32all.
507 h_dir = os.path.dirname(src)
508 rc_dir = os.path.dirname(obj)
509 try:
510 # first compile .MC to .RC and .H file
511 self.spawn([self.mc] +
512 ['-h', h_dir, '-r', rc_dir] + [src])
513 base, _ = os.path.splitext (os.path.basename (src))
514 rc_file = os.path.join (rc_dir, base + '.rc')
515 # then compile .RC to .RES file
516 self.spawn([self.rc] +
517 ["/fo" + obj] + [rc_file])
518
519 except DistutilsExecError as msg:
520 raise CompileError(msg)
521 continue
522 else:
523 # how to handle this file?
524 raise CompileError("Don't know how to compile %s to %s"
525 % (src, obj))
526
527 output_opt = "/Fo" + obj
528 try:
529 self.spawn([self.cc] + compile_opts + pp_opts +
530 [input_opt, output_opt] +
531 extra_postargs)
532 except DistutilsExecError as msg:
533 raise CompileError(msg)
534
535 return objects
536
537
538 def create_static_lib(self,
539 objects,
540 output_libname,
541 output_dir=None,
542 debug=0,
543 target_lang=None):
544
545 if not self.initialized:
546 self.initialize()
547 (objects, output_dir) = self._fix_object_args(objects, output_dir)
548 output_filename = self.library_filename(output_libname,
549 output_dir=output_dir)
550
551 if self._need_link(objects, output_filename):
552 lib_args = objects + ['/OUT:' + output_filename]
553 if debug:
554 pass # XXX what goes here?
555 try:
556 self.spawn([self.lib] + lib_args)
557 except DistutilsExecError as msg:
558 raise LibError(msg)
559 else:
560 log.debug("skipping %s (up-to-date)", output_filename)
561
562
563 def link(self,
564 target_desc,
565 objects,
566 output_filename,
567 output_dir=None,
568 libraries=None,
569 library_dirs=None,
570 runtime_library_dirs=None,
571 export_symbols=None,
572 debug=0,
573 extra_preargs=None,
574 extra_postargs=None,
575 build_temp=None,
576 target_lang=None):
577
578 if not self.initialized:
579 self.initialize()
580 (objects, output_dir) = self._fix_object_args(objects, output_dir)
581 fixed_args = self._fix_lib_args(libraries, library_dirs,
582 runtime_library_dirs)
583 (libraries, library_dirs, runtime_library_dirs) = fixed_args
584
585 if runtime_library_dirs:
586 self.warn ("I don't know what to do with 'runtime_library_dirs': "
587 + str (runtime_library_dirs))
588
589 lib_opts = gen_lib_options(self,
590 library_dirs, runtime_library_dirs,
591 libraries)
592 if output_dir is not None:
593 output_filename = os.path.join(output_dir, output_filename)
594
595 if self._need_link(objects, output_filename):
596 if target_desc == CCompiler.EXECUTABLE:
597 if debug:
598 ldflags = self.ldflags_shared_debug[1:]
599 else:
600 ldflags = self.ldflags_shared[1:]
601 else:
602 if debug:
603 ldflags = self.ldflags_shared_debug
604 else:
605 ldflags = self.ldflags_shared
606
607 export_opts = []
608 for sym in (export_symbols or []):
609 export_opts.append("/EXPORT:" + sym)
610
611 ld_args = (ldflags + lib_opts + export_opts +
612 objects + ['/OUT:' + output_filename])
613
614 # The MSVC linker generates .lib and .exp files, which cannot be
615 # suppressed by any linker switches. The .lib files may even be
616 # needed! Make sure they are generated in the temporary build
617 # directory. Since they have different names for debug and release
618 # builds, they can go into the same directory.
Mark Hammond7c5c8e62008-05-02 12:48:15 +0000619 build_temp = os.path.dirname(objects[0])
Christian Heimes3305c522007-12-03 13:47:29 +0000620 if export_symbols is not None:
621 (dll_name, dll_ext) = os.path.splitext(
622 os.path.basename(output_filename))
623 implib_file = os.path.join(
Mark Hammond7c5c8e62008-05-02 12:48:15 +0000624 build_temp,
Christian Heimes3305c522007-12-03 13:47:29 +0000625 self.library_filename(dll_name))
626 ld_args.append ('/IMPLIB:' + implib_file)
627
Mark Hammond7c5c8e62008-05-02 12:48:15 +0000628 # Embedded manifests are recommended - see MSDN article titled
629 # "How to: Embed a Manifest Inside a C/C++ Application"
630 # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
631 # Ask the linker to generate the manifest in the temp dir, so
632 # we can embed it later.
633 temp_manifest = os.path.join(
634 build_temp,
635 os.path.basename(output_filename) + ".manifest")
636 ld_args.append('/MANIFESTFILE:' + temp_manifest)
637
Christian Heimes3305c522007-12-03 13:47:29 +0000638 if extra_preargs:
639 ld_args[:0] = extra_preargs
640 if extra_postargs:
641 ld_args.extend(extra_postargs)
642
643 self.mkpath(os.path.dirname(output_filename))
644 try:
645 self.spawn([self.linker] + ld_args)
646 except DistutilsExecError as msg:
647 raise LinkError(msg)
648
Mark Hammond7c5c8e62008-05-02 12:48:15 +0000649 # embed the manifest
650 # XXX - this is somewhat fragile - if mt.exe fails, distutils
651 # will still consider the DLL up-to-date, but it will not have a
652 # manifest. Maybe we should link to a temp file? OTOH, that
653 # implies a build environment error that shouldn't go undetected.
Martin v. Löwiscc0c7fb2009-12-03 20:56:15 +0000654 if target_desc == CCompiler.EXECUTABLE:
655 mfid = 1
656 else:
657 mfid = 2
658 try:
659 # Remove references to the Visual C runtime, so they will
660 # fall through to the Visual C dependency of Python.exe.
661 # This way, when installed for a restricted user (e.g.
662 # runtimes are not in WinSxS folder, but in Python's own
663 # folder), the runtimes do not need to be in every folder
664 # with .pyd's.
665 manifest_f = open(temp_manifest, "rb")
666 manifest_buf = manifest_f.read()
667 manifest_f.close()
668 pattern = re.compile(
669 r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
670 r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
671 re.DOTALL)
672 manifest_buf = re.sub(pattern, "", manifest_buf)
673 pattern = "<dependentAssembly>\s*</dependentAssembly>"
674 manifest_buf = re.sub(pattern, "", manifest_buf)
675 manifest_f = open(temp_manifest, "wb")
676 manifest_f.write(manifest_buf)
677 manifest_f.close()
678 except IOError:
679 pass
Mark Hammond7c5c8e62008-05-02 12:48:15 +0000680 out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
681 try:
682 self.spawn(['mt.exe', '-nologo', '-manifest',
683 temp_manifest, out_arg])
684 except DistutilsExecError as msg:
685 raise LinkError(msg)
Christian Heimes3305c522007-12-03 13:47:29 +0000686 else:
687 log.debug("skipping %s (up-to-date)", output_filename)
688
689
690 # -- Miscellaneous methods -----------------------------------------
691 # These are all used by the 'gen_lib_options() function, in
692 # ccompiler.py.
693
694 def library_dir_option(self, dir):
695 return "/LIBPATH:" + dir
696
697 def runtime_library_dir_option(self, dir):
698 raise DistutilsPlatformError(
699 "don't know how to set runtime library search path for MSVC++")
700
701 def library_option(self, lib):
702 return self.library_filename(lib)
703
704
705 def find_library_file(self, dirs, lib, debug=0):
706 # Prefer a debugging library if found (and requested), but deal
707 # with it if we don't have one.
708 if debug:
709 try_names = [lib + "_d", lib]
710 else:
711 try_names = [lib]
712 for dir in dirs:
713 for name in try_names:
714 libfile = os.path.join(dir, self.library_filename (name))
715 if os.path.exists(libfile):
716 return libfile
717 else:
718 # Oops, didn't find it in *any* of 'dirs'
719 return None
720
721 # Helper methods for using the MSVC registry settings
722
723 def find_exe(self, exe):
724 """Return path to an MSVC executable program.
725
726 Tries to find the program in several places: first, one of the
727 MSVC program search paths from the registry; next, the directories
728 in the PATH environment variable. If any of those work, return an
729 absolute path that is known to exist. If none of them work, just
730 return the original program name, 'exe'.
731 """
732 for p in self.__paths:
733 fn = os.path.join(os.path.abspath(p), exe)
734 if os.path.isfile(fn):
735 return fn
736
737 # didn't find it; try existing path
738 for p in os.environ['Path'].split(';'):
739 fn = os.path.join(os.path.abspath(p),exe)
740 if os.path.isfile(fn):
741 return fn
742
743 return exe