(root)/
Python-3.11.7/
Lib/
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 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  from itertools import count
      27  
      28  def _find_vc2015():
      29      try:
      30          key = winreg.OpenKeyEx(
      31              winreg.HKEY_LOCAL_MACHINE,
      32              r"Software\Microsoft\VisualStudio\SxS\VC7",
      33              access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
      34          )
      35      except OSError:
      36          log.debug("Visual C++ is not registered")
      37          return None, None
      38  
      39      best_version = 0
      40      best_dir = None
      41      with key:
      42          for i in count():
      43              try:
      44                  v, vc_dir, vt = winreg.EnumValue(key, i)
      45              except OSError:
      46                  break
      47              if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
      48                  try:
      49                      version = int(float(v))
      50                  except (ValueError, TypeError):
      51                      continue
      52                  if version >= 14 and version > best_version:
      53                      best_version, best_dir = version, vc_dir
      54      return best_version, best_dir
      55  
      56  def _find_vc2017():
      57      """Returns "15, path" based on the result of invoking vswhere.exe
      58      If no install is found, returns "None, None"
      59  
      60      The version is returned to avoid unnecessarily changing the function
      61      result. It may be ignored when the path is not None.
      62  
      63      If vswhere.exe is not available, by definition, VS 2017 is not
      64      installed.
      65      """
      66      root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
      67      if not root:
      68          return None, None
      69  
      70      try:
      71          path = subprocess.check_output([
      72              os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
      73              "-latest",
      74              "-prerelease",
      75              "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
      76              "-property", "installationPath",
      77              "-products", "*",
      78          ], encoding="mbcs", errors="strict").strip()
      79      except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
      80          return None, None
      81  
      82      path = os.path.join(path, "VC", "Auxiliary", "Build")
      83      if os.path.isdir(path):
      84          return 15, path
      85  
      86      return None, None
      87  
      88  PLAT_SPEC_TO_RUNTIME = {
      89      'x86' : 'x86',
      90      'x86_amd64' : 'x64',
      91      'x86_arm' : 'arm',
      92      'x86_arm64' : 'arm64'
      93  }
      94  
      95  def _find_vcvarsall(plat_spec):
      96      # bpo-38597: Removed vcruntime return value
      97      _, best_dir = _find_vc2017()
      98  
      99      if not best_dir:
     100          best_version, best_dir = _find_vc2015()
     101  
     102      if not best_dir:
     103          log.debug("No suitable Visual C++ version found")
     104          return None, None
     105  
     106      vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
     107      if not os.path.isfile(vcvarsall):
     108          log.debug("%s cannot be found", vcvarsall)
     109          return None, None
     110  
     111      return vcvarsall, None
     112  
     113  def _get_vc_env(plat_spec):
     114      if os.getenv("DISTUTILS_USE_SDK"):
     115          return {
     116              key.lower(): value
     117              for key, value in os.environ.items()
     118          }
     119  
     120      vcvarsall, _ = _find_vcvarsall(plat_spec)
     121      if not vcvarsall:
     122          raise DistutilsPlatformError("Unable to find vcvarsall.bat")
     123  
     124      try:
     125          out = subprocess.check_output(
     126              'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
     127              stderr=subprocess.STDOUT,
     128          ).decode('utf-16le', errors='replace')
     129      except subprocess.CalledProcessError as exc:
     130          log.error(exc.output)
     131          raise DistutilsPlatformError("Error executing {}"
     132                  .format(exc.cmd))
     133  
     134      env = {
     135          key.lower(): value
     136          for key, _, value in
     137          (line.partition('=') for line in out.splitlines())
     138          if key and value
     139      }
     140  
     141      return env
     142  
     143  def _find_exe(exe, paths=None):
     144      """Return path to an MSVC executable program.
     145  
     146      Tries to find the program in several places: first, one of the
     147      MSVC program search paths from the registry; next, the directories
     148      in the PATH environment variable.  If any of those work, return an
     149      absolute path that is known to exist.  If none of them work, just
     150      return the original program name, 'exe'.
     151      """
     152      if not paths:
     153          paths = os.getenv('path').split(os.pathsep)
     154      for p in paths:
     155          fn = os.path.join(os.path.abspath(p), exe)
     156          if os.path.isfile(fn):
     157              return fn
     158      return exe
     159  
     160  # A map keyed by get_platform() return values to values accepted by
     161  # 'vcvarsall.bat'. Always cross-compile from x86 to work with the
     162  # lighter-weight MSVC installs that do not include native 64-bit tools.
     163  PLAT_TO_VCVARS = {
     164      'win32' : 'x86',
     165      'win-amd64' : 'x86_amd64',
     166      'win-arm32' : 'x86_arm',
     167      'win-arm64' : 'x86_arm64'
     168  }
     169  
     170  class ESC[4;38;5;81mMSVCCompiler(ESC[4;38;5;149mCCompiler) :
     171      """Concrete class that implements an interface to Microsoft Visual C++,
     172         as defined by the CCompiler abstract class."""
     173  
     174      compiler_type = 'msvc'
     175  
     176      # Just set this so CCompiler's constructor doesn't barf.  We currently
     177      # don't use the 'set_executables()' bureaucracy provided by CCompiler,
     178      # as it really isn't necessary for this sort of single-compiler class.
     179      # Would be nice to have a consistent interface with UnixCCompiler,
     180      # though, so it's worth thinking about.
     181      executables = {}
     182  
     183      # Private class data (need to distinguish C from C++ source for compiler)
     184      _c_extensions = ['.c']
     185      _cpp_extensions = ['.cc', '.cpp', '.cxx']
     186      _rc_extensions = ['.rc']
     187      _mc_extensions = ['.mc']
     188  
     189      # Needed for the filename generation methods provided by the
     190      # base class, CCompiler.
     191      src_extensions = (_c_extensions + _cpp_extensions +
     192                        _rc_extensions + _mc_extensions)
     193      res_extension = '.res'
     194      obj_extension = '.obj'
     195      static_lib_extension = '.lib'
     196      shared_lib_extension = '.dll'
     197      static_lib_format = shared_lib_format = '%s%s'
     198      exe_extension = '.exe'
     199  
     200  
     201      def __init__(self, verbose=0, dry_run=0, force=0):
     202          CCompiler.__init__ (self, verbose, dry_run, force)
     203          # target platform (.plat_name is consistent with 'bdist')
     204          self.plat_name = None
     205          self.initialized = False
     206  
     207      def initialize(self, plat_name=None):
     208          # multi-init means we would need to check platform same each time...
     209          assert not self.initialized, "don't init multiple times"
     210          if plat_name is None:
     211              plat_name = get_platform()
     212          # sanity check for platforms to prevent obscure errors later.
     213          if plat_name not in PLAT_TO_VCVARS:
     214              raise DistutilsPlatformError("--plat-name must be one of {}"
     215                                           .format(tuple(PLAT_TO_VCVARS)))
     216  
     217          # Get the vcvarsall.bat spec for the requested platform.
     218          plat_spec = PLAT_TO_VCVARS[plat_name]
     219  
     220          vc_env = _get_vc_env(plat_spec)
     221          if not vc_env:
     222              raise DistutilsPlatformError("Unable to find a compatible "
     223                  "Visual Studio installation.")
     224  
     225          self._paths = vc_env.get('path', '')
     226          paths = self._paths.split(os.pathsep)
     227          self.cc = _find_exe("cl.exe", paths)
     228          self.linker = _find_exe("link.exe", paths)
     229          self.lib = _find_exe("lib.exe", paths)
     230          self.rc = _find_exe("rc.exe", paths)   # resource compiler
     231          self.mc = _find_exe("mc.exe", paths)   # message compiler
     232          self.mt = _find_exe("mt.exe", paths)   # message compiler
     233  
     234          for dir in vc_env.get('include', '').split(os.pathsep):
     235              if dir:
     236                  self.add_include_dir(dir.rstrip(os.sep))
     237  
     238          for dir in vc_env.get('lib', '').split(os.pathsep):
     239              if dir:
     240                  self.add_library_dir(dir.rstrip(os.sep))
     241  
     242          self.preprocess_options = None
     243          # bpo-38597: Always compile with dynamic linking
     244          # Future releases of Python 3.x will include all past
     245          # versions of vcruntime*.dll for compatibility.
     246          self.compile_options = [
     247              '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD'
     248          ]
     249  
     250          self.compile_options_debug = [
     251              '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'
     252          ]
     253  
     254          ldflags = [
     255              '/nologo', '/INCREMENTAL:NO', '/LTCG'
     256          ]
     257  
     258          ldflags_debug = [
     259              '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL'
     260          ]
     261  
     262          self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
     263          self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
     264          self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
     265          self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
     266          self.ldflags_static = [*ldflags]
     267          self.ldflags_static_debug = [*ldflags_debug]
     268  
     269          self._ldflags = {
     270              (CCompiler.EXECUTABLE, None): self.ldflags_exe,
     271              (CCompiler.EXECUTABLE, False): self.ldflags_exe,
     272              (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug,
     273              (CCompiler.SHARED_OBJECT, None): self.ldflags_shared,
     274              (CCompiler.SHARED_OBJECT, False): self.ldflags_shared,
     275              (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
     276              (CCompiler.SHARED_LIBRARY, None): self.ldflags_static,
     277              (CCompiler.SHARED_LIBRARY, False): self.ldflags_static,
     278              (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
     279          }
     280  
     281          self.initialized = True
     282  
     283      # -- Worker methods ------------------------------------------------
     284  
     285      def object_filenames(self,
     286                           source_filenames,
     287                           strip_dir=0,
     288                           output_dir=''):
     289          ext_map = {
     290              **{ext: self.obj_extension for ext in self.src_extensions},
     291              **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions},
     292          }
     293  
     294          output_dir = output_dir or ''
     295  
     296          def make_out_path(p):
     297              base, ext = os.path.splitext(p)
     298              if strip_dir:
     299                  base = os.path.basename(base)
     300              else:
     301                  _, base = os.path.splitdrive(base)
     302                  if base.startswith((os.path.sep, os.path.altsep)):
     303                      base = base[1:]
     304              try:
     305                  # XXX: This may produce absurdly long paths. We should check
     306                  # the length of the result and trim base until we fit within
     307                  # 260 characters.
     308                  return os.path.join(output_dir, base + ext_map[ext])
     309              except LookupError:
     310                  # Better to raise an exception instead of silently continuing
     311                  # and later complain about sources and targets having
     312                  # different lengths
     313                  raise CompileError("Don't know how to compile {}".format(p))
     314  
     315          return list(map(make_out_path, source_filenames))
     316  
     317  
     318      def compile(self, sources,
     319                  output_dir=None, macros=None, include_dirs=None, debug=0,
     320                  extra_preargs=None, extra_postargs=None, depends=None):
     321  
     322          if not self.initialized:
     323              self.initialize()
     324          compile_info = self._setup_compile(output_dir, macros, include_dirs,
     325                                             sources, depends, extra_postargs)
     326          macros, objects, extra_postargs, pp_opts, build = compile_info
     327  
     328          compile_opts = extra_preargs or []
     329          compile_opts.append('/c')
     330          if debug:
     331              compile_opts.extend(self.compile_options_debug)
     332          else:
     333              compile_opts.extend(self.compile_options)
     334  
     335  
     336          add_cpp_opts = False
     337  
     338          for obj in objects:
     339              try:
     340                  src, ext = build[obj]
     341              except KeyError:
     342                  continue
     343              if debug:
     344                  # pass the full pathname to MSVC in debug mode,
     345                  # this allows the debugger to find the source file
     346                  # without asking the user to browse for it
     347                  src = os.path.abspath(src)
     348  
     349              if ext in self._c_extensions:
     350                  input_opt = "/Tc" + src
     351              elif ext in self._cpp_extensions:
     352                  input_opt = "/Tp" + src
     353                  add_cpp_opts = True
     354              elif ext in self._rc_extensions:
     355                  # compile .RC to .RES file
     356                  input_opt = src
     357                  output_opt = "/fo" + obj
     358                  try:
     359                      self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
     360                  except DistutilsExecError as msg:
     361                      raise CompileError(msg)
     362                  continue
     363              elif ext in self._mc_extensions:
     364                  # Compile .MC to .RC file to .RES file.
     365                  #   * '-h dir' specifies the directory for the
     366                  #     generated include file
     367                  #   * '-r dir' specifies the target directory of the
     368                  #     generated RC file and the binary message resource
     369                  #     it includes
     370                  #
     371                  # For now (since there are no options to change this),
     372                  # we use the source-directory for the include file and
     373                  # the build directory for the RC file and message
     374                  # resources. This works at least for win32all.
     375                  h_dir = os.path.dirname(src)
     376                  rc_dir = os.path.dirname(obj)
     377                  try:
     378                      # first compile .MC to .RC and .H file
     379                      self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
     380                      base, _ = os.path.splitext(os.path.basename (src))
     381                      rc_file = os.path.join(rc_dir, base + '.rc')
     382                      # then compile .RC to .RES file
     383                      self.spawn([self.rc, "/fo" + obj, rc_file])
     384  
     385                  except DistutilsExecError as msg:
     386                      raise CompileError(msg)
     387                  continue
     388              else:
     389                  # how to handle this file?
     390                  raise CompileError("Don't know how to compile {} to {}"
     391                                     .format(src, obj))
     392  
     393              args = [self.cc] + compile_opts + pp_opts
     394              if add_cpp_opts:
     395                  args.append('/EHsc')
     396              args.append(input_opt)
     397              args.append("/Fo" + obj)
     398              args.extend(extra_postargs)
     399  
     400              try:
     401                  self.spawn(args)
     402              except DistutilsExecError as msg:
     403                  raise CompileError(msg)
     404  
     405          return objects
     406  
     407  
     408      def create_static_lib(self,
     409                            objects,
     410                            output_libname,
     411                            output_dir=None,
     412                            debug=0,
     413                            target_lang=None):
     414  
     415          if not self.initialized:
     416              self.initialize()
     417          objects, output_dir = self._fix_object_args(objects, output_dir)
     418          output_filename = self.library_filename(output_libname,
     419                                                  output_dir=output_dir)
     420  
     421          if self._need_link(objects, output_filename):
     422              lib_args = objects + ['/OUT:' + output_filename]
     423              if debug:
     424                  pass # XXX what goes here?
     425              try:
     426                  log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
     427                  self.spawn([self.lib] + lib_args)
     428              except DistutilsExecError as msg:
     429                  raise LibError(msg)
     430          else:
     431              log.debug("skipping %s (up-to-date)", output_filename)
     432  
     433  
     434      def link(self,
     435               target_desc,
     436               objects,
     437               output_filename,
     438               output_dir=None,
     439               libraries=None,
     440               library_dirs=None,
     441               runtime_library_dirs=None,
     442               export_symbols=None,
     443               debug=0,
     444               extra_preargs=None,
     445               extra_postargs=None,
     446               build_temp=None,
     447               target_lang=None):
     448  
     449          if not self.initialized:
     450              self.initialize()
     451          objects, output_dir = self._fix_object_args(objects, output_dir)
     452          fixed_args = self._fix_lib_args(libraries, library_dirs,
     453                                          runtime_library_dirs)
     454          libraries, library_dirs, runtime_library_dirs = fixed_args
     455  
     456          if runtime_library_dirs:
     457              self.warn("I don't know what to do with 'runtime_library_dirs': "
     458                         + str(runtime_library_dirs))
     459  
     460          lib_opts = gen_lib_options(self,
     461                                     library_dirs, runtime_library_dirs,
     462                                     libraries)
     463          if output_dir is not None:
     464              output_filename = os.path.join(output_dir, output_filename)
     465  
     466          if self._need_link(objects, output_filename):
     467              ldflags = self._ldflags[target_desc, debug]
     468  
     469              export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
     470  
     471              ld_args = (ldflags + lib_opts + export_opts +
     472                         objects + ['/OUT:' + output_filename])
     473  
     474              # The MSVC linker generates .lib and .exp files, which cannot be
     475              # suppressed by any linker switches. The .lib files may even be
     476              # needed! Make sure they are generated in the temporary build
     477              # directory. Since they have different names for debug and release
     478              # builds, they can go into the same directory.
     479              build_temp = os.path.dirname(objects[0])
     480              if export_symbols is not None:
     481                  (dll_name, dll_ext) = os.path.splitext(
     482                      os.path.basename(output_filename))
     483                  implib_file = os.path.join(
     484                      build_temp,
     485                      self.library_filename(dll_name))
     486                  ld_args.append ('/IMPLIB:' + implib_file)
     487  
     488              if extra_preargs:
     489                  ld_args[:0] = extra_preargs
     490              if extra_postargs:
     491                  ld_args.extend(extra_postargs)
     492  
     493              output_dir = os.path.dirname(os.path.abspath(output_filename))
     494              self.mkpath(output_dir)
     495              try:
     496                  log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
     497                  self.spawn([self.linker] + ld_args)
     498              except DistutilsExecError as msg:
     499                  raise LinkError(msg)
     500          else:
     501              log.debug("skipping %s (up-to-date)", output_filename)
     502  
     503      def spawn(self, cmd):
     504          old_path = os.getenv('path')
     505          try:
     506              os.environ['path'] = self._paths
     507              return super().spawn(cmd)
     508          finally:
     509              os.environ['path'] = old_path
     510  
     511      # -- Miscellaneous methods -----------------------------------------
     512      # These are all used by the 'gen_lib_options() function, in
     513      # ccompiler.py.
     514  
     515      def library_dir_option(self, dir):
     516          return "/LIBPATH:" + dir
     517  
     518      def runtime_library_dir_option(self, dir):
     519          raise DistutilsPlatformError(
     520                "don't know how to set runtime library search path for MSVC")
     521  
     522      def library_option(self, lib):
     523          return self.library_filename(lib)
     524  
     525      def find_library_file(self, dirs, lib, debug=0):
     526          # Prefer a debugging library if found (and requested), but deal
     527          # with it if we don't have one.
     528          if debug:
     529              try_names = [lib + "_d", lib]
     530          else:
     531              try_names = [lib]
     532          for dir in dirs:
     533              for name in try_names:
     534                  libfile = os.path.join(dir, self.library_filename(name))
     535                  if os.path.isfile(libfile):
     536                      return libfile
     537          else:
     538              # Oops, didn't find it in *any* of 'dirs'
     539              return None