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