python (3.11.7)

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