python (3.11.7)
       1  """distutils.msvccompiler
       2  
       3  Contains MSVCCompiler, an implementation of the abstract CCompiler class
       4  for the Microsoft Visual Studio.
       5  """
       6  
       7  # Written by Perry Stoll
       8  # hacked by Robin Becker and Thomas Heller to do a better job of
       9  #   finding DevStudio (through the registry)
      10  
      11  import sys
      12  import os
      13  import warnings
      14  from distutils.errors import (
      15      DistutilsExecError,
      16      DistutilsPlatformError,
      17      CompileError,
      18      LibError,
      19      LinkError,
      20  )
      21  from distutils.ccompiler import CCompiler, gen_lib_options
      22  from distutils import log
      23  
      24  _can_read_reg = False
      25  try:
      26      import winreg
      27  
      28      _can_read_reg = True
      29      hkey_mod = winreg
      30  
      31      RegOpenKeyEx = winreg.OpenKeyEx
      32      RegEnumKey = winreg.EnumKey
      33      RegEnumValue = winreg.EnumValue
      34      RegError = winreg.error
      35  
      36  except ImportError:
      37      try:
      38          import win32api
      39          import win32con
      40  
      41          _can_read_reg = True
      42          hkey_mod = win32con
      43  
      44          RegOpenKeyEx = win32api.RegOpenKeyEx
      45          RegEnumKey = win32api.RegEnumKey
      46          RegEnumValue = win32api.RegEnumValue
      47          RegError = win32api.error
      48      except ImportError:
      49          log.info(
      50              "Warning: Can't read registry to find the "
      51              "necessary compiler setting\n"
      52              "Make sure that Python modules winreg, "
      53              "win32api or win32con are installed."
      54          )
      55          pass
      56  
      57  if _can_read_reg:
      58      HKEYS = (
      59          hkey_mod.HKEY_USERS,
      60          hkey_mod.HKEY_CURRENT_USER,
      61          hkey_mod.HKEY_LOCAL_MACHINE,
      62          hkey_mod.HKEY_CLASSES_ROOT,
      63      )
      64  
      65  
      66  warnings.warn(
      67      "msvccompiler is deprecated and slated to be removed "
      68      "in the future. Please discontinue use or file an issue "
      69      "with pypa/distutils describing your use case.",
      70      DeprecationWarning,
      71  )
      72  
      73  
      74  def read_keys(base, key):
      75      """Return list of registry keys."""
      76      try:
      77          handle = RegOpenKeyEx(base, key)
      78      except RegError:
      79          return None
      80      L = []
      81      i = 0
      82      while True:
      83          try:
      84              k = RegEnumKey(handle, i)
      85          except RegError:
      86              break
      87          L.append(k)
      88          i += 1
      89      return L
      90  
      91  
      92  def read_values(base, key):
      93      """Return dict of registry keys and values.
      94  
      95      All names are converted to lowercase.
      96      """
      97      try:
      98          handle = RegOpenKeyEx(base, key)
      99      except RegError:
     100          return None
     101      d = {}
     102      i = 0
     103      while True:
     104          try:
     105              name, value, type = RegEnumValue(handle, i)
     106          except RegError:
     107              break
     108          name = name.lower()
     109          d[convert_mbcs(name)] = convert_mbcs(value)
     110          i += 1
     111      return d
     112  
     113  
     114  def convert_mbcs(s):
     115      dec = getattr(s, "decode", None)
     116      if dec is not None:
     117          try:
     118              s = dec("mbcs")
     119          except UnicodeError:
     120              pass
     121      return s
     122  
     123  
     124  class ESC[4;38;5;81mMacroExpander:
     125      def __init__(self, version):
     126          self.macros = {}
     127          self.load_macros(version)
     128  
     129      def set_macro(self, macro, path, key):
     130          for base in HKEYS:
     131              d = read_values(base, path)
     132              if d:
     133                  self.macros["$(%s)" % macro] = d[key]
     134                  break
     135  
     136      def load_macros(self, version):
     137          vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
     138          self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
     139          self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
     140          net = r"Software\Microsoft\.NETFramework"
     141          self.set_macro("FrameworkDir", net, "installroot")
     142          try:
     143              if version > 7.0:
     144                  self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
     145              else:
     146                  self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
     147          except KeyError:
     148              raise DistutilsPlatformError(
     149                  """Python was built with Visual Studio 2003;
     150  extensions must be built with a compiler than can generate compatible binaries.
     151  Visual Studio 2003 was not found on this system. If you have Cygwin installed,
     152  you can try compiling with MingW32, by passing "-c mingw32" to setup.py."""
     153              )
     154  
     155          p = r"Software\Microsoft\NET Framework Setup\Product"
     156          for base in HKEYS:
     157              try:
     158                  h = RegOpenKeyEx(base, p)
     159              except RegError:
     160                  continue
     161              key = RegEnumKey(h, 0)
     162              d = read_values(base, r"{}\{}".format(p, key))
     163              self.macros["$(FrameworkVersion)"] = d["version"]
     164  
     165      def sub(self, s):
     166          for k, v in self.macros.items():
     167              s = s.replace(k, v)
     168          return s
     169  
     170  
     171  def get_build_version():
     172      """Return the version of MSVC that was used to build Python.
     173  
     174      For Python 2.3 and up, the version number is included in
     175      sys.version.  For earlier versions, assume the compiler is MSVC 6.
     176      """
     177      prefix = "MSC v."
     178      i = sys.version.find(prefix)
     179      if i == -1:
     180          return 6
     181      i = i + len(prefix)
     182      s, rest = sys.version[i:].split(" ", 1)
     183      majorVersion = int(s[:-2]) - 6
     184      if majorVersion >= 13:
     185          # v13 was skipped and should be v14
     186          majorVersion += 1
     187      minorVersion = int(s[2:3]) / 10.0
     188      # I don't think paths are affected by minor version in version 6
     189      if majorVersion == 6:
     190          minorVersion = 0
     191      if majorVersion >= 6:
     192          return majorVersion + minorVersion
     193      # else we don't know what version of the compiler this is
     194      return None
     195  
     196  
     197  def get_build_architecture():
     198      """Return the processor architecture.
     199  
     200      Possible results are "Intel" or "AMD64".
     201      """
     202  
     203      prefix = " bit ("
     204      i = sys.version.find(prefix)
     205      if i == -1:
     206          return "Intel"
     207      j = sys.version.find(")", i)
     208      return sys.version[i + len(prefix) : j]
     209  
     210  
     211  def normalize_and_reduce_paths(paths):
     212      """Return a list of normalized paths with duplicates removed.
     213  
     214      The current order of paths is maintained.
     215      """
     216      # Paths are normalized so things like:  /a and /a/ aren't both preserved.
     217      reduced_paths = []
     218      for p in paths:
     219          np = os.path.normpath(p)
     220          # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
     221          if np not in reduced_paths:
     222              reduced_paths.append(np)
     223      return reduced_paths
     224  
     225  
     226  class ESC[4;38;5;81mMSVCCompiler(ESC[4;38;5;149mCCompiler):
     227      """Concrete class that implements an interface to Microsoft Visual C++,
     228      as defined by the CCompiler abstract class."""
     229  
     230      compiler_type = 'msvc'
     231  
     232      # Just set this so CCompiler's constructor doesn't barf.  We currently
     233      # don't use the 'set_executables()' bureaucracy provided by CCompiler,
     234      # as it really isn't necessary for this sort of single-compiler class.
     235      # Would be nice to have a consistent interface with UnixCCompiler,
     236      # though, so it's worth thinking about.
     237      executables = {}
     238  
     239      # Private class data (need to distinguish C from C++ source for compiler)
     240      _c_extensions = ['.c']
     241      _cpp_extensions = ['.cc', '.cpp', '.cxx']
     242      _rc_extensions = ['.rc']
     243      _mc_extensions = ['.mc']
     244  
     245      # Needed for the filename generation methods provided by the
     246      # base class, CCompiler.
     247      src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions
     248      res_extension = '.res'
     249      obj_extension = '.obj'
     250      static_lib_extension = '.lib'
     251      shared_lib_extension = '.dll'
     252      static_lib_format = shared_lib_format = '%s%s'
     253      exe_extension = '.exe'
     254  
     255      def __init__(self, verbose=0, dry_run=0, force=0):
     256          super().__init__(verbose, dry_run, force)
     257          self.__version = get_build_version()
     258          self.__arch = get_build_architecture()
     259          if self.__arch == "Intel":
     260              # x86
     261              if self.__version >= 7:
     262                  self.__root = r"Software\Microsoft\VisualStudio"
     263                  self.__macros = MacroExpander(self.__version)
     264              else:
     265                  self.__root = r"Software\Microsoft\Devstudio"
     266              self.__product = "Visual Studio version %s" % self.__version
     267          else:
     268              # Win64. Assume this was built with the platform SDK
     269              self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
     270  
     271          self.initialized = False
     272  
     273      def initialize(self):
     274          self.__paths = []
     275          if (
     276              "DISTUTILS_USE_SDK" in os.environ
     277              and "MSSdk" in os.environ
     278              and self.find_exe("cl.exe")
     279          ):
     280              # Assume that the SDK set up everything alright; don't try to be
     281              # smarter
     282              self.cc = "cl.exe"
     283              self.linker = "link.exe"
     284              self.lib = "lib.exe"
     285              self.rc = "rc.exe"
     286              self.mc = "mc.exe"
     287          else:
     288              self.__paths = self.get_msvc_paths("path")
     289  
     290              if len(self.__paths) == 0:
     291                  raise DistutilsPlatformError(
     292                      "Python was built with %s, "
     293                      "and extensions need to be built with the same "
     294                      "version of the compiler, but it isn't installed." % self.__product
     295                  )
     296  
     297              self.cc = self.find_exe("cl.exe")
     298              self.linker = self.find_exe("link.exe")
     299              self.lib = self.find_exe("lib.exe")
     300              self.rc = self.find_exe("rc.exe")  # resource compiler
     301              self.mc = self.find_exe("mc.exe")  # message compiler
     302              self.set_path_env_var('lib')
     303              self.set_path_env_var('include')
     304  
     305          # extend the MSVC path with the current path
     306          try:
     307              for p in os.environ['path'].split(';'):
     308                  self.__paths.append(p)
     309          except KeyError:
     310              pass
     311          self.__paths = normalize_and_reduce_paths(self.__paths)
     312          os.environ['path'] = ";".join(self.__paths)
     313  
     314          self.preprocess_options = None
     315          if self.__arch == "Intel":
     316              self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GX', '/DNDEBUG']
     317              self.compile_options_debug = [
     318                  '/nologo',
     319                  '/Od',
     320                  '/MDd',
     321                  '/W3',
     322                  '/GX',
     323                  '/Z7',
     324                  '/D_DEBUG',
     325              ]
     326          else:
     327              # Win64
     328              self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG']
     329              self.compile_options_debug = [
     330                  '/nologo',
     331                  '/Od',
     332                  '/MDd',
     333                  '/W3',
     334                  '/GS-',
     335                  '/Z7',
     336                  '/D_DEBUG',
     337              ]
     338  
     339          self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
     340          if self.__version >= 7:
     341              self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG']
     342          else:
     343              self.ldflags_shared_debug = [
     344                  '/DLL',
     345                  '/nologo',
     346                  '/INCREMENTAL:no',
     347                  '/pdb:None',
     348                  '/DEBUG',
     349              ]
     350          self.ldflags_static = ['/nologo']
     351  
     352          self.initialized = True
     353  
     354      # -- Worker methods ------------------------------------------------
     355  
     356      def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
     357          # Copied from ccompiler.py, extended to return .res as 'object'-file
     358          # for .rc input file
     359          if output_dir is None:
     360              output_dir = ''
     361          obj_names = []
     362          for src_name in source_filenames:
     363              (base, ext) = os.path.splitext(src_name)
     364              base = os.path.splitdrive(base)[1]  # Chop off the drive
     365              base = base[os.path.isabs(base) :]  # If abs, chop off leading /
     366              if ext not in self.src_extensions:
     367                  # Better to raise an exception instead of silently continuing
     368                  # and later complain about sources and targets having
     369                  # different lengths
     370                  raise CompileError("Don't know how to compile %s" % src_name)
     371              if strip_dir:
     372                  base = os.path.basename(base)
     373              if ext in self._rc_extensions:
     374                  obj_names.append(os.path.join(output_dir, base + self.res_extension))
     375              elif ext in self._mc_extensions:
     376                  obj_names.append(os.path.join(output_dir, base + self.res_extension))
     377              else:
     378                  obj_names.append(os.path.join(output_dir, base + self.obj_extension))
     379          return obj_names
     380  
     381      def compile(  # noqa: C901
     382          self,
     383          sources,
     384          output_dir=None,
     385          macros=None,
     386          include_dirs=None,
     387          debug=0,
     388          extra_preargs=None,
     389          extra_postargs=None,
     390          depends=None,
     391      ):
     392  
     393          if not self.initialized:
     394              self.initialize()
     395          compile_info = self._setup_compile(
     396              output_dir, macros, include_dirs, sources, depends, extra_postargs
     397          )
     398          macros, objects, extra_postargs, pp_opts, build = compile_info
     399  
     400          compile_opts = extra_preargs or []
     401          compile_opts.append('/c')
     402          if debug:
     403              compile_opts.extend(self.compile_options_debug)
     404          else:
     405              compile_opts.extend(self.compile_options)
     406  
     407          for obj in objects:
     408              try:
     409                  src, ext = build[obj]
     410              except KeyError:
     411                  continue
     412              if debug:
     413                  # pass the full pathname to MSVC in debug mode,
     414                  # this allows the debugger to find the source file
     415                  # without asking the user to browse for it
     416                  src = os.path.abspath(src)
     417  
     418              if ext in self._c_extensions:
     419                  input_opt = "/Tc" + src
     420              elif ext in self._cpp_extensions:
     421                  input_opt = "/Tp" + src
     422              elif ext in self._rc_extensions:
     423                  # compile .RC to .RES file
     424                  input_opt = src
     425                  output_opt = "/fo" + obj
     426                  try:
     427                      self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt])
     428                  except DistutilsExecError as msg:
     429                      raise CompileError(msg)
     430                  continue
     431              elif ext in self._mc_extensions:
     432                  # Compile .MC to .RC file to .RES file.
     433                  #   * '-h dir' specifies the directory for the
     434                  #     generated include file
     435                  #   * '-r dir' specifies the target directory of the
     436                  #     generated RC file and the binary message resource
     437                  #     it includes
     438                  #
     439                  # For now (since there are no options to change this),
     440                  # we use the source-directory for the include file and
     441                  # the build directory for the RC file and message
     442                  # resources. This works at least for win32all.
     443                  h_dir = os.path.dirname(src)
     444                  rc_dir = os.path.dirname(obj)
     445                  try:
     446                      # first compile .MC to .RC and .H file
     447                      self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src])
     448                      base, _ = os.path.splitext(os.path.basename(src))
     449                      rc_file = os.path.join(rc_dir, base + '.rc')
     450                      # then compile .RC to .RES file
     451                      self.spawn([self.rc] + ["/fo" + obj] + [rc_file])
     452  
     453                  except DistutilsExecError as msg:
     454                      raise CompileError(msg)
     455                  continue
     456              else:
     457                  # how to handle this file?
     458                  raise CompileError(
     459                      "Don't know how to compile {} to {}".format(src, obj)
     460                  )
     461  
     462              output_opt = "/Fo" + obj
     463              try:
     464                  self.spawn(
     465                      [self.cc]
     466                      + compile_opts
     467                      + pp_opts
     468                      + [input_opt, output_opt]
     469                      + extra_postargs
     470                  )
     471              except DistutilsExecError as msg:
     472                  raise CompileError(msg)
     473  
     474          return objects
     475  
     476      def create_static_lib(
     477          self, objects, output_libname, output_dir=None, debug=0, target_lang=None
     478      ):
     479  
     480          if not self.initialized:
     481              self.initialize()
     482          (objects, output_dir) = self._fix_object_args(objects, output_dir)
     483          output_filename = self.library_filename(output_libname, output_dir=output_dir)
     484  
     485          if self._need_link(objects, output_filename):
     486              lib_args = objects + ['/OUT:' + output_filename]
     487              if debug:
     488                  pass  # XXX what goes here?
     489              try:
     490                  self.spawn([self.lib] + lib_args)
     491              except DistutilsExecError as msg:
     492                  raise LibError(msg)
     493          else:
     494              log.debug("skipping %s (up-to-date)", output_filename)
     495  
     496      def link(  # noqa: C901
     497          self,
     498          target_desc,
     499          objects,
     500          output_filename,
     501          output_dir=None,
     502          libraries=None,
     503          library_dirs=None,
     504          runtime_library_dirs=None,
     505          export_symbols=None,
     506          debug=0,
     507          extra_preargs=None,
     508          extra_postargs=None,
     509          build_temp=None,
     510          target_lang=None,
     511      ):
     512  
     513          if not self.initialized:
     514              self.initialize()
     515          (objects, output_dir) = self._fix_object_args(objects, output_dir)
     516          fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
     517          (libraries, library_dirs, runtime_library_dirs) = fixed_args
     518  
     519          if runtime_library_dirs:
     520              self.warn(
     521                  "I don't know what to do with 'runtime_library_dirs': "
     522                  + str(runtime_library_dirs)
     523              )
     524  
     525          lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
     526          if output_dir is not None:
     527              output_filename = os.path.join(output_dir, output_filename)
     528  
     529          if self._need_link(objects, output_filename):
     530              if target_desc == CCompiler.EXECUTABLE:
     531                  if debug:
     532                      ldflags = self.ldflags_shared_debug[1:]
     533                  else:
     534                      ldflags = self.ldflags_shared[1:]
     535              else:
     536                  if debug:
     537                      ldflags = self.ldflags_shared_debug
     538                  else:
     539                      ldflags = self.ldflags_shared
     540  
     541              export_opts = []
     542              for sym in export_symbols or []:
     543                  export_opts.append("/EXPORT:" + sym)
     544  
     545              ld_args = (
     546                  ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]
     547              )
     548  
     549              # The MSVC linker generates .lib and .exp files, which cannot be
     550              # suppressed by any linker switches. The .lib files may even be
     551              # needed! Make sure they are generated in the temporary build
     552              # directory. Since they have different names for debug and release
     553              # builds, they can go into the same directory.
     554              if export_symbols is not None:
     555                  (dll_name, dll_ext) = os.path.splitext(
     556                      os.path.basename(output_filename)
     557                  )
     558                  implib_file = os.path.join(
     559                      os.path.dirname(objects[0]), self.library_filename(dll_name)
     560                  )
     561                  ld_args.append('/IMPLIB:' + implib_file)
     562  
     563              if extra_preargs:
     564                  ld_args[:0] = extra_preargs
     565              if extra_postargs:
     566                  ld_args.extend(extra_postargs)
     567  
     568              self.mkpath(os.path.dirname(output_filename))
     569              try:
     570                  self.spawn([self.linker] + ld_args)
     571              except DistutilsExecError as msg:
     572                  raise LinkError(msg)
     573  
     574          else:
     575              log.debug("skipping %s (up-to-date)", output_filename)
     576  
     577      # -- Miscellaneous methods -----------------------------------------
     578      # These are all used by the 'gen_lib_options() function, in
     579      # ccompiler.py.
     580  
     581      def library_dir_option(self, dir):
     582          return "/LIBPATH:" + dir
     583  
     584      def runtime_library_dir_option(self, dir):
     585          raise DistutilsPlatformError(
     586              "don't know how to set runtime library search path for MSVC++"
     587          )
     588  
     589      def library_option(self, lib):
     590          return self.library_filename(lib)
     591  
     592      def find_library_file(self, dirs, lib, debug=0):
     593          # Prefer a debugging library if found (and requested), but deal
     594          # with it if we don't have one.
     595          if debug:
     596              try_names = [lib + "_d", lib]
     597          else:
     598              try_names = [lib]
     599          for dir in dirs:
     600              for name in try_names:
     601                  libfile = os.path.join(dir, self.library_filename(name))
     602                  if os.path.exists(libfile):
     603                      return libfile
     604          else:
     605              # Oops, didn't find it in *any* of 'dirs'
     606              return None
     607  
     608      # Helper methods for using the MSVC registry settings
     609  
     610      def find_exe(self, exe):
     611          """Return path to an MSVC executable program.
     612  
     613          Tries to find the program in several places: first, one of the
     614          MSVC program search paths from the registry; next, the directories
     615          in the PATH environment variable.  If any of those work, return an
     616          absolute path that is known to exist.  If none of them work, just
     617          return the original program name, 'exe'.
     618          """
     619          for p in self.__paths:
     620              fn = os.path.join(os.path.abspath(p), exe)
     621              if os.path.isfile(fn):
     622                  return fn
     623  
     624          # didn't find it; try existing path
     625          for p in os.environ['Path'].split(';'):
     626              fn = os.path.join(os.path.abspath(p), exe)
     627              if os.path.isfile(fn):
     628                  return fn
     629  
     630          return exe
     631  
     632      def get_msvc_paths(self, path, platform='x86'):
     633          """Get a list of devstudio directories (include, lib or path).
     634  
     635          Return a list of strings.  The list will be empty if unable to
     636          access the registry or appropriate registry keys not found.
     637          """
     638          if not _can_read_reg:
     639              return []
     640  
     641          path = path + " dirs"
     642          if self.__version >= 7:
     643              key = r"{}\{:0.1f}\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories".format(
     644                  self.__root,
     645                  self.__version,
     646              )
     647          else:
     648              key = (
     649                  r"%s\6.0\Build System\Components\Platforms"
     650                  r"\Win32 (%s)\Directories" % (self.__root, platform)
     651              )
     652  
     653          for base in HKEYS:
     654              d = read_values(base, key)
     655              if d:
     656                  if self.__version >= 7:
     657                      return self.__macros.sub(d[path]).split(";")
     658                  else:
     659                      return d[path].split(";")
     660          # MSVC 6 seems to create the registry entries we need only when
     661          # the GUI is run.
     662          if self.__version == 6:
     663              for base in HKEYS:
     664                  if read_values(base, r"%s\6.0" % self.__root) is not None:
     665                      self.warn(
     666                          "It seems you have Visual Studio 6 installed, "
     667                          "but the expected registry settings are not present.\n"
     668                          "You must at least run the Visual Studio GUI once "
     669                          "so that these entries are created."
     670                      )
     671                      break
     672          return []
     673  
     674      def set_path_env_var(self, name):
     675          """Set environment variable 'name' to an MSVC path type value.
     676  
     677          This is equivalent to a SET command prior to execution of spawned
     678          commands.
     679          """
     680  
     681          if name == "lib":
     682              p = self.get_msvc_paths("library")
     683          else:
     684              p = self.get_msvc_paths(name)
     685          if p:
     686              os.environ[name] = ';'.join(p)
     687  
     688  
     689  if get_build_version() >= 8.0:
     690      log.debug("Importing new compiler from distutils.msvc9compiler")
     691      OldMSVCCompiler = MSVCCompiler
     692      from distutils.msvc9compiler import MSVCCompiler
     693  
     694      # get_build_architecture not really relevant now we support cross-compile
     695      from distutils.msvc9compiler import MacroExpander  # noqa: F811