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