summaryrefslogtreecommitdiffstats
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
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)
-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