home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / TemaCD / webclean / !!!python!!! / BeOpen-Python-2.0.exe / BCPPCOMPILER.PY < prev    next >
Encoding:
Python Source  |  2000-09-28  |  14.4 KB  |  376 lines

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