PageRenderTime 513ms CodeModel.GetById 141ms app.highlight 227ms RepoModel.GetById 135ms app.codeStats 1ms

/Lib/distutils/msvc9compiler.py

http://unladen-swallow.googlecode.com/
Python | 708 lines | 631 code | 30 blank | 47 comment | 31 complexity | 9888b9375bbd2befc9963cfa201512a3 MD5 | raw file
  1"""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: msvc9compiler.py 68082 2008-12-30 23:06:46Z tarek.ziade $"
 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
 25from distutils.util import get_platform
 26
 27import _winreg
 28
 29RegOpenKeyEx = _winreg.OpenKeyEx
 30RegEnumKey = _winreg.EnumKey
 31RegEnumValue = _winreg.EnumValue
 32RegError = _winreg.error
 33
 34HKEYS = (_winreg.HKEY_USERS,
 35         _winreg.HKEY_CURRENT_USER,
 36         _winreg.HKEY_LOCAL_MACHINE,
 37         _winreg.HKEY_CLASSES_ROOT)
 38
 39VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
 40WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
 41NET_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.)
 46PLAT_TO_VCVARS = {
 47    'win32' : 'x86',
 48    'win-amd64' : 'amd64',
 49    'win-ia64' : 'ia64',
 50}
 51
 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
182def 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
196def 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
207def 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 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
279VERSION = get_build_version()
280if VERSION < 8.0:
281    raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
282# MACROS = MacroExpander(VERSION)
283
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
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