summaryrefslogtreecommitdiffstats
path: root/Lib/distutils/ccompiler.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/distutils/ccompiler.py')
-rw-r--r--Lib/distutils/ccompiler.py242
1 files changed, 168 insertions, 74 deletions
diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py
index 4c8b881..50246b8 100644
--- a/Lib/distutils/ccompiler.py
+++ b/Lib/distutils/ccompiler.py
@@ -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
-
- return (objects, skip_source)
+ 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 ()
@@ -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 -----------------------------------------------