See ChangeLog

git-svn-id: https://linkchecker.svn.sourceforge.net/svnroot/linkchecker/trunk/linkchecker@31 e7d03fd6-7b0d-0410-9947-9c21f3af8025
This commit is contained in:
calvin 2000-03-20 20:19:34 +00:00
parent cb5cf83d7f
commit e971098902
21 changed files with 2361 additions and 67 deletions

View file

@ -1,3 +1,7 @@
20.3.2000
* fix a bug in reporting download time
* added the distutils package
19.3.2000
* report the duration of checking a link (Check Time)
* rename httplib.py to http11lib.py so it does not silently

55
INSTALL
View file

@ -5,41 +5,27 @@ Requirements:
You need Python >= 1.5.2
You get Python from http://www.python.org
Unix Users:
1. Edit the file linkchecker.
Adjust the argument to sys.path.append to point to the distribution
directory.
2. HTTPS support (optional, you need SSLeay)
See below
3. Copy linkchecker to a location in your PATH (or make a symlink).
4. Check links happily by typing `linkchecker`.
Installation:
Execute "python setup.py install --create-uninstall", optionally with your
custom options (see below).
Now check links with "linkchecker" (Unix users) resp. "linkchecker.bat"
(Windows users).
Use the "build_ext" command to supply options for SSL compilation support.
Windows Users:
1. Edit the file linkchecker.
Adjust the argument to sys.path.append to point to the distribution
directory.
2. Edit the file linkchecker.bat.
a) Adjust the PYTHON variable to point to python.exe.
b) Adjust the LINKCHECKER variable to point to the distribution directory.
3. HTTPS support (optional, you need SSLeay)
See below
4. Add the distribution directory to your PATH.
5. Check links happily by typing `linkchecker.bat`.
HTTPS support:
Run "python setup.py build_ext" to compile the ssl library.
Here is the overall usage guide for setup.py:
Here is the usage guide for setup.py:
Global options:
--verbose (-v) run verbosely (default)
--quiet (-q) run quietly (turns verbosity off)
--dry-run (-n) don't actually do anything
--force (-f) skip dependency checking between files
--help (-h) show this help message
--verbose (-v) run verbosely (default)
--print-version (-V) print version
--quiet (-q) run quietly (turns verbosity off)
--dry-run (-n) don't actually do anything
--force (-f) skip dependency checking between files
--help (-h) show this help message
Options for 'build_ext' command:
--build-dir (-d) directory for compiled extension modules
--build-lib (-b) directory for compiled extension modules
--build-temp (-t) directory for temporary files (build by-products)
--inplace (-i) ignore build-lib and put compiled extensions into the
--include-dirs (-I) list of directories to search for header files
--define (-D) C preprocessor macros to define
--undef (-U) C preprocessor macros to undefine
@ -47,11 +33,12 @@ Options for 'build_ext' command:
--library-dirs (-L) directories to search for external C libraries
--rpath (-R) directories to search for shared C libraries at runtime
--link-objects (-O) extra explicit link objects to include in the link
--debug (-g) compile/link with debugging information
usage: ./setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: ./setup.py --help
or: ./setup.py --help-commands
or: ./setup.py cmd --help
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: setup.py --help
or: setup.py --help-commands
or: setup.py cmd --help
For example your openssl headers are in /usr/local/include/openssl and
the library is in /usr/local/lib:

View file

@ -1,8 +1,8 @@
VERSION=$(shell ./setup.py -q version)
VERSION=$(shell ./setup.py -V)
HOST=treasure.calvinsplayground.de
PROXY=treasure.calvinsplayground.de:5050 -s
PROXY=-P$(HOST):5050
#HOST=fsinfo.cs.uni-sb.de
#PROXY=www-proxy.uni-sb.de:3128
#PROXY=-Pwww-proxy.uni-sb.de:3128
PACKAGE = linkchecker
DEBPACKAGE = $(PACKAGE)_$(VERSION)_i386.deb
ALLPACKAGES = ../$(DEBPACKAGE)
@ -19,7 +19,6 @@ clean:
install:
./setup.py install --destdir=$(DESTDIR)
install -c 755 linkchecker $(DESTDIR)/usr/bin
install -c 644 linkcheckerrc $(DESTDIR)/etc
dist:
@ -27,7 +26,7 @@ dist:
fakeroot debian/rules binary
files:
./$(PACKAGE) -Wtext -Whtml -Wgml -Wsql -R -t0 -v -P$(PROXY) -i$(HOST) http://$(HOST)/~calvin/
./$(PACKAGE) -Wtext -Whtml -Wgml -Wsql -R -t0 -v $(PROXY) -i$(HOST) http://$(HOST)/~calvin/
homepage: files
scp *-out.* shell1.sourceforge.net:/home/groups/linkchecker/htdocs/

12
README
View file

@ -32,14 +32,12 @@ package release sequence number.
So for example 1.1.5 is the fifth release of the 1.1 development package.
Included packages:
http11lib from http://www.lyra.org/greg/python/
httplib from http://www.lyra.org/greg/python/
httpslib from http://home.att.net/~nvsoft1/ssl_wrapper.html
PyLR parser generator from http://starship.python.net/crew/scott/PyLR.html
DNS see README.dns
distutils from http://www.python.org/sigs/distutils-sig/
fcgi.py from ???
sz_fcgi.py from ???
fcgi.py and sz_fcgi.py from http://saarland.sz-sb.de/~ajung/sz_fcgi/
BEWARE: the PyLR and http11lib packages are modified by me!
It seems that http11lib and distutils will be included in Python 1.6, but
for now I provide them myself.
Note that the following packages are modified by me:
httplib.py (renamed to http11lib.py)
distutils

11
TODO
View file

@ -1,5 +1,8 @@
Is there a way to cleanly stop arbitrary Thread objects
(with exit handler)? Mail me solutions!
(1) I want to be able to supply a "break" command even when multiple
threads are running.
So a thread has to check regularly if a break command was issued? No,
I do not want this. The thread has to be interrupted from outside, but
there is at the moment no way how to do this with Python threads. What I
am doing is to call sys.exit(1). This kills the entire Python interpreter.
When Python offers this in the next release:
Internationalization and Distutils
(2) Internationalization

View file

@ -0,0 +1,16 @@
"""distutils.command
Package containing implementation of all the standard Distutils
commands."""
__revision__ = "$Id$"
__all__ = ['build',
'build_py',
'build_ext',
'install',
'install_py',
'install_ext',
'clean',
'sdist',
]

View file

@ -0,0 +1,97 @@
"""distutils.command.build
Implements the Distutils 'build' command."""
# created 1999/03/08, Greg Ward
__revision__ = "$Id$"
import sys, os
from distutils.core import Command
from distutils.util import get_platform
class build (Command):
description = "build everything needed to install"
user_options = [
('build-base=', 'b',
"base directory for build library"),
('build-purelib=', None,
"build directory for platform-neutral distributions"),
('build-platlib=', None,
"build directory for platform-specific distributions"),
('build-lib=', None,
"build directory for all distribution (defaults to either " +
"build-purelib or build-platlib"),
('build-temp=', 't',
"temporary build directory"),
('debug', 'g',
"compile extensions and libraries with debugging information"),
]
def initialize_options (self):
self.build_base = 'build'
# these are decided only after 'build_base' has its final value
# (unless overridden by the user or client)
self.build_purelib = None
self.build_platlib = None
self.build_lib = None
self.build_temp = None
self.debug = None
def finalize_options (self):
# Need this to name platform-specific directories, but sys.platform
# is not enough -- it only names the OS and version, not the
# hardware architecture!
self.plat = get_platform ()
# 'build_purelib' and 'build_platlib' just default to 'lib' and
# 'lib.<plat>' under the base build directory. We only use one of
# them for a given distribution, though --
if self.build_purelib is None:
self.build_purelib = os.path.join (self.build_base, 'lib')
if self.build_platlib is None:
self.build_platlib = os.path.join (self.build_base,
'lib.' + self.plat)
# 'build_lib' is the actual directory that we will use for this
# particular module distribution -- if user didn't supply it, pick
# one of 'build_purelib' or 'build_platlib'.
if self.build_lib is None:
if self.distribution.ext_modules:
self.build_lib = self.build_platlib
else:
self.build_lib = self.build_purelib
# 'build_temp' -- temporary directory for compiler turds,
# "build/temp.<plat>"
if self.build_temp is None:
self.build_temp = os.path.join (self.build_base,
'temp.' + self.plat)
# finalize_options ()
def run (self):
# For now, "build" means "build_py" then "build_ext". (Eventually
# it should also build documentation.)
# Invoke the 'build_py' command to "build" pure Python modules
# (ie. copy 'em into the build tree)
if self.distribution.packages or self.distribution.py_modules:
self.run_peer ('build_py')
# Build any standalone C libraries next -- they're most likely to
# be needed by extension modules, so obviously have to be done
# first!
if self.distribution.libraries:
self.run_peer ('build_clib')
# And now 'build_ext' -- compile extension modules and put them
# into the build tree
if self.distribution.ext_modules:
self.run_peer ('build_ext')
# end class Build

View file

@ -0,0 +1,201 @@
"""distutils.command.build_clib
Implements the Distutils 'build_clib' command, to build a C/C++ library
that is included in the module distribution and needed by an extension
module."""
# created (an empty husk) 1999/12/18, Greg Ward
# fleshed out 2000/02/03-04
__revision__ = "$Id$"
# XXX this module has *lots* of code ripped-off quite transparently from
# build_ext.py -- not surprisingly really, as the work required to build
# a static library from a collection of C source files is not really all
# that different from what's required to build a shared object file from
# a collection of C source files. Nevertheless, I haven't done the
# necessary refactoring to account for the overlap in code between the
# two modules, mainly because a number of subtle details changed in the
# cut 'n paste. Sigh.
import os, string
from types import *
from distutils.core import Command
from distutils.errors import *
from distutils.ccompiler import new_compiler
class build_clib (Command):
description = "build C/C++ libraries used by Python extensions"
user_options = [
('build-clib', 'b',
"directory to build C/C++ libraries to"),
('build-temp', 't',
"directory to put temporary build by-products"),
('debug', 'g',
"compile with debugging information"),
]
def initialize_options (self):
self.build_clib = None
self.build_temp = None
# List of libraries to build
self.libraries = None
# Compilation options for all libraries
self.include_dirs = None
self.define = None
self.undef = None
self.debug = None
# initialize_options()
def finalize_options (self):
# This might be confusing: both build-clib and build-temp default
# to build-temp as defined by the "build" command. This is because
# I think that C libraries are really just temporary build
# by-products, at least from the point of view of building Python
# extensions -- but I want to keep my options open.
self.set_undefined_options ('build',
('build_temp', 'build_clib'),
('build_temp', 'build_temp'),
('debug', 'debug'))
self.libraries = self.distribution.libraries
if self.libraries:
self.check_library_list (self.libraries)
if self.include_dirs is None:
self.include_dirs = self.distribution.include_dirs or []
if type (self.include_dirs) is StringType:
self.include_dirs = string.split (self.include_dirs,
os.pathsep)
# XXX same as for build_ext -- what about 'self.define' and
# 'self.undef' ?
# finalize_options()
def run (self):
if not self.libraries:
return
# Yech -- this is cut 'n pasted from build_ext.py!
self.compiler = new_compiler (plat=os.environ.get ('PLAT'),
verbose=self.verbose,
dry_run=self.dry_run,
force=self.force)
if self.include_dirs is not None:
self.compiler.set_include_dirs (self.include_dirs)
if self.define is not None:
# 'define' option is a list of (name,value) tuples
for (name,value) in self.define:
self.compiler.define_macro (name, value)
if self.undef is not None:
for macro in self.undef:
self.compiler.undefine_macro (macro)
self.build_libraries (self.libraries)
# run()
def check_library_list (self, libraries):
"""Ensure that the list of libraries (presumably provided as a
command option 'libraries') is valid, i.e. it is a list of
2-tuples, where the tuples are (library_name, build_info_dict).
Raise DistutilsValueError if the structure is invalid anywhere;
just returns otherwise."""
# Yechh, blecch, ackk: this is ripped straight out of build_ext.py,
# with only names changed to protect the innocent!
if type (libraries) is not ListType:
raise DistutilsValueError, \
"'libraries' option must be a list of tuples"
for lib in libraries:
if type (lib) is not TupleType and len (lib) != 2:
raise DistutilsValueError, \
"each element of 'libraries' must a 2-tuple"
if type (lib[0]) is not StringType:
raise DistutilsValueError, \
"first element of each tuple in 'libraries' " + \
"must be a string (the library name)"
if '/' in lib[0] or (os.sep != '/' and os.sep in lib[0]):
raise DistutilsValueError, \
("bad library name '%s': " +
"may not contain directory separators") % \
lib[0]
if type (lib[1]) is not DictionaryType:
raise DistutilsValueError, \
"second element of each tuple in 'libraries' " + \
"must be a dictionary (build info)"
# for lib
# check_library_list ()
def get_library_names (self):
# Assume the library list is valid -- 'check_library_list()' is
# called from 'finalize_options()', so it should be!
if not self.libraries:
return None
lib_names = []
for (lib_name, build_info) in self.libraries:
lib_names.append (lib_name)
return lib_names
# get_library_names ()
def build_libraries (self, libraries):
compiler = self.compiler
for (lib_name, build_info) in libraries:
sources = build_info.get ('sources')
if sources is None or type (sources) not in (ListType, TupleType):
raise DistutilsValueError, \
("in 'libraries' option (library '%s'), " +
"'sources' must be present and must be " +
"a list of source filenames") % lib_name
sources = list (sources)
self.announce ("building '%s' library" % lib_name)
# First, compile the source code to object files in the library
# directory. (This should probably change to putting object
# files in a temporary build directory.)
macros = build_info.get ('macros')
include_dirs = build_info.get ('include_dirs')
objects = self.compiler.compile (sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
debug=self.debug)
# Now "link" the object files together into a static library.
# (On Unix at least, this isn't really linking -- it just
# builds an archive. Whatever.)
self.compiler.create_static_lib (objects, lib_name,
output_dir=self.build_clib,
debug=self.debug)
# for libraries
# build_libraries ()
# class BuildLib

View file

@ -0,0 +1,320 @@
"""distutils.command.build_ext
Implements the Distutils 'build_ext' command, for building extension
modules (currently limited to C extensions, should accomodate C++
extensions ASAP)."""
# created 1999/08/09, Greg Ward
__revision__ = "$Id$"
import sys, os, string, re
from types import *
from distutils.core import Command
from distutils.errors import *
# An extension name is just a dot-separated list of Python NAMEs (ie.
# the same as a fully-qualified module name).
extension_name_re = re.compile \
(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
class build_ext (Command):
description = "build C/C++ extensions (compile/link to build directory)"
# XXX thoughts on how to deal with complex command-line options like
# these, i.e. how to make it so fancy_getopt can suck them off the
# command line and make it look like setup.py defined the appropriate
# lists of tuples of what-have-you.
# - each command needs a callback to process its command-line options
# - Command.__init__() needs access to its share of the whole
# command line (must ultimately come from
# Distribution.parse_command_line())
# - it then calls the current command class' option-parsing
# callback to deal with weird options like -D, which have to
# parse the option text and churn out some custom data
# structure
# - that data structure (in this case, a list of 2-tuples)
# will then be present in the command object by the time
# we get to finalize_options() (i.e. the constructor
# takes care of both command-line and client options
# in between initialize_options() and finalize_options())
user_options = [
('build-lib=', 'b',
"directory for compiled extension modules"),
('build-temp=', 't',
"directory for temporary files (build by-products)"),
('inplace', 'i',
"ignore build-lib and put compiled extensions into the source" +
"directory alongside your pure Python modules"),
('include-dirs=', 'I',
"list of directories to search for header files"),
('define=', 'D',
"C preprocessor macros to define"),
('undef=', 'U',
"C preprocessor macros to undefine"),
('libs=', 'l',
"external C libraries to link with"),
('library-dirs=', 'L',
"directories to search for external C libraries"),
('rpath=', 'R',
"directories to search for shared C libraries at runtime"),
('link-objects=', 'O',
"extra explicit link objects to include in the link"),
('debug', 'g',
"compile/link with debugging information"),
]
def initialize_options (self):
self.extensions = None
self.build_lib = None
self.build_temp = None
self.inplace = 0
self.package = None
self.include_dirs = None
self.define = None
self.undef = None
self.libs = None
self.library_dirs = None
self.rpath = None
self.link_objects = None
self.debug = None
def finalize_options (self):
from distutils import sysconfig
self.set_undefined_options ('build',
('build_lib', 'build_lib'),
('build_temp', 'build_temp'),
('debug', 'debug'))
if self.package is None:
self.package = self.distribution.ext_package
self.extensions = self.distribution.ext_modules
# Make sure Python's include directories (for Python.h, config.h,
# etc.) are in the include search path. We have to roll our own
# "exec include dir", because the Makefile parsed by sysconfig
# doesn't have it (sigh).
py_include = sysconfig.INCLUDEPY # prefix + "include" + "python" + ver
exec_py_include = os.path.join (sysconfig.exec_prefix, 'include',
'python' + sys.version[0:3])
if self.include_dirs is None:
self.include_dirs = self.distribution.include_dirs or []
if type (self.include_dirs) is StringType:
self.include_dirs = string.split (self.include_dirs,
os.pathsep)
self.include_dirs.insert (0, py_include)
if exec_py_include != py_include:
self.include_dirs.insert (0, exec_py_include)
if type (self.libs) is StringType:
self.libs = [self.libs]
# XXX how the heck are 'self.define' and 'self.undef' supposed to
# be set?
# finalize_options ()
def run (self):
from distutils.ccompiler import new_compiler
# 'self.extensions', as supplied by setup.py, is a list of 2-tuples.
# Each tuple is simple:
# (ext_name, build_info)
# build_info is a dictionary containing everything specific to
# building this extension. (Info pertaining to all extensions
# should be handled by general distutils options passed from
# setup.py down to right here, but that's not taken care of yet.)
if not self.extensions:
return
# First, sanity-check the 'self.extensions' list
self.check_extensions_list (self.extensions)
# Setup the CCompiler object that we'll use to do all the
# compiling and linking
self.compiler = new_compiler (plat=os.environ.get ('PLAT'),
verbose=self.verbose,
dry_run=self.dry_run,
force=self.force)
if self.include_dirs is not None:
self.compiler.set_include_dirs (self.include_dirs)
if self.define is not None:
# 'define' option is a list of (name,value) tuples
for (name,value) in self.define:
self.compiler.define_macro (name, value)
if self.undef is not None:
for macro in self.undef:
self.compiler.undefine_macro (macro)
if self.libs is not None:
self.compiler.set_libraries (self.libs)
if self.library_dirs is not None:
self.compiler.set_library_dirs (self.library_dirs)
if self.rpath is not None:
self.compiler.set_runtime_library_dirs (self.rpath)
if self.link_objects is not None:
self.compiler.set_link_objects (self.link_objects)
if self.distribution.libraries:
build_clib = self.find_peer ('build_clib')
self.libraries = build_clib.get_library_names () or []
self.library_dirs = [build_clib.build_clib]
else:
self.libraries = []
self.library_dirs = []
# Now the real loop over extensions
self.build_extensions (self.extensions)
def check_extensions_list (self, extensions):
"""Ensure that the list of extensions (presumably provided as a
command option 'extensions') is valid, i.e. it is a list of
2-tuples, where the tuples are (extension_name, build_info_dict).
Raise DistutilsValueError if the structure is invalid anywhere;
just returns otherwise."""
if type (extensions) is not ListType:
raise DistutilsValueError, \
"'ext_modules' option must be a list of tuples"
for ext in extensions:
if type (ext) is not TupleType and len (ext) != 2:
raise DistutilsValueError, \
"each element of 'ext_modules' option must be a 2-tuple"
if not (type (ext[0]) is StringType and
extension_name_re.match (ext[0])):
raise DistutilsValueError, \
"first element of each tuple in 'ext_modules' " + \
"must be the extension name (a string)"
if type (ext[1]) is not DictionaryType:
raise DistutilsValueError, \
"second element of each tuple in 'ext_modules' " + \
"must be a dictionary (build info)"
# end sanity-check for
# check_extensions_list ()
def get_source_files (self):
filenames = []
# Wouldn't it be neat if we knew the names of header files too...
for (extension_name, build_info) in self.extensions:
sources = build_info.get ('sources')
if type (sources) in (ListType, TupleType):
filenames.extend (sources)
return filenames
def build_extensions (self, extensions):
for (extension_name, build_info) in extensions:
sources = build_info.get ('sources')
if sources is None or type (sources) not in (ListType, TupleType):
raise DistutilsValueError, \
("in 'ext_modules' option (extension '%s'), " +
"'sources' must be present and must be " +
"a list of source filenames") % extension_name
sources = list (sources)
self.announce ("building '%s' extension" % extension_name)
# First step: compile the source code to object files. This
# drops the object files in the current directory, regardless
# of where the source is (may be a bad thing, but that's how a
# Makefile.pre.in-based system does it, so at least there's a
# precedent!)
macros = build_info.get ('macros')
include_dirs = build_info.get ('include_dirs')
objects = self.compiler.compile (sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
debug=self.debug)
# Now link the object files together into a "shared object" --
# of course, first we have to figure out all the other things
# that go into the mix.
extra_objects = build_info.get ('extra_objects')
if extra_objects:
objects.extend (extra_objects)
libraries = (self.libraries +
(build_info.get ('libraries') or []))
library_dirs = (self.library_dirs +
(build_info.get ('library_dirs') or []))
extra_args = build_info.get ('extra_link_args') or []
if self.compiler.compiler_type == 'msvc':
def_file = build_info.get ('def_file')
if def_file is None:
source_dir = os.path.dirname (sources[0])
ext_base = (string.split (extension_name, '.'))[-1]
def_file = os.path.join (source_dir, "%s.def" % ext_base)
if not os.path.exists (def_file):
def_file = None
if def_file is not None:
extra_args.append ('/DEF:' + def_file)
else:
modname = string.split (extension_name, '.')[-1]
extra_args.append('/export:init%s'%modname)
# end if MSVC
fullname = self.get_ext_fullname (extension_name)
if self.inplace:
# ignore build-lib -- put the compiled extension into
# the source tree along with pure Python modules
modpath = string.split (fullname, '.')
package = string.join (modpath[0:-1], '.')
base = modpath[-1]
build_py = self.find_peer ('build_py')
package_dir = build_py.get_package_dir (package)
ext_filename = os.path.join (package_dir,
self.get_ext_filename(base))
else:
ext_filename = os.path.join (self.build_lib,
self.get_ext_filename(fullname))
self.compiler.link_shared_object (objects, ext_filename,
libraries=libraries,
library_dirs=library_dirs,
extra_postargs=extra_args,
debug=self.debug)
# build_extensions ()
def get_ext_fullname (self, ext_name):
if self.package is None:
return ext_name
else:
return self.package + '.' + ext_name
def get_ext_filename (self, ext_name):
from distutils import sysconfig
ext_path = string.split (ext_name, '.')
return apply (os.path.join, ext_path) + sysconfig.SO
# class BuildExt

View file

@ -0,0 +1,309 @@
"""distutils.command.build_py
Implements the Distutils 'build_py' command."""
# created 1999/03/08, Greg Ward
__revision__ = "$Id$"
import sys, string, os
from types import *
from glob import glob
from distutils.core import Command
from distutils.errors import *
class build_py (Command):
description = "\"build\" pure Python modules (copy to build directory)"
user_options = [
('build-lib=', 'd', "directory to \"build\" (copy) to"),
]
def initialize_options (self):
self.build_lib = None
self.modules = None
self.package = None
self.package_dir = None
def finalize_options (self):
self.set_undefined_options ('build',
('build_lib', 'build_lib'))
# Get the distribution options that are aliases for build_py
# options -- list of packages and list of modules.
self.packages = self.distribution.packages
self.modules = self.distribution.py_modules
self.package_dir = self.distribution.package_dir
def run (self):
# XXX copy_file by default preserves atime and mtime. IMHO this is
# the right thing to do, but perhaps it should be an option -- in
# particular, a site administrator might want installed files to
# reflect the time of installation rather than the last
# modification time before the installed release.
# XXX copy_file by default preserves mode, which appears to be the
# wrong thing to do: if a file is read-only in the working
# directory, we want it to be installed read/write so that the next
# installation of the same module distribution can overwrite it
# without problems. (This might be a Unix-specific issue.) Thus
# we turn off 'preserve_mode' when copying to the build directory,
# since the build directory is supposed to be exactly what the
# installation will look like (ie. we preserve mode when
# installing).
# XXX copy_file does *not* preserve MacOS-specific file metadata.
# If this is a problem for building/installing Python modules, then
# we'll have to fix copy_file. (And what about installing scripts,
# when the time comes for that -- does MacOS use its special
# metadata to know that a file is meant to be interpreted by
# Python?)
infiles = []
outfiles = []
missing = []
# Two options control which modules will be installed: 'packages'
# and 'modules'. The former lets us work with whole packages, not
# specifying individual modules at all; the latter is for
# specifying modules one-at-a-time. Currently they are mutually
# exclusive: you can define one or the other (or neither), but not
# both. It remains to be seen how limiting this is.
# Dispose of the two "unusual" cases first: no pure Python modules
# at all (no problem, just return silently), and over-specified
# 'packages' and 'modules' options.
if not self.modules and not self.packages:
return
if self.modules and self.packages:
raise DistutilsOptionError, \
"build_py: supplying both 'packages' and 'modules' " + \
"options is not allowed"
# Now we're down to two cases: 'modules' only and 'packages' only.
if self.modules:
self.build_modules ()
else:
self.build_packages ()
# run ()
def get_package_dir (self, package):
"""Return the directory, relative to the top of the source
distribution, where package 'package' should be found
(at least according to the 'package_dir' option, if any)."""
if type (package) is StringType:
path = string.split (package, '.')
elif type (package) in (TupleType, ListType):
path = list (package)
else:
raise TypeError, "'package' must be a string, list, or tuple"
if not self.package_dir:
if path:
return apply (os.path.join, path)
else:
return ''
else:
tail = []
while path:
try:
pdir = self.package_dir[string.join (path, '.')]
except KeyError:
tail.insert (0, path[-1])
del path[-1]
else:
tail.insert (0, pdir)
return apply (os.path.join, tail)
else:
# arg! everything failed, we might as well have not even
# looked in package_dir -- oh well
if tail:
return apply (os.path.join, tail)
else:
return ''
# get_package_dir ()
def check_package (self, package, package_dir):
# Empty dir name means current directory, which we can probably
# assume exists. Also, os.path.exists and isdir don't know about
# my "empty string means current dir" convention, so we have to
# circumvent them.
if package_dir != "":
if not os.path.exists (package_dir):
raise DistutilsFileError, \
"package directory '%s' does not exist" % package_dir
if not os.path.isdir (package_dir):
raise DistutilsFileError, \
("supposed package directory '%s' exists, " +
"but is not a directory") % package_dir
# Require __init__.py for all but the "root package"
if package:
init_py = os.path.join (package_dir, "__init__.py")
if not os.path.isfile (init_py):
self.warn (("package init file '%s' not found " +
"(or not a regular file)") % init_py)
# check_package ()
def check_module (self, module, module_file):
if not os.path.isfile (module_file):
self.warn ("file %s (for module %s) not found" %
(module_file, module))
return 0
else:
return 1
# check_module ()
def find_package_modules (self, package, package_dir):
module_files = glob (os.path.join (package_dir, "*.py"))
module_pairs = []
setup_script = os.path.abspath (sys.argv[0])
for f in module_files:
abs_f = os.path.abspath (f)
if abs_f != setup_script:
module = os.path.splitext (os.path.basename (f))[0]
module_pairs.append ((module, f))
return module_pairs
def find_modules (self):
# Map package names to tuples of useful info about the package:
# (package_dir, checked)
# package_dir - the directory where we'll find source files for
# this package
# checked - true if we have checked that the package directory
# is valid (exists, contains __init__.py, ... ?)
packages = {}
# List of (module, package, filename) tuples to return
modules = []
# We treat modules-in-packages almost the same as toplevel modules,
# just the "package" for a toplevel is empty (either an empty
# string or empty list, depending on context). Differences:
# - don't check for __init__.py in directory for empty package
for module in self.modules:
path = string.split (module, '.')
package = tuple (path[0:-1])
module_base = path[-1]
try:
(package_dir, checked) = packages[package]
except KeyError:
package_dir = self.get_package_dir (package)
checked = 0
if not checked:
self.check_package (package, package_dir)
packages[package] = (package_dir, 1)
# XXX perhaps we should also check for just .pyc files
# (so greedy closed-source bastards can distribute Python
# modules too)
module_file = os.path.join (package_dir, module_base + ".py")
if not self.check_module (module, module_file):
continue
modules.append ((module, package, module_file))
return modules
# find_modules ()
def get_source_files (self):
if self.modules:
modules = self.find_modules ()
else:
modules = []
for package in self.packages:
package_dir = self.get_package_dir (package)
m = self.find_package_modules (package, package_dir)
modules.extend (m)
# Both find_modules() and find_package_modules() return a list of
# tuples where the last element of each tuple is the filename --
# what a happy coincidence!
filenames = []
for module in modules:
filenames.append (module[-1])
return filenames
def build_module (self, module, module_file, package):
if type (package) is StringType:
package = string.split (package, '.')
elif type (package) not in (ListType, TupleType):
raise TypeError, \
"'package' must be a string (dot-separated), list, or tuple"
# Now put the module source file into the "build" area -- this is
# easy, we just copy it somewhere under self.build_lib (the build
# directory for Python source).
outfile_path = list (package)
outfile_path.append (module + ".py")
outfile_path.insert (0, self.build_lib)
outfile = apply (os.path.join, outfile_path)
dir = os.path.dirname (outfile)
self.mkpath (dir)
self.copy_file (module_file, outfile, preserve_mode=0)
def build_modules (self):
modules = self.find_modules()
for (module, package, module_file) in modules:
# Now "build" the module -- ie. copy the source file to
# self.build_lib (the build directory for Python source).
# (Actually, it gets copied to the directory for this package
# under self.build_lib.)
self.build_module (module, module_file, package)
# build_modules ()
def build_packages (self):
for package in self.packages:
package_dir = self.get_package_dir (package)
self.check_package (package, package_dir)
# Get list of (module, module_file) tuples based on scanning
# the package directory. Here, 'module' is the *unqualified*
# module name (ie. no dots, no package -- we already know its
# package!), and module_file is the path to the .py file,
# relative to the current directory (ie. including
# 'package_dir').
modules = self.find_package_modules (package, package_dir)
# Now loop over the modules we found, "building" each one (just
# copy it to self.build_lib).
for (module, module_file) in modules:
self.build_module (module, module_file, package)
# build_packages ()
# end class BuildPy

View file

@ -0,0 +1,64 @@
"""distutils.command.clean
Implements the Distutils 'clean' command."""
# contributed by Bastian Kleineidam <calvin@cs.uni-sb.de>, added 2000-03-18
__revision__ = "$Id$"
import os
from distutils.core import Command
from distutils.util import remove_tree
class clean (Command):
description = "clean up output of 'build' command"
user_options = [
('build-base=', 'b',
"base build directory (default: 'build.build-base')"),
('build-lib=', None,
"build directory for all modules (default: 'build.build-lib')"),
('build-temp=', 't',
"temporary build directory (default: 'build.build-temp')"),
('all', 'a',
"remove all build output, not just temporary by-products")
]
def initialize_options(self):
self.build_base = None
self.build_lib = None
self.build_temp = None
self.all = None
def finalize_options(self):
if self.build_lib and not os.path.exists (self.build_lib):
self.warn ("'%s' does not exist -- can't clean it" %
self.build_lib)
if self.build_temp and not os.path.exists (self.build_temp):
self.warn ("'%s' does not exist -- can't clean it" %
self.build_temp)
self.set_undefined_options('build',
('build_base', 'build_base'),
('build_lib', 'build_lib'),
('build_temp', 'build_temp'))
def run(self):
# remove the build/temp.<plat> directory (unless it's already
# gone)
if os.path.exists (self.build_temp):
remove_tree (self.build_temp, self.verbose, self.dry_run)
if self.all:
# remove the module build directory (unless already gone)
if os.path.exists (self.build_lib):
remove_tree (self.build_lib, self.verbose, self.dry_run)
# just for the heck of it, try to remove the base build directory:
# we might have emptied it right now, but if not we don't care
if not self.dry_run:
try:
os.rmdir (self.build_base)
self.announce ("removing '%s'" % self.build_base)
except OSError:
pass

View file

@ -0,0 +1,351 @@
"""distutils.command.install
Implements the Distutils 'install' command."""
# created 1999/03/13, Greg Ward
__revision__ = "$Id$"
import sys, os, string
from types import *
from distutils.core import Command
from distutils.util import write_file
from distutils.errors import DistutilsOptionError
_uninstall_template = """#!/usr/bin/env python
# this file is generated from the distutils 'install' command
filelist = %s
if __name__ == '__main__':
import os
paths = filelist.keys()
# sort to catch all files before removing directories
paths.sort()
paths.reverse()
for f in paths:
print "removing",f
if os.path.exists(f) and not os.path.isdir(f):
os.remove(f)
elif os.path.isdir(f):
if os.listdir(f):
print "warning: directory "+f+" not empty so not removed"
else:
os.rmdir(f)
else:
raise AttributeError, "Unknown file type "+f
"""
class install (Command):
description = "install everything from build directory"
user_options = [
('destdir=', None, 'destination directory'),
('prefix=', None, "installation prefix"),
('exec-prefix=', None,
"prefix for platform-specific files"),
# Installation directories: where to put modules and packages
('install-lib=', None, "base Python library directory"),
('install-platlib=', None,
"platform-specific Python library directory"),
('install-bin=', None,
"directory for programs and scripts (default exec-prefix/bin)"),
('install-path=', None,
"extra intervening directories to put below install-lib"),
('create-uninstall', None, 'create and install uninstall script'),
# Build directories: where to find the files to install
('build-base=', None,
"base build directory"),
('build-lib=', None,
"build directory for pure Python modules"),
('build-platlib=', None,
"build directory for extension modules"),
# Where to install documentation (eventually!)
#('doc-format=', None, "format of documentation to generate"),
#('install-man=', None, "directory for Unix man pages"),
#('install-html=', None, "directory for HTML documentation"),
#('install-info=', None, "directory for GNU info files"),
# Flags for 'build_py'
#('compile-py', None, "compile .py to .pyc"),
#('optimize-py', None, "compile .py to .pyo (optimized)"),
]
def initialize_options (self):
# Don't define 'prefix' or 'exec_prefix' so we can know when the
# command is run whether the user supplied values
self.prefix = None
self.exec_prefix = None
self.destdir = None
# The actual installation directories are determined only at
# run-time, so the user can supply just prefix (and exec_prefix?)
# as a base for everything else
self.install_lib = None
self.install_platlib = None
self.install_path = None
self.install_bin = None
self.build_base = None
self.build_lib = None
self.build_platlib = None
self.install_man = None
self.install_html = None
self.install_info = None
self.create_uninstall = None
self.compile_py = 1
self.optimize_py = 1
def finalize_options (self):
# XXX this method is where the default installation directories
# for modules and extension modules are determined. (Someday,
# the default installation directories for scripts,
# documentation, and whatever else the Distutils can build and
# install will also be determined here.) Thus, this is a pretty
# important place to fiddle with for anyone interested in
# installation schemes for the Python library. Issues that
# are not yet resolved to my satisfaction:
# * how much platform dependence should be here, and
# how much can be pushed off to sysconfig (or, better, the
# Makefiles parsed by sysconfig)?
# * how independent of Python version should this be -- ie.
# should we have special cases to handle Python 1.5 and
# older, and then do it "the right way" for 1.6? Or should
# we install a site.py along with Distutils under pre-1.6
# Python to take care of the current deficiencies in
# Python's library installation scheme?
#
# Currently, this method has hacks to distinguish POSIX from
# non-POSIX systems (for installation of site-local modules),
# and assumes the Python 1.5 installation tree with no site.py
# to fix things.
# Figure out actual installation directories; the basic principle
# is: ...
if self.destdir is None:
self.destdir = ""
sys_prefix = os.path.normpath (sys.prefix)
sys_exec_prefix = os.path.normpath (sys.exec_prefix)
if self.prefix is None:
if self.exec_prefix is not None:
raise DistutilsOptionError, \
"you may not supply exec_prefix without prefix"
self.prefix = sys_prefix
else:
# This is handy to guarantee that self.prefix is normalized --
# but it could be construed as rude to go normalizing a
# user-supplied path (they might like to see their "../" or
# symlinks in the installation feedback).
self.prefix = os.path.normpath (self.prefix)
if self.exec_prefix is None:
if self.prefix == sys_prefix:
self.exec_prefix = sys_exec_prefix
else:
self.exec_prefix = self.prefix
else:
# Same as above about handy versus rude to normalize user's
# exec_prefix.
self.exec_prefix = os.path.normpath (self.exec_prefix)
if self.distribution.ext_modules: # any extensions to install?
effective_prefix = self.exec_prefix
else:
effective_prefix = self.prefix
if os.name == 'posix':
if self.install_lib is None:
if self.prefix == sys_prefix:
self.install_lib = \
os.path.join (effective_prefix,
"lib",
"python" + sys.version[:3],
"site-packages")
else:
self.install_lib = \
os.path.join (effective_prefix,
"lib",
"python") # + sys.version[:3] ???
# end if self.install_lib ...
if self.install_platlib is None:
if self.exec_prefix == sys_exec_prefix:
self.install_platlib = \
os.path.join (effective_prefix,
"lib",
"python" + sys.version[:3],
"site-packages")
else:
self.install_platlib = \
os.path.join (effective_prefix,
"lib",
"python") # + sys.version[:3] ???
# end if self.install_platlib ...
else:
raise DistutilsPlatformError, \
"duh, I'm clueless (for now) about installing on %s" % os.name
# end if/else on os.name
if self.install_bin is None:
self.install_bin = os.path.join(self.exec_prefix,"bin")
# 'path_file' and 'extra_dirs' are how we handle distributions that
# want to be installed to their own directory, but aren't
# package-ized yet. 'extra_dirs' is just a directory under
# 'install_lib' or 'install_platlib' where top-level modules will
# actually be installed; 'path_file' is the basename of a .pth file
# to drop in 'install_lib' or 'install_platlib' (depending on the
# distribution). Very often they will be the same, which is why we
# allow them to be supplied as a string or 1-tuple as well as a
# 2-element comma-separated string or a 2-tuple.
# XXX this will drop a .pth file in install_{lib,platlib} even if
# they're not one of the site-packages directories: this is wrong!
# we need to suppress path_file in those cases, and warn if
# "install_lib/extra_dirs" is not in sys.path.
if self.install_path is None:
self.install_path = self.distribution.install_path
if self.install_path is not None:
if type (self.install_path) is StringType:
self.install_path = string.split (self.install_path, ',')
if len (self.install_path) == 1:
path_file = extra_dirs = self.install_path[0]
elif len (self.install_path) == 2:
(path_file, extra_dirs) = self.install_path
else:
raise DistutilsOptionError, \
"'install_path' option must be a list, tuple, or " + \
"comma-separated string with 1 or 2 elements"
# install path has slashes in it -- might need to convert to
# local form
if string.find (extra_dirs, '/') and os.name != "posix":
extra_dirs = string.split (extra_dirs, '/')
extra_dirs = apply (os.path.join, extra_dirs)
else:
path_file = None
extra_dirs = ''
# XXX should we warn if path_file and not extra_dirs (in which case
# the path file would be harmless but pointless)
self.path_file = path_file
self.extra_dirs = extra_dirs
# Figure out the build directories, ie. where to install from
self.set_peer_option ('build', 'build_base', self.build_base)
self.set_undefined_options ('build',
('build_base', 'build_base'),
('build_lib', 'build_lib'),
('build_platlib', 'build_platlib'))
# Punt on doc directories for now -- after all, we're punting on
# documentation completely!
# finalize_options ()
def run (self):
# Obviously have to build before we can install
self.run_peer ('build')
# Install modules in two steps: "platform-shared" files (ie. pure
# Python modules) and platform-specific files (compiled C
# extensions). Note that 'install_py' is smart enough to install
# pure Python modules in the "platlib" directory if we built any
# extensions.
if self.distribution.packages or self.distribution.py_modules:
self.run_peer ('install_py')
if self.distribution.ext_modules:
self.run_peer ('install_ext')
if self.distribution.scripts or self.distribution.programs:
self.run_peer('install_bin')
if self.path_file:
self.create_path_file ()
if self.create_uninstall:
self.create_uninstall_file()
# run ()
def create_path_file (self):
if self.distribution.ext_modules:
base = self.platbase
else:
base = self.base
filename = os.path.join (base, self.path_file + ".pth")
self.execute (write_file,
(filename, [self.extra_dirs]),
"creating %s" % filename)
def create_uninstall_file(self):
"""Create an uninstall file <distribution name>_uninstall.py.
The distribution name must be given!
If there is already an uninstall file, we add any new files we
installed to it.
To uninstall, simply execute the uninstall file with python.
"""
# check for distribution name
if not self.distribution.name:
raise DistutilsOptionError, "you must supply a distribution name"
# construct the paths and filenames
if self.distribution.ext_modules:
base = self.install_platlib
else:
base = self.install_lib
modulename = self.distribution.name+"_uninstall"
filename = modulename+".py"
import __builtin__
cfilename = filename + (__builtin__.__debug__ and "c" or "o")
# add the uninstall script itself to outfiles
uninstall_script = os.path.join(base, filename)
uninstall_script_c = os.path.join(base, cfilename)
self.distribution.outfiles.append(uninstall_script)
self.distribution.outfiles.append(uninstall_script_c)
if self.destdir:
base = self.destdir + base
path = os.path.join(base, filename)
cpath = os.path.join(base, cfilename)
if not os.path.exists(path):
filelist = {}
else:
if base not in sys.path:
sys.path.insert(0, base)
exec "from %s import filelist" % modulename
for f in self.distribution.outfiles:
# to eliminate duplicates we use a dictionary
filelist[f] = 1
f = open(path, "w")
f.write(_uninstall_template % filelist)
f.close()
# byte-compile the script
from py_compile import compile
self.make_file (path, cpath, compile, (path,),
"byte-compiling %s" % path,
"byte-compilation of %s skipped" % path)
self.announce("to uninstall execute "+uninstall_script+" with python")
# class Install

View file

@ -0,0 +1,88 @@
"""install_bin
Implement the Distutils "install_bin" command to install programs
and scripts."""
from distutils.core import Command
import os,types,string
class install_bin(Command):
description = "install programs and scripts"
user_options = [
('install-dir=', 'd', "directory to install to"),
('destdir', None, 'destination directory'),
]
def initialize_options (self):
# let the 'install' command dictate our installation directory
self.install_dir = None
self.create_uninstall = None
self.destdir = None
def finalize_options (self):
self.set_undefined_options ('install',
('install_bin', 'install_dir'),
('create_uninstall', 'create_uninstall'),
('destdir', 'destdir'))
self.programs = self.get_platform_bins(self.distribution.programs)
self.scripts = self.get_platform_bins(self.distribution.scripts)
def get_platform_bins(self, bins):
filtered = []
if bins:
for b in bins:
if type(b)==types.TupleType:
if len(b)==2 and os.name==b[1]:
filtered.append(b[0])
else:
filtered.append(b)
return filtered
def run (self):
if not self.programs and not self.scripts:
return
# Copy specified programs and scripts to install_dir
# Additionally replace in scripts some variables
# (currently only @INSTALL_BIN@ with install_dir)
real_install_dir = self.install_dir
if self.destdir:
self.install_dir = self.destdir+self.install_dir
ddlen = len(self.destdir)
# create the install directory
outfiles = self.mkpath(self.install_dir)
# copy the files
if self.create_uninstall and not self.force:
# turn on self.force to catch all previous installed files
oldforce = self.force
self.force = 1
outfiles.extend(self.run_copy_files(real_install_dir))
# restore previous options
self.force = oldforce
else:
outfiles.extend(self.run_copy_files(real_install_dir))
if self.destdir:
# cut off destdir prefix
outfiles = map(lambda s,l=ddlen: s[l:], outfiles)
self.distribution.outfiles.extend(outfiles)
def run_copy_files(self, real_install_dir):
outfiles = []
for f in self.programs:
if self.copy_file(f, self.install_dir):
outfiles.append(os.path.join(real_install_dir, f))
for f in self.scripts:
if self.copy_file(f, self.install_dir):
outfiles.append(os.path.join(real_install_dir, f))
self.replace_vars(os.path.join(self.install_dir, f),
real_install_dir)
return outfiles
def replace_vars(self, file, directory):
data = open(file).read()
data = string.replace(data, "@INSTALL_BIN@", directory)
open(file,"w").write(data)

View file

@ -0,0 +1,62 @@
"""install_ext
Implement the Distutils "install_ext" command to install extension modules."""
# created 1999/09/12, Greg Ward
__revision__ = "$Id$"
from distutils.core import Command
from distutils.util import copy_tree
class install_ext (Command):
description = "install C/C++ extension modules"
user_options = [
('install-dir=', 'd', "directory to install to"),
('build-dir=','b', "build directory (where to install from)"),
('destdir', None, "destination directory")
]
def initialize_options (self):
# let the 'install' command dictate our installation directory
self.install_dir = None
self.build_dir = None
self.create_uninstall = None
self.destdir = None
def finalize_options (self):
self.set_undefined_options ('install',
('build_platlib', 'build_dir'),
('install_platlib', 'install_dir'),
('create_uninstall', 'create_uninstall'),
('destdir', 'destdir'))
def run (self):
# Make sure we have built all extension modules first
self.run_peer ('build_ext')
# Dump the entire "build/platlib" directory (or whatever it really
# is; "build/platlib" is the default) to the installation target
# (eg. "/usr/local/lib/python1.5/site-packages"). Note that
# putting files in the right package dir is already done when we
# build.
if self.destdir:
self.install_dir = self.destdir+self.install_dir
ddlen = len(self.destdir)
if self.create_uninstall and not self.force:
# turn on self.force to catch all previous installed files
oldforce = self.force
self.force = 1
outfiles = self.copy_tree (self.build_dir, self.install_dir)
# restore previous options
self.force = oldforce
else:
outfiles = self.copy_tree (self.build_dir, self.install_dir)
if self.destdir:
outfiles = map(lambda s,l=ddlen: s[l:], outfiles)
self.distribution.outfiles = self.distribution.outfiles + outfiles
# class InstallExt

View file

@ -0,0 +1,95 @@
# created 1999/03/13, Greg Ward
__revision__ = "$Id$"
import sys, string
from distutils.core import Command
from distutils.util import copy_tree
class install_py (Command):
description = "install pure Python modules"
user_options = [
('install-dir=', 'd', "directory to install to"),
('build-dir=','b', "build directory (where to install from)"),
('compile', 'c', "compile .py to .pyc"),
('optimize', 'o', "compile .py to .pyo (optimized)"),
('destdir', None, 'destination directory'),
]
def initialize_options (self):
# let the 'install' command dictate our installation directory
self.install_dir = None
self.build_dir = None
self.compile = 1
self.optimize = 1
self.create_uninstall = None
self.destdir = None
def finalize_options (self):
# Get all the information we need to install pure Python modules
# from the umbrella 'install' command -- build (source) directory,
# install (target) directory, and whether to compile .py files.
self.set_undefined_options ('install',
('build_lib', 'build_dir'),
('install_lib', 'install_dir'),
('create_uninstall', 'create_uninstall'),
('compile_py', 'compile'),
('optimize_py', 'optimize'),
('destdir', 'destdir'))
def run (self):
# Make sure we have "built" all pure Python modules first
self.run_peer ('build_py')
# Install everything: simply dump the entire contents of the build
# directory to the installation directory (that's the beauty of
# having a build directory!)
if self.destdir:
self.install_dir = self.destdir+self.install_dir
ddlen = len(self.destdir)
if self.create_uninstall and not self.force:
# turn on self.force to catch all previous installed files
oldforce = self.force
self.force = 1
outfiles = self.copy_tree (self.build_dir, self.install_dir)
self.force = oldforce
else:
outfiles = self.copy_tree (self.build_dir, self.install_dir)
# (Optionally) compile .py to .pyc
# XXX hey! we can't control whether we optimize or not; that's up
# to the invocation of the current Python interpreter (at least
# according to the py_compile docs). That sucks.
if self.compile:
from py_compile import compile
import __builtin__
for f in outfiles:
# XXX we can determine if we run python -O by looking
# at __builtin__.__debug__, but we can not change it
# only compile the file if it is actually a .py file
if f[-3:] == '.py':
out_fn = f + (__builtin__.__debug__ and "c" or "o")
outfiles.append(out_fn)
self.make_file (f, out_fn, compile, (f,),
"byte-compiling %s" % f,
"byte-compilation of %s skipped" % f)
# XXX ignore self.optimize for now, since we don't really know if
# we're compiling optimally or not, and couldn't pick what to do
# even if we did know. ;-(
if self.destdir:
outfiles = map(lambda s,l=ddlen: s[l:], outfiles)
self.distribution.outfiles = self.distribution.outfiles + outfiles
# run ()
# class InstallPy

704
distutils/command/sdist.py Normal file
View file

@ -0,0 +1,704 @@
"""distutils.command.sdist
Implements the Distutils 'sdist' command (create a source distribution)."""
# created 1999/09/22, Greg Ward
__revision__ = "$Id$"
import sys, os, string, re
import fnmatch
from types import *
from glob import glob
from distutils.core import Command
from distutils.util import newer, remove_tree
from distutils.text_file import TextFile
from distutils.errors import DistutilsExecError
class sdist (Command):
description = "create a source distribution (tarball, zip file, etc.)"
user_options = [
('template=', 't',
"name of manifest template file [default: MANIFEST.in]"),
('manifest=', 'm',
"name of manifest file [default: MANIFEST]"),
('use-defaults', None,
"include the default file set in the manifest "
"[default; disable with --no-defaults]"),
('manifest-only', None,
"just regenerate the manifest and then stop"),
('force-manifest', None,
"forcibly regenerate the manifest and carry on as usual"),
('formats=', None,
"formats for source distribution (tar, ztar, gztar, or zip)"),
('list-only', 'l',
"just list files that would be distributed"),
('keep-tree', 'k',
"keep the distribution tree around after creating " +
"archive file(s)"),
]
negative_opts = {'use-defaults': 'no-defaults'}
default_format = { 'posix': 'gztar',
'nt': 'zip' }
exclude_re = re.compile (r'\s*!\s*(\S+)') # for manifest lines
def initialize_options (self):
# 'template' and 'manifest' are, respectively, the names of
# the manifest template and manifest file.
self.template = None
self.manifest = None
# 'use_defaults': if true, we will include the default file set
# in the manifest
self.use_defaults = 1
self.manifest_only = 0
self.force_manifest = 0
self.formats = None
self.list_only = 0
self.keep_tree = 0
def finalize_options (self):
if self.manifest is None:
self.manifest = "MANIFEST"
if self.template is None:
self.template = "MANIFEST.in"
if self.formats is None:
try:
self.formats = [self.default_format[os.name]]
except KeyError:
raise DistutilsPlatformError, \
"don't know how to build source distributions on " + \
"%s platform" % os.name
elif type (self.formats) is StringType:
self.formats = string.split (self.formats, ',')
def run (self):
# 'files' is the list of files that will make up the manifest
self.files = []
# Ensure that all required meta-data is given; warn if not (but
# don't die, it's not *that* serious!)
self.check_metadata ()
# Do whatever it takes to get the list of files to process
# (process the manifest template, read an existing manifest,
# whatever). File list is put into 'self.files'.
self.get_file_list ()
# If user just wanted us to regenerate the manifest, stop now.
if self.manifest_only:
return
# Otherwise, go ahead and create the source distribution tarball,
# or zipfile, or whatever.
self.make_distribution ()
def check_metadata (self):
dist = self.distribution
missing = []
for attr in ('name', 'version', 'url'):
if not (hasattr (dist, attr) and getattr (dist, attr)):
missing.append (attr)
if missing:
self.warn ("missing required meta-data: " +
string.join (missing, ", "))
if dist.author:
if not dist.author_email:
self.warn ("missing meta-data: if 'author' supplied, " +
"'author_email' must be supplied too")
elif dist.maintainer:
if not dist.maintainer_email:
self.warn ("missing meta-data: if 'maintainer' supplied, " +
"'maintainer_email' must be supplied too")
else:
self.warn ("missing meta-data: either (author and author_email) " +
"or (maintainer and maintainer_email) " +
"must be supplied")
# check_metadata ()
def get_file_list (self):
"""Figure out the list of files to include in the source
distribution, and put it in 'self.files'. This might
involve reading the manifest template (and writing the
manifest), or just reading the manifest, or just using
the default file set -- it all depends on the user's
options and the state of the filesystem."""
template_exists = os.path.isfile (self.template)
if template_exists:
template_newer = newer (self.template, self.manifest)
# Regenerate the manifest if necessary (or if explicitly told to)
if ((template_exists and template_newer) or
self.force_manifest or
self.manifest_only):
if not template_exists:
self.warn (("manifest template '%s' does not exist " +
"(using default file list)") %
self.template)
# Add default file set to 'files'
if self.use_defaults:
self.find_defaults ()
# Read manifest template if it exists
if template_exists:
self.read_template ()
# File list now complete -- sort it so that higher-level files
# come first
sortable_files = map (os.path.split, self.files)
sortable_files.sort ()
self.files = []
for sort_tuple in sortable_files:
self.files.append (apply (os.path.join, sort_tuple))
# Remove duplicates from the file list
for i in range (len(self.files)-1, 0, -1):
if self.files[i] == self.files[i-1]:
del self.files[i]
# And write complete file list (including default file set) to
# the manifest.
self.write_manifest ()
# Don't regenerate the manifest, just read it in.
else:
self.read_manifest ()
# get_file_list ()
def find_defaults (self):
standards = [('README', 'README.txt'), 'setup.py']
for fn in standards:
if type (fn) is TupleType:
alts = fn
got_it = 0
for fn in alts:
if os.path.exists (fn):
got_it = 1
self.files.append (fn)
break
if not got_it:
self.warn ("standard file not found: should have one of " +
string.join (alts, ', '))
else:
if os.path.exists (fn):
self.files.append (fn)
else:
self.warn ("standard file '%s' not found" % fn)
optional = ['test/test*.py']
for pattern in optional:
files = filter (os.path.isfile, glob (pattern))
if files:
self.files.extend (files)
if self.distribution.packages or self.distribution.py_modules:
build_py = self.find_peer ('build_py')
build_py.ensure_ready ()
self.files.extend (build_py.get_source_files ())
if self.distribution.ext_modules:
build_ext = self.find_peer ('build_ext')
build_ext.ensure_ready ()
self.files.extend (build_ext.get_source_files ())
def search_dir (self, dir, pattern=None):
"""Recursively find files under 'dir' matching 'pattern' (a string
containing a Unix-style glob pattern). If 'pattern' is None,
find all files under 'dir'. Return the list of found
filenames."""
allfiles = findall (dir)
if pattern is None:
return allfiles
pattern_re = translate_pattern (pattern)
files = []
for file in allfiles:
if pattern_re.match (os.path.basename (file)):
files.append (file)
return files
# search_dir ()
def exclude_pattern (self, pattern):
"""Remove filenames from 'self.files' that match 'pattern'."""
print "exclude_pattern: pattern=%s" % pattern
pattern_re = translate_pattern (pattern)
for i in range (len (self.files)-1, -1, -1):
if pattern_re.match (self.files[i]):
print "removing %s" % self.files[i]
del self.files[i]
def recursive_exclude_pattern (self, dir, pattern=None):
"""Remove filenames from 'self.files' that are under 'dir'
and whose basenames match 'pattern'."""
print "recursive_exclude_pattern: dir=%s, pattern=%s" % (dir, pattern)
if pattern is None:
pattern_re = None
else:
pattern_re = translate_pattern (pattern)
for i in range (len (self.files)-1, -1, -1):
(cur_dir, cur_base) = os.path.split (self.files[i])
if (cur_dir == dir and
(pattern_re is None or pattern_re.match (cur_base))):
print "removing %s" % self.files[i]
del self.files[i]
def read_template (self):
"""Read and parse the manifest template file named by
'self.template' (usually "MANIFEST.in"). Process all file
specifications (include and exclude) in the manifest template
and add the resulting filenames to 'self.files'."""
assert self.files is not None and type (self.files) is ListType
template = TextFile (self.template,
strip_comments=1,
skip_blanks=1,
join_lines=1,
lstrip_ws=1,
rstrip_ws=1,
collapse_ws=1)
all_files = findall ()
while 1:
line = template.readline()
if line is None: # end of file
break
words = string.split (line)
action = words[0]
# First, check that the right number of words are present
# for the given action (which is the first word)
if action in ('include','exclude',
'global-include','global-exclude'):
if len (words) != 2:
template.warn \
("invalid manifest template line: " +
"'%s' expects a single <pattern>" %
action)
continue
pattern = words[1]
elif action in ('recursive-include','recursive-exclude'):
if len (words) != 3:
template.warn \
("invalid manifest template line: " +
"'%s' expects <dir> <pattern>" %
action)
continue
(dir, pattern) = words[1:3]
elif action in ('graft','prune'):
if len (words) != 2:
template.warn \
("invalid manifest template line: " +
"'%s' expects a single <dir_pattern>" %
action)
continue
dir_pattern = words[1]
else:
template.warn ("invalid manifest template line: " +
"unknown action '%s'" % action)
continue
# OK, now we know that the action is valid and we have the
# right number of words on the line for that action -- so we
# can proceed with minimal error-checking. Also, we have
# defined either 'patter', 'dir' and 'pattern', or
# 'dir_pattern' -- so we don't have to spend any time digging
# stuff up out of 'words'.
if action == 'include':
print "include", pattern
files = select_pattern (all_files, pattern, anchor=1)
if not files:
template.warn ("no files found matching '%s'" % pattern)
else:
self.files.extend (files)
elif action == 'exclude':
print "exclude", pattern
num = exclude_pattern (self.files, pattern, anchor=1)
if num == 0:
template.warn \
("no previously-included files found matching '%s'" %
pattern)
elif action == 'global-include':
print "global-include", pattern
files = select_pattern (all_files, pattern, anchor=0)
if not files:
template.warn (("no files found matching '%s' " +
"anywhere in distribution") %
pattern)
else:
self.files.extend (files)
elif action == 'global-exclude':
print "global-exclude", pattern
num = exclude_pattern (self.files, pattern, anchor=0)
if num == 0:
template.warn \
(("no previously-included files matching '%s' " +
"found anywhere in distribution") %
pattern)
elif action == 'recursive-include':
print "recursive-include", dir, pattern
files = select_pattern (all_files, pattern, prefix=dir)
if not files:
template.warn (("no files found matching '%s' " +
"under directory '%s'") %
(pattern, dir))
else:
self.files.extend (files)
elif action == 'recursive-exclude':
print "recursive-exclude", dir, pattern
num = exclude_pattern (self.files, pattern, prefix=dir)
if num == 0:
template.warn \
(("no previously-included files matching '%s' " +
"found under directory '%s'") %
(pattern, dir))
elif action == 'graft':
print "graft", dir_pattern
files = select_pattern (all_files, None, prefix=dir_pattern)
if not files:
template.warn ("no directories found matching '%s'" %
dir_pattern)
else:
self.files.extend (files)
elif action == 'prune':
print "prune", dir_pattern
num = exclude_pattern (self.files, None, prefix=dir_pattern)
if num == 0:
template.warn \
(("no previously-included directories found " +
"matching '%s'") %
dir_pattern)
else:
raise RuntimeError, \
"this cannot happen: invalid action '%s'" % action
# while loop over lines of template file
# read_template ()
def write_manifest (self):
"""Write the file list in 'self.files' (presumably as filled in
by 'find_defaults()' and 'read_template()') to the manifest file
named by 'self.manifest'."""
manifest = open (self.manifest, "w")
for fn in self.files:
manifest.write (fn + '\n')
manifest.close ()
# write_manifest ()
def read_manifest (self):
"""Read the manifest file (named by 'self.manifest') and use
it to fill in 'self.files', the list of files to include
in the source distribution."""
manifest = open (self.manifest)
while 1:
line = manifest.readline ()
if line == '': # end of file
break
if line[-1] == '\n':
line = line[0:-1]
self.files.append (line)
# read_manifest ()
def make_release_tree (self, base_dir, files):
# First get the list of directories to create
need_dir = {}
for file in files:
need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1
need_dirs = need_dir.keys()
need_dirs.sort()
# Now create them
for dir in need_dirs:
self.mkpath (dir)
# And walk over the list of files, either making a hard link (if
# os.link exists) to each one that doesn't already exist in its
# corresponding location under 'base_dir', or copying each file
# that's out-of-date in 'base_dir'. (Usually, all files will be
# out-of-date, because by default we blow away 'base_dir' when
# we're done making the distribution archives.)
try:
link = os.link
msg = "making hard links in %s..." % base_dir
except AttributeError:
link = 0
msg = "copying files to %s..." % base_dir
self.announce (msg)
for file in files:
dest = os.path.join (base_dir, file)
if link:
if not os.path.exists (dest):
self.execute (os.link, (file, dest),
"linking %s -> %s" % (file, dest))
else:
self.copy_file (file, dest)
# make_release_tree ()
def make_tarball (self, base_dir, compress="gzip"):
# XXX GNU tar 1.13 has a nifty option to add a prefix directory.
# It's pretty new, though, so we certainly can't require it --
# but it would be nice to take advantage of it to skip the
# "create a tree of hardlinks" step! (Would also be nice to
# detect GNU tar to use its 'z' option and save a step.)
if compress is not None and compress not in ('gzip', 'compress'):
raise ValueError, \
"if given, 'compress' must be 'gzip' or 'compress'"
archive_name = base_dir + ".tar"
self.spawn (["tar", "-cf", archive_name, base_dir])
if compress:
self.spawn ([compress, archive_name])
def make_zipfile (self, base_dir):
# This initially assumed the Unix 'zip' utility -- but
# apparently InfoZIP's zip.exe works the same under Windows, so
# no changes needed!
try:
self.spawn (["zip", "-r", base_dir + ".zip", base_dir])
except DistutilsExecError:
# XXX really should distinguish between "couldn't find
# external 'zip' command" and "zip failed" -- shouldn't try
# again in the latter case. (I think fixing this will
# require some cooperation from the spawn module -- perhaps
# a utility function to search the path, so we can fallback
# on zipfile.py without the failed spawn.)
try:
import zipfile
except ImportError:
raise DistutilsExecError, \
("unable to create zip file '%s.zip': " +
"could neither find a standalone zip utility nor " +
"import the 'zipfile' module") % base_dir
z = zipfile.ZipFile (base_dir + ".zip", "wb",
compression=zipfile.ZIP_DEFLATED)
def visit (z, dirname, names):
for name in names:
path = os.path.join (dirname, name)
if os.path.isfile (path):
z.write (path, path)
os.path.walk (base_dir, visit, z)
z.close()
def make_distribution (self):
# Don't warn about missing meta-data here -- should be done
# elsewhere.
name = self.distribution.name or "UNKNOWN"
version = self.distribution.version
if version:
base_dir = "%s-%s" % (name, version)
else:
base_dir = name
# Remove any files that match "base_dir" from the fileset -- we
# don't want to go distributing the distribution inside itself!
self.exclude_pattern (base_dir + "*")
self.make_release_tree (base_dir, self.files)
for fmt in self.formats:
if fmt == 'gztar':
self.make_tarball (base_dir, compress='gzip')
elif fmt == 'ztar':
self.make_tarball (base_dir, compress='compress')
elif fmt == 'tar':
self.make_tarball (base_dir, compress=None)
elif fmt == 'zip':
self.make_zipfile (base_dir)
if not self.keep_tree:
remove_tree (base_dir, self.verbose, self.dry_run)
# class Dist
# ----------------------------------------------------------------------
# Utility functions
def findall (dir = os.curdir):
"""Find all files under 'dir' and return the list of full
filenames (relative to 'dir')."""
list = []
stack = [dir]
pop = stack.pop
push = stack.append
while stack:
dir = pop()
names = os.listdir (dir)
for name in names:
if dir != os.curdir: # avoid the dreaded "./" syndrome
fullname = os.path.join (dir, name)
else:
fullname = name
list.append (fullname)
if os.path.isdir (fullname) and not os.path.islink(fullname):
push (fullname)
return list
def select_pattern (files, pattern, anchor=1, prefix=None):
"""Select strings (presumably filenames) from 'files' that match
'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
quite the same as implemented by the 'fnmatch' module: '*' and '?'
match non-special characters, where "special" is platform-dependent:
slash on Unix, colon, slash, and backslash on DOS/Windows, and colon
on Mac OS.
If 'anchor' is true (the default), then the pattern match is more
stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
'anchor' is false, both of these will match.
If 'prefix' is supplied, then only filenames starting with 'prefix'
(itself a pattern) and ending with 'pattern', with anything in
between them, will match. 'anchor' is ignored in this case.
Return the list of matching strings, possibly empty."""
matches = []
pattern_re = translate_pattern (pattern, anchor, prefix)
print "select_pattern: applying re %s" % pattern_re.pattern
for name in files:
if pattern_re.search (name):
matches.append (name)
print " adding", name
return matches
# select_pattern ()
def exclude_pattern (files, pattern, anchor=1, prefix=None):
pattern_re = translate_pattern (pattern, anchor, prefix)
print "exclude_pattern: applying re %s" % pattern_re.pattern
for i in range (len(files)-1, -1, -1):
if pattern_re.search (files[i]):
print " removing", files[i]
del files[i]
# exclude_pattern ()
def glob_to_re (pattern):
"""Translate a shell-like glob pattern to a regular expression;
return a string containing the regex. Differs from
'fnmatch.translate()' in that '*' does not match "special
characters" (which are platform-specific)."""
pattern_re = fnmatch.translate (pattern)
# '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
# IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
# and by extension they shouldn't match such "special characters" under
# any OS. So change all non-escaped dots in the RE to match any
# character except the special characters.
# XXX currently the "special characters" are just slash -- i.e. this is
# Unix-only.
pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
return pattern_re
# glob_to_re ()
def translate_pattern (pattern, anchor=1, prefix=None):
"""Translate a shell-like wildcard pattern to a compiled regular
expression. Return the compiled regex."""
if pattern:
pattern_re = glob_to_re (pattern)
else:
pattern_re = ''
if prefix is not None:
prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
else: # no prefix -- respect anchor flag
if anchor:
pattern_re = "^" + pattern_re
return re.compile (pattern_re)
# translate_pattern ()

View file

@ -1,4 +1,4 @@
import httplib,urlparse,sys,time
import http11lib,urlparse,sys,time
from UrlData import UrlData
from RobotsTxt import RobotsTxt
import Config,StringUtil
@ -116,7 +116,7 @@ class HttpUrlData(UrlData):
return self.urlConnection.getreply()
def _getHTTPObject(self, host):
return httplib.HTTP(host)
return http11lib.HTTP(host)
def getContent(self):
self.closeConnection()
@ -124,7 +124,7 @@ class HttpUrlData(UrlData):
self._getHttpRequest("GET")
self.urlConnection = self.urlConnection.getfile()
data = StringUtil.stripHtmlComments(self.urlConnection.read())
self.time = time.time() - t
self.downloadtime = time.time() - t
Config.debug(Config.DebugDelim+data+Config.DebugDelim)
return data

View file

@ -115,7 +115,8 @@ class StandardLogger:
self.fd.write(" found.\n")
self.stoptime = time.time()
self.fd.write("Stopped checking at "+_strtime(self.stoptime)+
(" (%.3f seconds)" % (self.stoptime - self.starttime)))
(" (%.3f seconds)" % (self.stoptime - self.starttime))+
"\n")
self.fd.flush()
self.close()

View file

@ -18,7 +18,7 @@ class UrlData:
recursionLevel,
parentName = None,
baseRef = None,
line = None, _time = None):
line = None):
self.urlName = urlName
self.recursionLevel = recursionLevel
self.parentName = parentName
@ -30,7 +30,7 @@ class UrlData:
self.valid = 1
self.url = None
self.line = line
self.downloadtime = _time
self.downloadtime = None
self.checktime = None
self.cached = 0
self.urlConnection = None

View file

@ -1,15 +1,14 @@
#!/usr/bin/env python
import getopt,sys,re,string
import sys
if sys.version[:5] < "1.5.2":
print "This program requires Python 1.5.2 or later."
sys.exit(1)
# add the path to linkcheck module
#sys.path.insert(0, "/home/calvin/projects/linkchecker")
import linkcheck
# add the path to linkcheck module if you install it manually somewhere
#sys.path.append("/home/calvin/projects/linkchecker")
import getopt,re,string,linkcheck
Usage = """USAGE\tlinkchecker [options] file_or_url...

View file

@ -1,8 +1,4 @@
@echo off
rem === adjust vars below ===
set PYTHON=c:\progra~1\python\python.exe
set LINKCHECKER=c:\progra~1\linkchecker-1.1.2
rem === end configure ===
%PYTHON% %LINKCHECKER%\linkchecker %1 %2 %3 %4 %5 %6 %7 %8 %9
rem Limited to 9 parameters? Is there a $* for Windows?
python @INSTALL_BIN@\linkchecker %1 %2 %3 %4 %5 %6 %7 %8 %9