(root)/
Python-3.12.0/
Tools/
c-analyzer/
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 DistutilsPlatformError
      21  from distutils.ccompiler import CCompiler
      22  from distutils import log
      23  
      24  import winreg
      25  
      26  RegOpenKeyEx = winreg.OpenKeyEx
      27  RegEnumKey = winreg.EnumKey
      28  RegEnumValue = winreg.EnumValue
      29  RegError = winreg.error
      30  
      31  HKEYS = (winreg.HKEY_USERS,
      32           winreg.HKEY_CURRENT_USER,
      33           winreg.HKEY_LOCAL_MACHINE,
      34           winreg.HKEY_CLASSES_ROOT)
      35  
      36  NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)
      37  if NATIVE_WIN64:
      38      # Visual C++ is a 32-bit application, so we need to look in
      39      # the corresponding registry branch, if we're running a
      40      # 64-bit Python on Win64
      41      VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"
      42      WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"
      43      NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"
      44  else:
      45      VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
      46      WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
      47      NET_BASE = r"Software\Microsoft\.NETFramework"
      48  
      49  # A map keyed by get_platform() return values to values accepted by
      50  # 'vcvarsall.bat'.  Note a cross-compile may combine these (eg, 'x86_amd64' is
      51  # the param to cross-compile on x86 targeting amd64.)
      52  PLAT_TO_VCVARS = {
      53      'win32' : 'x86',
      54      'win-amd64' : 'amd64',
      55  }
      56  
      57  class ESC[4;38;5;81mReg:
      58      """Helper class to read values from the registry
      59      """
      60  
      61      def get_value(cls, path, key):
      62          for base in HKEYS:
      63              d = cls.read_values(base, path)
      64              if d and key in d:
      65                  return d[key]
      66          raise KeyError(key)
      67      get_value = classmethod(get_value)
      68  
      69      def read_keys(cls, base, key):
      70          """Return list of registry keys."""
      71          try:
      72              handle = RegOpenKeyEx(base, key)
      73          except RegError:
      74              return None
      75          L = []
      76          i = 0
      77          while True:
      78              try:
      79                  k = RegEnumKey(handle, i)
      80              except RegError:
      81                  break
      82              L.append(k)
      83              i += 1
      84          return L
      85      read_keys = classmethod(read_keys)
      86  
      87      def read_values(cls, base, key):
      88          """Return dict of registry keys and values.
      89  
      90          All names are converted to lowercase.
      91          """
      92          try:
      93              handle = RegOpenKeyEx(base, key)
      94          except RegError:
      95              return None
      96          d = {}
      97          i = 0
      98          while True:
      99              try:
     100                  name, value, type = RegEnumValue(handle, i)
     101              except RegError:
     102                  break
     103              name = name.lower()
     104              d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
     105              i += 1
     106          return d
     107      read_values = classmethod(read_values)
     108  
     109      def convert_mbcs(s):
     110          dec = getattr(s, "decode", None)
     111          if dec is not None:
     112              try:
     113                  s = dec("mbcs")
     114              except UnicodeError:
     115                  pass
     116          return s
     117      convert_mbcs = staticmethod(convert_mbcs)
     118  
     119  class ESC[4;38;5;81mMacroExpander:
     120  
     121      def __init__(self, version):
     122          self.macros = {}
     123          self.vsbase = VS_BASE % version
     124          self.load_macros(version)
     125  
     126      def set_macro(self, macro, path, key):
     127          self.macros["$(%s)" % macro] = Reg.get_value(path, key)
     128  
     129      def load_macros(self, version):
     130          self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
     131          self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
     132          self.set_macro("FrameworkDir", NET_BASE, "installroot")
     133          try:
     134              if version >= 8.0:
     135                  self.set_macro("FrameworkSDKDir", NET_BASE,
     136                                 "sdkinstallrootv2.0")
     137              else:
     138                  raise KeyError("sdkinstallrootv2.0")
     139          except KeyError:
     140              raise DistutilsPlatformError(
     141              """Python was built with Visual Studio 2008;
     142  extensions must be built with a compiler than can generate compatible binaries.
     143  Visual Studio 2008 was not found on this system. If you have Cygwin installed,
     144  you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
     145  
     146          if version >= 9.0:
     147              self.set_macro("FrameworkVersion", self.vsbase, "clr version")
     148              self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
     149          else:
     150              p = r"Software\Microsoft\NET Framework Setup\Product"
     151              for base in HKEYS:
     152                  try:
     153                      h = RegOpenKeyEx(base, p)
     154                  except RegError:
     155                      continue
     156                  key = RegEnumKey(h, 0)
     157                  d = Reg.get_value(base, r"%s\%s" % (p, key))
     158                  self.macros["$(FrameworkVersion)"] = d["version"]
     159  
     160      def sub(self, s):
     161          for k, v in self.macros.items():
     162              s = s.replace(k, v)
     163          return s
     164  
     165  def get_build_version():
     166      """Return the version of MSVC that was used to build Python.
     167  
     168      For Python 2.3 and up, the version number is included in
     169      sys.version.  For earlier versions, assume the compiler is MSVC 6.
     170      """
     171      prefix = "MSC v."
     172      i = sys.version.find(prefix)
     173      if i == -1:
     174          return 6
     175      i = i + len(prefix)
     176      s, rest = sys.version[i:].split(" ", 1)
     177      majorVersion = int(s[:-2]) - 6
     178      if majorVersion >= 13:
     179          # v13 was skipped and should be v14
     180          majorVersion += 1
     181      minorVersion = int(s[2:3]) / 10.0
     182      # I don't think paths are affected by minor version in version 6
     183      if majorVersion == 6:
     184          minorVersion = 0
     185      if majorVersion >= 6:
     186          return majorVersion + minorVersion
     187      # else we don't know what version of the compiler this is
     188      return None
     189  
     190  def normalize_and_reduce_paths(paths):
     191      """Return a list of normalized paths with duplicates removed.
     192  
     193      The current order of paths is maintained.
     194      """
     195      # Paths are normalized so things like:  /a and /a/ aren't both preserved.
     196      reduced_paths = []
     197      for p in paths:
     198          np = os.path.normpath(p)
     199          # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
     200          if np not in reduced_paths:
     201              reduced_paths.append(np)
     202      return reduced_paths
     203  
     204  def removeDuplicates(variable):
     205      """Remove duplicate values of an environment variable.
     206      """
     207      oldList = variable.split(os.pathsep)
     208      newList = []
     209      for i in oldList:
     210          if i not in newList:
     211              newList.append(i)
     212      newVariable = os.pathsep.join(newList)
     213      return newVariable
     214  
     215  def find_vcvarsall(version):
     216      """Find the vcvarsall.bat file
     217  
     218      At first it tries to find the productdir of VS 2008 in the registry. If
     219      that fails it falls back to the VS90COMNTOOLS env var.
     220      """
     221      vsbase = VS_BASE % version
     222      try:
     223          productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
     224                                     "productdir")
     225      except KeyError:
     226          log.debug("Unable to find productdir in registry")
     227          productdir = None
     228  
     229      if not productdir or not os.path.isdir(productdir):
     230          toolskey = "VS%0.f0COMNTOOLS" % version
     231          toolsdir = os.environ.get(toolskey, None)
     232  
     233          if toolsdir and os.path.isdir(toolsdir):
     234              productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
     235              productdir = os.path.abspath(productdir)
     236              if not os.path.isdir(productdir):
     237                  log.debug("%s is not a valid directory" % productdir)
     238                  return None
     239          else:
     240              log.debug("Env var %s is not set or invalid" % toolskey)
     241      if not productdir:
     242          log.debug("No productdir found")
     243          return None
     244      vcvarsall = os.path.join(productdir, "vcvarsall.bat")
     245      if os.path.isfile(vcvarsall):
     246          return vcvarsall
     247      log.debug("Unable to find vcvarsall.bat")
     248      return None
     249  
     250  def query_vcvarsall(version, arch="x86"):
     251      """Launch vcvarsall.bat and read the settings from its environment
     252      """
     253      vcvarsall = find_vcvarsall(version)
     254      interesting = {"include", "lib", "libpath", "path"}
     255      result = {}
     256  
     257      if vcvarsall is None:
     258          raise DistutilsPlatformError("Unable to find vcvarsall.bat")
     259      log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
     260      popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
     261                               stdout=subprocess.PIPE,
     262                               stderr=subprocess.PIPE)
     263      try:
     264          stdout, stderr = popen.communicate()
     265          if popen.wait() != 0:
     266              raise DistutilsPlatformError(stderr.decode("mbcs"))
     267  
     268          stdout = stdout.decode("mbcs")
     269          for line in stdout.split("\n"):
     270              line = Reg.convert_mbcs(line)
     271              if '=' not in line:
     272                  continue
     273              line = line.strip()
     274              key, value = line.split('=', 1)
     275              key = key.lower()
     276              if key in interesting:
     277                  if value.endswith(os.pathsep):
     278                      value = value[:-1]
     279                  result[key] = removeDuplicates(value)
     280  
     281      finally:
     282          popen.stdout.close()
     283          popen.stderr.close()
     284  
     285      if len(result) != len(interesting):
     286          raise ValueError(str(list(result.keys())))
     287  
     288      return result
     289  
     290  # More globals
     291  VERSION = get_build_version()
     292  if VERSION < 8.0:
     293      raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
     294  # MACROS = MacroExpander(VERSION)
     295  
     296  class ESC[4;38;5;81mMSVCCompiler(ESC[4;38;5;149mCCompiler) :
     297      """Concrete class that implements an interface to Microsoft Visual C++,
     298         as defined by the CCompiler abstract class."""
     299  
     300      compiler_type = 'msvc'
     301  
     302      # Just set this so CCompiler's constructor doesn't barf.  We currently
     303      # don't use the 'set_executables()' bureaucracy provided by CCompiler,
     304      # as it really isn't necessary for this sort of single-compiler class.
     305      # Would be nice to have a consistent interface with UnixCCompiler,
     306      # though, so it's worth thinking about.
     307      executables = {}
     308  
     309      # Private class data (need to distinguish C from C++ source for compiler)
     310      _c_extensions = ['.c']
     311      _cpp_extensions = ['.cc', '.cpp', '.cxx']
     312      _rc_extensions = ['.rc']
     313      _mc_extensions = ['.mc']
     314  
     315      # Needed for the filename generation methods provided by the
     316      # base class, CCompiler.
     317      src_extensions = (_c_extensions + _cpp_extensions +
     318                        _rc_extensions + _mc_extensions)
     319      res_extension = '.res'
     320      obj_extension = '.obj'
     321      static_lib_extension = '.lib'
     322      shared_lib_extension = '.dll'
     323      static_lib_format = shared_lib_format = '%s%s'
     324      exe_extension = '.exe'
     325  
     326      def __init__(self, verbose=0, dry_run=0, force=0):
     327          CCompiler.__init__ (self, verbose, dry_run, force)
     328          self.__version = VERSION
     329          self.__root = r"Software\Microsoft\VisualStudio"
     330          # self.__macros = MACROS
     331          self.__paths = []
     332          # target platform (.plat_name is consistent with 'bdist')
     333          self.plat_name = None
     334          self.__arch = None # deprecated name
     335          self.initialized = False
     336  
     337      # -- Worker methods ------------------------------------------------
     338  
     339      def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
     340          # If we need a manifest at all, an embedded manifest is recommended.
     341          # See MSDN article titled
     342          # "How to: Embed a Manifest Inside a C/C++ Application"
     343          # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
     344          # Ask the linker to generate the manifest in the temp dir, so
     345          # we can check it, and possibly embed it, later.
     346          temp_manifest = os.path.join(
     347                  build_temp,
     348                  os.path.basename(output_filename) + ".manifest")
     349          ld_args.append('/MANIFESTFILE:' + temp_manifest)
     350  
     351      def manifest_get_embed_info(self, target_desc, ld_args):
     352          # If a manifest should be embedded, return a tuple of
     353          # (manifest_filename, resource_id).  Returns None if no manifest
     354          # should be embedded.  See http://bugs.python.org/issue7833 for why
     355          # we want to avoid any manifest for extension modules if we can.
     356          for arg in ld_args:
     357              if arg.startswith("/MANIFESTFILE:"):
     358                  temp_manifest = arg.split(":", 1)[1]
     359                  break
     360          else:
     361              # no /MANIFESTFILE so nothing to do.
     362              return None
     363          if target_desc == CCompiler.EXECUTABLE:
     364              # by default, executables always get the manifest with the
     365              # CRT referenced.
     366              mfid = 1
     367          else:
     368              # Extension modules try and avoid any manifest if possible.
     369              mfid = 2
     370              temp_manifest = self._remove_visual_c_ref(temp_manifest)
     371          if temp_manifest is None:
     372              return None
     373          return temp_manifest, mfid
     374  
     375      def _remove_visual_c_ref(self, manifest_file):
     376          try:
     377              # Remove references to the Visual C runtime, so they will
     378              # fall through to the Visual C dependency of Python.exe.
     379              # This way, when installed for a restricted user (e.g.
     380              # runtimes are not in WinSxS folder, but in Python's own
     381              # folder), the runtimes do not need to be in every folder
     382              # with .pyd's.
     383              # Returns either the filename of the modified manifest or
     384              # None if no manifest should be embedded.
     385              manifest_f = open(manifest_file)
     386              try:
     387                  manifest_buf = manifest_f.read()
     388              finally:
     389                  manifest_f.close()
     390              pattern = re.compile(
     391                  r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
     392                  r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
     393                  re.DOTALL)
     394              manifest_buf = re.sub(pattern, "", manifest_buf)
     395              pattern = r"<dependentAssembly>\s*</dependentAssembly>"
     396              manifest_buf = re.sub(pattern, "", manifest_buf)
     397              # Now see if any other assemblies are referenced - if not, we
     398              # don't want a manifest embedded.
     399              pattern = re.compile(
     400                  r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
     401                  r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL)
     402              if re.search(pattern, manifest_buf) is None:
     403                  return None
     404  
     405              manifest_f = open(manifest_file, 'w')
     406              try:
     407                  manifest_f.write(manifest_buf)
     408                  return manifest_file
     409              finally:
     410                  manifest_f.close()
     411          except OSError:
     412              pass
     413  
     414      # -- Miscellaneous methods -----------------------------------------
     415  
     416      # Helper methods for using the MSVC registry settings
     417  
     418      def find_exe(self, exe):
     419          """Return path to an MSVC executable program.
     420  
     421          Tries to find the program in several places: first, one of the
     422          MSVC program search paths from the registry; next, the directories
     423          in the PATH environment variable.  If any of those work, return an
     424          absolute path that is known to exist.  If none of them work, just
     425          return the original program name, 'exe'.
     426          """
     427          for p in self.__paths:
     428              fn = os.path.join(os.path.abspath(p), exe)
     429              if os.path.isfile(fn):
     430                  return fn
     431  
     432          # didn't find it; try existing path
     433          for p in os.environ['Path'].split(';'):
     434              fn = os.path.join(os.path.abspath(p),exe)
     435              if os.path.isfile(fn):
     436                  return fn
     437  
     438          return exe