python (3.11.7)

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