blob: 43fc5fa2dbac541dd6be476c73d0cae77f2562d3 [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.
Victor Stinner8914fed2011-05-20 00:05:58 +0200359 self.__paths = vc_env['path'].split(os.pathsep)
Victor Stinner99991852011-05-19 23:46:03 +0200360 os.environ['lib'] = vc_env['lib']
361 os.environ['include'] = vc_env['include']
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200362
363 if len(self.__paths) == 0:
364 raise PackagingPlatformError("Python was built with %s, "
365 "and extensions need to be built with the same "
366 "version of the compiler, but it isn't installed."
367 % self.__product)
368
369 self.cc = self.find_exe("cl.exe")
370 self.linker = self.find_exe("link.exe")
371 self.lib = self.find_exe("lib.exe")
372 self.rc = self.find_exe("rc.exe") # resource compiler
373 self.mc = self.find_exe("mc.exe") # message compiler
374 #self.set_path_env_var('lib')
375 #self.set_path_env_var('include')
376
377 # extend the MSVC path with the current path
378 try:
379 for p in os.environ['path'].split(';'):
380 self.__paths.append(p)
381 except KeyError:
382 pass
383 self.__paths = normalize_and_reduce_paths(self.__paths)
384 os.environ['path'] = ";".join(self.__paths)
385
386 self.preprocess_options = None
387 if self.__arch == "x86":
388 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
389 '/DNDEBUG']
390 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
391 '/Z7', '/D_DEBUG']
392 else:
393 # Win64
394 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
395 '/DNDEBUG']
396 self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
397 '/Z7', '/D_DEBUG']
398
399 self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
400 if self.__version >= 7:
401 self.ldflags_shared_debug = [
402 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
403 ]
404 self.ldflags_static = [ '/nologo']
405
406 self.initialized = True
407
408 # -- Worker methods ------------------------------------------------
409
410 def object_filenames(self,
411 source_filenames,
412 strip_dir=False,
413 output_dir=''):
414 # Copied from ccompiler.py, extended to return .res as 'object'-file
415 # for .rc input file
416 if output_dir is None: output_dir = ''
417 obj_names = []
418 for src_name in source_filenames:
419 base, ext = os.path.splitext(src_name)
420 base = os.path.splitdrive(base)[1] # Chop off the drive
421 base = base[os.path.isabs(base):] # If abs, chop off leading /
422 if ext not in self.src_extensions:
423 # Better to raise an exception instead of silently continuing
424 # and later complain about sources and targets having
425 # different lengths
426 raise CompileError("Don't know how to compile %s" % src_name)
427 if strip_dir:
428 base = os.path.basename(base)
429 if ext in self._rc_extensions:
430 obj_names.append(os.path.join(output_dir,
431 base + self.res_extension))
432 elif ext in self._mc_extensions:
433 obj_names.append(os.path.join(output_dir,
434 base + self.res_extension))
435 else:
436 obj_names.append(os.path.join(output_dir,
437 base + self.obj_extension))
438 return obj_names
439
440
441 def compile(self, sources,
442 output_dir=None, macros=None, include_dirs=None, debug=False,
443 extra_preargs=None, extra_postargs=None, depends=None):
444
445 if not self.initialized:
446 self.initialize()
447 compile_info = self._setup_compile(output_dir, macros, include_dirs,
448 sources, depends, extra_postargs)
449 macros, objects, extra_postargs, pp_opts, build = compile_info
450
451 compile_opts = extra_preargs or []
452 compile_opts.append('/c')
453 if debug:
454 compile_opts.extend(self.compile_options_debug)
455 else:
456 compile_opts.extend(self.compile_options)
457
458 for obj in objects:
459 try:
460 src, ext = build[obj]
461 except KeyError:
462 continue
463 if debug:
464 # pass the full pathname to MSVC in debug mode,
465 # this allows the debugger to find the source file
466 # without asking the user to browse for it
467 src = os.path.abspath(src)
468
469 if ext in self._c_extensions:
470 input_opt = "/Tc" + src
471 elif ext in self._cpp_extensions:
472 input_opt = "/Tp" + src
473 elif ext in self._rc_extensions:
474 # compile .RC to .RES file
475 input_opt = src
476 output_opt = "/fo" + obj
477 try:
478 self.spawn([self.rc] + pp_opts +
479 [output_opt] + [input_opt])
480 except PackagingExecError as msg:
481 raise CompileError(msg)
482 continue
483 elif ext in self._mc_extensions:
484 # Compile .MC to .RC file to .RES file.
485 # * '-h dir' specifies the directory for the
486 # generated include file
487 # * '-r dir' specifies the target directory of the
488 # generated RC file and the binary message resource
489 # it includes
490 #
491 # For now (since there are no options to change this),
492 # we use the source-directory for the include file and
493 # the build directory for the RC file and message
494 # resources. This works at least for win32all.
495 h_dir = os.path.dirname(src)
496 rc_dir = os.path.dirname(obj)
497 try:
498 # first compile .MC to .RC and .H file
499 self.spawn([self.mc] +
500 ['-h', h_dir, '-r', rc_dir] + [src])
501 base, _ = os.path.splitext(os.path.basename(src))
502 rc_file = os.path.join(rc_dir, base + '.rc')
503 # then compile .RC to .RES file
504 self.spawn([self.rc] +
505 ["/fo" + obj] + [rc_file])
506
507 except PackagingExecError as msg:
508 raise CompileError(msg)
509 continue
510 else:
511 # how to handle this file?
512 raise CompileError("Don't know how to compile %s to %s"
513 % (src, obj))
514
515 output_opt = "/Fo" + obj
516 try:
517 self.spawn([self.cc] + compile_opts + pp_opts +
518 [input_opt, output_opt] +
519 extra_postargs)
520 except PackagingExecError as msg:
521 raise CompileError(msg)
522
523 return objects
524
525
526 def create_static_lib(self,
527 objects,
528 output_libname,
529 output_dir=None,
530 debug=False,
531 target_lang=None):
532
533 if not self.initialized:
534 self.initialize()
535 objects, output_dir = self._fix_object_args(objects, output_dir)
536 output_filename = self.library_filename(output_libname,
537 output_dir=output_dir)
538
539 if self._need_link(objects, output_filename):
540 lib_args = objects + ['/OUT:' + output_filename]
541 if debug:
542 pass # XXX what goes here?
543 try:
544 self.spawn([self.lib] + lib_args)
545 except PackagingExecError as msg:
546 raise LibError(msg)
547 else:
548 logger.debug("skipping %s (up-to-date)", output_filename)
549
550
551 def link(self, target_desc, objects, output_filename, output_dir=None,
552 libraries=None, library_dirs=None, runtime_library_dirs=None,
553 export_symbols=None, debug=False, extra_preargs=None,
554 extra_postargs=None, build_temp=None, target_lang=None):
555 if not self.initialized:
556 self.initialize()
557 objects, output_dir = self._fix_object_args(objects, output_dir)
558 fixed_args = self._fix_lib_args(libraries, library_dirs,
559 runtime_library_dirs)
560 libraries, library_dirs, runtime_library_dirs = fixed_args
561
562 if runtime_library_dirs:
563 self.warn("don't know what to do with 'runtime_library_dirs': "
564 + str(runtime_library_dirs))
565
566 lib_opts = gen_lib_options(self,
567 library_dirs, runtime_library_dirs,
568 libraries)
569 if output_dir is not None:
570 output_filename = os.path.join(output_dir, output_filename)
571
572 if self._need_link(objects, output_filename):
573 if target_desc == CCompiler.EXECUTABLE:
574 if debug:
575 ldflags = self.ldflags_shared_debug[1:]
576 else:
577 ldflags = self.ldflags_shared[1:]
578 else:
579 if debug:
580 ldflags = self.ldflags_shared_debug
581 else:
582 ldflags = self.ldflags_shared
583
584 export_opts = []
585 for sym in (export_symbols or []):
586 export_opts.append("/EXPORT:" + sym)
587
588 ld_args = (ldflags + lib_opts + export_opts +
589 objects + ['/OUT:' + output_filename])
590
591 # The MSVC linker generates .lib and .exp files, which cannot be
592 # suppressed by any linker switches. The .lib files may even be
593 # needed! Make sure they are generated in the temporary build
594 # directory. Since they have different names for debug and release
595 # builds, they can go into the same directory.
596 build_temp = os.path.dirname(objects[0])
597 if export_symbols is not None:
598 dll_name, dll_ext = os.path.splitext(
599 os.path.basename(output_filename))
600 implib_file = os.path.join(
601 build_temp,
602 self.library_filename(dll_name))
603 ld_args.append('/IMPLIB:' + implib_file)
604
605 # Embedded manifests are recommended - see MSDN article titled
606 # "How to: Embed a Manifest Inside a C/C++ Application"
607 # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
608 # Ask the linker to generate the manifest in the temp dir, so
609 # we can embed it later.
610 temp_manifest = os.path.join(
611 build_temp,
612 os.path.basename(output_filename) + ".manifest")
613 ld_args.append('/MANIFESTFILE:' + temp_manifest)
614
615 if extra_preargs:
616 ld_args[:0] = extra_preargs
617 if extra_postargs:
618 ld_args.extend(extra_postargs)
619
620 self.mkpath(os.path.dirname(output_filename))
621 try:
622 self.spawn([self.linker] + ld_args)
623 except PackagingExecError as msg:
624 raise LinkError(msg)
625
626 # embed the manifest
627 # XXX - this is somewhat fragile - if mt.exe fails, distutils
628 # will still consider the DLL up-to-date, but it will not have a
629 # manifest. Maybe we should link to a temp file? OTOH, that
630 # implies a build environment error that shouldn't go undetected.
631 if target_desc == CCompiler.EXECUTABLE:
632 mfid = 1
633 else:
634 mfid = 2
635 self._remove_visual_c_ref(temp_manifest)
636 out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
637 try:
638 self.spawn(['mt.exe', '-nologo', '-manifest',
639 temp_manifest, out_arg])
640 except PackagingExecError as msg:
641 raise LinkError(msg)
642 else:
643 logger.debug("skipping %s (up-to-date)", output_filename)
644
645 def _remove_visual_c_ref(self, manifest_file):
646 try:
647 # Remove references to the Visual C runtime, so they will
648 # fall through to the Visual C dependency of Python.exe.
649 # This way, when installed for a restricted user (e.g.
650 # runtimes are not in WinSxS folder, but in Python's own
651 # folder), the runtimes do not need to be in every folder
652 # with .pyd's.
653 with open(manifest_file) as manifest_f:
654 manifest_buf = manifest_f.read()
655 pattern = re.compile(
656 r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
657 r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
658 re.DOTALL)
659 manifest_buf = re.sub(pattern, "", manifest_buf)
660 pattern = "<dependentAssembly>\s*</dependentAssembly>"
661 manifest_buf = re.sub(pattern, "", manifest_buf)
662 with open(manifest_file, 'w') as manifest_f:
663 manifest_f.write(manifest_buf)
664 except IOError:
665 pass
666
667 # -- Miscellaneous methods -----------------------------------------
668 # These are all used by the 'gen_lib_options() function, in
669 # ccompiler.py.
670
671 def library_dir_option(self, dir):
672 return "/LIBPATH:" + dir
673
674 def runtime_library_dir_option(self, dir):
675 raise PackagingPlatformError(
676 "don't know how to set runtime library search path for MSVC++")
677
678 def library_option(self, lib):
679 return self.library_filename(lib)
680
681
682 def find_library_file(self, dirs, lib, debug=False):
683 # Prefer a debugging library if found (and requested), but deal
684 # with it if we don't have one.
685 if debug:
686 try_names = [lib + "_d", lib]
687 else:
688 try_names = [lib]
689 for dir in dirs:
690 for name in try_names:
691 libfile = os.path.join(dir, self.library_filename(name))
692 if os.path.exists(libfile):
693 return libfile
694 else:
695 # Oops, didn't find it in *any* of 'dirs'
696 return None
697
698 # Helper methods for using the MSVC registry settings
699
700 def find_exe(self, exe):
701 """Return path to an MSVC executable program.
702
703 Tries to find the program in several places: first, one of the
704 MSVC program search paths from the registry; next, the directories
705 in the PATH environment variable. If any of those work, return an
706 absolute path that is known to exist. If none of them work, just
707 return the original program name, 'exe'.
708 """
709 for p in self.__paths:
710 fn = os.path.join(os.path.abspath(p), exe)
711 if os.path.isfile(fn):
712 return fn
713
714 # didn't find it; try existing path
715 for p in os.environ['Path'].split(';'):
716 fn = os.path.join(os.path.abspath(p),exe)
717 if os.path.isfile(fn):
718 return fn
719
720 return exe