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