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