python (3.11.7)

(root)/
lib/
python3.11/
site-packages/
setuptools/
_distutils/
bcppcompiler.py
       1  """distutils.bcppcompiler
       2  
       3  Contains BorlandCCompiler, an implementation of the abstract CCompiler class
       4  for the Borland C++ compiler.
       5  """
       6  
       7  # This implementation by Lyle Johnson, based on the original msvccompiler.py
       8  # module and using the directions originally published by Gordon Williams.
       9  
      10  # XXX looks like there's a LOT of overlap between these two classes:
      11  # someone should sit down and factor out the common code as
      12  # WindowsCCompiler!  --GPW
      13  
      14  
      15  import os
      16  import warnings
      17  
      18  from distutils.errors import (
      19      DistutilsExecError,
      20      CompileError,
      21      LibError,
      22      LinkError,
      23      UnknownFileError,
      24  )
      25  from distutils.ccompiler import CCompiler, gen_preprocess_options
      26  from distutils.file_util import write_file
      27  from distutils.dep_util import newer
      28  from distutils import log
      29  
      30  
      31  warnings.warn(
      32      "bcppcompiler is deprecated and slated to be removed "
      33      "in the future. Please discontinue use or file an issue "
      34      "with pypa/distutils describing your use case.",
      35      DeprecationWarning,
      36  )
      37  
      38  
      39  class ESC[4;38;5;81mBCPPCompiler(ESC[4;38;5;149mCCompiler):
      40      """Concrete class that implements an interface to the Borland C/C++
      41      compiler, as defined by the CCompiler abstract class.
      42      """
      43  
      44      compiler_type = 'bcpp'
      45  
      46      # Just set this so CCompiler's constructor doesn't barf.  We currently
      47      # don't use the 'set_executables()' bureaucracy provided by CCompiler,
      48      # as it really isn't necessary for this sort of single-compiler class.
      49      # Would be nice to have a consistent interface with UnixCCompiler,
      50      # though, so it's worth thinking about.
      51      executables = {}
      52  
      53      # Private class data (need to distinguish C from C++ source for compiler)
      54      _c_extensions = ['.c']
      55      _cpp_extensions = ['.cc', '.cpp', '.cxx']
      56  
      57      # Needed for the filename generation methods provided by the
      58      # base class, CCompiler.
      59      src_extensions = _c_extensions + _cpp_extensions
      60      obj_extension = '.obj'
      61      static_lib_extension = '.lib'
      62      shared_lib_extension = '.dll'
      63      static_lib_format = shared_lib_format = '%s%s'
      64      exe_extension = '.exe'
      65  
      66      def __init__(self, verbose=0, dry_run=0, force=0):
      67  
      68          super().__init__(verbose, dry_run, force)
      69  
      70          # These executables are assumed to all be in the path.
      71          # Borland doesn't seem to use any special registry settings to
      72          # indicate their installation locations.
      73  
      74          self.cc = "bcc32.exe"
      75          self.linker = "ilink32.exe"
      76          self.lib = "tlib.exe"
      77  
      78          self.preprocess_options = None
      79          self.compile_options = ['/tWM', '/O2', '/q', '/g0']
      80          self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0']
      81  
      82          self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x']
      83          self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x']
      84          self.ldflags_static = []
      85          self.ldflags_exe = ['/Gn', '/q', '/x']
      86          self.ldflags_exe_debug = ['/Gn', '/q', '/x', '/r']
      87  
      88      # -- Worker methods ------------------------------------------------
      89  
      90      def compile(  # noqa: C901
      91          self,
      92          sources,
      93          output_dir=None,
      94          macros=None,
      95          include_dirs=None,
      96          debug=0,
      97          extra_preargs=None,
      98          extra_postargs=None,
      99          depends=None,
     100      ):
     101  
     102          macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
     103              output_dir, macros, include_dirs, sources, depends, extra_postargs
     104          )
     105          compile_opts = extra_preargs or []
     106          compile_opts.append('-c')
     107          if debug:
     108              compile_opts.extend(self.compile_options_debug)
     109          else:
     110              compile_opts.extend(self.compile_options)
     111  
     112          for obj in objects:
     113              try:
     114                  src, ext = build[obj]
     115              except KeyError:
     116                  continue
     117              # XXX why do the normpath here?
     118              src = os.path.normpath(src)
     119              obj = os.path.normpath(obj)
     120              # XXX _setup_compile() did a mkpath() too but before the normpath.
     121              # Is it possible to skip the normpath?
     122              self.mkpath(os.path.dirname(obj))
     123  
     124              if ext == '.res':
     125                  # This is already a binary file -- skip it.
     126                  continue  # the 'for' loop
     127              if ext == '.rc':
     128                  # This needs to be compiled to a .res file -- do it now.
     129                  try:
     130                      self.spawn(["brcc32", "-fo", obj, src])
     131                  except DistutilsExecError as msg:
     132                      raise CompileError(msg)
     133                  continue  # the 'for' loop
     134  
     135              # The next two are both for the real compiler.
     136              if ext in self._c_extensions:
     137                  input_opt = ""
     138              elif ext in self._cpp_extensions:
     139                  input_opt = "-P"
     140              else:
     141                  # Unknown file type -- no extra options.  The compiler
     142                  # will probably fail, but let it just in case this is a
     143                  # file the compiler recognizes even if we don't.
     144                  input_opt = ""
     145  
     146              output_opt = "-o" + obj
     147  
     148              # Compiler command line syntax is: "bcc32 [options] file(s)".
     149              # Note that the source file names must appear at the end of
     150              # the command line.
     151              try:
     152                  self.spawn(
     153                      [self.cc]
     154                      + compile_opts
     155                      + pp_opts
     156                      + [input_opt, output_opt]
     157                      + extra_postargs
     158                      + [src]
     159                  )
     160              except DistutilsExecError as msg:
     161                  raise CompileError(msg)
     162  
     163          return objects
     164  
     165      # compile ()
     166  
     167      def create_static_lib(
     168          self, objects, output_libname, output_dir=None, debug=0, target_lang=None
     169      ):
     170  
     171          (objects, output_dir) = self._fix_object_args(objects, output_dir)
     172          output_filename = self.library_filename(output_libname, output_dir=output_dir)
     173  
     174          if self._need_link(objects, output_filename):
     175              lib_args = [output_filename, '/u'] + objects
     176              if debug:
     177                  pass  # XXX what goes here?
     178              try:
     179                  self.spawn([self.lib] + lib_args)
     180              except DistutilsExecError as msg:
     181                  raise LibError(msg)
     182          else:
     183              log.debug("skipping %s (up-to-date)", output_filename)
     184  
     185      # create_static_lib ()
     186  
     187      def link(  # noqa: C901
     188          self,
     189          target_desc,
     190          objects,
     191          output_filename,
     192          output_dir=None,
     193          libraries=None,
     194          library_dirs=None,
     195          runtime_library_dirs=None,
     196          export_symbols=None,
     197          debug=0,
     198          extra_preargs=None,
     199          extra_postargs=None,
     200          build_temp=None,
     201          target_lang=None,
     202      ):
     203  
     204          # XXX this ignores 'build_temp'!  should follow the lead of
     205          # msvccompiler.py
     206  
     207          (objects, output_dir) = self._fix_object_args(objects, output_dir)
     208          (libraries, library_dirs, runtime_library_dirs) = self._fix_lib_args(
     209              libraries, library_dirs, runtime_library_dirs
     210          )
     211  
     212          if runtime_library_dirs:
     213              log.warn(
     214                  "I don't know what to do with 'runtime_library_dirs': %s",
     215                  str(runtime_library_dirs),
     216              )
     217  
     218          if output_dir is not None:
     219              output_filename = os.path.join(output_dir, output_filename)
     220  
     221          if self._need_link(objects, output_filename):
     222  
     223              # Figure out linker args based on type of target.
     224              if target_desc == CCompiler.EXECUTABLE:
     225                  startup_obj = 'c0w32'
     226                  if debug:
     227                      ld_args = self.ldflags_exe_debug[:]
     228                  else:
     229                      ld_args = self.ldflags_exe[:]
     230              else:
     231                  startup_obj = 'c0d32'
     232                  if debug:
     233                      ld_args = self.ldflags_shared_debug[:]
     234                  else:
     235                      ld_args = self.ldflags_shared[:]
     236  
     237              # Create a temporary exports file for use by the linker
     238              if export_symbols is None:
     239                  def_file = ''
     240              else:
     241                  head, tail = os.path.split(output_filename)
     242                  modname, ext = os.path.splitext(tail)
     243                  temp_dir = os.path.dirname(objects[0])  # preserve tree structure
     244                  def_file = os.path.join(temp_dir, '%s.def' % modname)
     245                  contents = ['EXPORTS']
     246                  for sym in export_symbols or []:
     247                      contents.append('  {}=_{}'.format(sym, sym))
     248                  self.execute(write_file, (def_file, contents), "writing %s" % def_file)
     249  
     250              # Borland C++ has problems with '/' in paths
     251              objects2 = map(os.path.normpath, objects)
     252              # split objects in .obj and .res files
     253              # Borland C++ needs them at different positions in the command line
     254              objects = [startup_obj]
     255              resources = []
     256              for file in objects2:
     257                  (base, ext) = os.path.splitext(os.path.normcase(file))
     258                  if ext == '.res':
     259                      resources.append(file)
     260                  else:
     261                      objects.append(file)
     262  
     263              for ell in library_dirs:
     264                  ld_args.append("/L%s" % os.path.normpath(ell))
     265              ld_args.append("/L.")  # we sometimes use relative paths
     266  
     267              # list of object files
     268              ld_args.extend(objects)
     269  
     270              # XXX the command-line syntax for Borland C++ is a bit wonky;
     271              # certain filenames are jammed together in one big string, but
     272              # comma-delimited.  This doesn't mesh too well with the
     273              # Unix-centric attitude (with a DOS/Windows quoting hack) of
     274              # 'spawn()', so constructing the argument list is a bit
     275              # awkward.  Note that doing the obvious thing and jamming all
     276              # the filenames and commas into one argument would be wrong,
     277              # because 'spawn()' would quote any filenames with spaces in
     278              # them.  Arghghh!.  Apparently it works fine as coded...
     279  
     280              # name of dll/exe file
     281              ld_args.extend([',', output_filename])
     282              # no map file and start libraries
     283              ld_args.append(',,')
     284  
     285              for lib in libraries:
     286                  # see if we find it and if there is a bcpp specific lib
     287                  # (xxx_bcpp.lib)
     288                  libfile = self.find_library_file(library_dirs, lib, debug)
     289                  if libfile is None:
     290                      ld_args.append(lib)
     291                      # probably a BCPP internal library -- don't warn
     292                  else:
     293                      # full name which prefers bcpp_xxx.lib over xxx.lib
     294                      ld_args.append(libfile)
     295  
     296              # some default libraries
     297              ld_args.append('import32')
     298              ld_args.append('cw32mt')
     299  
     300              # def file for export symbols
     301              ld_args.extend([',', def_file])
     302              # add resource files
     303              ld_args.append(',')
     304              ld_args.extend(resources)
     305  
     306              if extra_preargs:
     307                  ld_args[:0] = extra_preargs
     308              if extra_postargs:
     309                  ld_args.extend(extra_postargs)
     310  
     311              self.mkpath(os.path.dirname(output_filename))
     312              try:
     313                  self.spawn([self.linker] + ld_args)
     314              except DistutilsExecError as msg:
     315                  raise LinkError(msg)
     316  
     317          else:
     318              log.debug("skipping %s (up-to-date)", output_filename)
     319  
     320      # link ()
     321  
     322      # -- Miscellaneous methods -----------------------------------------
     323  
     324      def find_library_file(self, dirs, lib, debug=0):
     325          # List of effective library names to try, in order of preference:
     326          # xxx_bcpp.lib is better than xxx.lib
     327          # and xxx_d.lib is better than xxx.lib if debug is set
     328          #
     329          # The "_bcpp" suffix is to handle a Python installation for people
     330          # with multiple compilers (primarily Distutils hackers, I suspect
     331          # ;-).  The idea is they'd have one static library for each
     332          # compiler they care about, since (almost?) every Windows compiler
     333          # seems to have a different format for static libraries.
     334          if debug:
     335              dlib = lib + "_d"
     336              try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)
     337          else:
     338              try_names = (lib + "_bcpp", lib)
     339  
     340          for dir in dirs:
     341              for name in try_names:
     342                  libfile = os.path.join(dir, self.library_filename(name))
     343                  if os.path.exists(libfile):
     344                      return libfile
     345          else:
     346              # Oops, didn't find it in *any* of 'dirs'
     347              return None
     348  
     349      # overwrite the one from CCompiler to support rc and res-files
     350      def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
     351          if output_dir is None:
     352              output_dir = ''
     353          obj_names = []
     354          for src_name in source_filenames:
     355              # use normcase to make sure '.rc' is really '.rc' and not '.RC'
     356              (base, ext) = os.path.splitext(os.path.normcase(src_name))
     357              if ext not in (self.src_extensions + ['.rc', '.res']):
     358                  raise UnknownFileError(
     359                      "unknown file type '{}' (from '{}')".format(ext, src_name)
     360                  )
     361              if strip_dir:
     362                  base = os.path.basename(base)
     363              if ext == '.res':
     364                  # these can go unchanged
     365                  obj_names.append(os.path.join(output_dir, base + ext))
     366              elif ext == '.rc':
     367                  # these need to be compiled to .res-files
     368                  obj_names.append(os.path.join(output_dir, base + '.res'))
     369              else:
     370                  obj_names.append(os.path.join(output_dir, base + self.obj_extension))
     371          return obj_names
     372  
     373      # object_filenames ()
     374  
     375      def preprocess(
     376          self,
     377          source,
     378          output_file=None,
     379          macros=None,
     380          include_dirs=None,
     381          extra_preargs=None,
     382          extra_postargs=None,
     383      ):
     384  
     385          (_, macros, include_dirs) = self._fix_compile_args(None, macros, include_dirs)
     386          pp_opts = gen_preprocess_options(macros, include_dirs)
     387          pp_args = ['cpp32.exe'] + pp_opts
     388          if output_file is not None:
     389              pp_args.append('-o' + output_file)
     390          if extra_preargs:
     391              pp_args[:0] = extra_preargs
     392          if extra_postargs:
     393              pp_args.extend(extra_postargs)
     394          pp_args.append(source)
     395  
     396          # We need to preprocess: either we're being forced to, or the
     397          # source file is newer than the target (or the target doesn't
     398          # exist).
     399          if self.force or output_file is None or newer(source, output_file):
     400              if output_file:
     401                  self.mkpath(os.path.dirname(output_file))
     402              try:
     403                  self.spawn(pp_args)
     404              except DistutilsExecError as msg:
     405                  print(msg)
     406                  raise CompileError(msg)
     407  
     408      # preprocess()