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)
This commit is contained in:
Greg Ward 2000-03-06 03:40:29 +00:00
parent eb3f75e636
commit 32c4a8a0ee
3 changed files with 394 additions and 356 deletions

View File

@ -12,7 +12,7 @@ from types import *
from copy import copy from copy import copy
from distutils.errors import * from distutils.errors import *
from distutils.spawn import spawn 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: class CCompiler:
@ -65,6 +65,18 @@ class CCompiler:
# library search path anyways. # 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, def __init__ (self,
verbose=0, verbose=0,
dry_run=0, dry_run=0,
@ -255,6 +267,138 @@ class CCompiler:
self.objects = copy (objects) 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 ------------------------------------------------ # -- Worker methods ------------------------------------------------
# (must be implemented by subclasses) # (must be implemented by subclasses)
@ -268,8 +412,16 @@ class CCompiler:
extra_postargs=None): extra_postargs=None):
"""Compile one or more C/C++ source files. 'sources' must be """Compile one or more C/C++ source files. 'sources' must be
a list of strings, each one the name of a C/C++ source a list of strings, each one the name of a C/C++ source
file. Return a list of the object filenames generated file. Return a list of object filenames, one per source
(one for each source filename in 'sources'). 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 'macros', if given, must be a list of macro definitions. A
macro definition is either a (name, value) 2-tuple or a (name,) 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 'debug' is a boolean; if true, the compiler will be instructed
to output debug symbols in (or alongside) the object file(s). to output debug symbols in (or alongside) the object file(s).
'extra_preargs' and 'extra_postargs' are optional lists of extra 'extra_preargs' and 'extra_postargs' are implementation-
command-line arguments that will be, respectively, prepended or dependent. On platforms that have the notion of a command-line
appended to the generated command line immediately before (e.g. Unix, DOS/Windows), they are most likely lists of strings:
execution. These will most likely be peculiar to the particular extra command-line arguments to prepand/append to the compiler
platform and compiler being worked with, but are a necessary 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 escape hatch for those occasions when the abstract compiler
framework doesn't cut the mustard.""" framework doesn't cut the mustard."""
@ -398,45 +551,88 @@ class CCompiler:
# -- Filename mangling methods ------------------------------------- # -- Filename generation methods -----------------------------------
# General principle for the filename-mangling methods: by default, # The default implementation of the filename generating methods are
# don't include a directory component, no matter what the caller # prejudiced towards the Unix/DOS/Windows view of the world:
# supplies. Eg. for UnixCCompiler, a source file of "foo/bar/baz.c" # * object files are named by replacing the source file extension
# becomes "baz.o" or "baz.so", etc. (That way, it's easiest for the # (eg. .c/.cpp -> .o/.obj)
# caller to decide where it wants to put/find the output file.) The # * library files (shared or static) are named by plugging the
# 'output_dir' parameter overrides this, of course -- the directory # library name and extension into a format string, eg.
# component of the input filenames is replaced by 'output_dir'. # "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, output_dir=None): def object_filenames (self,
"""Return the list of object filenames corresponding to each source_filenames,
specified source filename.""" strip_dir=0,
pass 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
def shared_object_filename (self, source_filename): # object_filenames ()
"""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): def shared_object_filename (self,
"""Return the shared library filename corresponding to the basename,
specified library name.""" strip_dir=0,
pass 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)
# 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
def shared_library_name (self, inname): def library_filename (self,
"""Given a name with no extension, return the name + shared object extension""" libname,
return inname + self._shared_lib_ext 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)
# -- Utility methods ----------------------------------------------- # -- Utility methods -----------------------------------------------
@ -606,4 +802,4 @@ def gen_lib_options (compiler, library_dirs, libraries):
return lib_opts return lib_opts
# _gen_lib_options () # gen_lib_options ()

View File

@ -5,12 +5,13 @@ for the Microsoft Visual Studio."""
# created 1999/08/19, Perry Stoll # 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$" __revision__ = "$Id$"
import os import sys, os, string
import sys from types import *
import string
from distutils.errors import * from distutils.errors import *
from distutils.ccompiler import \ from distutils.ccompiler import \
CCompiler, gen_preprocess_options, gen_lib_options CCompiler, gen_preprocess_options, gen_lib_options
@ -137,6 +138,20 @@ class MSVCCompiler (CCompiler) :
compiler_type = 'msvc' 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, def __init__ (self,
verbose=0, verbose=0,
dry_run=0, dry_run=0,
@ -169,9 +184,7 @@ class MSVCCompiler (CCompiler) :
self.preprocess_options = None self.preprocess_options = None
self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3' ] self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3' ]
self.compile_options_debug = [ self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG']
'/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG'
]
self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
self.ldflags_shared_debug = [ self.ldflags_shared_debug = [
@ -181,21 +194,7 @@ class MSVCCompiler (CCompiler) :
# -- Worker methods ------------------------------------------------ # -- 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, def compile (self,
sources, sources,
output_dir=None, output_dir=None,
@ -205,48 +204,43 @@ class MSVCCompiler (CCompiler) :
extra_preargs=None, extra_preargs=None,
extra_postargs=None): extra_postargs=None):
if macros is None: (output_dir, macros, include_dirs) = \
macros = [] self._fix_compile_args (output_dir, macros, include_dirs)
if include_dirs is None: (objects, skip_sources) = self._prep_compile (sources, output_dir)
include_dirs = []
objectFiles = [] if extra_postargs is None:
extra_postargs = []
base_pp_opts = \
gen_preprocess_options (self.macros + macros,
self.include_dirs + include_dirs)
base_pp_opts.append('/c')
pp_opts = gen_preprocess_options (macros, include_dirs)
compile_opts = extra_preargs or []
compile_opts.append ('/c')
if debug: if debug:
compile_options = self.compile_options_debug compile_opts.extend (self.compile_options_debug)
else: else:
compile_options = self.compile_options compile_opts.extend (self.compile_options)
for srcFile in sources: for i in range (len (sources)):
base,ext = os.path.splitext(srcFile) src = sources[i] ; obj = objects[i]
objFile = base + ".obj" ext = (os.path.splitext (src))[1]
if ext in self._c_extensions: if skip_sources[src]:
fileOpt = "/Tc" self.announce ("skipping %s (%s up-to-date)" % (src, obj))
elif ext in self._cpp_extensions: else:
fileOpt = "/Tp" if ext in self._c_extensions:
input_opt = "/Tc" + src
elif ext in self._cpp_extensions:
input_opt = "/Tp" + src
inputOpt = fileOpt + srcFile output_opt = "/Fo" + obj
outputOpt = "/Fo" + objFile
cc_args = compile_options + \ self.mkpath (os.path.dirname (obj))
base_pp_opts + \ self.spawn ([self.cc] + compile_opts + pp_opts +
[outputOpt, inputOpt] [input_opt, output_opt] +
extra_postargs)
if extra_preargs: return objects
cc_args[:0] = extra_preargs
if extra_postargs:
cc_args.extend (extra_postargs)
self.spawn ([self.cc] + cc_args) # compile ()
objectFiles.append( objFile )
return objectFiles
# XXX the signature of this method is different from CCompiler and # XXX the signature of this method is different from CCompiler and
@ -263,25 +257,30 @@ class MSVCCompiler (CCompiler) :
extra_preargs=None, extra_preargs=None,
extra_postargs=None): extra_postargs=None):
if libraries is None: (objects, output_dir, libraries, library_dirs) = \
libraries = [] self._fix_link_args (objects, output_dir, takes_libs=1,
if library_dirs is None: libraries=libraries,
library_dirs = [] library_dirs=library_dirs)
lib_opts = gen_lib_options (self.libraries + libraries, output_filename = \
self.library_dirs + library_dirs, self.library_filename (output_libname, output_dir=output_dir)
"%s.lib", "/LIBPATH:%s")
ld_args = self.ldflags_static + lib_opts + \ if self._need_link (objects, output_filename):
objects + ['/OUT:' + output_filename] lib_opts = gen_lib_options (libraries, library_dirs,
if debug: "%s.lib", "/LIBPATH:%s")
pass # XXX what goes here? ld_args = self.ldflags_static + lib_opts + \
if extra_preargs: objects + ['/OUT:' + output_filename]
ld_args[:0] = extra_preargs if debug:
if extra_postargs: pass # XXX what goes here?
ld_args.extend (extra_postargs) 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, def link_shared_lib (self,
@ -294,8 +293,6 @@ class MSVCCompiler (CCompiler) :
extra_preargs=None, extra_preargs=None,
extra_postargs=None): extra_postargs=None):
# XXX should we sanity check the library name? (eg. no
# slashes)
self.link_shared_object (objects, self.link_shared_object (objects,
self.shared_library_name(output_libname), self.shared_library_name(output_libname),
output_dir=output_dir, output_dir=output_dir,
@ -315,70 +312,48 @@ class MSVCCompiler (CCompiler) :
debug=0, debug=0,
extra_preargs=None, extra_preargs=None,
extra_postargs=None): extra_postargs=None):
"""Link a bunch of stuff together to create a shared object
file. Much like 'link_shared_lib()', except the output (objects, output_dir, libraries, library_dirs) = \
filename is explicitly supplied as 'output_filename'.""" self._fix_link_args (objects, output_dir, takes_libs=1,
if libraries is None: libraries=libraries, library_dirs=library_dirs)
libraries = []
if library_dirs is None:
library_dirs = []
lib_opts = gen_lib_options (self, lib_opts = gen_lib_options (self, library_dirs, libraries)
self.library_dirs + library_dirs, if type (output_dir) not in (StringType, NoneType):
self.libraries + libraries) 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]
if extra_preargs:
ld_args[:0] = extra_preargs
if extra_postargs:
ld_args.extend (extra_postargs)
self.mkpath (os.path.dirname (output_filename))
self.spawn ([self.link] + ld_args)
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: else:
ldflags = self.ldflags_shared self.announce ("skipping %s (up-to-date)" % output_filename)
ld_args = ldflags + lib_opts + \ # link_shared_object ()
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
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 )
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 )
def library_filename (self, libname):
"""Return the static library filename corresponding to the
specified library name."""
return "%s%s" %( libname, self._static_lib_ext )
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 )
# -- Miscellaneous methods -----------------------------------------
# These are all used by the 'gen_lib_options() function, in
# ccompiler.py.
def library_dir_option (self, dir): def library_dir_option (self, dir):
return "/LIBPATH:" + dir return "/LIBPATH:" + dir

View File

@ -20,10 +20,9 @@ __revision__ = "$Id$"
import string, re, os import string, re, os
from types import * from types import *
from copy import copy from copy import copy
from sysconfig import \ from distutils.sysconfig import \
CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO
from ccompiler import CCompiler, gen_preprocess_options, gen_lib_options from distutils.ccompiler import CCompiler, gen_preprocess_options, gen_lib_options
from util import move_file, newer_pairwise, newer_group
# XXX Things not currently handled: # XXX Things not currently handled:
# * optimization/debug/warning flags; we just use whatever's in Python's # * optimization/debug/warning flags; we just use whatever's in Python's
@ -55,10 +54,13 @@ class UnixCCompiler (CCompiler):
compiler_type = 'unix' compiler_type = 'unix'
_obj_ext = '.o' # Needed for the filename generation methods provided by the
_exe_ext = '' # base class, CCompiler.
_shared_lib_ext = SO src_extensions = [".c",".C",".cc",".cxx",".cpp"]
_static_lib_ext = '.a' 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 # Command to create a static library: seems to be pretty consistent
# across the major Unices. Might have to move down into the # across the major Unices. Might have to move down into the
@ -96,66 +98,24 @@ class UnixCCompiler (CCompiler):
self.ld_exec = self.cc self.ld_exec = self.cc
# __init__ ()
def compile (self, def compile (self,
sources, sources,
output_dir=None, output_dir=None,
keep_dir=0,
macros=None, macros=None,
include_dirs=None, include_dirs=None,
debug=0, debug=0,
extra_preargs=None, extra_preargs=None,
extra_postargs=None): extra_postargs=None):
if type (output_dir) not in (StringType, NoneType): (output_dir, macros, include_dirs) = \
raise TypeError, "'output_dir' must be a string or None" self._fix_compile_args (output_dir, macros, include_dirs)
if output_dir is None: (objects, skip_sources) = self._prep_compile (sources, output_dir)
output_dir = self.output_dir
if macros is None:
macros = []
if include_dirs is None:
include_dirs = []
if type (macros) is not ListType: # Figure out the options for the compiler command line.
raise TypeError, \ pp_opts = gen_preprocess_options (macros, include_dirs)
"'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]))
# 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)
cc_args = ['-c'] + pp_opts + self.ccflags + self.ccflags_shared cc_args = ['-c'] + pp_opts + self.ccflags + self.ccflags_shared
if debug: if debug:
cc_args[:0] = ['-g'] cc_args[:0] = ['-g']
@ -164,44 +124,21 @@ class UnixCCompiler (CCompiler):
if extra_postargs is None: if extra_postargs is None:
extra_postargs = [] extra_postargs = []
if output_dir is not None: # Compile all source files that weren't eliminated by
self.mkpath (output_dir) # '_prep_compile()'.
for (source,object) in srcobj: for i in range (len (sources)):
self.spawn ([self.cc] + cc_args + src = sources[i] ; obj = objects[i]
[source, '-o', object] + if skip_sources[src]:
extra_postargs) 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)
# Have to re-fetch list of object filenames, because we want to # Return *all* object filenames, not just the ones we just built.
# return *all* of them, including those that weren't recompiled on return objects
# 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)
return (output_dir, libraries, library_dirs)
# compile ()
def link_static_lib (self, def link_static_lib (self,
objects, objects,
@ -209,35 +146,17 @@ class UnixCCompiler (CCompiler):
output_dir=None, output_dir=None,
debug=0): debug=0):
if type (objects) not in (ListType, TupleType): (objects, output_dir) = self._fix_link_args (objects, output_dir, takes_libs=0)
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
output_filename = self.library_filename (output_libname) output_filename = \
if output_dir is not None: self.library_filename (output_libname, output_dir=output_dir)
output_filename = os.path.join (output_dir, output_filename)
# Check timestamps: if any of the object files are newer than if self._need_link (objects, output_filename):
# 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:
self.mkpath (os.path.dirname (output_filename)) self.mkpath (os.path.dirname (output_filename))
self.spawn ([self.archiver, self.spawn ([self.archiver,
self.archiver_options, self.archiver_options,
output_filename] + output_filename] +
objects) objects + self.objects)
else: else:
self.announce ("skipping %s (up-to-date)" % output_filename) self.announce ("skipping %s (up-to-date)" % output_filename)
@ -253,11 +172,9 @@ class UnixCCompiler (CCompiler):
debug=0, debug=0,
extra_preargs=None, extra_preargs=None,
extra_postargs=None): extra_postargs=None):
# XXX should we sanity check the library name? (eg. no
# slashes)
self.link_shared_object ( self.link_shared_object (
objects, objects,
"lib%s%s" % (output_libname, self._shared_lib_ext), self.shared_library_filename (output_libname),
output_dir, output_dir,
libraries, libraries,
library_dirs, library_dirs,
@ -276,30 +193,19 @@ class UnixCCompiler (CCompiler):
extra_preargs=None, extra_preargs=None,
extra_postargs=None): extra_postargs=None):
(output_dir, libraries, library_dirs) = \ (objects, output_dir, libraries, library_dirs) = \
self._fix_link_args (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, lib_opts = gen_lib_options (self, library_dirs, libraries)
self.library_dirs + library_dirs,
self.libraries + libraries)
if type (output_dir) not in (StringType, NoneType): if type (output_dir) not in (StringType, NoneType):
raise TypeError, "'output_dir' must be a string or None" raise TypeError, "'output_dir' must be a string or None"
if output_dir is not None: if output_dir is not None:
output_filename = os.path.join (output_dir, output_filename) output_filename = os.path.join (output_dir, output_filename)
# If any of the input object files are newer than the output shared if self._need_link (objects, output_filename):
# object, relink. Again, this is a simplistic dependency check: ld_args = (self.ldflags_shared + objects + self.objects +
# doesn't look at any of the libraries we might be linking with. lib_opts + ['-o', output_filename])
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 debug: if debug:
ld_args[:0] = ['-g'] ld_args[:0] = ['-g']
if extra_preargs: if extra_preargs:
@ -324,25 +230,17 @@ class UnixCCompiler (CCompiler):
extra_preargs=None, extra_preargs=None,
extra_postargs=None): extra_postargs=None):
(output_dir, libraries, library_dirs) = \ (objects, output_dir, libraries, library_dirs) = \
self._fix_link_args (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, lib_opts = gen_lib_options (self, library_dirs, libraries)
self.library_dirs + library_dirs,
self.libraries + libraries)
output_filename = output_progname # Unix-ism! output_filename = output_progname # Unix-ism!
if output_dir is not None: if output_dir is not None:
output_filename = os.path.join (output_dir, output_filename) output_filename = os.path.join (output_dir, output_filename)
# Same ol' simplistic-but-still-useful dependency check. if self._need_link (objects, output_filename):
if not self.force: ld_args = objects + self.objects + lib_opts + ['-o', output_filename]
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 debug: if debug:
ld_args[:0] = ['-g'] ld_args[:0] = ['-g']
if extra_preargs: if extra_preargs:
@ -357,41 +255,10 @@ class UnixCCompiler (CCompiler):
# link_executable () # link_executable ()
# -- Filename-mangling (etc.) methods ------------------------------ # -- Miscellaneous methods -----------------------------------------
# These are all used by the 'gen_lib_options() function, in
def object_filenames (self, source_filenames, # ccompiler.py.
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))
def library_dir_option (self, dir): def library_dir_option (self, dir):
return "-L" + dir return "-L" + dir