(root)/
Python-3.11.7/
Lib/
distutils/
msvc9compiler.py
       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_lib_options
      23  from distutils import log
      24  from distutils.util import get_platform
      25  
      26  import winreg
      27  
      28  RegOpenKeyEx = winreg.OpenKeyEx
      29  RegEnumKey = winreg.EnumKey
      30  RegEnumValue = winreg.EnumValue
      31  RegError = winreg.error
      32  
      33  HKEYS = (winreg.HKEY_USERS,
      34           winreg.HKEY_CURRENT_USER,
      35           winreg.HKEY_LOCAL_MACHINE,
      36           winreg.HKEY_CLASSES_ROOT)
      37  
      38  NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)
      39  if 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"
      46  else:
      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.)
      54  PLAT_TO_VCVARS = {
      55      'win32' : 'x86',
      56      'win-amd64' : 'amd64',
      57  }
      58  
      59  class ESC[4;38;5;81mReg:
      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  
     121  class ESC[4;38;5;81mMacroExpander:
     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;
     144  extensions must be built with a compiler than can generate compatible binaries.
     145  Visual Studio 2008 was not found on this system. If you have Cygwin installed,
     146  you 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  
     167  def 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  
     192  def 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  
     206  def 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  
     217  def 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  
     252  def 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
     293  VERSION = get_build_version()
     294  if VERSION < 8.0:
     295      raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
     296  # MACROS = MacroExpander(VERSION)
     297  
     298  class ESC[4;38;5;81mMSVCCompiler(ESC[4;38;5;149mCCompiler) :
     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