summaryrefslogtreecommitdiffstats
path: root/Lib/distutils
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
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')
-rw-r--r--Lib/distutils/ccompiler.py290
-rw-r--r--Lib/distutils/msvccompiler.py235
-rw-r--r--Lib/distutils/unixccompiler.py237
3 files changed, 400 insertions, 362 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 ()
diff --git a/Lib/distutils/msvccompiler.py b/Lib/distutils/msvccompiler.py
index 2dd8dc1..847c611 100644
--- a/Lib/distutils/msvccompiler.py
+++ b/Lib/distutils/msvccompiler.py
@@ -5,12 +5,13 @@ for the Microsoft Visual Studio."""
# created 1999/08/19, Perry Stoll
-#
+# hacked by Robin Becker and Thomas Heller to do a better job of
+# finding DevStudio (through the registry)
+
__revision__ = "$Id$"
-import os
-import sys
-import string
+import sys, os, string
+from types import *
from distutils.errors import *
from distutils.ccompiler import \
CCompiler, gen_preprocess_options, gen_lib_options
@@ -137,6 +138,20 @@ class MSVCCompiler (CCompiler) :
compiler_type = 'msvc'
+ # Private class data (need to distinguish C from C++ source for compiler)
+ _c_extensions = ['.c']
+ _cpp_extensions = ['.cc','.cpp']
+
+ # Needed for the filename generation methods provided by the
+ # base class, CCompiler.
+ src_extensions = _c_extensions + _cpp_extensions
+ obj_extension = '.obj'
+ static_lib_extension = '.lib'
+ shared_lib_extension = '.dll'
+ static_lib_format = shared_lib_format = '%s%s'
+ exe_extension = '.exe'
+
+
def __init__ (self,
verbose=0,
dry_run=0,
@@ -169,9 +184,7 @@ class MSVCCompiler (CCompiler) :
self.preprocess_options = None
self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3' ]
- self.compile_options_debug = [
- '/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG'
- ]
+ self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG']
self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
self.ldflags_shared_debug = [
@@ -181,21 +194,7 @@ class MSVCCompiler (CCompiler) :
# -- Worker methods ------------------------------------------------
- # (must be implemented by subclasses)
- _c_extensions = [ '.c' ]
- _cpp_extensions = [ '.cc', '.cpp' ]
-
- _obj_ext = '.obj'
- _exe_ext = '.exe'
- _shared_lib_ext = '.dll'
- _static_lib_ext = '.lib'
-
- # XXX the 'output_dir' parameter is ignored by the methods in this
- # class! I just put it in to be consistent with CCompiler and
- # UnixCCompiler, but someone who actually knows Visual C++ will
- # have to make it work...
-
def compile (self,
sources,
output_dir=None,
@@ -205,48 +204,43 @@ class MSVCCompiler (CCompiler) :
extra_preargs=None,
extra_postargs=None):
- if macros is None:
- macros = []
- if include_dirs is None:
- include_dirs = []
-
- objectFiles = []
+ (output_dir, macros, include_dirs) = \
+ self._fix_compile_args (output_dir, macros, include_dirs)
+ (objects, skip_sources) = self._prep_compile (sources, output_dir)
- base_pp_opts = \
- gen_preprocess_options (self.macros + macros,
- self.include_dirs + include_dirs)
-
- base_pp_opts.append('/c')
+ if extra_postargs is None:
+ extra_postargs = []
+ pp_opts = gen_preprocess_options (macros, include_dirs)
+ compile_opts = extra_preargs or []
+ compile_opts.append ('/c')
if debug:
- compile_options = self.compile_options_debug
+ compile_opts.extend (self.compile_options_debug)
else:
- compile_options = self.compile_options
+ compile_opts.extend (self.compile_options)
- for srcFile in sources:
- base,ext = os.path.splitext(srcFile)
- objFile = base + ".obj"
+ for i in range (len (sources)):
+ src = sources[i] ; obj = objects[i]
+ ext = (os.path.splitext (src))[1]
- if ext in self._c_extensions:
- fileOpt = "/Tc"
- elif ext in self._cpp_extensions:
- fileOpt = "/Tp"
+ if skip_sources[src]:
+ self.announce ("skipping %s (%s up-to-date)" % (src, obj))
+ else:
+ if ext in self._c_extensions:
+ input_opt = "/Tc" + src
+ elif ext in self._cpp_extensions:
+ input_opt = "/Tp" + src
- inputOpt = fileOpt + srcFile
- outputOpt = "/Fo" + objFile
+ output_opt = "/Fo" + obj
- cc_args = compile_options + \
- base_pp_opts + \
- [outputOpt, inputOpt]
+ self.mkpath (os.path.dirname (obj))
+ self.spawn ([self.cc] + compile_opts + pp_opts +
+ [input_opt, output_opt] +
+ extra_postargs)
- if extra_preargs:
- cc_args[:0] = extra_preargs
- if extra_postargs:
- cc_args.extend (extra_postargs)
+ return objects
- self.spawn ([self.cc] + cc_args)
- objectFiles.append( objFile )
- return objectFiles
+ # compile ()
# XXX the signature of this method is different from CCompiler and
@@ -263,25 +257,30 @@ class MSVCCompiler (CCompiler) :
extra_preargs=None,
extra_postargs=None):
- if libraries is None:
- libraries = []
- if library_dirs is None:
- library_dirs = []
+ (objects, output_dir, libraries, library_dirs) = \
+ self._fix_link_args (objects, output_dir, takes_libs=1,
+ libraries=libraries,
+ library_dirs=library_dirs)
- lib_opts = gen_lib_options (self.libraries + libraries,
- self.library_dirs + library_dirs,
- "%s.lib", "/LIBPATH:%s")
-
- ld_args = self.ldflags_static + lib_opts + \
- objects + ['/OUT:' + output_filename]
- if debug:
- pass # XXX what goes here?
- if extra_preargs:
- ld_args[:0] = extra_preargs
- if extra_postargs:
- ld_args.extend (extra_postargs)
+ output_filename = \
+ self.library_filename (output_libname, output_dir=output_dir)
+
+ if self._need_link (objects, output_filename):
+ lib_opts = gen_lib_options (libraries, library_dirs,
+ "%s.lib", "/LIBPATH:%s")
+ ld_args = self.ldflags_static + lib_opts + \
+ objects + ['/OUT:' + output_filename]
+ if debug:
+ pass # XXX what goes here?
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend (extra_postargs)
+ self.spawn ([self.link] + ld_args)
+ else:
+ self.announce ("skipping %s (up-to-date)" % output_filename)
- self.spawn ( [ self.link ] + ld_args )
+ # link_static_lib ()
def link_shared_lib (self,
@@ -294,8 +293,6 @@ class MSVCCompiler (CCompiler) :
extra_preargs=None,
extra_postargs=None):
- # XXX should we sanity check the library name? (eg. no
- # slashes)
self.link_shared_object (objects,
self.shared_library_name(output_libname),
output_dir=output_dir,
@@ -315,70 +312,48 @@ class MSVCCompiler (CCompiler) :
debug=0,
extra_preargs=None,
extra_postargs=None):
- """Link a bunch of stuff together to create a shared object
- file. Much like 'link_shared_lib()', except the output
- filename is explicitly supplied as 'output_filename'."""
- if libraries is None:
- libraries = []
- if library_dirs is None:
- library_dirs = []
-
- lib_opts = gen_lib_options (self,
- self.library_dirs + library_dirs,
- self.libraries + libraries)
-
- if debug:
- ldflags = self.ldflags_shared_debug
- basename, ext = os.path.splitext (output_filename)
- #XXX not sure this belongs here
- # extensions in debug_mode are named 'module_d.pyd'
- output_filename = basename + '_d' + ext
- else:
- ldflags = self.ldflags_shared
-
- ld_args = ldflags + lib_opts + \
- objects + ['/OUT:' + output_filename]
-
- if extra_preargs:
- ld_args[:0] = extra_preargs
- if extra_postargs:
- ld_args.extend (extra_postargs)
-
- self.spawn ( [ self.link ] + ld_args )
-
- # -- Filename mangling methods -------------------------------------
-
- def _change_extensions( self, filenames, newExtension ):
- object_filenames = []
-
- for srcFile in filenames:
- base,ext = os.path.splitext( srcFile )
- # XXX should we strip off any existing path?
- object_filenames.append( base + newExtension )
-
- return object_filenames
+ (objects, output_dir, libraries, library_dirs) = \
+ self._fix_link_args (objects, output_dir, takes_libs=1,
+ libraries=libraries, library_dirs=library_dirs)
+
+ lib_opts = gen_lib_options (self, library_dirs, libraries)
+ if type (output_dir) not in (StringType, NoneType):
+ raise TypeError, "'output_dir' must be a string or None"
+ if output_dir is not None:
+ output_filename = os.path.join (output_dir, output_filename)
+
+ if self._need_link (objects, output_filename):
+
+ if debug:
+ ldflags = self.ldflags_shared_debug
+ # XXX not sure this belongs here
+ # extensions in debug_mode are named 'module_d.pyd'
+ basename, ext = os.path.splitext (output_filename)
+ output_filename = basename + '_d' + ext
+ else:
+ ldflags = self.ldflags_shared
+
+ ld_args = ldflags + lib_opts + \
+ objects + ['/OUT:' + output_filename]
- def object_filenames (self, source_filenames):
- """Return the list of object filenames corresponding to each
- specified source filename."""
- return self._change_extensions( source_filenames, self._obj_ext )
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend (extra_postargs)
- def shared_object_filename (self, source_filename):
- """Return the shared object filename corresponding to a
- specified source filename."""
- return self._change_extensions( source_filenames, self._shared_lib_ext )
+ self.mkpath (os.path.dirname (output_filename))
+ self.spawn ([self.link] + ld_args)
- def library_filename (self, libname):
- """Return the static library filename corresponding to the
- specified library name."""
- return "%s%s" %( libname, self._static_lib_ext )
+ else:
+ self.announce ("skipping %s (up-to-date)" % output_filename)
- def shared_library_filename (self, libname):
- """Return the shared library filename corresponding to the
- specified library name."""
- return "%s%s" %( libname, self._shared_lib_ext )
+ # link_shared_object ()
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function, in
+ # ccompiler.py.
def library_dir_option (self, dir):
return "/LIBPATH:" + dir
diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py
index 7765faf..35390de 100644
--- a/Lib/distutils/unixccompiler.py
+++ b/Lib/distutils/unixccompiler.py
@@ -20,10 +20,9 @@ __revision__ = "$Id$"
import string, re, os
from types import *
from copy import copy
-from sysconfig import \
+from distutils.sysconfig import \
CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO
-from ccompiler import CCompiler, gen_preprocess_options, gen_lib_options
-from util import move_file, newer_pairwise, newer_group
+from distutils.ccompiler import CCompiler, gen_preprocess_options, gen_lib_options
# XXX Things not currently handled:
# * optimization/debug/warning flags; we just use whatever's in Python's
@@ -55,10 +54,13 @@ class UnixCCompiler (CCompiler):
compiler_type = 'unix'
- _obj_ext = '.o'
- _exe_ext = ''
- _shared_lib_ext = SO
- _static_lib_ext = '.a'
+ # Needed for the filename generation methods provided by the
+ # base class, CCompiler.
+ src_extensions = [".c",".C",".cc",".cxx",".cpp"]
+ obj_extension = ".o"
+ static_lib_extension = ".a"
+ shared_lib_extension = ".so"
+ static_lib_format = shared_lib_format = "lib%s%s"
# Command to create a static library: seems to be pretty consistent
# across the major Unices. Might have to move down into the
@@ -96,66 +98,24 @@ class UnixCCompiler (CCompiler):
self.ld_exec = self.cc
+ # __init__ ()
+
def compile (self,
sources,
output_dir=None,
- keep_dir=0,
macros=None,
include_dirs=None,
debug=0,
extra_preargs=None,
extra_postargs=None):
- if type (output_dir) not in (StringType, NoneType):
- raise TypeError, "'output_dir' must be a string or None"
- if output_dir is None:
- output_dir = self.output_dir
- if macros is None:
- macros = []
- if include_dirs is None:
- include_dirs = []
-
- if type (macros) is not ListType:
- raise TypeError, \
- "'macros' (if supplied) must be a list of tuples"
- if type (include_dirs) not in (ListType, TupleType):
- raise TypeError, \
- "'include_dirs' (if supplied) must be a list of strings"
- include_dirs = list (include_dirs)
-
- pp_opts = gen_preprocess_options (self.macros + macros,
- self.include_dirs + include_dirs)
-
- # So we can mangle 'sources' without hurting the caller's data
- orig_sources = sources
- sources = copy (sources)
-
- # Get the list of expected output (object) files and drop files we
- # don't have to recompile. (Simplistic check -- we just compare the
- # source and object file, no deep dependency checking involving
- # header files. Hmmm.)
- objects = self.object_filenames (sources,
- output_dir=output_dir,
- keep_dir=keep_dir)
- all_objects = copy (objects) # preserve full list to return
-
- if not self.force:
- skipped = newer_pairwise (sources, objects)
- for skipped_pair in skipped:
- self.announce ("skipping %s (%s up-to-date)" % skipped_pair)
-
- # Build list of (source,object) tuples for convenience
- srcobj = []
- for i in range (len (sources)):
- srcobj.append ((sources[i], objects[i]))
+ (output_dir, macros, include_dirs) = \
+ self._fix_compile_args (output_dir, macros, include_dirs)
+ (objects, skip_sources) = self._prep_compile (sources, output_dir)
- # Compile all source files that weren't eliminated by
- # 'newer_pairwise()'.
- # XXX use of ccflags_shared means we're blithely assuming
- # that we're compiling for inclusion in a shared object!
- # (will have to fix this when I add the ability to build a
- # new Python)
+ # Figure out the options for the compiler command line.
+ pp_opts = gen_preprocess_options (macros, include_dirs)
cc_args = ['-c'] + pp_opts + self.ccflags + self.ccflags_shared
if debug:
cc_args[:0] = ['-g']
@@ -164,44 +124,21 @@ class UnixCCompiler (CCompiler):
if extra_postargs is None:
extra_postargs = []
- if output_dir is not None:
- self.mkpath (output_dir)
- for (source,object) in srcobj:
- self.spawn ([self.cc] + cc_args +
- [source, '-o', object] +
- extra_postargs)
-
- # Have to re-fetch list of object filenames, because we want to
- # return *all* of them, including those that weren't recompiled on
- # this call!
- return all_objects
-
-
- def _fix_link_args (self, output_dir, libraries, library_dirs):
- """Fixes up the arguments supplied to the 'link_*' methods:
- if output_dir is None, use self.output_dir; ensure that
- libraries and library_dirs are both lists (could be None or
- tuples on input -- both are converted to lists). Return
- a tuple of the three input arguments."""
-
- if output_dir is None:
- output_dir = self.output_dir
- if libraries is None:
- libraries = []
- if library_dirs is None:
- library_dirs = []
-
- if type (libraries) not in (ListType, TupleType):
- raise TypeError, \
- "'libraries' (if supplied) must be a list of strings"
- if type (library_dirs) not in (ListType, TupleType):
- raise TypeError, \
- "'library_dirs' (if supplied) must be a list of strings"
- libraries = list (libraries)
- library_dirs = list (library_dirs)
+ # Compile all source files that weren't eliminated by
+ # '_prep_compile()'.
+ for i in range (len (sources)):
+ src = sources[i] ; obj = objects[i]
+ if skip_sources[src]:
+ self.announce ("skipping %s (%s up-to-date)" % (src, obj))
+ else:
+ self.mkpath (os.path.dirname (obj))
+ self.spawn ([self.cc] + cc_args + [src, '-o', obj] + extra_postargs)
- return (output_dir, libraries, library_dirs)
+ # Return *all* object filenames, not just the ones we just built.
+ return objects
+ # compile ()
+
def link_static_lib (self,
objects,
@@ -209,35 +146,17 @@ class UnixCCompiler (CCompiler):
output_dir=None,
debug=0):
- if type (objects) not in (ListType, TupleType):
- raise TypeError, \
- "'objects' must be a list or tuple of strings"
- objects = list (objects)
-
- if type (output_dir) not in (StringType, NoneType):
- raise TypeError, "'output_dir' must be a string or None"
- if output_dir is None:
- output_dir = self.output_dir
+ (objects, output_dir) = self._fix_link_args (objects, output_dir, takes_libs=0)
- output_filename = self.library_filename (output_libname)
- if output_dir is not None:
- output_filename = os.path.join (output_dir, output_filename)
+ output_filename = \
+ self.library_filename (output_libname, output_dir=output_dir)
- # Check timestamps: if any of the object files are newer than
- # the library file, *or* if "force" is true, then we'll
- # recreate the library.
- if not self.force:
- if self.dry_run:
- newer = newer_group (objects, output_filename, missing='newer')
- else:
- newer = newer_group (objects, output_filename)
-
- if self.force or newer:
+ if self._need_link (objects, output_filename):
self.mkpath (os.path.dirname (output_filename))
self.spawn ([self.archiver,
self.archiver_options,
output_filename] +
- objects)
+ objects + self.objects)
else:
self.announce ("skipping %s (up-to-date)" % output_filename)
@@ -253,11 +172,9 @@ class UnixCCompiler (CCompiler):
debug=0,
extra_preargs=None,
extra_postargs=None):
- # XXX should we sanity check the library name? (eg. no
- # slashes)
self.link_shared_object (
objects,
- "lib%s%s" % (output_libname, self._shared_lib_ext),
+ self.shared_library_filename (output_libname),
output_dir,
libraries,
library_dirs,
@@ -276,30 +193,19 @@ class UnixCCompiler (CCompiler):
extra_preargs=None,
extra_postargs=None):
- (output_dir, libraries, library_dirs) = \
- self._fix_link_args (output_dir, libraries, library_dirs)
+ (objects, output_dir, libraries, library_dirs) = \
+ self._fix_link_args (objects, output_dir, takes_libs=1,
+ libraries=libraries, library_dirs=library_dirs)
- lib_opts = gen_lib_options (self,
- self.library_dirs + library_dirs,
- self.libraries + libraries)
+ lib_opts = gen_lib_options (self, library_dirs, libraries)
if type (output_dir) not in (StringType, NoneType):
raise TypeError, "'output_dir' must be a string or None"
if output_dir is not None:
output_filename = os.path.join (output_dir, output_filename)
- # If any of the input object files are newer than the output shared
- # object, relink. Again, this is a simplistic dependency check:
- # doesn't look at any of the libraries we might be linking with.
-
- if not self.force:
- if self.dry_run:
- newer = newer_group (objects, output_filename, missing='newer')
- else:
- newer = newer_group (objects, output_filename)
-
- if self.force or newer:
- ld_args = self.ldflags_shared + objects + \
- lib_opts + ['-o', output_filename]
+ if self._need_link (objects, output_filename):
+ ld_args = (self.ldflags_shared + objects + self.objects +
+ lib_opts + ['-o', output_filename])
if debug:
ld_args[:0] = ['-g']
if extra_preargs:
@@ -324,25 +230,17 @@ class UnixCCompiler (CCompiler):
extra_preargs=None,
extra_postargs=None):
- (output_dir, libraries, library_dirs) = \
- self._fix_link_args (output_dir, libraries, library_dirs)
+ (objects, output_dir, libraries, library_dirs) = \
+ self._fix_link_args (objects, output_dir, takes_libs=1,
+ libraries=libraries, library_dirs=library_dirs)
- lib_opts = gen_lib_options (self,
- self.library_dirs + library_dirs,
- self.libraries + libraries)
+ lib_opts = gen_lib_options (self, library_dirs, libraries)
output_filename = output_progname # Unix-ism!
if output_dir is not None:
output_filename = os.path.join (output_dir, output_filename)
- # Same ol' simplistic-but-still-useful dependency check.
- if not self.force:
- if self.dry_run:
- newer = newer_group (objects, output_filename, missing='newer')
- else:
- newer = newer_group (objects, output_filename)
-
- if self.force or newer:
- ld_args = objects + lib_opts + ['-o', output_filename]
+ if self._need_link (objects, output_filename):
+ ld_args = objects + self.objects + lib_opts + ['-o', output_filename]
if debug:
ld_args[:0] = ['-g']
if extra_preargs:
@@ -357,41 +255,10 @@ class UnixCCompiler (CCompiler):
# link_executable ()
- # -- Filename-mangling (etc.) methods ------------------------------
-
- def object_filenames (self, source_filenames,
- keep_dir=0, output_dir=None):
- outnames = []
- for inname in source_filenames:
- outname = re.sub (r'\.(c|C|cc|cxx|cpp)$', self._obj_ext, inname)
- if not keep_dir:
- outname = os.path.basename (outname)
- if output_dir is not None:
- outname = os.path.join (output_dir, outname)
- outnames.append (outname)
- return outnames
-
- def shared_object_filename (self, source_filename,
- keep_dir=0, output_dir=None):
- outname = re.sub (r'\.(c|C|cc|cxx|cpp)$', self._shared_lib_ext)
- if not keep_dir:
- outname = os.path.basename (outname)
- if output_dir is not None:
- outname = os.path.join (output_dir, outname)
- return outname
-
-
- def library_filename (self, libname):
- (dirname, basename) = os.path.split (libname)
- return os.path.join (dirname,
- "lib%s%s" % (basename, self._static_lib_ext))
-
- def shared_library_filename (self, libname):
- (dirname, basename) = os.path.split (libname)
- return os.path.join (dirname,
- "lib%s%s" % (basename, self._shared_lib_ext))
-
-
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function, in
+ # ccompiler.py.
+
def library_dir_option (self, dir):
return "-L" + dir