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 __revision__ = "$Id$"
     16 
     17 import os
     18 import subprocess
     19 import sys
     20 import re
     21 
     22 from distutils.errors import (DistutilsExecError, DistutilsPlatformError,
     23                               CompileError, LibError, LinkError)
     24 from distutils.ccompiler import CCompiler, gen_lib_options
     25 from distutils import log
     26 from distutils.util import get_platform
     27 
     28 import _winreg
     29 
     30 RegOpenKeyEx = _winreg.OpenKeyEx
     31 RegEnumKey = _winreg.EnumKey
     32 RegEnumValue = _winreg.EnumValue
     33 RegError = _winreg.error
     34 
     35 HKEYS = (_winreg.HKEY_USERS,
     36          _winreg.HKEY_CURRENT_USER,
     37          _winreg.HKEY_LOCAL_MACHINE,
     38          _winreg.HKEY_CLASSES_ROOT)
     39 
     40 NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)
     41 if 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"
     49 else:
     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.)
     58 PLAT_TO_VCVARS = {
     59     'win32' : 'x86',
     60     'win-amd64' : 'amd64',
     61     'win-ia64' : 'ia64',
     62 }
     63 
     64 class 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 
    126 class 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;
    149 extensions must be built with a compiler than can generate compatible binaries.
    150 Visual Studio 2008 was not found on this system. If you have Cygwin installed,
    151 you 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 
    172 def 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 
    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         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 
    263 def 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
    304 VERSION = get_build_version()
    305 if VERSION < 8.0:
    306     raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
    307 # MACROS = MacroExpander(VERSION)
    308 
    309 class 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             self.manifest_setup_ldargs(output_filename, build_temp, ld_args)
    644 
    645             if extra_preargs:
    646                 ld_args[:0] = extra_preargs
    647             if extra_postargs:
    648                 ld_args.extend(extra_postargs)
    649 
    650             self.mkpath(os.path.dirname(output_filename))
    651             try:
    652                 self.spawn([self.linker] + ld_args)
    653             except DistutilsExecError, msg:
    654                 raise LinkError(msg)
    655 
    656             # embed the manifest
    657             # XXX - this is somewhat fragile - if mt.exe fails, distutils
    658             # will still consider the DLL up-to-date, but it will not have a
    659             # manifest.  Maybe we should link to a temp file?  OTOH, that
    660             # implies a build environment error that shouldn't go undetected.
    661             mfinfo = self.manifest_get_embed_info(target_desc, ld_args)
    662             if mfinfo is not None:
    663                 mffilename, mfid = mfinfo
    664                 out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
    665                 try:
    666                     self.spawn(['mt.exe', '-nologo', '-manifest',
    667                                 mffilename, out_arg])
    668                 except DistutilsExecError, msg:
    669                     raise LinkError(msg)
    670         else:
    671             log.debug("skipping %s (up-to-date)", output_filename)
    672 
    673     def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
    674         # If we need a manifest at all, an embedded manifest is recommended.
    675         # See MSDN article titled
    676         # "How to: Embed a Manifest Inside a C/C++ Application"
    677         # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
    678         # Ask the linker to generate the manifest in the temp dir, so
    679         # we can check it, and possibly embed it, later.
    680         temp_manifest = os.path.join(
    681                 build_temp,
    682                 os.path.basename(output_filename) + ".manifest")
    683         ld_args.append('/MANIFESTFILE:' + temp_manifest)
    684 
    685     def manifest_get_embed_info(self, target_desc, ld_args):
    686         # If a manifest should be embedded, return a tuple of
    687         # (manifest_filename, resource_id).  Returns None if no manifest
    688         # should be embedded.  See http://bugs.python.org/issue7833 for why
    689         # we want to avoid any manifest for extension modules if we can)
    690         for arg in ld_args:
    691             if arg.startswith("/MANIFESTFILE:"):
    692                 temp_manifest = arg.split(":", 1)[1]
    693                 break
    694         else:
    695             # no /MANIFESTFILE so nothing to do.
    696             return None
    697         if target_desc == CCompiler.EXECUTABLE:
    698             # by default, executables always get the manifest with the
    699             # CRT referenced.
    700             mfid = 1
    701         else:
    702             # Extension modules try and avoid any manifest if possible.
    703             mfid = 2
    704             temp_manifest = self._remove_visual_c_ref(temp_manifest)
    705         if temp_manifest is None:
    706             return None
    707         return temp_manifest, mfid
    708 
    709     def _remove_visual_c_ref(self, manifest_file):
    710         try:
    711             # Remove references to the Visual C runtime, so they will
    712             # fall through to the Visual C dependency of Python.exe.
    713             # This way, when installed for a restricted user (e.g.
    714             # runtimes are not in WinSxS folder, but in Python's own
    715             # folder), the runtimes do not need to be in every folder
    716             # with .pyd's.
    717             # Returns either the filename of the modified manifest or
    718             # None if no manifest should be embedded.
    719             manifest_f = open(manifest_file)
    720             try:
    721                 manifest_buf = manifest_f.read()
    722             finally:
    723                 manifest_f.close()
    724             pattern = re.compile(
    725                 r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
    726                 r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
    727                 re.DOTALL)
    728             manifest_buf = re.sub(pattern, "", manifest_buf)
    729             pattern = "<dependentAssembly>\s*</dependentAssembly>"
    730             manifest_buf = re.sub(pattern, "", manifest_buf)
    731             # Now see if any other assemblies are referenced - if not, we
    732             # don't want a manifest embedded.
    733             pattern = re.compile(
    734                 r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
    735                 r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL)
    736             if re.search(pattern, manifest_buf) is None:
    737                 return None
    738 
    739             manifest_f = open(manifest_file, 'w')
    740             try:
    741                 manifest_f.write(manifest_buf)
    742                 return manifest_file
    743             finally:
    744                 manifest_f.close()
    745         except IOError:
    746             pass
    747 
    748     # -- Miscellaneous methods -----------------------------------------
    749     # These are all used by the 'gen_lib_options() function, in
    750     # ccompiler.py.
    751 
    752     def library_dir_option(self, dir):
    753         return "/LIBPATH:" + dir
    754 
    755     def runtime_library_dir_option(self, dir):
    756         raise DistutilsPlatformError(
    757               "don't know how to set runtime library search path for MSVC++")
    758 
    759     def library_option(self, lib):
    760         return self.library_filename(lib)
    761 
    762 
    763     def find_library_file(self, dirs, lib, debug=0):
    764         # Prefer a debugging library if found (and requested), but deal
    765         # with it if we don't have one.
    766         if debug:
    767             try_names = [lib + "_d", lib]
    768         else:
    769             try_names = [lib]
    770         for dir in dirs:
    771             for name in try_names:
    772                 libfile = os.path.join(dir, self.library_filename (name))
    773                 if os.path.exists(libfile):
    774                     return libfile
    775         else:
    776             # Oops, didn't find it in *any* of 'dirs'
    777             return None
    778 
    779     # Helper methods for using the MSVC registry settings
    780 
    781     def find_exe(self, exe):
    782         """Return path to an MSVC executable program.
    783 
    784         Tries to find the program in several places: first, one of the
    785         MSVC program search paths from the registry; next, the directories
    786         in the PATH environment variable.  If any of those work, return an
    787         absolute path that is known to exist.  If none of them work, just
    788         return the original program name, 'exe'.
    789         """
    790         for p in self.__paths:
    791             fn = os.path.join(os.path.abspath(p), exe)
    792             if os.path.isfile(fn):
    793                 return fn
    794 
    795         # didn't find it; try existing path
    796         for p in os.environ['Path'].split(';'):
    797             fn = os.path.join(os.path.abspath(p),exe)
    798             if os.path.isfile(fn):
    799                 return fn
    800 
    801         return exe
    802