summaryrefslogtreecommitdiffstats
path: root/Lib/distutils/ccompiler.py
diff options
context:
space:
mode:
authorGreg Ward <gward@python.net>2000-03-06 03:40:29 (GMT)
committerGreg Ward <gward@python.net>2000-03-06 03:40:29 (GMT)
commit32c4a8a0ee74ab932c693de3c8658f4fe57c1ca9 (patch)
tree5180654fe8178af8320c4cc3efb51f0f6999a107 /Lib/distutils/ccompiler.py
parenteb3f75e636316c7ee1edeb42b4ae2c0de9665f8c (diff)
downloadcpython-32c4a8a0ee74ab932c693de3c8658f4fe57c1ca9.zip
cpython-32c4a8a0ee74ab932c693de3c8658f4fe57c1ca9.tar.gz
cpython-32c4a8a0ee74ab932c693de3c8658f4fe57c1ca9.tar.bz2
Serious overhaul of the C compiler interface and the two classes that
implement it (so far): * moved filename generation methods into CCompiler base class, driven by data supplied by implementation classes * moved a bunch of common code from UnixCCompiler to convenience methods in CCompiler * overhauled MSVCCompiler's compile/link methods to look and act as much as possible like UnixCCompiler's, in order to regularize both interface and behaviour (especially by using those new convenience methods)
Diffstat (limited to 'Lib/distutils/ccompiler.py')
-rw-r--r--Lib/distutils/ccompiler.py290
1 files changed, 243 insertions, 47 deletions
diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py
index 4819b23..2336e96 100644
--- a/Lib/distutils/ccompiler.py
+++ b/Lib/distutils/ccompiler.py
@@ -12,7 +12,7 @@ from types import *
from copy import copy
from distutils.errors import *
from distutils.spawn import spawn
-from distutils.util import move_file, mkpath
+from distutils.util import move_file, mkpath, newer_pairwise, newer_group
class CCompiler:
@@ -65,6 +65,18 @@ class CCompiler:
# library search path anyways.
+ # Subclasses that rely on the standard filename generation methods
+ # implemented below should override these; see the comment near
+ # those methods ('object_filenames()' et. al.) for details:
+ src_extensions = None # list of strings
+ obj_extension = None # string
+ static_lib_extension = None
+ shared_lib_extension = None # string
+ static_lib_format = None # format string
+ shared_lib_format = None # prob. same as static_lib_format
+ exe_extension = None # string
+
+
def __init__ (self,
verbose=0,
dry_run=0,
@@ -255,6 +267,138 @@ class CCompiler:
self.objects = copy (objects)
+ # -- Priviate utility methods --------------------------------------
+ # (here for the convenience of subclasses)
+
+ 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' is
+ None, replaces it with 'self.output_dir'; ensures that 'macros'
+ is a list, and augments it with 'self.macros'; ensures that
+ 'include_dirs' is a list, and augments it with
+ 'self.include_dirs'. Guarantees that the returned values are of
+ the correct type, i.e. for 'output_dir' either string or None,
+ and for 'macros' and 'include_dirs' either list or None."""
+
+ if output_dir is None:
+ output_dir = self.output_dir
+ elif type (output_dir) 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 include_dirs is None:
+ include_dirs = self.include_dirs
+ elif type (include_dirs) in (ListType, TupleType):
+ include_dirs = list (include_dirs) + (self.include_dirs or [])
+ else:
+ raise TypeError, \
+ "'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,
+ output_dir=output_dir)
+
+ 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.
+ 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 out-of-date
+ skip_source[source] = 0
+
+ return (objects, skip_source)
+
+ # _prep_compile ()
+
+
+ def _fix_link_args (self, objects, output_dir,
+ takes_libs=0, libraries=None, library_dirs=None):
+ """Typecheck and fix up some of the arguments supplied to the
+ 'link_*' methods and return the fixed values. Specifically:
+ ensure that 'objects' is a list; if output_dir is None, use
+ self.output_dir; ensure that 'libraries' and 'library_dirs' are
+ both lists, and augment them with 'self.libraries' and
+ 'self.library_dirs'. If 'takes_libs' is true, return a tuple
+ (objects, output_dir, libraries, library_dirs; else return
+ (objects, output_dir)."""
+
+ if type (objects) not in (ListType, TupleType):
+ raise TypeError, \
+ "'objects' must be a list or tuple of strings"
+ objects = list (objects)
+
+ if output_dir is None:
+ output_dir = self.output_dir
+ elif type (output_dir) is not StringType:
+ raise TypeError, "'output_dir' must be a string or None"
+
+ if takes_libs:
+ if libraries is None:
+ libraries = self.libraries
+ elif type (libraries) in (ListType, TupleType):
+ libraries = list (libraries) + (self.libraries or [])
+ else:
+ raise TypeError, \
+ "'libraries' (if supplied) must be a list of strings"
+
+ if library_dirs is None:
+ library_dirs = self.library_dirs
+ elif type (library_dirs) in (ListType, TupleType):
+ library_dirs = list (library_dirs) + (self.library_dirs or [])
+ else:
+ raise TypeError, \
+ "'library_dirs' (if supplied) must be a list of strings"
+
+ return (objects, output_dir, libraries, library_dirs)
+ else:
+ return (objects, output_dir)
+
+ # _fix_link_args ()
+
+
+ def _need_link (self, objects, output_file):
+ """Return true if we need to relink the files listed in 'objects' to
+ recreate 'output_file'."""
+
+ if self.force:
+ return 1
+ else:
+ if self.dry_run:
+ newer = newer_group (objects, output_file, missing='newer')
+ else:
+ newer = newer_group (objects, output_file)
+ return newer
+
+ # _need_link ()
+
+
# -- Worker methods ------------------------------------------------
# (must be implemented by subclasses)
@@ -268,8 +412,16 @@ class CCompiler:
extra_postargs=None):
"""Compile one or more C/C++ source files. 'sources' must be
a list of strings, each one the name of a C/C++ source
- file. Return a list of the object filenames generated
- (one for each source filename in 'sources').
+ file. 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" normally compiles to "foo/bar.o" (for a Unix
+ implementation); if 'output_dir' is "build", then it would
+ compile to "build/foo/bar.o".
'macros', if given, must be a list of macro definitions. A
macro definition is either a (name, value) 2-tuple or a (name,)
@@ -285,11 +437,12 @@ class CCompiler:
'debug' is a boolean; if true, the compiler will be instructed
to output debug symbols in (or alongside) the object file(s).
- 'extra_preargs' and 'extra_postargs' are optional lists of extra
- command-line arguments that will be, respectively, prepended or
- appended to the generated command line immediately before
- execution. These will most likely be peculiar to the particular
- platform and compiler being worked with, but are a necessary
+ 'extra_preargs' and 'extra_postargs' are implementation-
+ dependent. On platforms that have the notion of a command-line
+ (e.g. Unix, DOS/Windows), they are most likely lists of strings:
+ extra command-line arguments to prepand/append to the compiler
+ command line. On other platforms, consult the implementation
+ class documentation. In any event, they are intended as an
escape hatch for those occasions when the abstract compiler
framework doesn't cut the mustard."""
@@ -398,45 +551,88 @@ class CCompiler:
- # -- Filename mangling methods -------------------------------------
-
- # General principle for the filename-mangling methods: by default,
- # don't include a directory component, no matter what the caller
- # supplies. Eg. for UnixCCompiler, a source file of "foo/bar/baz.c"
- # becomes "baz.o" or "baz.so", etc. (That way, it's easiest for the
- # caller to decide where it wants to put/find the output file.) The
- # 'output_dir' parameter overrides this, of course -- the directory
- # component of the input filenames is replaced by 'output_dir'.
-
- def object_filenames (self, source_filenames, output_dir=None):
- """Return the list of object filenames corresponding to each
- specified source filename."""
- pass
-
- def shared_object_filename (self, source_filename):
- """Return the shared object filename corresponding to a
- specified source filename (assuming the same directory)."""
- pass
-
- def library_filename (self, libname):
- """Return the static library filename corresponding to the
- specified library name."""
-
- pass
-
- def shared_library_filename (self, libname):
- """Return the shared library filename corresponding to the
- specified library name."""
- pass
-
- # XXX ugh -- these should go!
- def object_name (self, inname):
- """Given a name with no extension, return the name + object extension"""
- return inname + self._obj_ext
+ # -- Filename generation methods -----------------------------------
+
+ # The default implementation of the filename generating methods are
+ # prejudiced towards the Unix/DOS/Windows view of the world:
+ # * object files are named by replacing the source file extension
+ # (eg. .c/.cpp -> .o/.obj)
+ # * library files (shared or static) are named by plugging the
+ # library name and extension into a format string, eg.
+ # "lib%s.%s" % (lib_name, ".a") for Unix static libraries
+ # * executables are named by appending an extension (possibly
+ # empty) to the program name: eg. progname + ".exe" for
+ # Windows
+ #
+ # To reduce redundant code, these methods expect to find
+ # several attributes in the current object (presumably defined
+ # as class attributes):
+ # * src_extensions -
+ # list of C/C++ source file extensions, eg. ['.c', '.cpp']
+ # * obj_extension -
+ # object file extension, eg. '.o' or '.obj'
+ # * static_lib_extension -
+ # extension for static library files, eg. '.a' or '.lib'
+ # * shared_lib_extension -
+ # extension for shared library/object files, eg. '.so', '.dll'
+ # * static_lib_format -
+ # format string for generating static library filenames,
+ # eg. 'lib%s.%s' or '%s.%s'
+ # * shared_lib_format
+ # format string for generating shared library filenames
+ # (probably same as static_lib_format, since the extension
+ # is one of the intended parameters to the format string)
+ # * 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 = ''
+ obj_names = []
+ for src_name in source_filenames:
+ (base, ext) = os.path.splitext (src_name)
+ if ext not in self.src_extensions:
+ continue
+ 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 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"):
+ raise ValueError, "'lib_type' must be \"static\" or \"shared\""
+ 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)
- def shared_library_name (self, inname):
- """Given a name with no extension, return the name + shared object extension"""
- return inname + self._shared_lib_ext
# -- Utility methods -----------------------------------------------
@@ -606,4 +802,4 @@ def gen_lib_options (compiler, library_dirs, libraries):
return lib_opts
-# _gen_lib_options ()
+# gen_lib_options ()