]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/python/contrib/Lib/distutils/msvc9compiler.py
Inital import
[l4.git] / l4 / pkg / python / contrib / Lib / distutils / msvc9compiler.py
1 """distutils.msvc9compiler
2
3 Contains MSVCCompiler, an implementation of the abstract CCompiler class
4 for the Microsoft Visual Studio 2008.
5
6 The module is compatible with VS 2005 and VS 2008. You can find legacy support
7 for 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: msvc9compiler.py 68082 2008-12-30 23:06:46Z tarek.ziade $"
16
17 import os
18 import subprocess
19 import sys
20 from distutils.errors import (DistutilsExecError, DistutilsPlatformError,
21     CompileError, LibError, LinkError)
22 from distutils.ccompiler import (CCompiler, gen_preprocess_options,
23     gen_lib_options)
24 from distutils import log
25 from distutils.util import get_platform
26
27 import _winreg
28
29 RegOpenKeyEx = _winreg.OpenKeyEx
30 RegEnumKey = _winreg.EnumKey
31 RegEnumValue = _winreg.EnumValue
32 RegError = _winreg.error
33
34 HKEYS = (_winreg.HKEY_USERS,
35          _winreg.HKEY_CURRENT_USER,
36          _winreg.HKEY_LOCAL_MACHINE,
37          _winreg.HKEY_CLASSES_ROOT)
38
39 VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
40 WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
41 NET_BASE = r"Software\Microsoft\.NETFramework"
42
43 # 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.)
46 PLAT_TO_VCVARS = {
47     'win32' : 'x86',
48     'win-amd64' : 'amd64',
49     'win-ia64' : 'ia64',
50 }
51
52 class 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
114 class 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;
137 extensions must be built with a compiler than can generate compatible binaries.
138 Visual Studio 2008 was not found on this system. If you have Cygwin installed,
139 you 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
160 def 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
182 def 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
196 def 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
207 def 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
242 def 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 DistutilsPlatformError("Unable to find vcvarsall.bat")
251     log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
252     popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
253                              stdout=subprocess.PIPE,
254                              stderr=subprocess.PIPE)
255
256     stdout, stderr = popen.communicate()
257     if popen.wait() != 0:
258         raise DistutilsPlatformError(stderr.decode("mbcs"))
259
260     stdout = stdout.decode("mbcs")
261     for line in stdout.split("\n"):
262         line = Reg.convert_mbcs(line)
263         if '=' not in line:
264             continue
265         line = line.strip()
266         key, value = line.split('=', 1)
267         key = key.lower()
268         if key in interesting:
269             if value.endswith(os.pathsep):
270                 value = value[:-1]
271             result[key] = removeDuplicates(value)
272
273     if len(result) != len(interesting):
274         raise ValueError(str(list(result.keys())))
275
276     return result
277
278 # More globals
279 VERSION = get_build_version()
280 if VERSION < 8.0:
281     raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
282 # MACROS = MacroExpander(VERSION)
283
284 class 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
317         self.__root = r"Software\Microsoft\VisualStudio"
318         # self.__macros = MACROS
319         self.__paths = []
320         # target platform (.plat_name is consistent with 'bdist')
321         self.plat_name = None
322         self.__arch = None # deprecated name
323         self.initialized = False
324
325     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
336         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:
345             # 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             # take care to only use strings in the environment.
361             self.__paths = vc_env['path'].encode('mbcs').split(os.pathsep)
362             os.environ['lib'] = vc_env['lib'].encode('mbcs')
363             os.environ['include'] = vc_env['include'].encode('mbcs')
364
365             if len(self.__paths) == 0:
366                 raise DistutilsPlatformError("Python was built with %s, "
367                        "and extensions need to be built with the same "
368                        "version of the compiler, but it isn't installed."
369                        % self.__product)
370
371             self.cc = self.find_exe("cl.exe")
372             self.linker = self.find_exe("link.exe")
373             self.lib = self.find_exe("lib.exe")
374             self.rc = self.find_exe("rc.exe")   # resource compiler
375             self.mc = self.find_exe("mc.exe")   # message compiler
376             #self.set_path_env_var('lib')
377             #self.set_path_env_var('include')
378
379         # extend the MSVC path with the current path
380         try:
381             for p in os.environ['path'].split(';'):
382                 self.__paths.append(p)
383         except KeyError:
384             pass
385         self.__paths = normalize_and_reduce_paths(self.__paths)
386         os.environ['path'] = ";".join(self.__paths)
387
388         self.preprocess_options = None
389         if self.__arch == "x86":
390             self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
391                                      '/DNDEBUG']
392             self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
393                                           '/Z7', '/D_DEBUG']
394         else:
395             # Win64
396             self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
397                                      '/DNDEBUG']
398             self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
399                                           '/Z7', '/D_DEBUG']
400
401         self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
402         if self.__version >= 7:
403             self.ldflags_shared_debug = [
404                 '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
405                 ]
406         self.ldflags_static = [ '/nologo']
407
408         self.initialized = True
409
410     # -- Worker methods ------------------------------------------------
411
412     def object_filenames(self,
413                          source_filenames,
414                          strip_dir=0,
415                          output_dir=''):
416         # Copied from ccompiler.py, extended to return .res as 'object'-file
417         # for .rc input file
418         if output_dir is None: output_dir = ''
419         obj_names = []
420         for src_name in source_filenames:
421             (base, ext) = os.path.splitext (src_name)
422             base = os.path.splitdrive(base)[1] # Chop off the drive
423             base = base[os.path.isabs(base):]  # If abs, chop off leading /
424             if ext not in self.src_extensions:
425                 # Better to raise an exception instead of silently continuing
426                 # and later complain about sources and targets having
427                 # different lengths
428                 raise CompileError ("Don't know how to compile %s" % src_name)
429             if strip_dir:
430                 base = os.path.basename (base)
431             if ext in self._rc_extensions:
432                 obj_names.append (os.path.join (output_dir,
433                                                 base + self.res_extension))
434             elif ext in self._mc_extensions:
435                 obj_names.append (os.path.join (output_dir,
436                                                 base + self.res_extension))
437             else:
438                 obj_names.append (os.path.join (output_dir,
439                                                 base + self.obj_extension))
440         return obj_names
441
442
443     def compile(self, sources,
444                 output_dir=None, macros=None, include_dirs=None, debug=0,
445                 extra_preargs=None, extra_postargs=None, depends=None):
446
447         if not self.initialized:
448             self.initialize()
449         compile_info = self._setup_compile(output_dir, macros, include_dirs,
450                                            sources, depends, extra_postargs)
451         macros, objects, extra_postargs, pp_opts, build = compile_info
452
453         compile_opts = extra_preargs or []
454         compile_opts.append ('/c')
455         if debug:
456             compile_opts.extend(self.compile_options_debug)
457         else:
458             compile_opts.extend(self.compile_options)
459
460         for obj in objects:
461             try:
462                 src, ext = build[obj]
463             except KeyError:
464                 continue
465             if debug:
466                 # pass the full pathname to MSVC in debug mode,
467                 # this allows the debugger to find the source file
468                 # without asking the user to browse for it
469                 src = os.path.abspath(src)
470
471             if ext in self._c_extensions:
472                 input_opt = "/Tc" + src
473             elif ext in self._cpp_extensions:
474                 input_opt = "/Tp" + src
475             elif ext in self._rc_extensions:
476                 # compile .RC to .RES file
477                 input_opt = src
478                 output_opt = "/fo" + obj
479                 try:
480                     self.spawn([self.rc] + pp_opts +
481                                [output_opt] + [input_opt])
482                 except DistutilsExecError as msg:
483                     raise CompileError(msg)
484                 continue
485             elif ext in self._mc_extensions:
486                 # Compile .MC to .RC file to .RES file.
487                 #   * '-h dir' specifies the directory for the
488                 #     generated include file
489                 #   * '-r dir' specifies the target directory of the
490                 #     generated RC file and the binary message resource
491                 #     it includes
492                 #
493                 # For now (since there are no options to change this),
494                 # we use the source-directory for the include file and
495                 # the build directory for the RC file and message
496                 # resources. This works at least for win32all.
497                 h_dir = os.path.dirname(src)
498                 rc_dir = os.path.dirname(obj)
499                 try:
500                     # first compile .MC to .RC and .H file
501                     self.spawn([self.mc] +
502                                ['-h', h_dir, '-r', rc_dir] + [src])
503                     base, _ = os.path.splitext (os.path.basename (src))
504                     rc_file = os.path.join (rc_dir, base + '.rc')
505                     # then compile .RC to .RES file
506                     self.spawn([self.rc] +
507                                ["/fo" + obj] + [rc_file])
508
509                 except DistutilsExecError as msg:
510                     raise CompileError(msg)
511                 continue
512             else:
513                 # how to handle this file?
514                 raise CompileError("Don't know how to compile %s to %s"
515                                    % (src, obj))
516
517             output_opt = "/Fo" + obj
518             try:
519                 self.spawn([self.cc] + compile_opts + pp_opts +
520                            [input_opt, output_opt] +
521                            extra_postargs)
522             except DistutilsExecError as msg:
523                 raise CompileError(msg)
524
525         return objects
526
527
528     def create_static_lib(self,
529                           objects,
530                           output_libname,
531                           output_dir=None,
532                           debug=0,
533                           target_lang=None):
534
535         if not self.initialized:
536             self.initialize()
537         (objects, output_dir) = self._fix_object_args(objects, output_dir)
538         output_filename = self.library_filename(output_libname,
539                                                 output_dir=output_dir)
540
541         if self._need_link(objects, output_filename):
542             lib_args = objects + ['/OUT:' + output_filename]
543             if debug:
544                 pass # XXX what goes here?
545             try:
546                 self.spawn([self.lib] + lib_args)
547             except DistutilsExecError as msg:
548                 raise LibError(msg)
549         else:
550             log.debug("skipping %s (up-to-date)", output_filename)
551
552
553     def link(self,
554              target_desc,
555              objects,
556              output_filename,
557              output_dir=None,
558              libraries=None,
559              library_dirs=None,
560              runtime_library_dirs=None,
561              export_symbols=None,
562              debug=0,
563              extra_preargs=None,
564              extra_postargs=None,
565              build_temp=None,
566              target_lang=None):
567
568         if not self.initialized:
569             self.initialize()
570         (objects, output_dir) = self._fix_object_args(objects, output_dir)
571         fixed_args = self._fix_lib_args(libraries, library_dirs,
572                                         runtime_library_dirs)
573         (libraries, library_dirs, runtime_library_dirs) = fixed_args
574
575         if runtime_library_dirs:
576             self.warn ("I don't know what to do with 'runtime_library_dirs': "
577                        + str (runtime_library_dirs))
578
579         lib_opts = gen_lib_options(self,
580                                    library_dirs, runtime_library_dirs,
581                                    libraries)
582         if output_dir is not None:
583             output_filename = os.path.join(output_dir, output_filename)
584
585         if self._need_link(objects, output_filename):
586             if target_desc == CCompiler.EXECUTABLE:
587                 if debug:
588                     ldflags = self.ldflags_shared_debug[1:]
589                 else:
590                     ldflags = self.ldflags_shared[1:]
591             else:
592                 if debug:
593                     ldflags = self.ldflags_shared_debug
594                 else:
595                     ldflags = self.ldflags_shared
596
597             export_opts = []
598             for sym in (export_symbols or []):
599                 export_opts.append("/EXPORT:" + sym)
600
601             ld_args = (ldflags + lib_opts + export_opts +
602                        objects + ['/OUT:' + output_filename])
603
604             # The MSVC linker generates .lib and .exp files, which cannot be
605             # suppressed by any linker switches. The .lib files may even be
606             # needed! Make sure they are generated in the temporary build
607             # directory. Since they have different names for debug and release
608             # builds, they can go into the same directory.
609             build_temp = os.path.dirname(objects[0])
610             if export_symbols is not None:
611                 (dll_name, dll_ext) = os.path.splitext(
612                     os.path.basename(output_filename))
613                 implib_file = os.path.join(
614                     build_temp,
615                     self.library_filename(dll_name))
616                 ld_args.append ('/IMPLIB:' + implib_file)
617
618             # Embedded manifests are recommended - see MSDN article titled
619             # "How to: Embed a Manifest Inside a C/C++ Application"
620             # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
621             # Ask the linker to generate the manifest in the temp dir, so
622             # we can embed it later.
623             temp_manifest = os.path.join(
624                     build_temp,
625                     os.path.basename(output_filename) + ".manifest")
626             ld_args.append('/MANIFESTFILE:' + temp_manifest)
627
628             if extra_preargs:
629                 ld_args[:0] = extra_preargs
630             if extra_postargs:
631                 ld_args.extend(extra_postargs)
632
633             self.mkpath(os.path.dirname(output_filename))
634             try:
635                 self.spawn([self.linker] + ld_args)
636             except DistutilsExecError as msg:
637                 raise LinkError(msg)
638
639             # embed the manifest
640             # XXX - this is somewhat fragile - if mt.exe fails, distutils
641             # will still consider the DLL up-to-date, but it will not have a
642             # manifest.  Maybe we should link to a temp file?  OTOH, that
643             # implies a build environment error that shouldn't go undetected.
644             mfid = 1 if target_desc == CCompiler.EXECUTABLE else 2
645             out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
646             try:
647                 self.spawn(['mt.exe', '-nologo', '-manifest',
648                             temp_manifest, out_arg])
649             except DistutilsExecError as msg:
650                 raise LinkError(msg)
651         else:
652             log.debug("skipping %s (up-to-date)", output_filename)
653
654
655     # -- Miscellaneous methods -----------------------------------------
656     # These are all used by the 'gen_lib_options() function, in
657     # ccompiler.py.
658
659     def library_dir_option(self, dir):
660         return "/LIBPATH:" + dir
661
662     def runtime_library_dir_option(self, dir):
663         raise DistutilsPlatformError(
664               "don't know how to set runtime library search path for MSVC++")
665
666     def library_option(self, lib):
667         return self.library_filename(lib)
668
669
670     def find_library_file(self, dirs, lib, debug=0):
671         # Prefer a debugging library if found (and requested), but deal
672         # with it if we don't have one.
673         if debug:
674             try_names = [lib + "_d", lib]
675         else:
676             try_names = [lib]
677         for dir in dirs:
678             for name in try_names:
679                 libfile = os.path.join(dir, self.library_filename (name))
680                 if os.path.exists(libfile):
681                     return libfile
682         else:
683             # Oops, didn't find it in *any* of 'dirs'
684             return None
685
686     # Helper methods for using the MSVC registry settings
687
688     def find_exe(self, exe):
689         """Return path to an MSVC executable program.
690
691         Tries to find the program in several places: first, one of the
692         MSVC program search paths from the registry; next, the directories
693         in the PATH environment variable.  If any of those work, return an
694         absolute path that is known to exist.  If none of them work, just
695         return the original program name, 'exe'.
696         """
697         for p in self.__paths:
698             fn = os.path.join(os.path.abspath(p), exe)
699             if os.path.isfile(fn):
700                 return fn
701
702         # didn't find it; try existing path
703         for p in os.environ['Path'].split(';'):
704             fn = os.path.join(os.path.abspath(p),exe)
705             if os.path.isfile(fn):
706                 return fn
707
708         return exe