blob: 7a19f47447a1c3c617e89550a3b3c998759dfe90 [file] [log] [blame]
Tarek Ziade1231a4e2011-05-19 13:07:25 +02001"""CCompiler implementation for the Microsoft Visual Studio 2008 compiler.
2
3The MSVCCompiler class is compatible with VS 2005 and VS 2008. Legacy
4support for older versions of VS are in the msvccompiler module.
5"""
6
7# Written by Perry Stoll
8# hacked by Robin Becker and Thomas Heller to do a better job of
9# finding DevStudio (through the registry)
10# ported to VS2005 and VS 2008 by Christian Heimes
11import os
12import subprocess
13import sys
14import re
15
16from packaging.errors import (PackagingExecError, PackagingPlatformError,
17 CompileError, LibError, LinkError)
18from packaging.compiler.ccompiler import CCompiler
19from packaging.compiler import gen_lib_options
20from packaging import logger
21from packaging.util import get_platform
22
23import winreg
24
25RegOpenKeyEx = winreg.OpenKeyEx
26RegEnumKey = winreg.EnumKey
27RegEnumValue = winreg.EnumValue
28RegError = winreg.error
29
30HKEYS = (winreg.HKEY_USERS,
31 winreg.HKEY_CURRENT_USER,
32 winreg.HKEY_LOCAL_MACHINE,
33 winreg.HKEY_CLASSES_ROOT)
34
35VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
36WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
37NET_BASE = r"Software\Microsoft\.NETFramework"
38
39# A map keyed by get_platform() return values to values accepted by
40# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
41# the param to cross-compile on x86 targetting amd64.)
42PLAT_TO_VCVARS = {
43 'win32' : 'x86',
44 'win-amd64' : 'amd64',
45 'win-ia64' : 'ia64',
46}
47
48
49class Reg:
50 """Helper class to read values from the registry
51 """
52
53 def get_value(cls, path, key):
54 for base in HKEYS:
55 d = cls.read_values(base, path)
56 if d and key in d:
57 return d[key]
58 raise KeyError(key)
59 get_value = classmethod(get_value)
60
61 def read_keys(cls, base, key):
62 """Return list of registry keys."""
63 try:
64 handle = RegOpenKeyEx(base, key)
65 except RegError:
66 return None
67 L = []
68 i = 0
69 while True:
70 try:
71 k = RegEnumKey(handle, i)
72 except RegError:
73 break
74 L.append(k)
75 i += 1
76 return L
77 read_keys = classmethod(read_keys)
78
79 def read_values(cls, base, key):
80 """Return dict of registry keys and values.
81
82 All names are converted to lowercase.
83 """
84 try:
85 handle = RegOpenKeyEx(base, key)
86 except RegError:
87 return None
88 d = {}
89 i = 0
90 while True:
91 try:
92 name, value, type = RegEnumValue(handle, i)
93 except RegError:
94 break
95 name = name.lower()
96 d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
97 i += 1
98 return d
99 read_values = classmethod(read_values)
100
101 def convert_mbcs(s):
102 dec = getattr(s, "decode", None)
103 if dec is not None:
104 try:
105 s = dec("mbcs")
106 except UnicodeError:
107 pass
108 return s
109 convert_mbcs = staticmethod(convert_mbcs)
110
111class MacroExpander:
112
113 def __init__(self, version):
114 self.macros = {}
115 self.vsbase = VS_BASE % version
116 self.load_macros(version)
117
118 def set_macro(self, macro, path, key):
119 self.macros["$(%s)" % macro] = Reg.get_value(path, key)
120
121 def load_macros(self, version):
122 self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
123 self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
124 self.set_macro("FrameworkDir", NET_BASE, "installroot")
125 try:
126 if version >= 8.0:
127 self.set_macro("FrameworkSDKDir", NET_BASE,
128 "sdkinstallrootv2.0")
129 else:
130 raise KeyError("sdkinstallrootv2.0")
131 except KeyError:
132 raise PackagingPlatformError(
133 """Python was built with Visual Studio 2008;
134extensions must be built with a compiler than can generate compatible binaries.
135Visual Studio 2008 was not found on this system. If you have Cygwin installed,
136you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
137
138 if version >= 9.0:
139 self.set_macro("FrameworkVersion", self.vsbase, "clr version")
140 self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
141 else:
142 p = r"Software\Microsoft\NET Framework Setup\Product"
143 for base in HKEYS:
144 try:
145 h = RegOpenKeyEx(base, p)
146 except RegError:
147 continue
148 key = RegEnumKey(h, 0)
149 d = Reg.get_value(base, r"%s\%s" % (p, key))
150 self.macros["$(FrameworkVersion)"] = d["version"]
151
152 def sub(self, s):
153 for k, v in self.macros.items():
154 s = s.replace(k, v)
155 return s
156
157def get_build_version():
158 """Return the version of MSVC that was used to build Python.
159
160 For Python 2.3 and up, the version number is included in
161 sys.version. For earlier versions, assume the compiler is MSVC 6.
162 """
163 prefix = "MSC v."
164 i = sys.version.find(prefix)
165 if i == -1:
166 return 6
167 i = i + len(prefix)
168 s, rest = sys.version[i:].split(" ", 1)
169 majorVersion = int(s[:-2]) - 6
170 minorVersion = int(s[2:3]) / 10.0
171 # I don't think paths are affected by minor version in version 6
172 if majorVersion == 6:
173 minorVersion = 0
174 if majorVersion >= 6:
175 return majorVersion + minorVersion
176 # else we don't know what version of the compiler this is
177 return None
178
179def normalize_and_reduce_paths(paths):
180 """Return a list of normalized paths with duplicates removed.
181
182 The current order of paths is maintained.
183 """
184 # Paths are normalized so things like: /a and /a/ aren't both preserved.
185 reduced_paths = []
186 for p in paths:
187 np = os.path.normpath(p)
188 # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
189 if np not in reduced_paths:
190 reduced_paths.append(np)
191 return reduced_paths
192
193def removeDuplicates(variable):
194 """Remove duplicate values of an environment variable.
195 """
196 oldList = variable.split(os.pathsep)
197 newList = []
198 for i in oldList:
199 if i not in newList:
200 newList.append(i)
201 newVariable = os.pathsep.join(newList)
202 return newVariable
203
204def find_vcvarsall(version):
205 """Find the vcvarsall.bat file
206
207 At first it tries to find the productdir of VS 2008 in the registry. If
208 that fails it falls back to the VS90COMNTOOLS env var.
209 """
210 vsbase = VS_BASE % version
211 try:
212 productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
213 "productdir")
214 except KeyError:
215 logger.debug("Unable to find productdir in registry")
216 productdir = None
217
218 if not productdir or not os.path.isdir(productdir):
219 toolskey = "VS%0.f0COMNTOOLS" % version
220 toolsdir = os.environ.get(toolskey, None)
221
222 if toolsdir and os.path.isdir(toolsdir):
223 productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
224 productdir = os.path.abspath(productdir)
225 if not os.path.isdir(productdir):
226 logger.debug("%s is not a valid directory", productdir)
227 return None
228 else:
229 logger.debug("env var %s is not set or invalid", toolskey)
230 if not productdir:
231 logger.debug("no productdir found")
232 return None
233 vcvarsall = os.path.join(productdir, "vcvarsall.bat")
234 if os.path.isfile(vcvarsall):
235 return vcvarsall
236 logger.debug("unable to find vcvarsall.bat")
237 return None
238
239def query_vcvarsall(version, arch="x86"):
240 """Launch vcvarsall.bat and read the settings from its environment
241 """
242 vcvarsall = find_vcvarsall(version)
243 interesting = set(("include", "lib", "libpath", "path"))
244 result = {}
245
246 if vcvarsall is None:
247 raise PackagingPlatformError("Unable to find vcvarsall.bat")
248 logger.debug("calling 'vcvarsall.bat %s' (version=%s)", arch, version)
249 popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
250 stdout=subprocess.PIPE,
251 stderr=subprocess.PIPE)
252
253 stdout, stderr = popen.communicate()
254 if popen.wait() != 0:
255 raise PackagingPlatformError(stderr.decode("mbcs"))
256
257 stdout = stdout.decode("mbcs")
258 for line in stdout.split("\n"):
259 line = Reg.convert_mbcs(line)
260 if '=' not in line:
261 continue
262 line = line.strip()
263 key, value = line.split('=', 1)
264 key = key.lower()
265 if key in interesting:
266 if value.endswith(os.pathsep):
267 value = value[:-1]
268 result[key] = removeDuplicates(value)
269
270 if len(result) != len(interesting):
271 raise ValueError(str(list(result)))
272
273 return result
274
275# More globals
276VERSION = get_build_version()
277if VERSION < 8.0:
278 raise PackagingPlatformError("VC %0.1f is not supported by this module" % VERSION)
279# MACROS = MacroExpander(VERSION)
280
281class MSVCCompiler(CCompiler) :
282 """Concrete class that implements an interface to Microsoft Visual C++,
283 as defined by the CCompiler abstract class."""
284
285 name = 'msvc'
286 description = 'Microsoft Visual C++'
287
288 # Just set this so CCompiler's constructor doesn't barf. We currently
289 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
290 # as it really isn't necessary for this sort of single-compiler class.
291 # Would be nice to have a consistent interface with UnixCCompiler,
292 # though, so it's worth thinking about.
293 executables = {}
294
295 # Private class data (need to distinguish C from C++ source for compiler)
296 _c_extensions = ['.c']
297 _cpp_extensions = ['.cc', '.cpp', '.cxx']
298 _rc_extensions = ['.rc']
299 _mc_extensions = ['.mc']
300
301 # Needed for the filename generation methods provided by the
302 # base class, CCompiler.
303 src_extensions = (_c_extensions + _cpp_extensions +
304 _rc_extensions + _mc_extensions)
305 res_extension = '.res'
306 obj_extension = '.obj'
307 static_lib_extension = '.lib'
308 shared_lib_extension = '.dll'
309 static_lib_format = shared_lib_format = '%s%s'
310 exe_extension = '.exe'
311
312 def __init__(self, verbose=0, dry_run=False, force=False):
313 CCompiler.__init__(self, verbose, dry_run, force)
314 self.__version = VERSION
315 self.__root = r"Software\Microsoft\VisualStudio"
316 # self.__macros = MACROS
317 self.__paths = []
318 # target platform (.plat_name is consistent with 'bdist')
319 self.plat_name = None
320 self.__arch = None # deprecated name
321 self.initialized = False
322
323 def initialize(self, plat_name=None):
324 # multi-init means we would need to check platform same each time...
325 assert not self.initialized, "don't init multiple times"
326 if plat_name is None:
327 plat_name = get_platform()
328 # sanity check for platforms to prevent obscure errors later.
329 ok_plats = 'win32', 'win-amd64', 'win-ia64'
330 if plat_name not in ok_plats:
331 raise PackagingPlatformError("--plat-name must be one of %s" %
332 (ok_plats,))
333
334 if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
335 # Assume that the SDK set up everything alright; don't try to be
336 # smarter
337 self.cc = "cl.exe"
338 self.linker = "link.exe"
339 self.lib = "lib.exe"
340 self.rc = "rc.exe"
341 self.mc = "mc.exe"
342 else:
343 # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
344 # to cross compile, you use 'x86_amd64'.
345 # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
346 # compile use 'x86' (ie, it runs the x86 compiler directly)
347 # No idea how itanium handles this, if at all.
348 if plat_name == get_platform() or plat_name == 'win32':
349 # native build or cross-compile to win32
350 plat_spec = PLAT_TO_VCVARS[plat_name]
351 else:
352 # cross compile from win32 -> some 64bit
353 plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \
354 PLAT_TO_VCVARS[plat_name]
355
356 vc_env = query_vcvarsall(VERSION, plat_spec)
357
358 # take care to only use strings in the environment.
Tarek Ziade0e3f3a72011-05-19 15:51:54 +0200359 self.__paths = [part.encode('mbcs') for part in
360 vc_env['path'].split(os.pathsep)]
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200361 os.environ['lib'] = vc_env['lib'].encode('mbcs')
362 os.environ['include'] = vc_env['include'].encode('mbcs')
363
364 if len(self.__paths) == 0:
365 raise PackagingPlatformError("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=False,
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=False,
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 PackagingExecError 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 PackagingExecError 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 PackagingExecError 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=False,
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 PackagingExecError as msg:
547 raise LibError(msg)
548 else:
549 logger.debug("skipping %s (up-to-date)", output_filename)
550
551
552 def link(self, target_desc, objects, output_filename, output_dir=None,
553 libraries=None, library_dirs=None, runtime_library_dirs=None,
554 export_symbols=None, debug=False, extra_preargs=None,
555 extra_postargs=None, build_temp=None, target_lang=None):
556 if not self.initialized:
557 self.initialize()
558 objects, output_dir = self._fix_object_args(objects, output_dir)
559 fixed_args = self._fix_lib_args(libraries, library_dirs,
560 runtime_library_dirs)
561 libraries, library_dirs, runtime_library_dirs = fixed_args
562
563 if runtime_library_dirs:
564 self.warn("don't know what to do with 'runtime_library_dirs': "
565 + str(runtime_library_dirs))
566
567 lib_opts = gen_lib_options(self,
568 library_dirs, runtime_library_dirs,
569 libraries)
570 if output_dir is not None:
571 output_filename = os.path.join(output_dir, output_filename)
572
573 if self._need_link(objects, output_filename):
574 if target_desc == CCompiler.EXECUTABLE:
575 if debug:
576 ldflags = self.ldflags_shared_debug[1:]
577 else:
578 ldflags = self.ldflags_shared[1:]
579 else:
580 if debug:
581 ldflags = self.ldflags_shared_debug
582 else:
583 ldflags = self.ldflags_shared
584
585 export_opts = []
586 for sym in (export_symbols or []):
587 export_opts.append("/EXPORT:" + sym)
588
589 ld_args = (ldflags + lib_opts + export_opts +
590 objects + ['/OUT:' + output_filename])
591
592 # The MSVC linker generates .lib and .exp files, which cannot be
593 # suppressed by any linker switches. The .lib files may even be
594 # needed! Make sure they are generated in the temporary build
595 # directory. Since they have different names for debug and release
596 # builds, they can go into the same directory.
597 build_temp = os.path.dirname(objects[0])
598 if export_symbols is not None:
599 dll_name, dll_ext = os.path.splitext(
600 os.path.basename(output_filename))
601 implib_file = os.path.join(
602 build_temp,
603 self.library_filename(dll_name))
604 ld_args.append('/IMPLIB:' + implib_file)
605
606 # Embedded manifests are recommended - see MSDN article titled
607 # "How to: Embed a Manifest Inside a C/C++ Application"
608 # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
609 # Ask the linker to generate the manifest in the temp dir, so
610 # we can embed it later.
611 temp_manifest = os.path.join(
612 build_temp,
613 os.path.basename(output_filename) + ".manifest")
614 ld_args.append('/MANIFESTFILE:' + temp_manifest)
615
616 if extra_preargs:
617 ld_args[:0] = extra_preargs
618 if extra_postargs:
619 ld_args.extend(extra_postargs)
620
621 self.mkpath(os.path.dirname(output_filename))
622 try:
623 self.spawn([self.linker] + ld_args)
624 except PackagingExecError as msg:
625 raise LinkError(msg)
626
627 # embed the manifest
628 # XXX - this is somewhat fragile - if mt.exe fails, distutils
629 # will still consider the DLL up-to-date, but it will not have a
630 # manifest. Maybe we should link to a temp file? OTOH, that
631 # implies a build environment error that shouldn't go undetected.
632 if target_desc == CCompiler.EXECUTABLE:
633 mfid = 1
634 else:
635 mfid = 2
636 self._remove_visual_c_ref(temp_manifest)
637 out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
638 try:
639 self.spawn(['mt.exe', '-nologo', '-manifest',
640 temp_manifest, out_arg])
641 except PackagingExecError as msg:
642 raise LinkError(msg)
643 else:
644 logger.debug("skipping %s (up-to-date)", output_filename)
645
646 def _remove_visual_c_ref(self, manifest_file):
647 try:
648 # Remove references to the Visual C runtime, so they will
649 # fall through to the Visual C dependency of Python.exe.
650 # This way, when installed for a restricted user (e.g.
651 # runtimes are not in WinSxS folder, but in Python's own
652 # folder), the runtimes do not need to be in every folder
653 # with .pyd's.
654 with open(manifest_file) as manifest_f:
655 manifest_buf = manifest_f.read()
656 pattern = re.compile(
657 r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
658 r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
659 re.DOTALL)
660 manifest_buf = re.sub(pattern, "", manifest_buf)
661 pattern = "<dependentAssembly>\s*</dependentAssembly>"
662 manifest_buf = re.sub(pattern, "", manifest_buf)
663 with open(manifest_file, 'w') as manifest_f:
664 manifest_f.write(manifest_buf)
665 except IOError:
666 pass
667
668 # -- Miscellaneous methods -----------------------------------------
669 # These are all used by the 'gen_lib_options() function, in
670 # ccompiler.py.
671
672 def library_dir_option(self, dir):
673 return "/LIBPATH:" + dir
674
675 def runtime_library_dir_option(self, dir):
676 raise PackagingPlatformError(
677 "don't know how to set runtime library search path for MSVC++")
678
679 def library_option(self, lib):
680 return self.library_filename(lib)
681
682
683 def find_library_file(self, dirs, lib, debug=False):
684 # Prefer a debugging library if found (and requested), but deal
685 # with it if we don't have one.
686 if debug:
687 try_names = [lib + "_d", lib]
688 else:
689 try_names = [lib]
690 for dir in dirs:
691 for name in try_names:
692 libfile = os.path.join(dir, self.library_filename(name))
693 if os.path.exists(libfile):
694 return libfile
695 else:
696 # Oops, didn't find it in *any* of 'dirs'
697 return None
698
699 # Helper methods for using the MSVC registry settings
700
701 def find_exe(self, exe):
702 """Return path to an MSVC executable program.
703
704 Tries to find the program in several places: first, one of the
705 MSVC program search paths from the registry; next, the directories
706 in the PATH environment variable. If any of those work, return an
707 absolute path that is known to exist. If none of them work, just
708 return the original program name, 'exe'.
709 """
710 for p in self.__paths:
711 fn = os.path.join(os.path.abspath(p), exe)
712 if os.path.isfile(fn):
713 return fn
714
715 # didn't find it; try existing path
716 for p in os.environ['Path'].split(';'):
717 fn = os.path.join(os.path.abspath(p),exe)
718 if os.path.isfile(fn):
719 return fn
720
721 return exe