mirror of
https://github.com/Hopiu/linkchecker.git
synced 2026-05-25 14:43:44 +00:00
added distutis files
git-svn-id: https://linkchecker.svn.sourceforge.net/svnroot/linkchecker/trunk/linkchecker@37 e7d03fd6-7b0d-0410-9947-9c21f3af8025
This commit is contained in:
parent
c912cfc2d4
commit
5573689e61
12 changed files with 4508 additions and 0 deletions
11
distutils/__init__.py
Normal file
11
distutils/__init__.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
"""distutils
|
||||
|
||||
The main package for the Python Module Distribtion Utilities. Normally
|
||||
used from a setup script as
|
||||
|
||||
from distutils.core import setup
|
||||
|
||||
setup (...)
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
809
distutils/ccompiler.py
Normal file
809
distutils/ccompiler.py
Normal file
|
|
@ -0,0 +1,809 @@
|
|||
"""distutils.ccompiler
|
||||
|
||||
Contains CCompiler, an abstract base class that defines the interface
|
||||
for the Distutils compiler abstraction model."""
|
||||
|
||||
# created 1999/07/05, Greg Ward
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys, os
|
||||
from types import *
|
||||
from copy import copy
|
||||
from distutils.errors import *
|
||||
from distutils.spawn import spawn
|
||||
from distutils.util import move_file, mkpath, newer_pairwise, newer_group
|
||||
|
||||
|
||||
class CCompiler:
|
||||
"""Abstract base class to define the interface that must be implemented
|
||||
by real compiler abstraction classes. Might have some use as a
|
||||
place for shared code, but it's not yet clear what code can be
|
||||
shared between compiler abstraction models for different platforms.
|
||||
|
||||
The basic idea behind a compiler abstraction class is that each
|
||||
instance can be used for all the compile/link steps in building
|
||||
a single project. Thus, attributes common to all of those compile
|
||||
and link steps -- include directories, macros to define, libraries
|
||||
to link against, etc. -- are attributes of the compiler instance.
|
||||
To allow for variability in how individual files are treated,
|
||||
most (all?) of those attributes may be varied on a per-compilation
|
||||
or per-link basis."""
|
||||
|
||||
# 'compiler_type' is a class attribute that identifies this class. It
|
||||
# keeps code that wants to know what kind of compiler it's dealing with
|
||||
# from having to import all possible compiler classes just to do an
|
||||
# 'isinstance'. In concrete CCompiler subclasses, 'compiler_type'
|
||||
# should really, really be one of the keys of the 'compiler_class'
|
||||
# dictionary (see below -- used by the 'new_compiler()' factory
|
||||
# function) -- authors of new compiler interface classes are
|
||||
# responsible for updating 'compiler_class'!
|
||||
compiler_type = None
|
||||
|
||||
# XXX things not handled by this compiler abstraction model:
|
||||
# * client can't provide additional options for a compiler,
|
||||
# e.g. warning, optimization, debugging flags. Perhaps this
|
||||
# should be the domain of concrete compiler abstraction classes
|
||||
# (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base
|
||||
# class should have methods for the common ones.
|
||||
# * can't put output files (object files, libraries, whatever)
|
||||
# into a separate directory from their inputs. Should this be
|
||||
# handled by an 'output_dir' attribute of the whole object, or a
|
||||
# parameter to the compile/link_* methods, or both?
|
||||
# * can't completely override the include or library searchg
|
||||
# path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2".
|
||||
# I'm not sure how widely supported this is even by Unix
|
||||
# compilers, much less on other platforms. And I'm even less
|
||||
# sure how useful it is; maybe for cross-compiling, but
|
||||
# support for that is a ways off. (And anyways, cross
|
||||
# compilers probably have a dedicated binary with the
|
||||
# right paths compiled in. I hope.)
|
||||
# * can't do really freaky things with the library list/library
|
||||
# dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against
|
||||
# different versions of libfoo.a in different locations. I
|
||||
# think this is useless without the ability to null out the
|
||||
# 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,
|
||||
force=0):
|
||||
|
||||
self.verbose = verbose
|
||||
self.dry_run = dry_run
|
||||
self.force = force
|
||||
|
||||
# 'output_dir': a common output directory for object, library,
|
||||
# shared object, and shared library files
|
||||
self.output_dir = None
|
||||
|
||||
# 'macros': a list of macro definitions (or undefinitions). A
|
||||
# macro definition is a 2-tuple (name, value), where the value is
|
||||
# either a string or None (no explicit value). A macro
|
||||
# undefinition is a 1-tuple (name,).
|
||||
self.macros = []
|
||||
|
||||
# 'include_dirs': a list of directories to search for include files
|
||||
self.include_dirs = []
|
||||
|
||||
# 'libraries': a list of libraries to include in any link
|
||||
# (library names, not filenames: eg. "foo" not "libfoo.a")
|
||||
self.libraries = []
|
||||
|
||||
# 'library_dirs': a list of directories to search for libraries
|
||||
self.library_dirs = []
|
||||
|
||||
# 'runtime_library_dirs': a list of directories to search for
|
||||
# shared libraries/objects at runtime
|
||||
self.runtime_library_dirs = []
|
||||
|
||||
# 'objects': a list of object files (or similar, such as explicitly
|
||||
# named library files) to include on any link
|
||||
self.objects = []
|
||||
|
||||
# __init__ ()
|
||||
|
||||
|
||||
def _find_macro (self, name):
|
||||
i = 0
|
||||
for defn in self.macros:
|
||||
if defn[0] == name:
|
||||
return i
|
||||
i = i + 1
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _check_macro_definitions (self, definitions):
|
||||
"""Ensures that every element of 'definitions' is a valid macro
|
||||
definition, ie. either (name,value) 2-tuple or a (name,)
|
||||
tuple. Do nothing if all definitions are OK, raise
|
||||
TypeError otherwise."""
|
||||
|
||||
for defn in definitions:
|
||||
if not (type (defn) is TupleType and
|
||||
(len (defn) == 1 or
|
||||
(len (defn) == 2 and
|
||||
(type (defn[1]) is StringType or defn[1] is None))) and
|
||||
type (defn[0]) is StringType):
|
||||
raise TypeError, \
|
||||
("invalid macro definition '%s': " % defn) + \
|
||||
"must be tuple (string,), (string, string), or " + \
|
||||
"(string, None)"
|
||||
|
||||
|
||||
# -- Bookkeeping methods -------------------------------------------
|
||||
|
||||
def define_macro (self, name, value=None):
|
||||
"""Define a preprocessor macro for all compilations driven by
|
||||
this compiler object. The optional parameter 'value' should be
|
||||
a string; if it is not supplied, then the macro will be defined
|
||||
without an explicit value and the exact outcome depends on the
|
||||
compiler used (XXX true? does ANSI say anything about this?)"""
|
||||
|
||||
# Delete from the list of macro definitions/undefinitions if
|
||||
# already there (so that this one will take precedence).
|
||||
i = self._find_macro (name)
|
||||
if i is not None:
|
||||
del self.macros[i]
|
||||
|
||||
defn = (name, value)
|
||||
self.macros.append (defn)
|
||||
|
||||
|
||||
def undefine_macro (self, name):
|
||||
"""Undefine a preprocessor macro for all compilations driven by
|
||||
this compiler object. If the same macro is defined by
|
||||
'define_macro()' and undefined by 'undefine_macro()' the last
|
||||
call takes precedence (including multiple redefinitions or
|
||||
undefinitions). If the macro is redefined/undefined on a
|
||||
per-compilation basis (ie. in the call to 'compile()'), then
|
||||
that takes precedence."""
|
||||
|
||||
# Delete from the list of macro definitions/undefinitions if
|
||||
# already there (so that this one will take precedence).
|
||||
i = self._find_macro (name)
|
||||
if i is not None:
|
||||
del self.macros[i]
|
||||
|
||||
undefn = (name,)
|
||||
self.macros.append (undefn)
|
||||
|
||||
|
||||
def add_include_dir (self, dir):
|
||||
"""Add 'dir' to the list of directories that will be searched
|
||||
for header files. The compiler is instructed to search
|
||||
directories in the order in which they are supplied by
|
||||
successive calls to 'add_include_dir()'."""
|
||||
self.include_dirs.append (dir)
|
||||
|
||||
def set_include_dirs (self, dirs):
|
||||
"""Set the list of directories that will be searched to 'dirs'
|
||||
(a list of strings). Overrides any preceding calls to
|
||||
'add_include_dir()'; subsequence calls to 'add_include_dir()'
|
||||
add to the list passed to 'set_include_dirs()'. This does
|
||||
not affect any list of standard include directories that
|
||||
the compiler may search by default."""
|
||||
self.include_dirs = copy (dirs)
|
||||
|
||||
|
||||
def add_library (self, libname):
|
||||
"""Add 'libname' to the list of libraries that will be included
|
||||
in all links driven by this compiler object. Note that
|
||||
'libname' should *not* be the name of a file containing a
|
||||
library, but the name of the library itself: the actual filename
|
||||
will be inferred by the linker, the compiler, or the compiler
|
||||
abstraction class (depending on the platform).
|
||||
|
||||
The linker will be instructed to link against libraries in the
|
||||
order they were supplied to 'add_library()' and/or
|
||||
'set_libraries()'. It is perfectly valid to duplicate library
|
||||
names; the linker will be instructed to link against libraries
|
||||
as many times as they are mentioned."""
|
||||
self.libraries.append (libname)
|
||||
|
||||
def set_libraries (self, libnames):
|
||||
"""Set the list of libraries to be included in all links driven
|
||||
by this compiler object to 'libnames' (a list of strings).
|
||||
This does not affect any standard system libraries that the
|
||||
linker may include by default."""
|
||||
|
||||
self.libraries = copy (libnames)
|
||||
|
||||
|
||||
def add_library_dir (self, dir):
|
||||
"""Add 'dir' to the list of directories that will be searched for
|
||||
libraries specified to 'add_library()' and 'set_libraries()'.
|
||||
The linker will be instructed to search for libraries in the
|
||||
order they are supplied to 'add_library_dir()' and/or
|
||||
'set_library_dirs()'."""
|
||||
self.library_dirs.append (dir)
|
||||
|
||||
def set_library_dirs (self, dirs):
|
||||
"""Set the list of library search directories to 'dirs' (a list
|
||||
of strings). This does not affect any standard library
|
||||
search path that the linker may search by default."""
|
||||
self.library_dirs = copy (dirs)
|
||||
|
||||
|
||||
def add_runtime_library_dir (self, dir):
|
||||
"""Add 'dir' to the list of directories that will be searched for
|
||||
shared libraries at runtime."""
|
||||
self.runtime_library_dirs.append (dir)
|
||||
|
||||
def set_runtime_library_dirs (self, dirs):
|
||||
"""Set the list of directories to search for shared libraries
|
||||
at runtime to 'dirs' (a list of strings). This does not affect
|
||||
any standard search path that the runtime linker may search by
|
||||
default."""
|
||||
self.runtime_library_dirs = copy (dirs)
|
||||
|
||||
|
||||
def add_link_object (self, object):
|
||||
"""Add 'object' to the list of object files (or analogues, such
|
||||
as explictly named library files or the output of "resource
|
||||
compilers") to be included in every link driven by this
|
||||
compiler object."""
|
||||
self.objects.append (object)
|
||||
|
||||
def set_link_objects (self, objects):
|
||||
"""Set the list of object files (or analogues) to be included
|
||||
in every link to 'objects'. This does not affect any
|
||||
standard object files that the linker may include by default
|
||||
(such as system libraries)."""
|
||||
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)
|
||||
|
||||
def compile (self,
|
||||
sources,
|
||||
output_dir=None,
|
||||
macros=None,
|
||||
include_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
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 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,)
|
||||
1-tuple. The former defines a macro; if the value is None, the
|
||||
macro is defined without an explicit value. The 1-tuple case
|
||||
undefines a macro. Later definitions/redefinitions/
|
||||
undefinitions take precedence.
|
||||
|
||||
'include_dirs', if given, must be a list of strings, the
|
||||
directories to add to the default include file search path for
|
||||
this compilation only.
|
||||
|
||||
'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 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."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def create_static_lib (self,
|
||||
objects,
|
||||
output_libname,
|
||||
output_dir=None,
|
||||
debug=0):
|
||||
"""Link a bunch of stuff together to create a static library
|
||||
file. The "bunch of stuff" consists of the list of object
|
||||
files supplied as 'objects', the extra object files supplied
|
||||
to 'add_link_object()' and/or 'set_link_objects()', the
|
||||
libraries supplied to 'add_library()' and/or
|
||||
'set_libraries()', and the libraries supplied as 'libraries'
|
||||
(if any).
|
||||
|
||||
'output_libname' should be a library name, not a filename; the
|
||||
filename will be inferred from the library name. 'output_dir'
|
||||
is the directory where the library file will be put.
|
||||
|
||||
'debug' is a boolean; if true, debugging information will be
|
||||
included in the library (note that on most platforms, it is the
|
||||
compile step where this matters: the 'debug' flag is included
|
||||
here just for consistency)."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def link_shared_lib (self,
|
||||
objects,
|
||||
output_libname,
|
||||
output_dir=None,
|
||||
libraries=None,
|
||||
library_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
"""Link a bunch of stuff together to create a shared library
|
||||
file. Similar semantics to 'create_static_lib()', with the
|
||||
addition of other libraries to link against and directories to
|
||||
search for them. Also, of course, the type and name of
|
||||
the generated file will almost certainly be different, as will
|
||||
the program used to create it.
|
||||
|
||||
'libraries' is a list of libraries to link against. These are
|
||||
library names, not filenames, since they're translated into
|
||||
filenames in a platform-specific way (eg. "foo" becomes
|
||||
"libfoo.a" on Unix and "foo.lib" on DOS/Windows). However, they
|
||||
can include a directory component, which means the linker will
|
||||
look in that specific directory rather than searching all the
|
||||
normal locations.
|
||||
|
||||
'library_dirs', if supplied, should be a list of directories to
|
||||
search for libraries that were specified as bare library names
|
||||
(ie. no directory component). These are on top of the system
|
||||
default and those supplied to 'add_library_dir()' and/or
|
||||
'set_library_dirs()'.
|
||||
|
||||
'debug' is as for 'compile()' and 'create_static_lib()', with the
|
||||
slight distinction that it actually matters on most platforms
|
||||
(as opposed to 'create_static_lib()', which includes a 'debug'
|
||||
flag mostly for form's sake).
|
||||
|
||||
'extra_preargs' and 'extra_postargs' are as for 'compile()'
|
||||
(except of course that they supply command-line arguments
|
||||
for the particular linker being used)."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def link_shared_object (self,
|
||||
objects,
|
||||
output_filename,
|
||||
output_dir=None,
|
||||
libraries=None,
|
||||
library_dirs=None,
|
||||
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 'output_dir' is
|
||||
supplied, 'output_filename' is relative to it
|
||||
(i.e. 'output_filename' can provide directory components if
|
||||
needed)."""
|
||||
pass
|
||||
|
||||
|
||||
def link_executable (self,
|
||||
objects,
|
||||
output_progname,
|
||||
output_dir=None,
|
||||
libraries=None,
|
||||
library_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
"""Link a bunch of stuff together to create a binary executable
|
||||
file. The "bunch of stuff" is as for 'link_shared_lib()'.
|
||||
'output_progname' should be the base name of the executable
|
||||
program--e.g. on Unix the same as the output filename, but
|
||||
on DOS/Windows ".exe" will be appended."""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
# -- 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)
|
||||
|
||||
|
||||
# -- Utility methods -----------------------------------------------
|
||||
|
||||
def announce (self, msg, level=1):
|
||||
if self.verbose >= level:
|
||||
print msg
|
||||
|
||||
def warn (self, msg):
|
||||
sys.stderr.write ("warning: %s\n" % msg)
|
||||
|
||||
def spawn (self, cmd):
|
||||
spawn (cmd, verbose=self.verbose, dry_run=self.dry_run)
|
||||
|
||||
def move_file (self, src, dst):
|
||||
return move_file (src, dst, verbose=self.verbose, dry_run=self.dry_run)
|
||||
|
||||
def mkpath (self, name, mode=0777):
|
||||
mkpath (name, mode, self.verbose, self.dry_run)
|
||||
|
||||
|
||||
# class CCompiler
|
||||
|
||||
|
||||
# Map a platform ('posix', 'nt') to the default compiler type for
|
||||
# that platform.
|
||||
default_compiler = { 'posix': 'unix',
|
||||
'nt': 'msvc',
|
||||
}
|
||||
|
||||
# Map compiler types to (module_name, class_name) pairs -- ie. where to
|
||||
# find the code that implements an interface to this compiler. (The module
|
||||
# is assumed to be in the 'distutils' package.)
|
||||
compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler'),
|
||||
'msvc': ('msvccompiler', 'MSVCCompiler'),
|
||||
}
|
||||
|
||||
|
||||
def new_compiler (plat=None,
|
||||
compiler=None,
|
||||
verbose=0,
|
||||
dry_run=0,
|
||||
force=0):
|
||||
|
||||
"""Generate an instance of some CCompiler subclass for the supplied
|
||||
platform/compiler combination. 'plat' defaults to 'os.name'
|
||||
(eg. 'posix', 'nt'), and 'compiler' defaults to the default
|
||||
compiler for that platform. Currently only 'posix' and 'nt'
|
||||
are supported, and the default compilers are "traditional Unix
|
||||
interface" (UnixCCompiler class) and Visual C++ (MSVCCompiler
|
||||
class). Note that it's perfectly possible to ask for a Unix
|
||||
compiler object under Windows, and a Microsoft compiler object
|
||||
under Unix -- if you supply a value for 'compiler', 'plat'
|
||||
is ignored."""
|
||||
|
||||
if plat is None:
|
||||
plat = os.name
|
||||
|
||||
try:
|
||||
if compiler is None:
|
||||
compiler = default_compiler[plat]
|
||||
|
||||
(module_name, class_name) = compiler_class[compiler]
|
||||
except KeyError:
|
||||
msg = "don't know how to compile C/C++ code on platform '%s'" % plat
|
||||
if compiler is not None:
|
||||
msg = msg + " with '%s' compiler" % compiler
|
||||
raise DistutilsPlatformError, msg
|
||||
|
||||
try:
|
||||
module_name = "distutils." + module_name
|
||||
__import__ (module_name)
|
||||
module = sys.modules[module_name]
|
||||
klass = vars(module)[class_name]
|
||||
except ImportError:
|
||||
raise DistutilsModuleError, \
|
||||
"can't compile C/C++ code: unable to load module '%s'" % \
|
||||
module_name
|
||||
except KeyError:
|
||||
raise DistutilsModuleError, \
|
||||
("can't compile C/C++ code: unable to find class '%s' " +
|
||||
"in module '%s'") % (class_name, module_name)
|
||||
|
||||
return klass (verbose, dry_run, force)
|
||||
|
||||
|
||||
def gen_preprocess_options (macros, include_dirs):
|
||||
"""Generate C pre-processor options (-D, -U, -I) as used by at
|
||||
least two types of compilers: the typical Unix compiler and Visual
|
||||
C++. 'macros' is the usual thing, a list of 1- or 2-tuples, where
|
||||
(name,) means undefine (-U) macro 'name', and (name,value) means
|
||||
define (-D) macro 'name' to 'value'. 'include_dirs' is just a list of
|
||||
directory names to be added to the header file search path (-I).
|
||||
Returns a list of command-line options suitable for either
|
||||
Unix compilers or Visual C++."""
|
||||
|
||||
# XXX it would be nice (mainly aesthetic, and so we don't generate
|
||||
# stupid-looking command lines) to go over 'macros' and eliminate
|
||||
# redundant definitions/undefinitions (ie. ensure that only the
|
||||
# latest mention of a particular macro winds up on the command
|
||||
# line). I don't think it's essential, though, since most (all?)
|
||||
# Unix C compilers only pay attention to the latest -D or -U
|
||||
# mention of a macro on their command line. Similar situation for
|
||||
# 'include_dirs'. I'm punting on both for now. Anyways, weeding out
|
||||
# redundancies like this should probably be the province of
|
||||
# CCompiler, since the data structures used are inherited from it
|
||||
# and therefore common to all CCompiler classes.
|
||||
|
||||
pp_opts = []
|
||||
for macro in macros:
|
||||
|
||||
if not (type (macro) is TupleType and
|
||||
1 <= len (macro) <= 2):
|
||||
raise TypeError, \
|
||||
("bad macro definition '%s': " +
|
||||
"each element of 'macros' list must be a 1- or 2-tuple") % \
|
||||
macro
|
||||
|
||||
if len (macro) == 1: # undefine this macro
|
||||
pp_opts.append ("-U%s" % macro[0])
|
||||
elif len (macro) == 2:
|
||||
if macro[1] is None: # define with no explicit value
|
||||
pp_opts.append ("-D%s" % macro[0])
|
||||
else:
|
||||
# XXX *don't* need to be clever about quoting the
|
||||
# macro value here, because we're going to avoid the
|
||||
# shell at all costs when we spawn the command!
|
||||
pp_opts.append ("-D%s=%s" % macro)
|
||||
|
||||
for dir in include_dirs:
|
||||
pp_opts.append ("-I%s" % dir)
|
||||
|
||||
return pp_opts
|
||||
|
||||
# gen_preprocess_options ()
|
||||
|
||||
|
||||
def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries):
|
||||
"""Generate linker options for searching library directories and
|
||||
linking with specific libraries. 'libraries' and 'library_dirs'
|
||||
are, respectively, lists of library names (not filenames!) and
|
||||
search directories. Returns a list of command-line options suitable
|
||||
for use with some compiler (depending on the two format strings
|
||||
passed in)."""
|
||||
|
||||
lib_opts = []
|
||||
|
||||
for dir in library_dirs:
|
||||
lib_opts.append (compiler.library_dir_option (dir))
|
||||
|
||||
for dir in runtime_library_dirs:
|
||||
lib_opts.append (compiler.runtime_library_dir_option (dir))
|
||||
|
||||
# XXX it's important that we *not* remove redundant library mentions!
|
||||
# sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
|
||||
# resolve all symbols. I just hope we never have to say "-lfoo obj.o
|
||||
# -lbar" to get things to work -- that's certainly a possibility, but a
|
||||
# pretty nasty way to arrange your C code.
|
||||
|
||||
for lib in libraries:
|
||||
(lib_dir, lib_name) = os.path.split (lib)
|
||||
if lib_dir:
|
||||
lib_file = compiler.find_library_file ([lib_dir], lib_name)
|
||||
if lib_file:
|
||||
lib_opts.append (lib_file)
|
||||
else:
|
||||
compiler.warn ("no library file corresponding to "
|
||||
"'%s' found (skipping)" % lib)
|
||||
else:
|
||||
lib_opts.append (compiler.library_option (lib))
|
||||
|
||||
return lib_opts
|
||||
|
||||
# gen_lib_options ()
|
||||
1016
distutils/core.py
Normal file
1016
distutils/core.py
Normal file
File diff suppressed because it is too large
Load diff
84
distutils/errors.py
Normal file
84
distutils/errors.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
"""distutils.errors
|
||||
|
||||
Provides exceptions used by the Distutils modules. Note that Distutils
|
||||
modules may raise standard exceptions; in particular, SystemExit is
|
||||
usually raised for errors that are obviously the end-user's fault
|
||||
(eg. bad command-line arguments).
|
||||
|
||||
This module safe to use in "from ... import *" mode; it only exports
|
||||
symbols whose names start with "Distutils" and end with "Error"."""
|
||||
|
||||
# created 1999/03/03, Greg Ward
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import types
|
||||
|
||||
if type (RuntimeError) is types.ClassType:
|
||||
|
||||
# DistutilsError is the root of all Distutils evil.
|
||||
class DistutilsError (Exception):
|
||||
pass
|
||||
|
||||
# DistutilsModuleError is raised if we are unable to load an expected
|
||||
# module, or find an expected class within some module
|
||||
class DistutilsModuleError (DistutilsError):
|
||||
pass
|
||||
|
||||
# DistutilsClassError is raised if we encounter a distribution or command
|
||||
# class that's not holding up its end of the bargain.
|
||||
class DistutilsClassError (DistutilsError):
|
||||
pass
|
||||
|
||||
# DistutilsGetoptError (help me -- I have JavaProgrammersDisease!) is
|
||||
# raised if the option table provided to fancy_getopt is bogus.
|
||||
class DistutilsGetoptError (DistutilsError):
|
||||
pass
|
||||
|
||||
# DistutilsArgError is raised by fancy_getopt in response to getopt.error;
|
||||
# distutils.core then turns around and raises SystemExit from that. (Thus
|
||||
# client code should never see DistutilsArgError.)
|
||||
class DistutilsArgError (DistutilsError):
|
||||
pass
|
||||
|
||||
# DistutilsFileError is raised for any problems in the filesystem:
|
||||
# expected file not found, etc.
|
||||
class DistutilsFileError (DistutilsError):
|
||||
pass
|
||||
|
||||
# DistutilsOptionError is raised anytime an attempt is made to access
|
||||
# (get or set) an option that does not exist for a particular command
|
||||
# (or for the distribution itself).
|
||||
class DistutilsOptionError (DistutilsError):
|
||||
pass
|
||||
|
||||
# DistutilsValueError is raised anytime an option value (presumably
|
||||
# provided by setup.py) is invalid.
|
||||
class DistutilsValueError (DistutilsError):
|
||||
pass
|
||||
|
||||
# DistutilsPlatformError is raised when we find that we don't
|
||||
# know how to do something on the current platform (but we do
|
||||
# know how to do it on some platform).
|
||||
class DistutilsPlatformError (DistutilsError):
|
||||
pass
|
||||
|
||||
# DistutilsExecError is raised if there are any problems executing
|
||||
# an external program
|
||||
class DistutilsExecError (DistutilsError):
|
||||
pass
|
||||
|
||||
# String-based exceptions
|
||||
else:
|
||||
DistutilsError = 'DistutilsError'
|
||||
DistutilsModuleError = 'DistutilsModuleError'
|
||||
DistutilsClassError = 'DistutilsClassError'
|
||||
DistutilsGetoptError = 'DistutilsGetoptError'
|
||||
DistutilsArgError = 'DistutilsArgError'
|
||||
DistutilsFileError = 'DistutilsFileError'
|
||||
DistutilsOptionError = 'DistutilsOptionError'
|
||||
DistutilsValueError = 'DistutilsValueError'
|
||||
DistutilsPlatformError = 'DistutilsPlatformError'
|
||||
DistutilsExecError = 'DistutilsExecError'
|
||||
|
||||
del types
|
||||
311
distutils/fancy_getopt.py
Normal file
311
distutils/fancy_getopt.py
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
"""distutils.fancy_getopt
|
||||
|
||||
Wrapper around the standard getopt module that provides the following
|
||||
additional features:
|
||||
* short and long options are tied together
|
||||
* options have help strings, so fancy_getopt could potentially
|
||||
create a complete usage summary
|
||||
* options set attributes of a passed-in object
|
||||
"""
|
||||
|
||||
# created 1999/03/03, Greg Ward
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys, string, re
|
||||
from types import *
|
||||
import getopt
|
||||
from distutils.errors import *
|
||||
|
||||
# Much like command_re in distutils.core, this is close to but not quite
|
||||
# the same as a Python NAME -- except, in the spirit of most GNU
|
||||
# utilities, we use '-' in place of '_'. (The spirit of LISP lives on!)
|
||||
# The similarities to NAME are again not a coincidence...
|
||||
longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)'
|
||||
longopt_re = re.compile (r'^%s$' % longopt_pat)
|
||||
|
||||
# For recognizing "negative alias" options, eg. "quiet=!verbose"
|
||||
neg_alias_re = re.compile ("^(%s)=!(%s)$" % (longopt_pat, longopt_pat))
|
||||
|
||||
|
||||
# This is used to translate long options to legitimate Python identifiers
|
||||
# (for use as attributes of some object).
|
||||
longopt_xlate = string.maketrans ('-', '_')
|
||||
|
||||
|
||||
def fancy_getopt (options, negative_opt, object, args):
|
||||
|
||||
# The 'options' table is a list of 3-tuples:
|
||||
# (long_option, short_option, help_string)
|
||||
# if an option takes an argument, its long_option should have '='
|
||||
# appended; short_option should just be a single character, no ':' in
|
||||
# any case. If a long_option doesn't have a corresponding
|
||||
# short_option, short_option should be None. All option tuples must
|
||||
# have long options.
|
||||
|
||||
# Build the short_opts string and long_opts list, remembering how
|
||||
# the two are tied together
|
||||
|
||||
short_opts = [] # we'll join 'em when done
|
||||
long_opts = []
|
||||
short2long = {}
|
||||
attr_name = {}
|
||||
takes_arg = {}
|
||||
|
||||
for option in options:
|
||||
try:
|
||||
(long, short, help) = option
|
||||
except ValueError:
|
||||
raise DistutilsGetoptError, \
|
||||
"invalid option tuple " + str (option)
|
||||
|
||||
# Type-check the option names
|
||||
if type (long) is not StringType or len (long) < 2:
|
||||
raise DistutilsGetoptError, \
|
||||
"long option '%s' must be a string of length >= 2" % \
|
||||
long
|
||||
|
||||
if (not ((short is None) or
|
||||
(type (short) is StringType and len (short) == 1))):
|
||||
raise DistutilsGetoptError, \
|
||||
"short option '%s' must be None or string of length 1" % \
|
||||
short
|
||||
|
||||
long_opts.append (long)
|
||||
|
||||
if long[-1] == '=': # option takes an argument?
|
||||
if short: short = short + ':'
|
||||
long = long[0:-1]
|
||||
takes_arg[long] = 1
|
||||
else:
|
||||
|
||||
# Is option is a "negative alias" for some other option (eg.
|
||||
# "quiet" == "!verbose")?
|
||||
alias_to = negative_opt.get(long)
|
||||
if alias_to is not None:
|
||||
if not takes_arg.has_key(alias_to) or takes_arg[alias_to]:
|
||||
raise DistutilsGetoptError, \
|
||||
("option '%s' is a negative alias for '%s', " +
|
||||
"which either hasn't been defined yet " +
|
||||
"or takes an argument") % (long, alias_to)
|
||||
|
||||
long_opts[-1] = long
|
||||
takes_arg[long] = 0
|
||||
|
||||
else:
|
||||
takes_arg[long] = 0
|
||||
|
||||
|
||||
# Now enforce some bondage on the long option name, so we can later
|
||||
# translate it to an attribute name in 'object'. Have to do this a
|
||||
# bit late to make sure we've removed any trailing '='.
|
||||
if not longopt_re.match (long):
|
||||
raise DistutilsGetoptError, \
|
||||
("invalid long option name '%s' " +
|
||||
"(must be letters, numbers, hyphens only") % long
|
||||
|
||||
attr_name[long] = string.translate (long, longopt_xlate)
|
||||
if short:
|
||||
short_opts.append (short)
|
||||
short2long[short[0]] = long
|
||||
|
||||
# end loop over 'options'
|
||||
|
||||
short_opts = string.join (short_opts)
|
||||
try:
|
||||
(opts, args) = getopt.getopt (args, short_opts, long_opts)
|
||||
except getopt.error, msg:
|
||||
raise DistutilsArgError, msg
|
||||
|
||||
for (opt, val) in opts:
|
||||
if len (opt) == 2 and opt[0] == '-': # it's a short option
|
||||
opt = short2long[opt[1]]
|
||||
|
||||
elif len (opt) > 2 and opt[0:2] == '--':
|
||||
opt = opt[2:]
|
||||
|
||||
else:
|
||||
raise RuntimeError, "getopt lies! (bad option string '%s')" % \
|
||||
opt
|
||||
|
||||
attr = attr_name[opt]
|
||||
if takes_arg[opt]:
|
||||
setattr (object, attr, val)
|
||||
else:
|
||||
if val == '':
|
||||
alias = negative_opt.get (opt)
|
||||
if alias:
|
||||
setattr (object, attr_name[alias], 0)
|
||||
else:
|
||||
setattr (object, attr, 1)
|
||||
else:
|
||||
raise RuntimeError, "getopt lies! (bad value '%s')" % value
|
||||
|
||||
# end loop over options found in 'args'
|
||||
|
||||
return args
|
||||
|
||||
# fancy_getopt()
|
||||
|
||||
|
||||
WS_TRANS = string.maketrans (string.whitespace, ' ' * len (string.whitespace))
|
||||
|
||||
def wrap_text (text, width):
|
||||
|
||||
if text is None:
|
||||
return []
|
||||
if len (text) <= width:
|
||||
return [text]
|
||||
|
||||
text = string.expandtabs (text)
|
||||
text = string.translate (text, WS_TRANS)
|
||||
chunks = re.split (r'( +|-+)', text)
|
||||
chunks = filter (None, chunks) # ' - ' results in empty strings
|
||||
lines = []
|
||||
|
||||
while chunks:
|
||||
|
||||
cur_line = [] # list of chunks (to-be-joined)
|
||||
cur_len = 0 # length of current line
|
||||
|
||||
while chunks:
|
||||
l = len (chunks[0])
|
||||
if cur_len + l <= width: # can squeeze (at least) this chunk in
|
||||
cur_line.append (chunks[0])
|
||||
del chunks[0]
|
||||
cur_len = cur_len + l
|
||||
else: # this line is full
|
||||
# drop last chunk if all space
|
||||
if cur_line and cur_line[-1][0] == ' ':
|
||||
del cur_line[-1]
|
||||
break
|
||||
|
||||
if chunks: # any chunks left to process?
|
||||
|
||||
# if the current line is still empty, then we had a single
|
||||
# chunk that's too big too fit on a line -- so we break
|
||||
# down and break it up at the line width
|
||||
if cur_len == 0:
|
||||
cur_line.append (chunks[0][0:width])
|
||||
chunks[0] = chunks[0][width:]
|
||||
|
||||
# all-whitespace chunks at the end of a line can be discarded
|
||||
# (and we know from the re.split above that if a chunk has
|
||||
# *any* whitespace, it is *all* whitespace)
|
||||
if chunks[0][0] == ' ':
|
||||
del chunks[0]
|
||||
|
||||
# and store this line in the list-of-all-lines -- as a single
|
||||
# string, of course!
|
||||
lines.append (string.join (cur_line, ''))
|
||||
|
||||
# while chunks
|
||||
|
||||
return lines
|
||||
|
||||
# wrap_text ()
|
||||
|
||||
|
||||
def generate_help (options, header=None):
|
||||
"""Generate help text (a list of strings, one per suggested line of
|
||||
output) from an option table."""
|
||||
|
||||
# Blithely assume the option table is good: probably wouldn't call
|
||||
# 'generate_help()' unless you've already called 'fancy_getopt()'.
|
||||
|
||||
# First pass: determine maximum length of long option names
|
||||
max_opt = 0
|
||||
for option in options:
|
||||
long = option[0]
|
||||
short = option[1]
|
||||
l = len (long)
|
||||
if long[-1] == '=':
|
||||
l = l - 1
|
||||
if short is not None:
|
||||
l = l + 5 # " (-x)" where short == 'x'
|
||||
if l > max_opt:
|
||||
max_opt = l
|
||||
|
||||
opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter
|
||||
|
||||
# Typical help block looks like this:
|
||||
# --foo controls foonabulation
|
||||
# Help block for longest option looks like this:
|
||||
# --flimflam set the flim-flam level
|
||||
# and with wrapped text:
|
||||
# --flimflam set the flim-flam level (must be between
|
||||
# 0 and 100, except on Tuesdays)
|
||||
# Options with short names will have the short name shown (but
|
||||
# it doesn't contribute to max_opt):
|
||||
# --foo (-f) controls foonabulation
|
||||
# If adding the short option would make the left column too wide,
|
||||
# we push the explanation off to the next line
|
||||
# --flimflam (-l)
|
||||
# set the flim-flam level
|
||||
# Important parameters:
|
||||
# - 2 spaces before option block start lines
|
||||
# - 2 dashes for each long option name
|
||||
# - min. 2 spaces between option and explanation (gutter)
|
||||
# - 5 characters (incl. space) for short option name
|
||||
|
||||
# Now generate lines of help text.
|
||||
line_width = 78 # if 80 columns were good enough for
|
||||
text_width = line_width - opt_width # Jesus, then 78 are good enough for me
|
||||
big_indent = ' ' * opt_width
|
||||
if header:
|
||||
lines = [header]
|
||||
else:
|
||||
lines = ['Option summary:']
|
||||
|
||||
for (long,short,help) in options:
|
||||
|
||||
text = wrap_text (help, text_width)
|
||||
if long[-1] == '=':
|
||||
long = long[0:-1]
|
||||
|
||||
# Case 1: no short option at all (makes life easy)
|
||||
if short is None:
|
||||
if text:
|
||||
lines.append (" --%-*s %s" % (max_opt, long, text[0]))
|
||||
else:
|
||||
lines.append (" --%-*s " % (max_opt, long))
|
||||
|
||||
for l in text[1:]:
|
||||
lines.append (big_indent + l)
|
||||
|
||||
# Case 2: we have a short option, so we have to include it
|
||||
# just after the long option
|
||||
else:
|
||||
opt_names = "%s (-%s)" % (long, short)
|
||||
if text:
|
||||
lines.append (" --%-*s %s" %
|
||||
(max_opt, opt_names, text[0]))
|
||||
else:
|
||||
lines.append (" --%-*s" % opt_names)
|
||||
|
||||
# for loop over options
|
||||
|
||||
return lines
|
||||
|
||||
# generate_help ()
|
||||
|
||||
|
||||
def print_help (options, file=None, header=None):
|
||||
if file is None:
|
||||
file = sys.stdout
|
||||
for line in generate_help (options, header):
|
||||
file.write (line + "\n")
|
||||
# print_help ()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
text = """\
|
||||
Tra-la-la, supercalifragilisticexpialidocious.
|
||||
How *do* you spell that odd word, anyways?
|
||||
(Someone ask Mary -- she'll know [or she'll
|
||||
say, "How should I know?"].)"""
|
||||
|
||||
for w in (10, 20, 30, 40):
|
||||
print "width: %d" % w
|
||||
print string.join (wrap_text (text, w), "\n")
|
||||
print
|
||||
374
distutils/msvccompiler.py
Normal file
374
distutils/msvccompiler.py
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
"""distutils.ccompiler
|
||||
|
||||
Contains MSVCCompiler, an implementation of the abstract CCompiler class
|
||||
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 sys, os, string
|
||||
from types import *
|
||||
from distutils.errors import *
|
||||
from distutils.ccompiler import \
|
||||
CCompiler, gen_preprocess_options, gen_lib_options
|
||||
|
||||
|
||||
def get_devstudio_versions ():
|
||||
"""Get list of devstudio versions from the Windows registry. Return a
|
||||
list of strings containing version numbers; the list will be
|
||||
empty if we were unable to access the registry (eg. couldn't import
|
||||
a registry-access module) or the appropriate registry keys weren't
|
||||
found."""
|
||||
|
||||
try:
|
||||
import win32api
|
||||
import win32con
|
||||
except ImportError:
|
||||
return []
|
||||
|
||||
K = 'Software\\Microsoft\\Devstudio'
|
||||
L = []
|
||||
for base in (win32con.HKEY_CLASSES_ROOT,
|
||||
win32con.HKEY_LOCAL_MACHINE,
|
||||
win32con.HKEY_CURRENT_USER,
|
||||
win32con.HKEY_USERS):
|
||||
try:
|
||||
k = win32api.RegOpenKeyEx(base,K)
|
||||
i = 0
|
||||
while 1:
|
||||
try:
|
||||
p = win32api.RegEnumKey(k,i)
|
||||
if p[0] in '123456789' and p not in L:
|
||||
L.append(p)
|
||||
except win32api.error:
|
||||
break
|
||||
i = i + 1
|
||||
except win32api.error:
|
||||
pass
|
||||
L.sort()
|
||||
L.reverse()
|
||||
return L
|
||||
|
||||
# get_devstudio_versions ()
|
||||
|
||||
|
||||
def get_msvc_paths (path, version='6.0', platform='x86'):
|
||||
"""Get a list of devstudio directories (include, lib or path). Return
|
||||
a list of strings; will be empty list if unable to access the
|
||||
registry or appropriate registry keys not found."""
|
||||
|
||||
try:
|
||||
import win32api
|
||||
import win32con
|
||||
except ImportError:
|
||||
return []
|
||||
|
||||
L = []
|
||||
if path=='lib':
|
||||
path= 'Library'
|
||||
path = string.upper(path + ' Dirs')
|
||||
K = ('Software\\Microsoft\\Devstudio\\%s\\' +
|
||||
'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
|
||||
(version,platform)
|
||||
for base in (win32con.HKEY_CLASSES_ROOT,
|
||||
win32con.HKEY_LOCAL_MACHINE,
|
||||
win32con.HKEY_CURRENT_USER,
|
||||
win32con.HKEY_USERS):
|
||||
try:
|
||||
k = win32api.RegOpenKeyEx(base,K)
|
||||
i = 0
|
||||
while 1:
|
||||
try:
|
||||
(p,v,t) = win32api.RegEnumValue(k,i)
|
||||
if string.upper(p) == path:
|
||||
V = string.split(v,';')
|
||||
for v in V:
|
||||
if v == '' or v in L: continue
|
||||
L.append(v)
|
||||
break
|
||||
i = i + 1
|
||||
except win32api.error:
|
||||
break
|
||||
except win32api.error:
|
||||
pass
|
||||
return L
|
||||
|
||||
# get_msvc_paths()
|
||||
|
||||
|
||||
def find_exe (exe, version_number):
|
||||
"""Try to find an MSVC executable program 'exe' (from version
|
||||
'version_number' of MSVC) in several places: first, one of the MSVC
|
||||
program search paths from the registry; next, the directories in the
|
||||
PATH environment variable. If any of those work, return an absolute
|
||||
path that is known to exist. If none of them work, just return the
|
||||
original program name, 'exe'."""
|
||||
|
||||
for p in get_msvc_paths ('path', version_number):
|
||||
fn = os.path.join (os.path.abspath(p), exe)
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
|
||||
# didn't find it; try existing path
|
||||
for p in string.split (os.environ['Path'],';'):
|
||||
fn = os.path.join(os.path.abspath(p),exe)
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
|
||||
return exe # last desperate hope
|
||||
|
||||
|
||||
def set_path_env_var (name, version_number):
|
||||
"""Set environment variable 'name' to an MSVC path type value obtained
|
||||
from 'get_msvc_paths()'. This is equivalent to a SET command prior
|
||||
to execution of spawned commands."""
|
||||
|
||||
p = get_msvc_paths (name, version_number)
|
||||
if p:
|
||||
os.environ[name] = string.join (p,';')
|
||||
|
||||
|
||||
class MSVCCompiler (CCompiler) :
|
||||
"""Concrete class that implements an interface to Microsoft Visual C++,
|
||||
as defined by the CCompiler abstract class."""
|
||||
|
||||
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,
|
||||
force=0):
|
||||
|
||||
CCompiler.__init__ (self, verbose, dry_run, force)
|
||||
|
||||
self.add_library_dir( os.path.join( sys.exec_prefix, 'libs' ) )
|
||||
|
||||
versions = get_devstudio_versions ()
|
||||
|
||||
if versions:
|
||||
version = versions[0] # highest version
|
||||
|
||||
self.cc = _find_exe("cl.exe", version)
|
||||
self.link = _find_exe("link.exe", version)
|
||||
self.lib = _find_exe("lib.exe", version)
|
||||
set_path_env_var ('lib', version)
|
||||
set_path_env_var ('include', version)
|
||||
path=get_msvc_paths('path', version)
|
||||
try:
|
||||
for p in string.split(os.environ['path'],';'):
|
||||
path.append(p)
|
||||
except KeyError:
|
||||
pass
|
||||
os.environ['path'] = string.join(path,';')
|
||||
else:
|
||||
# devstudio not found in the registry
|
||||
self.cc = "cl.exe"
|
||||
self.link = "link.exe"
|
||||
self.lib = "lib.exe"
|
||||
|
||||
self.preprocess_options = None
|
||||
self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3' ]
|
||||
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG']
|
||||
|
||||
self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
|
||||
self.ldflags_shared_debug = [
|
||||
'/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
|
||||
]
|
||||
self.ldflags_static = [ '/nologo']
|
||||
|
||||
|
||||
# -- Worker methods ------------------------------------------------
|
||||
|
||||
def compile (self,
|
||||
sources,
|
||||
output_dir=None,
|
||||
macros=None,
|
||||
include_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
|
||||
(output_dir, macros, include_dirs) = \
|
||||
self._fix_compile_args (output_dir, macros, include_dirs)
|
||||
(objects, skip_sources) = self._prep_compile (sources, output_dir)
|
||||
|
||||
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_opts.extend (self.compile_options_debug)
|
||||
else:
|
||||
compile_opts.extend (self.compile_options)
|
||||
|
||||
for i in range (len (sources)):
|
||||
src = sources[i] ; obj = objects[i]
|
||||
ext = (os.path.splitext (src))[1]
|
||||
|
||||
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
|
||||
|
||||
output_opt = "/Fo" + obj
|
||||
|
||||
self.mkpath (os.path.dirname (obj))
|
||||
self.spawn ([self.cc] + compile_opts + pp_opts +
|
||||
[input_opt, output_opt] +
|
||||
extra_postargs)
|
||||
|
||||
return objects
|
||||
|
||||
# compile ()
|
||||
|
||||
|
||||
def create_static_lib (self,
|
||||
objects,
|
||||
output_libname,
|
||||
output_dir=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
|
||||
(objects, output_dir) = \
|
||||
self._fix_link_args (objects, output_dir, takes_libs=0)
|
||||
output_filename = \
|
||||
self.library_filename (output_libname, output_dir=output_dir)
|
||||
|
||||
if self._need_link (objects, output_filename):
|
||||
lib_args = objects + ['/OUT:' + output_filename]
|
||||
if debug:
|
||||
pass # XXX what goes here?
|
||||
if extra_preargs:
|
||||
lib_args[:0] = extra_preargs
|
||||
if extra_postargs:
|
||||
lib_args.extend (extra_postargs)
|
||||
self.spawn ([self.link] + ld_args)
|
||||
else:
|
||||
self.announce ("skipping %s (up-to-date)" % output_filename)
|
||||
|
||||
# create_static_lib ()
|
||||
|
||||
|
||||
def link_shared_lib (self,
|
||||
objects,
|
||||
output_libname,
|
||||
output_dir=None,
|
||||
libraries=None,
|
||||
library_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
|
||||
self.link_shared_object (objects,
|
||||
self.shared_library_name(output_libname),
|
||||
output_dir=output_dir,
|
||||
libraries=libraries,
|
||||
library_dirs=library_dirs,
|
||||
debug=debug,
|
||||
extra_preargs=extra_preargs,
|
||||
extra_postargs=extra_postargs)
|
||||
|
||||
|
||||
def link_shared_object (self,
|
||||
objects,
|
||||
output_filename,
|
||||
output_dir=None,
|
||||
libraries=None,
|
||||
library_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
|
||||
(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, self.runtime_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]
|
||||
|
||||
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)
|
||||
|
||||
else:
|
||||
self.announce ("skipping %s (up-to-date)" % output_filename)
|
||||
|
||||
# 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
|
||||
|
||||
def runtime_library_dir_option (self, dir):
|
||||
raise DistutilsPlatformError, \
|
||||
"don't know how to set runtime library search path for MSVC++"
|
||||
|
||||
def library_option (self, lib):
|
||||
return self.library_filename (lib)
|
||||
|
||||
|
||||
def find_library_file (self, dirs, lib):
|
||||
|
||||
for dir in dirs:
|
||||
libfile = os.path.join (dir, self.library_filename (lib))
|
||||
if os.path.exists (libfile):
|
||||
return libfile
|
||||
|
||||
else:
|
||||
# Oops, didn't find it in *any* of 'dirs'
|
||||
return None
|
||||
|
||||
# find_library_file ()
|
||||
|
||||
# class MSVCCompiler
|
||||
149
distutils/spawn.py
Normal file
149
distutils/spawn.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
"""distutils.spawn
|
||||
|
||||
Provides the 'spawn()' function, a front-end to various platform-
|
||||
specific functions for launching another program in a sub-process."""
|
||||
|
||||
# created 1999/07/24, Greg Ward
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys, os, string
|
||||
from distutils.errors import *
|
||||
|
||||
|
||||
def spawn (cmd,
|
||||
search_path=1,
|
||||
verbose=0,
|
||||
dry_run=0):
|
||||
|
||||
"""Run another program, specified as a command list 'cmd', in a new
|
||||
process. 'cmd' is just the argument list for the new process, ie.
|
||||
cmd[0] is the program to run and cmd[1:] are the rest of its
|
||||
arguments. There is no way to run a program with a name different
|
||||
from that of its executable.
|
||||
|
||||
If 'search_path' is true (the default), the system's executable
|
||||
search path will be used to find the program; otherwise, cmd[0] must
|
||||
be the exact path to the executable. If 'verbose' is true, a
|
||||
one-line summary of the command will be printed before it is run.
|
||||
If 'dry_run' is true, the command will not actually be run.
|
||||
|
||||
Raise DistutilsExecError if running the program fails in any way;
|
||||
just return on success."""
|
||||
|
||||
if os.name == 'posix':
|
||||
_spawn_posix (cmd, search_path, verbose, dry_run)
|
||||
elif os.name == 'nt':
|
||||
_spawn_nt (cmd, search_path, verbose, dry_run)
|
||||
else:
|
||||
raise DistutilsPlatformError, \
|
||||
"don't know how to spawn programs on platform '%s'" % os.name
|
||||
|
||||
# spawn ()
|
||||
|
||||
|
||||
def _nt_quote_args (args):
|
||||
"""Obscure quoting command line arguments on NT.
|
||||
Simply quote every argument which contains blanks."""
|
||||
|
||||
# XXX this doesn't seem very robust to me -- but if the Windows guys
|
||||
# say it'll work, I guess I'll have to accept it. (What if an arg
|
||||
# contains quotes? What other magic characters, other than spaces,
|
||||
# have to be escaped? Is there an escaping mechanism other than
|
||||
# quoting?)
|
||||
|
||||
for i in range (len (args)):
|
||||
if string.find (args[i], ' ') == -1:
|
||||
args[i] = '"%s"' % args[i]
|
||||
return args
|
||||
|
||||
def _spawn_nt (cmd,
|
||||
search_path=1,
|
||||
verbose=0,
|
||||
dry_run=0):
|
||||
|
||||
executable = cmd[0]
|
||||
cmd = _nt_quote_args (cmd)
|
||||
if search_path:
|
||||
paths = string.split( os.environ['PATH'], os.pathsep)
|
||||
base,ext = os.path.splitext(executable)
|
||||
if (ext != '.exe'):
|
||||
executable = executable + '.exe'
|
||||
if not os.path.isfile(executable):
|
||||
paths.reverse() # go over the paths and keep the last one
|
||||
for p in paths:
|
||||
f = os.path.join( p, executable )
|
||||
if os.path.isfile ( f ):
|
||||
# the file exists, we have a shot at spawn working
|
||||
executable = f
|
||||
if verbose:
|
||||
print string.join ([executable] + cmd[1:], ' ')
|
||||
if not dry_run:
|
||||
# spawn for NT requires a full path to the .exe
|
||||
try:
|
||||
rc = os.spawnv (os.P_WAIT, executable, cmd)
|
||||
except OSError, exc:
|
||||
# this seems to happen when the command isn't found
|
||||
raise DistutilsExecError, \
|
||||
"command '%s' failed: %s" % (cmd[0], exc[-1])
|
||||
if rc != 0:
|
||||
# and this reflects the command running but failing
|
||||
raise DistutilsExecError, \
|
||||
"command '%s' failed with exit status %d" % (cmd[0], rc)
|
||||
|
||||
|
||||
|
||||
def _spawn_posix (cmd,
|
||||
search_path=1,
|
||||
verbose=0,
|
||||
dry_run=0):
|
||||
|
||||
if verbose:
|
||||
print string.join (cmd, ' ')
|
||||
if dry_run:
|
||||
return
|
||||
exec_fn = search_path and os.execvp or os.execv
|
||||
|
||||
pid = os.fork ()
|
||||
|
||||
if pid == 0: # in the child
|
||||
try:
|
||||
#print "cmd[0] =", cmd[0]
|
||||
#print "cmd =", cmd
|
||||
exec_fn (cmd[0], cmd)
|
||||
except OSError, e:
|
||||
sys.stderr.write ("unable to execute %s: %s\n" %
|
||||
(cmd[0], e.strerror))
|
||||
os._exit (1)
|
||||
|
||||
sys.stderr.write ("unable to execute %s for unknown reasons" % cmd[0])
|
||||
os._exit (1)
|
||||
|
||||
|
||||
else: # in the parent
|
||||
# Loop until the child either exits or is terminated by a signal
|
||||
# (ie. keep waiting if it's merely stopped)
|
||||
while 1:
|
||||
(pid, status) = os.waitpid (pid, 0)
|
||||
if os.WIFSIGNALED (status):
|
||||
raise DistutilsExecError, \
|
||||
"command '%s' terminated by signal %d" % \
|
||||
(cmd[0], os.WTERMSIG (status))
|
||||
|
||||
elif os.WIFEXITED (status):
|
||||
exit_status = os.WEXITSTATUS (status)
|
||||
if exit_status == 0:
|
||||
return # hey, it succeeded!
|
||||
else:
|
||||
raise DistutilsExecError, \
|
||||
"command '%s' failed with exit status %d" % \
|
||||
(cmd[0], exit_status)
|
||||
|
||||
elif os.WIFSTOPPED (status):
|
||||
continue
|
||||
|
||||
else:
|
||||
raise DistutilsExecError, \
|
||||
"unknown error executing '%s': termination status %d" % \
|
||||
(cmd[0], status)
|
||||
# _spawn_posix ()
|
||||
266
distutils/sysconfig.py
Normal file
266
distutils/sysconfig.py
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
"""Provide access to Python's configuration information. The specific names
|
||||
defined in the module depend heavily on the platform and configuration.
|
||||
|
||||
Written by: Fred L. Drake, Jr.
|
||||
Email: <fdrake@acm.org>
|
||||
Initial date: 17-Dec-1998
|
||||
"""
|
||||
|
||||
__version__ = "$Revision$"
|
||||
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
|
||||
from errors import DistutilsPlatformError
|
||||
|
||||
|
||||
prefix = os.path.normpath(sys.prefix)
|
||||
exec_prefix = os.path.normpath(sys.exec_prefix)
|
||||
|
||||
|
||||
def get_python_inc(plat_specific=0):
|
||||
"""Return the directory containing installed Python header files.
|
||||
|
||||
If 'plat_specific' is false (the default), this is the path to the
|
||||
non-platform-specific header files, i.e. Python.h and so on;
|
||||
otherwise, this is the path to platform-specific header files
|
||||
(namely config.h).
|
||||
|
||||
"""
|
||||
the_prefix = (plat_specific and exec_prefix or prefix)
|
||||
if os.name == "posix":
|
||||
return os.path.join(the_prefix, "include", "python" + sys.version[:3])
|
||||
elif os.name == "nt":
|
||||
return os.path.join(the_prefix, "Include") # include or Include?
|
||||
elif os.name == "mac":
|
||||
return os.path.join(the_prefix, "Include")
|
||||
else:
|
||||
raise DistutilsPlatformError, \
|
||||
("I don't know where Python installs its C header files " +
|
||||
"on platform '%s'") % os.name
|
||||
|
||||
|
||||
def get_python_lib(plat_specific=0, standard_lib=0):
|
||||
"""Return the directory containing the Python library (standard or
|
||||
site additions).
|
||||
|
||||
If 'plat_specific' is true, return the directory containing
|
||||
platform-specific modules, i.e. any module from a non-pure-Python
|
||||
module distribution; otherwise, return the platform-shared library
|
||||
directory. If 'standard_lib' is true, return the directory
|
||||
containing standard Python library modules; otherwise, return the
|
||||
directory for site-specific modules.
|
||||
|
||||
"""
|
||||
the_prefix = (plat_specific and exec_prefix or prefix)
|
||||
|
||||
if os.name == "posix":
|
||||
libpython = os.path.join(the_prefix,
|
||||
"lib", "python" + sys.version[:3])
|
||||
if standard_lib:
|
||||
return libpython
|
||||
else:
|
||||
return os.path.join(libpython, "site-packages")
|
||||
|
||||
elif os.name == "nt":
|
||||
if standard_lib:
|
||||
return os.path.join(the_prefix, "Lib")
|
||||
else:
|
||||
return the_prefix
|
||||
|
||||
elif os.name == "mac":
|
||||
if platform_specific:
|
||||
if standard_lib:
|
||||
return os.path.join(exec_prefix, "Mac", "Plugins")
|
||||
else:
|
||||
raise DistutilsPlatformError, \
|
||||
"OK, where DO site-specific extensions go on the Mac?"
|
||||
else:
|
||||
if standard_lib:
|
||||
return os.path.join(prefix, "Lib")
|
||||
else:
|
||||
raise DistutilsPlatformError, \
|
||||
"OK, where DO site-specific modules go on the Mac?"
|
||||
else:
|
||||
raise DistutilsPlatformError, \
|
||||
("I don't know where Python installs its library " +
|
||||
"on platform '%s'") % os.name
|
||||
|
||||
# get_python_lib()
|
||||
|
||||
|
||||
def get_config_h_filename():
|
||||
"""Return full pathname of installed config.h file."""
|
||||
inc_dir = get_python_inc(plat_specific=1)
|
||||
return os.path.join(inc_dir, "config.h")
|
||||
|
||||
|
||||
def get_makefile_filename():
|
||||
"""Return full pathname of installed Makefile from the Python build."""
|
||||
lib_dir = get_python_lib(plat_specific=1, standard_lib=1)
|
||||
return os.path.join(lib_dir, "config", "Makefile")
|
||||
|
||||
|
||||
def parse_config_h(fp, g=None):
|
||||
"""Parse a config.h-style file.
|
||||
|
||||
A dictionary containing name/value pairs is returned. If an
|
||||
optional dictionary is passed in as the second argument, it is
|
||||
used instead of a new dictionary.
|
||||
"""
|
||||
if g is None:
|
||||
g = {}
|
||||
define_rx = re.compile("#define ([A-Z][A-Z0-9_]+) (.*)\n")
|
||||
undef_rx = re.compile("/[*] #undef ([A-Z][A-Z0-9_]+) [*]/\n")
|
||||
#
|
||||
while 1:
|
||||
line = fp.readline()
|
||||
if not line:
|
||||
break
|
||||
m = define_rx.match(line)
|
||||
if m:
|
||||
n, v = m.group(1, 2)
|
||||
try: v = string.atoi(v)
|
||||
except ValueError: pass
|
||||
g[n] = v
|
||||
else:
|
||||
m = undef_rx.match(line)
|
||||
if m:
|
||||
g[m.group(1)] = 0
|
||||
return g
|
||||
|
||||
def parse_makefile(fp, g=None):
|
||||
"""Parse a Makefile-style file.
|
||||
|
||||
A dictionary containing name/value pairs is returned. If an
|
||||
optional dictionary is passed in as the second argument, it is
|
||||
used instead of a new dictionary.
|
||||
|
||||
"""
|
||||
if g is None:
|
||||
g = {}
|
||||
variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)\n")
|
||||
done = {}
|
||||
notdone = {}
|
||||
#
|
||||
while 1:
|
||||
line = fp.readline()
|
||||
if not line:
|
||||
break
|
||||
m = variable_rx.match(line)
|
||||
if m:
|
||||
n, v = m.group(1, 2)
|
||||
v = string.strip(v)
|
||||
if "$" in v:
|
||||
notdone[n] = v
|
||||
else:
|
||||
try: v = string.atoi(v)
|
||||
except ValueError: pass
|
||||
done[n] = v
|
||||
|
||||
# do variable interpolation here
|
||||
findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
|
||||
findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
|
||||
while notdone:
|
||||
for name in notdone.keys():
|
||||
value = notdone[name]
|
||||
m = findvar1_rx.search(value)
|
||||
if not m:
|
||||
m = findvar2_rx.search(value)
|
||||
if m:
|
||||
n = m.group(1)
|
||||
if done.has_key(n):
|
||||
after = value[m.end():]
|
||||
value = value[:m.start()] + done[n] + after
|
||||
if "$" in after:
|
||||
notdone[name] = value
|
||||
else:
|
||||
try: value = string.atoi(value)
|
||||
except ValueError: pass
|
||||
done[name] = string.strip(value)
|
||||
del notdone[name]
|
||||
elif notdone.has_key(n):
|
||||
# get it on a subsequent round
|
||||
pass
|
||||
else:
|
||||
done[n] = ""
|
||||
after = value[m.end():]
|
||||
value = value[:m.start()] + after
|
||||
if "$" in after:
|
||||
notdone[name] = value
|
||||
else:
|
||||
try: value = string.atoi(value)
|
||||
except ValueError: pass
|
||||
done[name] = string.strip(value)
|
||||
del notdone[name]
|
||||
else:
|
||||
# bogus variable reference; just drop it since we can't deal
|
||||
del notdone[name]
|
||||
|
||||
# save the results in the global dictionary
|
||||
g.update(done)
|
||||
return g
|
||||
|
||||
|
||||
def _init_posix():
|
||||
"""Initialize the module as appropriate for POSIX systems."""
|
||||
g = globals()
|
||||
# load the installed config.h:
|
||||
parse_config_h(open(get_config_h_filename()), g)
|
||||
# load the installed Makefile:
|
||||
parse_makefile(open(get_makefile_filename()), g)
|
||||
|
||||
|
||||
def _init_nt():
|
||||
"""Initialize the module as appropriate for NT"""
|
||||
g = globals()
|
||||
# load config.h, though I don't know how useful this is
|
||||
parse_config_h(open(get_config_h_filename()), g)
|
||||
# set basic install directories
|
||||
g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
|
||||
g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
|
||||
|
||||
# XXX hmmm.. a normal install puts include files here
|
||||
g['INCLUDEPY'] = get_python_inc(plat_specific=0)
|
||||
|
||||
g['SO'] = '.pyd'
|
||||
g['exec_prefix'] = exec_prefix
|
||||
|
||||
|
||||
def _init_mac():
|
||||
"""Initialize the module as appropriate for Macintosh systems"""
|
||||
g = globals()
|
||||
# load the installed config.h (what if not installed? - still need to
|
||||
# be able to install packages which don't require compilation)
|
||||
parse_config_h(open(
|
||||
os.path.join(sys.exec_prefix, "Mac", "Include", "config.h")), g)
|
||||
# set basic install directories
|
||||
g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
|
||||
g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
|
||||
|
||||
# XXX hmmm.. a normal install puts include files here
|
||||
g['INCLUDEPY'] = get_python_inc(plat_specific=0)
|
||||
|
||||
g['SO'] = '.ppc.slb'
|
||||
g['exec_prefix'] = sys.exec_prefix
|
||||
print sys.prefix
|
||||
|
||||
# XXX are these used anywhere?
|
||||
g['install_lib'] = os.path.join(sys.exec_prefix, "Lib")
|
||||
g['install_platlib'] = os.path.join(sys.exec_prefix, "Mac", "Lib")
|
||||
|
||||
|
||||
try:
|
||||
exec "_init_" + os.name
|
||||
except NameError:
|
||||
# not needed for this platform
|
||||
pass
|
||||
else:
|
||||
exec "_init_%s()" % os.name
|
||||
|
||||
|
||||
del _init_posix
|
||||
del _init_nt
|
||||
del _init_mac
|
||||
355
distutils/text_file.py
Normal file
355
distutils/text_file.py
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
"""text_file
|
||||
|
||||
provides the TextFile class, which gives an interface to text files
|
||||
that (optionally) takes care of stripping comments, ignoring blank
|
||||
lines, and joining lines with backslashes."""
|
||||
|
||||
# created 1999/01/12, Greg Ward
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from types import *
|
||||
import sys, os, string, re
|
||||
|
||||
|
||||
class TextFile:
|
||||
|
||||
"""Provides a file-like object that takes care of all the things you
|
||||
commonly want to do when processing a text file that has some
|
||||
line-by-line syntax: strip comments (as long as "#" is your comment
|
||||
character), skip blank lines, join adjacent lines by escaping the
|
||||
newline (ie. backslash at end of line), strip leading and/or
|
||||
trailing whitespace, and collapse internal whitespace. All of these
|
||||
are optional and independently controllable.
|
||||
|
||||
Provides a 'warn()' method so you can generate warning messages that
|
||||
report physical line number, even if the logical line in question
|
||||
spans multiple physical lines. Also provides 'unreadline()' for
|
||||
implementing line-at-a-time lookahead.
|
||||
|
||||
Constructor is called as:
|
||||
|
||||
TextFile (filename=None, file=None, **options)
|
||||
|
||||
It bombs (RuntimeError) if both 'filename' and 'file' are None;
|
||||
'filename' should be a string, and 'file' a file object (or
|
||||
something that provides 'readline()' and 'close()' methods). It is
|
||||
recommended that you supply at least 'filename', so that TextFile
|
||||
can include it in warning messages. If 'file' is not supplied,
|
||||
TextFile creates its own using the 'open()' builtin.
|
||||
|
||||
The options are all boolean, and affect the value returned by
|
||||
'readline()':
|
||||
strip_comments [default: true]
|
||||
strip from "#" to end-of-line, as well as any whitespace
|
||||
leading up to the "#" -- unless it is escaped by a backslash
|
||||
lstrip_ws [default: false]
|
||||
strip leading whitespace from each line before returning it
|
||||
rstrip_ws [default: true]
|
||||
strip trailing whitespace (including line terminator!) from
|
||||
each line before returning it
|
||||
skip_blanks [default: true}
|
||||
skip lines that are empty *after* stripping comments and
|
||||
whitespace. (If both lstrip_ws and rstrip_ws are true,
|
||||
then some lines may consist of solely whitespace: these will
|
||||
*not* be skipped, even if 'skip_blanks' is true.)
|
||||
join_lines [default: false]
|
||||
if a backslash is the last non-newline character on a line
|
||||
after stripping comments and whitespace, join the following line
|
||||
to it to form one "logical line"; if N consecutive lines end
|
||||
with a backslash, then N+1 physical lines will be joined to
|
||||
form one logical line.
|
||||
collapse_ws [default: false]
|
||||
after stripping comments and whitespace and joining physical
|
||||
lines into logical lines, all internal whitespace (strings of
|
||||
whitespace surrounded by non-whitespace characters, and not at
|
||||
the beginning or end of the logical line) will be collapsed
|
||||
to a single space.
|
||||
|
||||
Note that since 'rstrip_ws' can strip the trailing newline, the
|
||||
semantics of 'readline()' must differ from those of the builtin file
|
||||
object's 'readline()' method! In particular, 'readline()' returns
|
||||
None for end-of-file: an empty string might just be a blank line (or
|
||||
an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is
|
||||
not."""
|
||||
|
||||
default_options = { 'strip_comments': 1,
|
||||
'skip_blanks': 1,
|
||||
'join_lines': 0,
|
||||
'lstrip_ws': 0,
|
||||
'rstrip_ws': 1,
|
||||
'collapse_ws': 0,
|
||||
}
|
||||
|
||||
def __init__ (self, filename=None, file=None, **options):
|
||||
"""Construct a new TextFile object. At least one of 'filename'
|
||||
(a string) and 'file' (a file-like object) must be supplied.
|
||||
They keyword argument options are described above and affect
|
||||
the values returned by 'readline()'."""
|
||||
|
||||
if filename is None and file is None:
|
||||
raise RuntimeError, \
|
||||
"you must supply either or both of 'filename' and 'file'"
|
||||
|
||||
# set values for all options -- either from client option hash
|
||||
# or fallback to default_options
|
||||
for opt in self.default_options.keys():
|
||||
if options.has_key (opt):
|
||||
setattr (self, opt, options[opt])
|
||||
|
||||
else:
|
||||
setattr (self, opt, self.default_options[opt])
|
||||
|
||||
# sanity check client option hash
|
||||
for opt in options.keys():
|
||||
if not self.default_options.has_key (opt):
|
||||
raise KeyError, "invalid TextFile option '%s'" % opt
|
||||
|
||||
if file is None:
|
||||
self.open (filename)
|
||||
else:
|
||||
self.filename = filename
|
||||
self.file = file
|
||||
self.current_line = 0 # assuming that file is at BOF!
|
||||
|
||||
# 'linebuf' is a stack of lines that will be emptied before we
|
||||
# actually read from the file; it's only populated by an
|
||||
# 'unreadline()' operation
|
||||
self.linebuf = []
|
||||
|
||||
|
||||
def open (self, filename):
|
||||
"""Open a new file named 'filename'. This overrides both the
|
||||
'filename' and 'file' arguments to the constructor."""
|
||||
|
||||
self.filename = filename
|
||||
self.file = open (self.filename, 'r')
|
||||
self.current_line = 0
|
||||
|
||||
|
||||
def close (self):
|
||||
"""Close the current file and forget everything we know about it
|
||||
(filename, current line number)."""
|
||||
|
||||
self.file.close ()
|
||||
self.file = None
|
||||
self.filename = None
|
||||
self.current_line = None
|
||||
|
||||
|
||||
def warn (self, msg, line=None):
|
||||
"""Print (to stderr) a warning message tied to the current logical
|
||||
line in the current file. If the current logical line in the
|
||||
file spans multiple physical lines, the warning refers to the
|
||||
whole range, eg. "lines 3-5". If 'line' supplied, it overrides
|
||||
the current line number; it may be a list or tuple to indicate a
|
||||
range of physical lines, or an integer for a single physical
|
||||
line."""
|
||||
|
||||
if line is None:
|
||||
line = self.current_line
|
||||
sys.stderr.write (self.filename + ", ")
|
||||
if type (line) in (ListType, TupleType):
|
||||
sys.stderr.write ("lines %d-%d: " % tuple (line))
|
||||
else:
|
||||
sys.stderr.write ("line %d: " % line)
|
||||
sys.stderr.write (str (msg) + "\n")
|
||||
|
||||
|
||||
def readline (self):
|
||||
"""Read and return a single logical line from the current file (or
|
||||
from an internal buffer if lines have previously been "unread"
|
||||
with 'unreadline()'). If the 'join_lines' option is true, this
|
||||
may involve reading multiple physical lines concatenated into a
|
||||
single string. Updates the current line number, so calling
|
||||
'warn()' after 'readline()' emits a warning about the physical
|
||||
line(s) just read. Returns None on end-of-file, since the empty
|
||||
string can occur if 'rstrip_ws' is true but 'strip_blanks' is
|
||||
not."""
|
||||
|
||||
# If any "unread" lines waiting in 'linebuf', return the top
|
||||
# one. (We don't actually buffer read-ahead data -- lines only
|
||||
# get put in 'linebuf' if the client explicitly does an
|
||||
# 'unreadline()'.
|
||||
if self.linebuf:
|
||||
line = self.linebuf[-1]
|
||||
del self.linebuf[-1]
|
||||
return line
|
||||
|
||||
buildup_line = ''
|
||||
|
||||
while 1:
|
||||
# read the line, make it None if EOF
|
||||
line = self.file.readline()
|
||||
if line == '': line = None
|
||||
|
||||
if self.strip_comments and line:
|
||||
|
||||
# Look for the first "#" in the line. If none, never
|
||||
# mind. If we find one and it's the first character, or
|
||||
# is not preceded by "\", then it starts a comment --
|
||||
# strip the comment, strip whitespace before it, and
|
||||
# carry on. Otherwise, it's just an escaped "#", so
|
||||
# unescape it (and any other escaped "#"'s that might be
|
||||
# lurking in there) and otherwise leave the line alone.
|
||||
|
||||
pos = string.find (line, "#")
|
||||
if pos == -1: # no "#" -- no comments
|
||||
pass
|
||||
elif pos == 0 or line[pos-1] != "\\": # it's a comment
|
||||
|
||||
# Have to preserve the trailing newline, because it's
|
||||
# the job of a later step (rstrip_ws) to remove it --
|
||||
# and if rstrip_ws is false, we'd better preserve it!
|
||||
# (NB. this means that if the final line is all comment
|
||||
# and has no trailing newline, we will think that it's
|
||||
# EOF; I think that's OK.)
|
||||
eol = (line[-1] == '\n') and '\n' or ''
|
||||
line = line[0:pos] + eol
|
||||
|
||||
else: # it's an escaped "#"
|
||||
line = string.replace (line, "\\#", "#")
|
||||
|
||||
|
||||
# did previous line end with a backslash? then accumulate
|
||||
if self.join_lines and buildup_line:
|
||||
# oops: end of file
|
||||
if line is None:
|
||||
self.warn ("continuation line immediately precedes "
|
||||
"end-of-file")
|
||||
return buildup_line
|
||||
|
||||
line = buildup_line + line
|
||||
|
||||
# careful: pay attention to line number when incrementing it
|
||||
if type (self.current_line) is ListType:
|
||||
self.current_line[1] = self.current_line[1] + 1
|
||||
else:
|
||||
self.current_line = [self.current_line, self.current_line+1]
|
||||
# just an ordinary line, read it as usual
|
||||
else:
|
||||
if line is None: # eof
|
||||
return None
|
||||
|
||||
# still have to be careful about incrementing the line number!
|
||||
if type (self.current_line) is ListType:
|
||||
self.current_line = self.current_line[1] + 1
|
||||
else:
|
||||
self.current_line = self.current_line + 1
|
||||
|
||||
|
||||
# strip whitespace however the client wants (leading and
|
||||
# trailing, or one or the other, or neither)
|
||||
if self.lstrip_ws and self.rstrip_ws:
|
||||
line = string.strip (line)
|
||||
elif self.lstrip_ws:
|
||||
line = string.lstrip (line)
|
||||
elif self.rstrip_ws:
|
||||
line = string.rstrip (line)
|
||||
|
||||
# blank line (whether we rstrip'ed or not)? skip to next line
|
||||
# if appropriate
|
||||
if line == '' or line == '\n' and self.skip_blanks:
|
||||
continue
|
||||
|
||||
if self.join_lines:
|
||||
if line[-1] == '\\':
|
||||
buildup_line = line[:-1]
|
||||
continue
|
||||
|
||||
if line[-2:] == '\\\n':
|
||||
buildup_line = line[0:-2] + '\n'
|
||||
continue
|
||||
|
||||
# collapse internal whitespace (*after* joining lines!)
|
||||
if self.collapse_ws:
|
||||
line = re.sub (r'(\S)\s+(\S)', r'\1 \2', line)
|
||||
|
||||
# well, I guess there's some actual content there: return it
|
||||
return line
|
||||
|
||||
# end readline
|
||||
|
||||
|
||||
def readlines (self):
|
||||
"""Read and return the list of all logical lines remaining in the
|
||||
current file."""
|
||||
|
||||
lines = []
|
||||
while 1:
|
||||
line = self.readline()
|
||||
if line is None:
|
||||
return lines
|
||||
lines.append (line)
|
||||
|
||||
|
||||
def unreadline (self, line):
|
||||
"""Push 'line' (a string) onto an internal buffer that will be
|
||||
checked by future 'readline()' calls. Handy for implementing
|
||||
a parser with line-at-a-time lookahead."""
|
||||
|
||||
self.linebuf.append (line)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_data = """# test file
|
||||
|
||||
line 3 \\
|
||||
continues on next line
|
||||
"""
|
||||
|
||||
|
||||
# result 1: no fancy options
|
||||
result1 = map (lambda x: x + "\n", string.split (test_data, "\n")[0:-1])
|
||||
|
||||
# result 2: just strip comments
|
||||
result2 = ["\n", "\n", "line 3 \\\n", "continues on next line\n"]
|
||||
|
||||
# result 3: just strip blank lines
|
||||
result3 = ["# test file\n", "line 3 \\\n", "continues on next line\n"]
|
||||
|
||||
# result 4: default, strip comments, blank lines, and trailing whitespace
|
||||
result4 = ["line 3 \\", "continues on next line"]
|
||||
|
||||
# result 5: full processing, strip comments and blanks, plus join lines
|
||||
result5 = ["line 3 continues on next line"]
|
||||
|
||||
def test_input (count, description, file, expected_result):
|
||||
result = file.readlines ()
|
||||
# result = string.join (result, '')
|
||||
if result == expected_result:
|
||||
print "ok %d (%s)" % (count, description)
|
||||
else:
|
||||
print "not ok %d (%s):" % (count, description)
|
||||
print "** expected:"
|
||||
print expected_result
|
||||
print "** received:"
|
||||
print result
|
||||
|
||||
|
||||
filename = "test.txt"
|
||||
out_file = open (filename, "w")
|
||||
out_file.write (test_data)
|
||||
out_file.close ()
|
||||
|
||||
in_file = TextFile (filename, strip_comments=0, skip_blanks=0,
|
||||
lstrip_ws=0, rstrip_ws=0)
|
||||
test_input (1, "no processing", in_file, result1)
|
||||
|
||||
in_file = TextFile (filename, strip_comments=1, skip_blanks=0,
|
||||
lstrip_ws=0, rstrip_ws=0)
|
||||
test_input (2, "strip comments", in_file, result2)
|
||||
|
||||
in_file = TextFile (filename, strip_comments=0, skip_blanks=1,
|
||||
lstrip_ws=0, rstrip_ws=0)
|
||||
test_input (3, "strip blanks", in_file, result3)
|
||||
|
||||
in_file = TextFile (filename)
|
||||
test_input (4, "default processing", in_file, result4)
|
||||
|
||||
in_file = TextFile (filename, strip_comments=1, skip_blanks=1,
|
||||
join_lines=1, rstrip_ws=1)
|
||||
test_input (5, "full processing", in_file, result5)
|
||||
|
||||
os.remove (filename)
|
||||
|
||||
304
distutils/unixccompiler.py
Normal file
304
distutils/unixccompiler.py
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
"""distutils.unixccompiler
|
||||
|
||||
Contains the UnixCCompiler class, a subclass of CCompiler that handles
|
||||
the "typical" Unix-style command-line C compiler:
|
||||
* macros defined with -Dname[=value]
|
||||
* macros undefined with -Uname
|
||||
* include search directories specified with -Idir
|
||||
* libraries specified with -lllib
|
||||
* library search directories specified with -Ldir
|
||||
* compile handled by 'cc' (or similar) executable with -c option:
|
||||
compiles .c to .o
|
||||
* link static library handled by 'ar' command (possibly with 'ranlib')
|
||||
* link shared library handled by 'cc -shared'
|
||||
"""
|
||||
|
||||
# created 1999/07/05, Greg Ward
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import string, re, os
|
||||
from types import *
|
||||
from copy import copy
|
||||
from distutils.sysconfig import \
|
||||
CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO
|
||||
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
|
||||
# Makefile and live with it. Is this adequate? If not, we might
|
||||
# have to have a bunch of subclasses GNUCCompiler, SGICCompiler,
|
||||
# SunCCompiler, and I suspect down that road lies madness.
|
||||
# * even if we don't know a warning flag from an optimization flag,
|
||||
# we need some way for outsiders to feed preprocessor/compiler/linker
|
||||
# flags in to us -- eg. a sysadmin might want to mandate certain flags
|
||||
# via a site config file, or a user might want to set something for
|
||||
# compiling this module distribution only via the setup.py command
|
||||
# line, whatever. As long as these options come from something on the
|
||||
# current system, they can be as system-dependent as they like, and we
|
||||
# should just happily stuff them into the preprocessor/compiler/linker
|
||||
# options and carry on.
|
||||
|
||||
|
||||
class UnixCCompiler (CCompiler):
|
||||
|
||||
# XXX perhaps there should really be *three* kinds of include
|
||||
# directories: those built in to the preprocessor, those from Python's
|
||||
# Makefiles, and those supplied to {add,set}_include_dirs(). Currently
|
||||
# we make no distinction between the latter two at this point; it's all
|
||||
# up to the client class to select the include directories to use above
|
||||
# and beyond the compiler's defaults. That is, both the Python include
|
||||
# directories and any module- or package-specific include directories
|
||||
# are specified via {add,set}_include_dirs(), and there's no way to
|
||||
# distinguish them. This might be a bug.
|
||||
|
||||
compiler_type = 'unix'
|
||||
|
||||
# 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
|
||||
# constructor if we need platform-specific guesswork.
|
||||
archiver = "ar"
|
||||
archiver_options = "-cr"
|
||||
|
||||
|
||||
def __init__ (self,
|
||||
verbose=0,
|
||||
dry_run=0,
|
||||
force=0):
|
||||
|
||||
CCompiler.__init__ (self, verbose, dry_run, force)
|
||||
|
||||
self.preprocess_options = None
|
||||
self.compile_options = None
|
||||
|
||||
# Munge CC and OPT together in case there are flags stuck in CC.
|
||||
# Note that using these variables from sysconfig immediately makes
|
||||
# this module specific to building Python extensions and
|
||||
# inappropriate as a general-purpose C compiler front-end. So sue
|
||||
# me. Note also that we use OPT rather than CFLAGS, because CFLAGS
|
||||
# is the flags used to compile Python itself -- not only are there
|
||||
# -I options in there, they are the *wrong* -I options. We'll
|
||||
# leave selection of include directories up to the class using
|
||||
# UnixCCompiler!
|
||||
|
||||
(self.cc, self.ccflags) = \
|
||||
_split_command (CC + ' ' + OPT)
|
||||
self.ccflags_shared = string.split (CCSHARED)
|
||||
|
||||
(self.ld_shared, self.ldflags_shared) = \
|
||||
_split_command (LDSHARED)
|
||||
|
||||
self.ld_exec = self.cc
|
||||
|
||||
# __init__ ()
|
||||
|
||||
|
||||
def compile (self,
|
||||
sources,
|
||||
output_dir=None,
|
||||
macros=None,
|
||||
include_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
|
||||
(output_dir, macros, include_dirs) = \
|
||||
self._fix_compile_args (output_dir, macros, include_dirs)
|
||||
(objects, skip_sources) = self._prep_compile (sources, output_dir)
|
||||
|
||||
# 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']
|
||||
if extra_preargs:
|
||||
cc_args[:0] = extra_preargs
|
||||
if extra_postargs is None:
|
||||
extra_postargs = []
|
||||
|
||||
# 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 *all* object filenames, not just the ones we just built.
|
||||
return objects
|
||||
|
||||
# compile ()
|
||||
|
||||
|
||||
def create_static_lib (self,
|
||||
objects,
|
||||
output_libname,
|
||||
output_dir=None,
|
||||
debug=0):
|
||||
|
||||
(objects, output_dir) = self._fix_link_args (objects, output_dir, takes_libs=0)
|
||||
|
||||
output_filename = \
|
||||
self.library_filename (output_libname, output_dir=output_dir)
|
||||
|
||||
if self._need_link (objects, output_filename):
|
||||
self.mkpath (os.path.dirname (output_filename))
|
||||
self.spawn ([self.archiver,
|
||||
self.archiver_options,
|
||||
output_filename] +
|
||||
objects + self.objects)
|
||||
else:
|
||||
self.announce ("skipping %s (up-to-date)" % output_filename)
|
||||
|
||||
# create_static_lib ()
|
||||
|
||||
|
||||
def link_shared_lib (self,
|
||||
objects,
|
||||
output_libname,
|
||||
output_dir=None,
|
||||
libraries=None,
|
||||
library_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
self.link_shared_object (
|
||||
objects,
|
||||
self.shared_library_filename (output_libname),
|
||||
output_dir,
|
||||
libraries,
|
||||
library_dirs,
|
||||
debug,
|
||||
extra_preargs,
|
||||
extra_postargs)
|
||||
|
||||
|
||||
def link_shared_object (self,
|
||||
objects,
|
||||
output_filename,
|
||||
output_dir=None,
|
||||
libraries=None,
|
||||
library_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
|
||||
(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, self.runtime_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):
|
||||
ld_args = (self.ldflags_shared + objects + self.objects +
|
||||
lib_opts + ['-o', output_filename])
|
||||
if debug:
|
||||
ld_args[:0] = ['-g']
|
||||
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.ld_shared] + ld_args)
|
||||
else:
|
||||
self.announce ("skipping %s (up-to-date)" % output_filename)
|
||||
|
||||
# link_shared_object ()
|
||||
|
||||
|
||||
def link_executable (self,
|
||||
objects,
|
||||
output_progname,
|
||||
output_dir=None,
|
||||
libraries=None,
|
||||
library_dirs=None,
|
||||
debug=0,
|
||||
extra_preargs=None,
|
||||
extra_postargs=None):
|
||||
|
||||
(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, self.runtime_library_dirs,
|
||||
libraries)
|
||||
output_filename = output_progname # Unix-ism!
|
||||
if output_dir is not None:
|
||||
output_filename = os.path.join (output_dir, 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:
|
||||
ld_args[:0] = extra_preargs
|
||||
if extra_postargs:
|
||||
ld_args.extend (extra_postargs)
|
||||
self.mkpath (os.path.dirname (output_filename))
|
||||
self.spawn ([self.ld_exec] + ld_args)
|
||||
else:
|
||||
self.announce ("skipping %s (up-to-date)" % output_filename)
|
||||
|
||||
# link_executable ()
|
||||
|
||||
|
||||
# -- Miscellaneous methods -----------------------------------------
|
||||
# These are all used by the 'gen_lib_options() function, in
|
||||
# ccompiler.py.
|
||||
|
||||
def library_dir_option (self, dir):
|
||||
return "-L" + dir
|
||||
|
||||
def runtime_library_dir_option (self, dir):
|
||||
return "-R" + dir
|
||||
|
||||
def library_option (self, lib):
|
||||
return "-l" + lib
|
||||
|
||||
|
||||
def find_library_file (self, dirs, lib):
|
||||
|
||||
for dir in dirs:
|
||||
shared = os.path.join (dir, self.shared_library_filename (lib))
|
||||
static = os.path.join (dir, self.library_filename (lib))
|
||||
|
||||
# We're second-guessing the linker here, with not much hard
|
||||
# data to go on: GCC seems to prefer the shared library, so I'm
|
||||
# assuming that *all* Unix C compilers do. And of course I'm
|
||||
# ignoring even GCC's "-static" option. So sue me.
|
||||
if os.path.exists (shared):
|
||||
return shared
|
||||
elif os.path.exists (static):
|
||||
return static
|
||||
|
||||
else:
|
||||
# Oops, didn't find it in *any* of 'dirs'
|
||||
return None
|
||||
|
||||
# find_library_file ()
|
||||
|
||||
# class UnixCCompiler
|
||||
|
||||
|
||||
def _split_command (cmd):
|
||||
"""Split a command string up into the progam to run (a string) and
|
||||
the list of arguments; return them as (cmd, arglist)."""
|
||||
args = string.split (cmd)
|
||||
return (args[0], args[1:])
|
||||
528
distutils/util.py
Normal file
528
distutils/util.py
Normal file
|
|
@ -0,0 +1,528 @@
|
|||
"""distutils.util
|
||||
|
||||
General-purpose utility functions used throughout the Distutils
|
||||
(especially in command classes). Mostly filesystem manipulation, but
|
||||
not limited to that. The functions in this module generally raise
|
||||
DistutilsFileError when they have problems with the filesystem, because
|
||||
os.error in pre-1.5.2 Python only gives the error message and not the
|
||||
file causing it."""
|
||||
|
||||
# created 1999/03/08, Greg Ward
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import os, string, shutil, sys
|
||||
from distutils.errors import *
|
||||
|
||||
|
||||
# cache for by mkpath() -- in addition to cheapening redundant calls,
|
||||
# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
|
||||
PATH_CREATED = {}
|
||||
|
||||
# I don't use os.makedirs because a) it's new to Python 1.5.2, and
|
||||
# b) it blows up if the directory already exists (I want to silently
|
||||
# succeed in that case).
|
||||
def mkpath (name, mode=0777, verbose=0, dry_run=0):
|
||||
"""Create a directory and any missing ancestor directories. If the
|
||||
directory already exists, return silently. Raise
|
||||
DistutilsFileError if unable to create some directory along the
|
||||
way (eg. some sub-path exists, but is a file rather than a
|
||||
directory). If 'verbose' is true, print a one-line summary of
|
||||
each mkdir to stdout."""
|
||||
|
||||
global PATH_CREATED
|
||||
|
||||
# XXX what's the better way to handle verbosity? print as we create
|
||||
# each directory in the path (the current behaviour), or only announce
|
||||
# the creation of the whole path? (quite easy to do the latter since
|
||||
# we're not using a recursive algorithm)
|
||||
|
||||
name = os.path.normpath (name)
|
||||
created_dirs = []
|
||||
if os.path.isdir (name) or name == '':
|
||||
return created_dirs
|
||||
if PATH_CREATED.get (name):
|
||||
return created_dirs
|
||||
|
||||
(head, tail) = os.path.split (name)
|
||||
tails = [tail] # stack of lone dirs to create
|
||||
|
||||
while head and tail and not os.path.isdir (head):
|
||||
#print "splitting '%s': " % head,
|
||||
(head, tail) = os.path.split (head)
|
||||
#print "to ('%s','%s')" % (head, tail)
|
||||
tails.insert (0, tail) # push next higher dir onto stack
|
||||
|
||||
#print "stack of tails:", tails
|
||||
|
||||
# now 'head' contains the deepest directory that already exists
|
||||
# (that is, the child of 'head' in 'name' is the highest directory
|
||||
# that does *not* exist)
|
||||
for d in tails:
|
||||
#print "head = %s, d = %s: " % (head, d),
|
||||
head = os.path.join (head, d)
|
||||
if PATH_CREATED.get (head):
|
||||
continue
|
||||
|
||||
if verbose:
|
||||
print "creating", head
|
||||
|
||||
if not dry_run:
|
||||
try:
|
||||
os.mkdir (head)
|
||||
created_dirs.append(head)
|
||||
except os.error, (errno, errstr):
|
||||
raise DistutilsFileError, "'%s': %s" % (head, errstr)
|
||||
|
||||
PATH_CREATED[head] = 1
|
||||
return created_dirs
|
||||
|
||||
# mkpath ()
|
||||
|
||||
|
||||
def newer (source, target):
|
||||
"""Return true if 'source' exists and is more recently modified than
|
||||
'target', or if 'source' exists and 'target' doesn't. Return
|
||||
false if both exist and 'target' is the same age or younger than
|
||||
'source'. Raise DistutilsFileError if 'source' does not
|
||||
exist."""
|
||||
|
||||
if not os.path.exists (source):
|
||||
raise DistutilsFileError, "file '%s' does not exist" % source
|
||||
if not os.path.exists (target):
|
||||
return 1
|
||||
|
||||
from stat import ST_MTIME
|
||||
mtime1 = os.stat(source)[ST_MTIME]
|
||||
mtime2 = os.stat(target)[ST_MTIME]
|
||||
|
||||
return mtime1 > mtime2
|
||||
|
||||
# newer ()
|
||||
|
||||
|
||||
def newer_pairwise (sources, targets):
|
||||
"""Walk two filename lists in parallel, testing if each source is newer
|
||||
than its corresponding target. Return a pair of lists (sources,
|
||||
targets) where source is newer than target, according to the
|
||||
semantics of 'newer()'."""
|
||||
|
||||
if len (sources) != len (targets):
|
||||
raise ValueError, "'sources' and 'targets' must be same length"
|
||||
|
||||
# build a pair of lists (sources, targets) where source is newer
|
||||
n_sources = []
|
||||
n_targets = []
|
||||
for i in range (len (sources)):
|
||||
if newer (sources[i], targets[i]):
|
||||
n_sources.append (sources[i])
|
||||
n_targets.append (targets[i])
|
||||
|
||||
return (n_sources, n_targets)
|
||||
|
||||
# newer_pairwise ()
|
||||
|
||||
|
||||
def newer_group (sources, target, missing='error'):
|
||||
"""Return true if 'target' is out-of-date with respect to any
|
||||
file listed in 'sources'. In other words, if 'target' exists and
|
||||
is newer than every file in 'sources', return false; otherwise
|
||||
return true. 'missing' controls what we do when a source file is
|
||||
missing; the default ("error") is to blow up with an OSError from
|
||||
inside 'stat()'; if it is "ignore", we silently drop any missing
|
||||
source files; if it is "newer", any missing source files make us
|
||||
assume that 'target' is out-of-date (this is handy in "dry-run"
|
||||
mode: it'll make you pretend to carry out commands that wouldn't
|
||||
work because inputs are missing, but that doesn't matter because
|
||||
you're not actually going to run the commands)."""
|
||||
|
||||
# If the target doesn't even exist, then it's definitely out-of-date.
|
||||
if not os.path.exists (target):
|
||||
return 1
|
||||
|
||||
# Otherwise we have to find out the hard way: if *any* source file
|
||||
# is more recent than 'target', then 'target' is out-of-date and
|
||||
# we can immediately return true. If we fall through to the end
|
||||
# of the loop, then 'target' is up-to-date and we return false.
|
||||
from stat import ST_MTIME
|
||||
target_mtime = os.stat (target)[ST_MTIME]
|
||||
for source in sources:
|
||||
if not os.path.exists (source):
|
||||
if missing == 'error': # blow up when we stat() the file
|
||||
pass
|
||||
elif missing == 'ignore': # missing source dropped from
|
||||
continue # target's dependency list
|
||||
elif missing == 'newer': # missing source means target is
|
||||
return 1 # out-of-date
|
||||
|
||||
source_mtime = os.stat(source)[ST_MTIME]
|
||||
if source_mtime > target_mtime:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
# newer_group ()
|
||||
|
||||
|
||||
# XXX this isn't used anywhere, and worse, it has the same name as a method
|
||||
# in Command with subtly different semantics. (This one just has one
|
||||
# source -> one dest; that one has many sources -> one dest.) Nuke it?
|
||||
def make_file (src, dst, func, args,
|
||||
verbose=0, update_message=None, noupdate_message=None):
|
||||
"""Makes 'dst' from 'src' (both filenames) by calling 'func' with
|
||||
'args', but only if it needs to: i.e. if 'dst' does not exist or
|
||||
'src' is newer than 'dst'."""
|
||||
|
||||
if newer (src, dst):
|
||||
if verbose and update_message:
|
||||
print update_message
|
||||
apply (func, args)
|
||||
else:
|
||||
if verbose and noupdate_message:
|
||||
print noupdate_message
|
||||
|
||||
# make_file ()
|
||||
|
||||
|
||||
def _copy_file_contents (src, dst, buffer_size=16*1024):
|
||||
"""Copy the file 'src' to 'dst'; both must be filenames. Any error
|
||||
opening either file, reading from 'src', or writing to 'dst',
|
||||
raises DistutilsFileError. Data is read/written in chunks of
|
||||
'buffer_size' bytes (default 16k). No attempt is made to handle
|
||||
anything apart from regular files."""
|
||||
|
||||
# Stolen from shutil module in the standard library, but with
|
||||
# custom error-handling added.
|
||||
|
||||
fsrc = None
|
||||
fdst = None
|
||||
try:
|
||||
try:
|
||||
fsrc = open(src, 'rb')
|
||||
except os.error, (errno, errstr):
|
||||
raise DistutilsFileError, \
|
||||
"could not open '%s': %s" % (src, errstr)
|
||||
|
||||
try:
|
||||
fdst = open(dst, 'wb')
|
||||
except os.error, (errno, errstr):
|
||||
raise DistutilsFileError, \
|
||||
"could not create '%s': %s" % (dst, errstr)
|
||||
|
||||
while 1:
|
||||
try:
|
||||
buf = fsrc.read (buffer_size)
|
||||
except os.error, (errno, errstr):
|
||||
raise DistutilsFileError, \
|
||||
"could not read from '%s': %s" % (src, errstr)
|
||||
|
||||
if not buf:
|
||||
break
|
||||
|
||||
try:
|
||||
fdst.write(buf)
|
||||
except os.error, (errno, errstr):
|
||||
raise DistutilsFileError, \
|
||||
"could not write to '%s': %s" % (dst, errstr)
|
||||
|
||||
finally:
|
||||
if fdst:
|
||||
fdst.close()
|
||||
if fsrc:
|
||||
fsrc.close()
|
||||
|
||||
# _copy_file_contents()
|
||||
|
||||
|
||||
def copy_file (src, dst,
|
||||
preserve_mode=1,
|
||||
preserve_times=1,
|
||||
update=0,
|
||||
verbose=0,
|
||||
dry_run=0):
|
||||
|
||||
"""Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src'
|
||||
is copied there with the same name; otherwise, it must be a
|
||||
filename. (If the file exists, it will be ruthlessly clobbered.)
|
||||
If 'preserve_mode' is true (the default), the file's mode (type
|
||||
and permission bits, or whatever is analogous on the current
|
||||
platform) is copied. If 'preserve_times' is true (the default),
|
||||
the last-modified and last-access times are copied as well. If
|
||||
'update' is true, 'src' will only be copied if 'dst' does not
|
||||
exist, or if 'dst' does exist but is older than 'src'. If
|
||||
'verbose' is true, then a one-line summary of the copy will be
|
||||
printed to stdout.
|
||||
|
||||
Return true if the file was copied (or would have been copied),
|
||||
false otherwise (ie. 'update' was true and the destination is
|
||||
up-to-date)."""
|
||||
|
||||
# XXX doesn't copy Mac-specific metadata
|
||||
|
||||
from stat import *
|
||||
|
||||
if not os.path.isfile (src):
|
||||
raise DistutilsFileError, \
|
||||
"can't copy '%s': not a regular file" % src
|
||||
|
||||
if os.path.isdir (dst):
|
||||
dir = dst
|
||||
dst = os.path.join (dst, os.path.basename (src))
|
||||
else:
|
||||
dir = os.path.dirname (dst)
|
||||
|
||||
if update and not newer (src, dst):
|
||||
if verbose:
|
||||
print "not copying %s (output up-to-date)" % src
|
||||
return 0
|
||||
|
||||
if verbose:
|
||||
print "copying %s -> %s" % (src, dir)
|
||||
|
||||
if dry_run:
|
||||
return 1
|
||||
|
||||
# On a Mac, use the native file copy routine
|
||||
if os.name == 'mac':
|
||||
import macostools
|
||||
try:
|
||||
macostools.copy (src, dst, 0, preserve_times)
|
||||
except OSError, exc:
|
||||
raise DistutilsFileError, \
|
||||
"could not copy '%s' to '%s': %s" % (src, dst, exc[-1])
|
||||
return 1
|
||||
|
||||
# Otherwise use custom routine
|
||||
_copy_file_contents (src, dst)
|
||||
if preserve_mode or preserve_times:
|
||||
st = os.stat (src)
|
||||
|
||||
# According to David Ascher <da@ski.org>, utime() should be done
|
||||
# before chmod() (at least under NT).
|
||||
if preserve_times:
|
||||
os.utime (dst, (st[ST_ATIME], st[ST_MTIME]))
|
||||
if preserve_mode:
|
||||
os.chmod (dst, S_IMODE (st[ST_MODE]))
|
||||
|
||||
return 1
|
||||
|
||||
# copy_file ()
|
||||
|
||||
|
||||
def copy_tree (src, dst,
|
||||
preserve_mode=1,
|
||||
preserve_times=1,
|
||||
preserve_symlinks=0,
|
||||
update=0,
|
||||
verbose=0,
|
||||
dry_run=0):
|
||||
|
||||
|
||||
"""Copy an entire directory tree 'src' to a new location 'dst'. Both
|
||||
'src' and 'dst' must be directory names. If 'src' is not a
|
||||
directory, raise DistutilsFileError. If 'dst' does not exist, it
|
||||
is created with 'mkpath()'. The end result of the copy is that
|
||||
every file in 'src' is copied to 'dst', and directories under
|
||||
'src' are recursively copied to 'dst'. Return the list of files
|
||||
copied (under their output names) -- note that if 'update' is true,
|
||||
this might be less than the list of files considered. Return
|
||||
value is not affected by 'dry_run'.
|
||||
|
||||
'preserve_mode' and 'preserve_times' are the same as for
|
||||
'copy_file'; note that they only apply to regular files, not to
|
||||
directories. If 'preserve_symlinks' is true, symlinks will be
|
||||
copied as symlinks (on platforms that support them!); otherwise
|
||||
(the default), the destination of the symlink will be copied.
|
||||
'update' and 'verbose' are the same as for 'copy_file'."""
|
||||
|
||||
if not dry_run and not os.path.isdir (src):
|
||||
raise DistutilsFileError, \
|
||||
"cannot copy tree '%s': not a directory" % src
|
||||
try:
|
||||
names = os.listdir (src)
|
||||
except os.error, (errno, errstr):
|
||||
if dry_run:
|
||||
names = []
|
||||
else:
|
||||
raise DistutilsFileError, \
|
||||
"error listing files in '%s': %s" % (src, errstr)
|
||||
outputs = []
|
||||
|
||||
if not dry_run:
|
||||
outputs.extend(mkpath (dst, verbose=verbose))
|
||||
|
||||
|
||||
for n in names:
|
||||
src_name = os.path.join (src, n)
|
||||
dst_name = os.path.join (dst, n)
|
||||
|
||||
if preserve_symlinks and os.path.islink (src_name):
|
||||
link_dest = os.readlink (src_name)
|
||||
if verbose:
|
||||
print "linking %s -> %s" % (dst_name, link_dest)
|
||||
if not dry_run:
|
||||
os.symlink (link_dest, dst_name)
|
||||
outputs.append (dst_name)
|
||||
|
||||
elif os.path.isdir (src_name):
|
||||
outputs.extend (
|
||||
copy_tree (src_name, dst_name,
|
||||
preserve_mode, preserve_times, preserve_symlinks,
|
||||
update, verbose, dry_run))
|
||||
else:
|
||||
if (copy_file (src_name, dst_name,
|
||||
preserve_mode, preserve_times,
|
||||
update, verbose, dry_run)):
|
||||
outputs.append (dst_name)
|
||||
|
||||
return outputs
|
||||
|
||||
# copy_tree ()
|
||||
|
||||
|
||||
def remove_tree (directory, verbose=0, dry_run=0):
|
||||
"""Recursively remove an entire directory tree. Any errors are ignored
|
||||
(apart from being reported to stdout if 'verbose' is true)."""
|
||||
|
||||
if verbose:
|
||||
print "removing '%s' (and everything under it)" % directory
|
||||
if dry_run:
|
||||
return
|
||||
try:
|
||||
shutil.rmtree(directory,1)
|
||||
except (IOError, OSError), exc:
|
||||
if verbose:
|
||||
if exc.filename:
|
||||
print "error removing %s: %s (%s)" % \
|
||||
(directory, exc.strerror, exc.filename)
|
||||
else:
|
||||
print "error removing %s: %s" % (directory, exc.strerror)
|
||||
|
||||
|
||||
# XXX I suspect this is Unix-specific -- need porting help!
|
||||
def move_file (src, dst,
|
||||
verbose=0,
|
||||
dry_run=0):
|
||||
|
||||
"""Move a file 'src' to 'dst'. If 'dst' is a directory, the file
|
||||
will be moved into it with the same name; otherwise, 'src' is
|
||||
just renamed to 'dst'. Return the new full name of the file.
|
||||
|
||||
Handles cross-device moves on Unix using
|
||||
'copy_file()'. What about other systems???"""
|
||||
|
||||
from os.path import exists, isfile, isdir, basename, dirname
|
||||
|
||||
if verbose:
|
||||
print "moving %s -> %s" % (src, dst)
|
||||
|
||||
if dry_run:
|
||||
return dst
|
||||
|
||||
if not isfile (src):
|
||||
raise DistutilsFileError, \
|
||||
"can't move '%s': not a regular file" % src
|
||||
|
||||
if isdir (dst):
|
||||
dst = os.path.join (dst, basename (src))
|
||||
elif exists (dst):
|
||||
raise DistutilsFileError, \
|
||||
"can't move '%s': destination '%s' already exists" % \
|
||||
(src, dst)
|
||||
|
||||
if not isdir (dirname (dst)):
|
||||
raise DistutilsFileError, \
|
||||
"can't move '%s': destination '%s' not a valid path" % \
|
||||
(src, dst)
|
||||
|
||||
copy_it = 0
|
||||
try:
|
||||
os.rename (src, dst)
|
||||
except os.error, (num, msg):
|
||||
if num == errno.EXDEV:
|
||||
copy_it = 1
|
||||
else:
|
||||
raise DistutilsFileError, \
|
||||
"couldn't move '%s' to '%s': %s" % (src, dst, msg)
|
||||
|
||||
if copy_it:
|
||||
copy_file (src, dst)
|
||||
try:
|
||||
os.unlink (src)
|
||||
except os.error, (num, msg):
|
||||
try:
|
||||
os.unlink (dst)
|
||||
except os.error:
|
||||
pass
|
||||
raise DistutilsFileError, \
|
||||
("couldn't move '%s' to '%s' by copy/delete: " +
|
||||
"delete '%s' failed: %s") % \
|
||||
(src, dst, src, msg)
|
||||
|
||||
return dst
|
||||
|
||||
# move_file ()
|
||||
|
||||
|
||||
def write_file (filename, contents):
|
||||
"""Create a file with the specified name and write 'contents' (a
|
||||
sequence of strings without line terminators) to it."""
|
||||
|
||||
f = open (filename, "w")
|
||||
for line in contents:
|
||||
f.write (line + "\n")
|
||||
f.close ()
|
||||
|
||||
|
||||
def get_platform ():
|
||||
"""Return a string (suitable for tacking onto directory names) that
|
||||
identifies the current platform. Under Unix, identifies both the OS
|
||||
and hardware architecture, e.g. "linux-i586", "solaris-sparc",
|
||||
"irix-mips". For Windows and Mac OS, just returns 'sys.platform' --
|
||||
i.e. "???" or "???"."""
|
||||
|
||||
if os.name == 'posix':
|
||||
uname = os.uname()
|
||||
OS = uname[0]
|
||||
arch = uname[4]
|
||||
return "%s-%s" % (string.lower (OS), string.lower (arch))
|
||||
else:
|
||||
return sys.platform
|
||||
|
||||
# get_platform()
|
||||
|
||||
|
||||
def native_path (pathname):
|
||||
"""Return 'pathname' as a name that will work on the native
|
||||
filesystem, i.e. split it on '/' and put it back together again
|
||||
using the current directory separator. Needed because filenames in
|
||||
the setup script are always supplied in Unix style, and have to be
|
||||
converted to the local convention before we can actually use them in
|
||||
the filesystem. Raises DistutilsValueError if 'pathname' is
|
||||
absolute (starts with '/') or contains local directory separators
|
||||
(unless the local separator is '/', of course)."""
|
||||
|
||||
if pathname[0] == '/':
|
||||
raise DistutilsValueError, "path '%s' cannot be absolute" % pathname
|
||||
if pathname[-1] == '/':
|
||||
raise DistutilsValueError, "path '%s' cannot end with '/'" % pathname
|
||||
if os.sep != '/':
|
||||
if os.sep in pathname:
|
||||
raise DistutilsValueError, \
|
||||
"path '%s' cannot contain '%c' character" % \
|
||||
(pathname, os.sep)
|
||||
|
||||
paths = string.split (pathname, '/')
|
||||
return apply (os.path.join, paths)
|
||||
else:
|
||||
return pathname
|
||||
|
||||
# native_path ()
|
||||
|
||||
def add_path_prefix(prefix, path):
|
||||
return prefix+os.path.splitdrive(path)[1]
|
||||
|
||||
def remove_path_prefix(prefix, path):
|
||||
drive = os.path.splitdrive(path)[0]
|
||||
return drive + path[len(prefix):]
|
||||
|
||||
301
distutils/version.py
Normal file
301
distutils/version.py
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
#
|
||||
# distutils/version.py
|
||||
#
|
||||
# Implements multiple version numbering conventions for the
|
||||
# Python Module Distribution Utilities.
|
||||
#
|
||||
# written by Greg Ward, 1998/12/17
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
"""Provides classes to represent module version numbers (one class for
|
||||
each style of version numbering). There are currently two such classes
|
||||
implemented: StrictVersion and LooseVersion.
|
||||
|
||||
Every version number class implements the following interface:
|
||||
* the 'parse' method takes a string and parses it to some internal
|
||||
representation; if the string is an invalid version number,
|
||||
'parse' raises a ValueError exception
|
||||
* the class constructor takes an optional string argument which,
|
||||
if supplied, is passed to 'parse'
|
||||
* __str__ reconstructs the string that was passed to 'parse' (or
|
||||
an equivalent string -- ie. one that will generate an equivalent
|
||||
version number instance)
|
||||
* __repr__ generates Python code to recreate the version number instance
|
||||
* __cmp__ compares the current instance with either another instance
|
||||
of the same class or a string (which will be parsed to an instance
|
||||
of the same class, thus must follow the same rules)
|
||||
"""
|
||||
|
||||
import string, re
|
||||
from types import StringType
|
||||
|
||||
class Version:
|
||||
"""Abstract base class for version numbering classes. Just provides
|
||||
constructor (__init__) and reproducer (__repr__), because those
|
||||
seem to be the same for all version numbering classes.
|
||||
"""
|
||||
|
||||
def __init__ (self, vstring=None):
|
||||
if vstring:
|
||||
self.parse (vstring)
|
||||
|
||||
def __repr__ (self):
|
||||
return "%s ('%s')" % (self.__class__.__name__, str (self))
|
||||
|
||||
|
||||
# Interface for version-number classes -- must be implemented
|
||||
# by the following classes (the concrete ones -- Version should
|
||||
# be treated as an abstract class).
|
||||
# __init__ (string) - create and take same action as 'parse'
|
||||
# (string parameter is optional)
|
||||
# parse (string) - convert a string representation to whatever
|
||||
# internal representation is appropriate for
|
||||
# this style of version numbering
|
||||
# __str__ (self) - convert back to a string; should be very similar
|
||||
# (if not identical to) the string supplied to parse
|
||||
# __repr__ (self) - generate Python code to recreate
|
||||
# the instance
|
||||
# __cmp__ (self, other) - compare two version numbers ('other' may
|
||||
# be an unparsed version string, or another
|
||||
# instance of your version class)
|
||||
|
||||
|
||||
class StrictVersion (Version):
|
||||
|
||||
"""Version numbering for anal retentives and software idealists.
|
||||
Implements the standard interface for version number classes as
|
||||
described above. A version number consists of two or three
|
||||
dot-separated numeric components, with an optional "pre-release" tag
|
||||
on the end. The pre-release tag consists of the letter 'a' or 'b'
|
||||
followed by a number. If the numeric components of two version
|
||||
numbers are equal, then one with a pre-release tag will always
|
||||
be deemed earlier (lesser) than one without.
|
||||
|
||||
The following are valid version numbers (shown in the order that
|
||||
would be obtained by sorting according to the supplied cmp function):
|
||||
|
||||
0.4 0.4.0 (these two are equivalent)
|
||||
0.4.1
|
||||
0.5a1
|
||||
0.5b3
|
||||
0.5
|
||||
0.9.6
|
||||
1.0
|
||||
1.0.4a3
|
||||
1.0.4b1
|
||||
1.0.4
|
||||
|
||||
The following are examples of invalid version numbers:
|
||||
|
||||
1
|
||||
2.7.2.2
|
||||
1.3.a4
|
||||
1.3pl1
|
||||
1.3c4
|
||||
|
||||
The rationale for this version numbering system will be explained
|
||||
in the distutils documentation.
|
||||
"""
|
||||
|
||||
version_re = re.compile (r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
|
||||
re.VERBOSE)
|
||||
|
||||
|
||||
def parse (self, vstring):
|
||||
match = self.version_re.match (vstring)
|
||||
if not match:
|
||||
raise ValueError, "invalid version number '%s'" % vstring
|
||||
|
||||
(major, minor, patch, prerelease, prerelease_num) = \
|
||||
match.group (1, 2, 4, 5, 6)
|
||||
|
||||
if patch:
|
||||
self.version = tuple (map (string.atoi, [major, minor, patch]))
|
||||
else:
|
||||
self.version = tuple (map (string.atoi, [major, minor]) + [0])
|
||||
|
||||
if prerelease:
|
||||
self.prerelease = (prerelease[0], string.atoi (prerelease_num))
|
||||
else:
|
||||
self.prerelease = None
|
||||
|
||||
|
||||
def __str__ (self):
|
||||
|
||||
if self.version[2] == 0:
|
||||
vstring = string.join (map (str, self.version[0:2]), '.')
|
||||
else:
|
||||
vstring = string.join (map (str, self.version), '.')
|
||||
|
||||
if self.prerelease:
|
||||
vstring = vstring + self.prerelease[0] + str (self.prerelease[1])
|
||||
|
||||
return vstring
|
||||
|
||||
|
||||
def __cmp__ (self, other):
|
||||
if isinstance (other, StringType):
|
||||
other = StrictVersion (other)
|
||||
|
||||
compare = cmp (self.version, other.version)
|
||||
if (compare == 0): # have to compare prerelease
|
||||
|
||||
# case 1: neither has prerelease; they're equal
|
||||
# case 2: self has prerelease, other doesn't; other is greater
|
||||
# case 3: self doesn't have prerelease, other does: self is greater
|
||||
# case 4: both have prerelease: must compare them!
|
||||
|
||||
if (not self.prerelease and not other.prerelease):
|
||||
return 0
|
||||
elif (self.prerelease and not other.prerelease):
|
||||
return -1
|
||||
elif (not self.prerelease and other.prerelease):
|
||||
return 1
|
||||
elif (self.prerelease and other.prerelease):
|
||||
return cmp (self.prerelease, other.prerelease)
|
||||
|
||||
else: # numeric versions don't match --
|
||||
return compare # prerelease stuff doesn't matter
|
||||
|
||||
|
||||
# end class StrictVersion
|
||||
|
||||
|
||||
# The rules according to Greg Stein:
|
||||
# 1) a version number has 1 or more numbers separate by a period or by
|
||||
# sequences of letters. If only periods, then these are compared
|
||||
# left-to-right to determine an ordering.
|
||||
# 2) sequences of letters are part of the tuple for comparison and are
|
||||
# compared lexicographically
|
||||
# 3) recognize the numeric components may have leading zeroes
|
||||
#
|
||||
# The LooseVersion class below implements these rules: a version number
|
||||
# string is split up into a tuple of integer and string components, and
|
||||
# comparison is a simple tuple comparison. This means that version
|
||||
# numbers behave in a predictable and obvious way, but a way that might
|
||||
# not necessarily be how people *want* version numbers to behave. There
|
||||
# wouldn't be a problem if people could stick to purely numeric version
|
||||
# numbers: just split on period and compare the numbers as tuples.
|
||||
# However, people insist on putting letters into their version numbers;
|
||||
# the most common purpose seems to be:
|
||||
# - indicating a "pre-release" version
|
||||
# ('alpha', 'beta', 'a', 'b', 'pre', 'p')
|
||||
# - indicating a post-release patch ('p', 'pl', 'patch')
|
||||
# but of course this can't cover all version number schemes, and there's
|
||||
# no way to know what a programmer means without asking him.
|
||||
#
|
||||
# The problem is what to do with letters (and other non-numeric
|
||||
# characters) in a version number. The current implementation does the
|
||||
# obvious and predictable thing: keep them as strings and compare
|
||||
# lexically within a tuple comparison. This has the desired effect if
|
||||
# an appended letter sequence implies something "post-release":
|
||||
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
|
||||
#
|
||||
# However, if letters in a version number imply a pre-release version,
|
||||
# the "obvious" thing isn't correct. Eg. you would expect that
|
||||
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
|
||||
# implemented here, this just isn't so.
|
||||
#
|
||||
# Two possible solutions come to mind. The first is to tie the
|
||||
# comparison algorithm to a particular set of semantic rules, as has
|
||||
# been done in the StrictVersion class above. This works great as long
|
||||
# as everyone can go along with bondage and discipline. Hopefully a
|
||||
# (large) subset of Python module programmers will agree that the
|
||||
# particular flavour of bondage and discipline provided by StrictVersion
|
||||
# provides enough benefit to be worth using, and will submit their
|
||||
# version numbering scheme to its domination. The free-thinking
|
||||
# anarchists in the lot will never give in, though, and something needs
|
||||
# to be done to accomodate them.
|
||||
#
|
||||
# Perhaps a "moderately strict" version class could be implemented that
|
||||
# lets almost anything slide (syntactically), and makes some heuristic
|
||||
# assumptions about non-digits in version number strings. This could
|
||||
# sink into special-case-hell, though; if I was as talented and
|
||||
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
|
||||
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
|
||||
# just as happy dealing with things like "2g6" and "1.13++". I don't
|
||||
# think I'm smart enough to do it right though.
|
||||
#
|
||||
# In any case, I've coded the test suite for this module (see
|
||||
# ../test/test_version.py) specifically to fail on things like comparing
|
||||
# "1.2a2" and "1.2". That's not because the *code* is doing anything
|
||||
# wrong, it's because the simple, obvious design doesn't match my
|
||||
# complicated, hairy expectations for real-world version numbers. It
|
||||
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
|
||||
# the Right Thing" (ie. the code matches the conception). But I'd rather
|
||||
# have a conception that matches common notions about version numbers.
|
||||
|
||||
class LooseVersion (Version):
|
||||
|
||||
"""Version numbering for anarchists and software realists.
|
||||
Implements the standard interface for version number classes as
|
||||
described above. A version number consists of a series of numbers,
|
||||
separated by either periods or strings of letters. When comparing
|
||||
version numbers, the numeric components will be compared
|
||||
numerically, and the alphabetic components lexically. The following
|
||||
are all valid version numbers, in no particular order:
|
||||
|
||||
1.5.1
|
||||
1.5.2b2
|
||||
161
|
||||
3.10a
|
||||
8.02
|
||||
3.4j
|
||||
1996.07.12
|
||||
3.2.pl0
|
||||
3.1.1.6
|
||||
2g6
|
||||
11g
|
||||
0.960923
|
||||
2.2beta29
|
||||
1.13++
|
||||
5.5.kw
|
||||
2.0b1pl0
|
||||
|
||||
In fact, there is no such thing as an invalid version number under
|
||||
this scheme; the rules for comparison are simple and predictable,
|
||||
but may not always give the results you want (for some definition
|
||||
of "want").
|
||||
"""
|
||||
|
||||
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
|
||||
|
||||
def __init__ (self, vstring=None):
|
||||
if vstring:
|
||||
self.parse (vstring)
|
||||
|
||||
|
||||
def parse (self, vstring):
|
||||
# I've given up on thinking I can reconstruct the version string
|
||||
# from the parsed tuple -- so I just store the string here for
|
||||
# use by __str__
|
||||
self.vstring = vstring
|
||||
components = filter (lambda x: x and x != '.',
|
||||
self.component_re.split (vstring))
|
||||
for i in range (len (components)):
|
||||
try:
|
||||
components[i] = int (components[i])
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self.version = components
|
||||
|
||||
|
||||
def __str__ (self):
|
||||
return self.vstring
|
||||
|
||||
|
||||
def __repr__ (self):
|
||||
return "LooseVersion ('%s')" % str (self)
|
||||
|
||||
|
||||
def __cmp__ (self, other):
|
||||
if isinstance (other, StringType):
|
||||
other = LooseVersion (other)
|
||||
|
||||
return cmp (self.version, other.version)
|
||||
|
||||
|
||||
# end class LooseVersion
|
||||
Loading…
Reference in a new issue