(root)/
Python-3.12.0/
Tools/
c-analyzer/
distutils/
_msvccompiler.py
       1  """distutils._msvccompiler
       2  
       3  Contains MSVCCompiler, an implementation of the abstract CCompiler class
       4  for Microsoft Visual Studio 2015.
       5  
       6  The module is compatible with VS 2015 and later. You can find legacy support
       7  for older versions in distutils.msvc9compiler and 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 VS 2005 and VS 2008 by Christian Heimes
      14  # ported to VS 2015 by Steve Dower
      15  
      16  import os
      17  import subprocess
      18  import winreg
      19  
      20  from distutils.errors import DistutilsPlatformError
      21  from distutils.ccompiler import CCompiler
      22  from distutils import log
      23  
      24  from itertools import count
      25  
      26  def _find_vc2015():
      27      try:
      28          key = winreg.OpenKeyEx(
      29              winreg.HKEY_LOCAL_MACHINE,
      30              r"Software\Microsoft\VisualStudio\SxS\VC7",
      31              access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
      32          )
      33      except OSError:
      34          log.debug("Visual C++ is not registered")
      35          return None, None
      36  
      37      best_version = 0
      38      best_dir = None
      39      with key:
      40          for i in count():
      41              try:
      42                  v, vc_dir, vt = winreg.EnumValue(key, i)
      43              except OSError:
      44                  break
      45              if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
      46                  try:
      47                      version = int(float(v))
      48                  except (ValueError, TypeError):
      49                      continue
      50                  if version >= 14 and version > best_version:
      51                      best_version, best_dir = version, vc_dir
      52      return best_version, best_dir
      53  
      54  def _find_vc2017():
      55      """Returns "15, path" based on the result of invoking vswhere.exe
      56      If no install is found, returns "None, None"
      57  
      58      The version is returned to avoid unnecessarily changing the function
      59      result. It may be ignored when the path is not None.
      60  
      61      If vswhere.exe is not available, by definition, VS 2017 is not
      62      installed.
      63      """
      64      root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
      65      if not root:
      66          return None, None
      67  
      68      try:
      69          path = subprocess.check_output([
      70              os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
      71              "-latest",
      72              "-prerelease",
      73              "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
      74              "-property", "installationPath",
      75              "-products", "*",
      76          ], encoding="mbcs", errors="strict").strip()
      77      except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
      78          return None, None
      79  
      80      path = os.path.join(path, "VC", "Auxiliary", "Build")
      81      if os.path.isdir(path):
      82          return 15, path
      83  
      84      return None, None
      85  
      86  PLAT_SPEC_TO_RUNTIME = {
      87      'x86' : 'x86',
      88      'x86_amd64' : 'x64',
      89      'x86_arm' : 'arm',
      90      'x86_arm64' : 'arm64'
      91  }
      92  
      93  def _find_vcvarsall(plat_spec):
      94      # bpo-38597: Removed vcruntime return value
      95      _, best_dir = _find_vc2017()
      96  
      97      if not best_dir:
      98          best_version, best_dir = _find_vc2015()
      99  
     100      if not best_dir:
     101          log.debug("No suitable Visual C++ version found")
     102          return None, None
     103  
     104      vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
     105      if not os.path.isfile(vcvarsall):
     106          log.debug("%s cannot be found", vcvarsall)
     107          return None, None
     108  
     109      return vcvarsall, None
     110  
     111  def _get_vc_env(plat_spec):
     112      if os.getenv("DISTUTILS_USE_SDK"):
     113          return {
     114              key.lower(): value
     115              for key, value in os.environ.items()
     116          }
     117  
     118      vcvarsall, _ = _find_vcvarsall(plat_spec)
     119      if not vcvarsall:
     120          raise DistutilsPlatformError("Unable to find vcvarsall.bat")
     121  
     122      try:
     123          out = subprocess.check_output(
     124              'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
     125              stderr=subprocess.STDOUT,
     126          ).decode('utf-16le', errors='replace')
     127      except subprocess.CalledProcessError as exc:
     128          log.error(exc.output)
     129          raise DistutilsPlatformError("Error executing {}"
     130                  .format(exc.cmd))
     131  
     132      env = {
     133          key.lower(): value
     134          for key, _, value in
     135          (line.partition('=') for line in out.splitlines())
     136          if key and value
     137      }
     138  
     139      return env
     140  
     141  def _find_exe(exe, paths=None):
     142      """Return path to an MSVC executable program.
     143  
     144      Tries to find the program in several places: first, one of the
     145      MSVC program search paths from the registry; next, the directories
     146      in the PATH environment variable.  If any of those work, return an
     147      absolute path that is known to exist.  If none of them work, just
     148      return the original program name, 'exe'.
     149      """
     150      if not paths:
     151          paths = os.getenv('path').split(os.pathsep)
     152      for p in paths:
     153          fn = os.path.join(os.path.abspath(p), exe)
     154          if os.path.isfile(fn):
     155              return fn
     156      return exe
     157  
     158  # A map keyed by get_platform() return values to values accepted by
     159  # 'vcvarsall.bat'. Always cross-compile from x86 to work with the
     160  # lighter-weight MSVC installs that do not include native 64-bit tools.
     161  PLAT_TO_VCVARS = {
     162      'win32' : 'x86',
     163      'win-amd64' : 'x86_amd64',
     164      'win-arm32' : 'x86_arm',
     165      'win-arm64' : 'x86_arm64'
     166  }
     167  
     168  class ESC[4;38;5;81mMSVCCompiler(ESC[4;38;5;149mCCompiler) :
     169      """Concrete class that implements an interface to Microsoft Visual C++,
     170         as defined by the CCompiler abstract class."""
     171  
     172      compiler_type = 'msvc'
     173  
     174      # Just set this so CCompiler's constructor doesn't barf.  We currently
     175      # don't use the 'set_executables()' bureaucracy provided by CCompiler,
     176      # as it really isn't necessary for this sort of single-compiler class.
     177      # Would be nice to have a consistent interface with UnixCCompiler,
     178      # though, so it's worth thinking about.
     179      executables = {}
     180  
     181      # Private class data (need to distinguish C from C++ source for compiler)
     182      _c_extensions = ['.c']
     183      _cpp_extensions = ['.cc', '.cpp', '.cxx']
     184      _rc_extensions = ['.rc']
     185      _mc_extensions = ['.mc']
     186  
     187      # Needed for the filename generation methods provided by the
     188      # base class, CCompiler.
     189      src_extensions = (_c_extensions + _cpp_extensions +
     190                        _rc_extensions + _mc_extensions)
     191      res_extension = '.res'
     192      obj_extension = '.obj'
     193      static_lib_extension = '.lib'
     194      shared_lib_extension = '.dll'
     195      static_lib_format = shared_lib_format = '%s%s'
     196      exe_extension = '.exe'
     197  
     198  
     199      def __init__(self, verbose=0, dry_run=0, force=0):
     200          CCompiler.__init__ (self, verbose, dry_run, force)
     201          # target platform (.plat_name is consistent with 'bdist')
     202          self.plat_name = None
     203          self.initialized = False