[Python-checkins] python/dist/src/Lib/distutils ccompiler.py,1.44,1.45

jhylton@users.sourceforge.net jhylton@users.sourceforge.net
Thu, 13 Jun 2002 10:26:33 -0700


Update of /cvsroot/python/python/dist/src/Lib/distutils
In directory usw-pr-cvs1:/tmp/cvs-serv28656

Modified Files:
	ccompiler.py 
Log Message:
Extend compiler() method with optional depends argument.

This change is not backwards compatible.  If a compiler subclass
exists outside the distutils package, it may get called with the
unexpected keyword arg.  It's easy to extend that compiler by having
it ignore the argument, and not much harder to do the right thing.  If
this ends up being burdensome, we can change it before 2.3 final to
work harder at compatibility.

Also add _setup_compile() and _get_cc_args() helper functions that
factor out much of the boilerplate for each concrete compiler class.


Index: ccompiler.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/distutils/ccompiler.py,v
retrieving revision 1.44
retrieving revision 1.45
diff -C2 -d -r1.44 -r1.45
*** ccompiler.py	4 Jun 2002 20:14:42 -0000	1.44
--- ccompiler.py	13 Jun 2002 17:26:30 -0000	1.45
***************
*** 161,165 ****
  
  
- 
      def _find_macro (self, name):
          i = 0
--- 161,164 ----
***************
*** 322,325 ****
--- 321,418 ----
      # (here for the convenience of subclasses)
  
+     # Helper method to prep compiler in subclass compile() methods
+ 
+     def _setup_compile(self, outdir, macros, incdirs, sources, depends,
+                        extra):
+         """Process arguments and decide which source files to compile.
+ 
+         Merges _fix_compile_args() and _prep_compile().
+         """
+         if outdir is None:
+             outdir = self.output_dir
+         elif type(outdir) is not StringType:
+             raise TypeError, "'output_dir' must be a string or None"
+ 
+         if macros is None:
+             macros = self.macros
+         elif type(macros) is ListType:
+             macros = macros + (self.macros or [])
+         else:
+             raise TypeError, "'macros' (if supplied) must be a list of tuples"
+ 
+         if incdirs is None:
+             incdirs = self.include_dirs
+         elif type(incdirs) in (ListType, TupleType):
+             incdirs = list(incdirs) + (self.include_dirs or [])
+         else:
+             raise TypeError, \
+                   "'include_dirs' (if supplied) must be a list of strings"
+ 
+         if extra is None:
+             extra = []
+ 
+         # Get the list of expected output (object) files
+         objects = self.object_filenames(sources, 1, outdir)
+         assert len(objects) == len(sources)
+ 
+         # XXX should redo this code to eliminate skip_source entirely.
+         # XXX instead create build and issue skip messages inline
+ 
+         if self.force:
+             skip_source = {}            # rebuild everything
+             for source in sources:
+                 skip_source[source] = 0
+         elif depends is None:
+             # If depends is None, figure out which source files we
+             # have to recompile according to a simplistic check. We
+             # just compare the source and object file, no deep
+             # dependency checking involving header files.
+             skip_source = {}            # rebuild everything
+             for source in sources:      # no wait, rebuild nothing
+                 skip_source[source] = 1
+ 
+             n_sources, n_objects = newer_pairwise(sources, objects)
+             for source in n_sources:    # no really, only rebuild what's
+                 skip_source[source] = 0 # out-of-date
+         else:
+             # If depends is a list of files, then do a different
+             # simplistic check.  Assume that each object depends on
+             # its source and all files in the depends list.
+             skip_source = {}
+             # L contains all the depends plus a spot at the end for a
+             # particular source file
+             L = depends[:] + [None]
+             for i in range(len(objects)):
+                 source = sources[i]
+                 L[-1] = source
+                 if newer_group(L, objects[i]):
+                     skip_source[source] = 0
+                 else:
+                     skip_source[source] = 1
+ 
+         pp_opts = gen_preprocess_options(macros, incdirs)
+ 
+         build = {}
+         for i in range(len(sources)):
+             src = sources[i]
+             obj = objects[i]
+             ext = os.path.splitext(src)[1]
+             self.mkpath(os.path.dirname(obj))
+             if skip_source[src]:
+                 log.debug("skipping %s (%s up-to-date)", src, obj)
+             else:
+                 build[obj] = src, ext
+ 
+         return macros, objects, extra, pp_opts, build
+ 
+     def _get_cc_args(self, pp_opts, debug, before):
+         # works for unixccompiler, emxccompiler, cygwinccompiler
+         cc_args = pp_opts + ['-c']
+         if debug:
+             cc_args[:0] = ['-g']
+         if before:
+             cc_args[:0] = before
+         return cc_args
+ 
      def _fix_compile_args (self, output_dir, macros, include_dirs):
          """Typecheck and fix-up some of the arguments to the 'compile()'
***************
*** 342,347 ****
              macros = macros + (self.macros or [])
          else:
!             raise TypeError, \
!                   "'macros' (if supplied) must be a list of tuples"
  
          if include_dirs is None:
--- 435,439 ----
              macros = macros + (self.macros or [])
          else:
!             raise TypeError, "'macros' (if supplied) must be a list of tuples"
  
          if include_dirs is None:
***************
*** 353,371 ****
                    "'include_dirs' (if supplied) must be a list of strings"
  
!         return (output_dir, macros, include_dirs)
  
      # _fix_compile_args ()
  
  
!     def _prep_compile (self, sources, output_dir):
!         """Determine the list of object files corresponding to 'sources',
!         and figure out which ones really need to be recompiled.  Return a
!         list of all object files and a dictionary telling which source
!         files can be skipped.
          """
          # Get the list of expected output (object) files
!         objects = self.object_filenames (sources,
!                                          strip_dir=1,
!                                          output_dir=output_dir)
  
          if self.force:
--- 445,465 ----
                    "'include_dirs' (if supplied) must be a list of strings"
  
!         return output_dir, macros, include_dirs
  
      # _fix_compile_args ()
  
  
!     def _prep_compile(self, sources, output_dir, depends=None):
!         """Decide which souce files must be recompiled.
! 
!         Determine the list of object files corresponding to 'sources',
!         and figure out which ones really need to be recompiled.
!         Return a list of all object files and a dictionary telling
!         which source files can be skipped.
          """
          # Get the list of expected output (object) files
!         objects = self.object_filenames(sources, strip_dir=1,
!                                         output_dir=output_dir)
!         assert len(objects) == len(sources)
  
          if self.force:
***************
*** 373,390 ****
              for source in sources:
                  skip_source[source] = 0
!         else:
!             # Figure out which source files we have to recompile according
!             # to a simplistic check -- we just compare the source and
!             # object file, no deep dependency checking involving header
!             # files.
              skip_source = {}            # rebuild everything
              for source in sources:      # no wait, rebuild nothing
                  skip_source[source] = 1
  
!             (n_sources, n_objects) = newer_pairwise (sources, objects)
              for source in n_sources:    # no really, only rebuild what's
                  skip_source[source] = 0 # out-of-date
  
!         return (objects, skip_source)
  
      # _prep_compile ()
--- 467,499 ----
              for source in sources:
                  skip_source[source] = 0
!         elif depends is None:
!             # If depends is None, figure out which source files we
!             # have to recompile according to a simplistic check. We
!             # just compare the source and object file, no deep
!             # dependency checking involving header files.
              skip_source = {}            # rebuild everything
              for source in sources:      # no wait, rebuild nothing
                  skip_source[source] = 1
  
!             n_sources, n_objects = newer_pairwise(sources, objects)
              for source in n_sources:    # no really, only rebuild what's
                  skip_source[source] = 0 # out-of-date
+         else:
+             # If depends is a list of files, then do a different
+             # simplistic check.  Assume that each object depends on
+             # its source and all files in the depends list.
+             skip_source = {}
+             # L contains all the depends plus a spot at the end for a
+             # particular source file
+             L = depends[:] + [None]
+             for i in range(len(objects)):
+                 source = sources[i]
+                 L[-1] = source
+                 if newer_group(L, objects[i]):
+                     skip_source[source] = 0
+                 else:
+                     skip_source[source] = 1
  
!         return objects, skip_source
  
      # _prep_compile ()
***************
*** 485,504 ****
          pass
  
!     def compile (self,
!                  sources,
!                  output_dir=None,
!                  macros=None,
!                  include_dirs=None,
!                  debug=0,
!                  extra_preargs=None,
!                  extra_postargs=None):
!         """Compile one or more source files.  'sources' must be a list of
!         filenames, most likely C/C++ files, but in reality anything that
!         can be handled by a particular compiler and compiler class
!         (eg. MSVCCompiler can handle resource files in 'sources').  Return
!         a list of object filenames, one per source filename in 'sources'.
!         Depending on the implementation, not all source files will
!         necessarily be compiled, but all corresponding object filenames
!         will be returned.
  
          If 'output_dir' is given, object files will be put under it, while
--- 594,610 ----
          pass
  
!     def compile(self, sources, output_dir=None, macros=None,
!                 include_dirs=None, debug=0, extra_preargs=None,
!                 extra_postargs=None, depends=None):
!         """Compile one or more source files.
! 
!         'sources' must be a list of filenames, most likely C/C++
!         files, but in reality anything that can be handled by a
!         particular compiler and compiler class (eg. MSVCCompiler can
!         handle resource files in 'sources').  Return a list of object
!         filenames, one per source filename in 'sources'.  Depending on
!         the implementation, not all source files will necessarily be
!         compiled, but all corresponding object filenames will be
!         returned.
  
          If 'output_dir' is given, object files will be put under it, while
***************
*** 531,534 ****
--- 637,646 ----
          cut the mustard.
  
+         'depends', if given, is a list of filenames that all targets
+         depend on.  If a source file is older than any file in
+         depends, then the source file will be recompiled.  This
+         supports dependency tracking, but only at a coarse
+         granularity.
+ 
          Raises CompileError on failure.
          """
***************
*** 711,715 ****
          raise NotImplementedError
  
- 
      # -- Filename generation methods -----------------------------------
  
--- 823,826 ----
***************
*** 746,806 ****
      #     extension for executable files, eg. '' or '.exe'
  
!     def object_filenames (self,
!                           source_filenames,
!                           strip_dir=0,
!                           output_dir=''):
!         if output_dir is None: output_dir = ''
          obj_names = []
          for src_name in source_filenames:
!             (base, ext) = os.path.splitext (src_name)
              if ext not in self.src_extensions:
                  raise UnknownFileError, \
!                       "unknown file type '%s' (from '%s')" % \
!                       (ext, src_name)
              if strip_dir:
!                 base = os.path.basename (base)
!             obj_names.append (os.path.join (output_dir,
!                                             base + self.obj_extension))
          return obj_names
  
!     # object_filenames ()
! 
! 
!     def shared_object_filename (self,
!                                 basename,
!                                 strip_dir=0,
!                                 output_dir=''):
!         if output_dir is None: output_dir = ''
          if strip_dir:
              basename = os.path.basename (basename)
!         return os.path.join (output_dir, basename + self.shared_lib_extension)
  
!     def executable_filename (self,
!                                 basename,
!                                 strip_dir=0,
!                                 output_dir=''):
!         if output_dir is None: output_dir = ''
          if strip_dir:
              basename = os.path.basename (basename)
          return os.path.join(output_dir, basename + (self.exe_extension or ''))
  
!     def library_filename (self,
!                           libname,
!                           lib_type='static',     # or 'shared'
!                           strip_dir=0,
!                           output_dir=''):
! 
!         if output_dir is None: output_dir = ''
!         if lib_type not in ("static","shared","dylib"):
              raise ValueError, "'lib_type' must be \"static\", \"shared\" or \"dylib\""
!         fmt = getattr (self, lib_type + "_lib_format")
!         ext = getattr (self, lib_type + "_lib_extension")
  
!         (dir, base) = os.path.split (libname)
          filename = fmt % (base, ext)
          if strip_dir:
              dir = ''
  
!         return os.path.join (output_dir, dir, filename)
  
  
--- 857,900 ----
      #     extension for executable files, eg. '' or '.exe'
  
!     def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
!         assert output_dir is not None
          obj_names = []
          for src_name in source_filenames:
!             base, ext = os.path.splitext(src_name)
              if ext not in self.src_extensions:
                  raise UnknownFileError, \
!                       "unknown file type '%s' (from '%s')" % (ext, src_name)
              if strip_dir:
!                 base = os.path.basename(base)
!             obj_names.append(os.path.join(output_dir,
!                                           base + self.obj_extension))
          return obj_names
  
!     def shared_object_filename(self, basename, strip_dir=0, output_dir=''):
!         assert output_dir is not None
          if strip_dir:
              basename = os.path.basename (basename)
!         return os.path.join(output_dir, basename + self.shared_lib_extension)
  
!     def executable_filename(self, basename, strip_dir=0, output_dir=''):
!         assert output_dir is not None
          if strip_dir:
              basename = os.path.basename (basename)
          return os.path.join(output_dir, basename + (self.exe_extension or ''))
  
!     def library_filename(self, libname, lib_type='static',     # or 'shared'
!                          strip_dir=0, output_dir=''):
!         assert output_dir is not None
!         if lib_type not in ("static", "shared", "dylib"):
              raise ValueError, "'lib_type' must be \"static\", \"shared\" or \"dylib\""
!         fmt = getattr(self, lib_type + "_lib_format")
!         ext = getattr(self, lib_type + "_lib_extension")
  
!         dir, base = os.path.split (libname)
          filename = fmt % (base, ext)
          if strip_dir:
              dir = ''
  
!         return os.path.join(output_dir, dir, filename)