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.
This commit is contained in:
Jeremy Hylton 2002-06-13 17:26:30 +00:00
parent 65ce6de35c
commit 59b103cf87

View File

@ -160,7 +160,6 @@ class CCompiler:
setattr(self, key, value)
def _find_macro (self, name):
i = 0
for defn in self.macros:
@ -321,6 +320,100 @@ class CCompiler:
# -- Private utility methods --------------------------------------
# (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()'
method, and return fixed-up values. Specifically: if 'output_dir'
@ -341,8 +434,7 @@ class CCompiler:
elif type (macros) is ListType:
macros = macros + (self.macros or [])
else:
raise TypeError, \
"'macros' (if supplied) must be a list of tuples"
raise TypeError, "'macros' (if supplied) must be a list of tuples"
if include_dirs is None:
include_dirs = self.include_dirs
@ -352,40 +444,57 @@ class CCompiler:
raise TypeError, \
"'include_dirs' (if supplied) must be a list of strings"
return (output_dir, macros, include_dirs)
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.
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)
objects = self.object_filenames(sources, strip_dir=1,
output_dir=output_dir)
assert len(objects) == len(sources)
if self.force:
skip_source = {} # rebuild everything
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.
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)
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)
return objects, skip_source
# _prep_compile ()
@ -484,22 +593,19 @@ class CCompiler:
"""
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.
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
retaining their original path component. That is, "foo/bar.c"
@ -530,6 +636,12 @@ class CCompiler:
for those occasions when the abstract compiler framework doesn't
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.
"""
pass
@ -710,7 +822,6 @@ class CCompiler:
"""
raise NotImplementedError
# -- Filename generation methods -----------------------------------
# The default implementation of the filename generating methods are
@ -745,63 +856,46 @@ class CCompiler:
# * exe_extension -
# 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 = ''
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)
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)
"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))
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 = ''
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)
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 = ''
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=''):
if output_dir is None: output_dir = ''
if lib_type not in ("static","shared","dylib"):
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")
fmt = getattr(self, lib_type + "_lib_format")
ext = getattr(self, lib_type + "_lib_extension")
(dir, base) = os.path.split (libname)
dir, base = os.path.split (libname)
filename = fmt % (base, ext)
if strip_dir:
dir = ''
return os.path.join (output_dir, dir, filename)
return os.path.join(output_dir, dir, filename)
# -- Utility methods -----------------------------------------------