292 lines
9.2 KiB
Python
292 lines
9.2 KiB
Python
"""distutils.core
|
|
|
|
The only module that needs to be imported to use the Distutils; provides
|
|
the 'setup' function (which is to be called from the setup script). Also
|
|
indirectly provides the Distribution and Command classes, although they are
|
|
really defined in distutils.dist and distutils.cmd.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import tokenize
|
|
|
|
from distutils.debug import DEBUG
|
|
from distutils.errors import (
|
|
DistutilsSetupError,
|
|
DistutilsError,
|
|
CCompilerError,
|
|
DistutilsArgError,
|
|
)
|
|
|
|
# Mainly import these so setup scripts can "from distutils.core import" them.
|
|
from distutils.dist import Distribution
|
|
from distutils.cmd import Command
|
|
from distutils.config import PyPIRCCommand
|
|
from distutils.extension import Extension
|
|
|
|
|
|
__all__ = ['Distribution', 'Command', 'PyPIRCCommand', 'Extension', 'setup']
|
|
|
|
# This is a barebones help message generated displayed when the user
|
|
# runs the setup script with no arguments at all. More useful help
|
|
# is generated with various --help options: global help, list commands,
|
|
# and per-command help.
|
|
USAGE = """\
|
|
usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
|
|
or: %(script)s --help [cmd1 cmd2 ...]
|
|
or: %(script)s --help-commands
|
|
or: %(script)s cmd --help
|
|
"""
|
|
|
|
|
|
def gen_usage(script_name):
|
|
script = os.path.basename(script_name)
|
|
return USAGE % locals()
|
|
|
|
|
|
# Some mild magic to control the behaviour of 'setup()' from 'run_setup()'.
|
|
_setup_stop_after = None
|
|
_setup_distribution = None
|
|
|
|
# Legal keyword arguments for the setup() function
|
|
setup_keywords = (
|
|
'distclass',
|
|
'script_name',
|
|
'script_args',
|
|
'options',
|
|
'name',
|
|
'version',
|
|
'author',
|
|
'author_email',
|
|
'maintainer',
|
|
'maintainer_email',
|
|
'url',
|
|
'license',
|
|
'description',
|
|
'long_description',
|
|
'keywords',
|
|
'platforms',
|
|
'classifiers',
|
|
'download_url',
|
|
'requires',
|
|
'provides',
|
|
'obsoletes',
|
|
)
|
|
|
|
# Legal keyword arguments for the Extension constructor
|
|
extension_keywords = (
|
|
'name',
|
|
'sources',
|
|
'include_dirs',
|
|
'define_macros',
|
|
'undef_macros',
|
|
'library_dirs',
|
|
'libraries',
|
|
'runtime_library_dirs',
|
|
'extra_objects',
|
|
'extra_compile_args',
|
|
'extra_link_args',
|
|
'swig_opts',
|
|
'export_symbols',
|
|
'depends',
|
|
'language',
|
|
)
|
|
|
|
|
|
def setup(**attrs): # noqa: C901
|
|
"""The gateway to the Distutils: do everything your setup script needs
|
|
to do, in a highly flexible and user-driven way. Briefly: create a
|
|
Distribution instance; find and parse config files; parse the command
|
|
line; run each Distutils command found there, customized by the options
|
|
supplied to 'setup()' (as keyword arguments), in config files, and on
|
|
the command line.
|
|
|
|
The Distribution instance might be an instance of a class supplied via
|
|
the 'distclass' keyword argument to 'setup'; if no such class is
|
|
supplied, then the Distribution class (in dist.py) is instantiated.
|
|
All other arguments to 'setup' (except for 'cmdclass') are used to set
|
|
attributes of the Distribution instance.
|
|
|
|
The 'cmdclass' argument, if supplied, is a dictionary mapping command
|
|
names to command classes. Each command encountered on the command line
|
|
will be turned into a command class, which is in turn instantiated; any
|
|
class found in 'cmdclass' is used in place of the default, which is
|
|
(for command 'foo_bar') class 'foo_bar' in module
|
|
'distutils.command.foo_bar'. The command class must provide a
|
|
'user_options' attribute which is a list of option specifiers for
|
|
'distutils.fancy_getopt'. Any command-line options between the current
|
|
and the next command are used to set attributes of the current command
|
|
object.
|
|
|
|
When the entire command-line has been successfully parsed, calls the
|
|
'run()' method on each command object in turn. This method will be
|
|
driven entirely by the Distribution object (which each command object
|
|
has a reference to, thanks to its constructor), and the
|
|
command-specific options that became attributes of each command
|
|
object.
|
|
"""
|
|
|
|
global _setup_stop_after, _setup_distribution
|
|
|
|
# Determine the distribution class -- either caller-supplied or
|
|
# our Distribution (see below).
|
|
klass = attrs.get('distclass')
|
|
if klass:
|
|
del attrs['distclass']
|
|
else:
|
|
klass = Distribution
|
|
|
|
if 'script_name' not in attrs:
|
|
attrs['script_name'] = os.path.basename(sys.argv[0])
|
|
if 'script_args' not in attrs:
|
|
attrs['script_args'] = sys.argv[1:]
|
|
|
|
# Create the Distribution instance, using the remaining arguments
|
|
# (ie. everything except distclass) to initialize it
|
|
try:
|
|
_setup_distribution = dist = klass(attrs)
|
|
except DistutilsSetupError as msg:
|
|
if 'name' not in attrs:
|
|
raise SystemExit("error in setup command: %s" % msg)
|
|
else:
|
|
raise SystemExit("error in {} setup command: {}".format(attrs['name'], msg))
|
|
|
|
if _setup_stop_after == "init":
|
|
return dist
|
|
|
|
# Find and parse the config file(s): they will override options from
|
|
# the setup script, but be overridden by the command line.
|
|
dist.parse_config_files()
|
|
|
|
if DEBUG:
|
|
print("options (after parsing config files):")
|
|
dist.dump_option_dicts()
|
|
|
|
if _setup_stop_after == "config":
|
|
return dist
|
|
|
|
# Parse the command line and override config files; any
|
|
# command-line errors are the end user's fault, so turn them into
|
|
# SystemExit to suppress tracebacks.
|
|
try:
|
|
ok = dist.parse_command_line()
|
|
except DistutilsArgError as msg:
|
|
raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg)
|
|
|
|
if DEBUG:
|
|
print("options (after parsing command line):")
|
|
dist.dump_option_dicts()
|
|
|
|
if _setup_stop_after == "commandline":
|
|
return dist
|
|
|
|
# And finally, run all the commands found on the command line.
|
|
if ok:
|
|
return run_commands(dist)
|
|
|
|
return dist
|
|
|
|
|
|
# setup ()
|
|
|
|
|
|
def run_commands(dist):
|
|
"""Given a Distribution object run all the commands,
|
|
raising ``SystemExit`` errors in the case of failure.
|
|
|
|
This function assumes that either ``sys.argv`` or ``dist.script_args``
|
|
is already set accordingly.
|
|
"""
|
|
try:
|
|
dist.run_commands()
|
|
except KeyboardInterrupt:
|
|
raise SystemExit("interrupted")
|
|
except OSError as exc:
|
|
if DEBUG:
|
|
sys.stderr.write("error: {}\n".format(exc))
|
|
raise
|
|
else:
|
|
raise SystemExit("error: {}".format(exc))
|
|
|
|
except (DistutilsError, CCompilerError) as msg:
|
|
if DEBUG:
|
|
raise
|
|
else:
|
|
raise SystemExit("error: " + str(msg))
|
|
|
|
return dist
|
|
|
|
|
|
def run_setup(script_name, script_args=None, stop_after="run"):
|
|
"""Run a setup script in a somewhat controlled environment, and
|
|
return the Distribution instance that drives things. This is useful
|
|
if you need to find out the distribution meta-data (passed as
|
|
keyword args from 'script' to 'setup()', or the contents of the
|
|
config files or command-line.
|
|
|
|
'script_name' is a file that will be read and run with 'exec()';
|
|
'sys.argv[0]' will be replaced with 'script' for the duration of the
|
|
call. 'script_args' is a list of strings; if supplied,
|
|
'sys.argv[1:]' will be replaced by 'script_args' for the duration of
|
|
the call.
|
|
|
|
'stop_after' tells 'setup()' when to stop processing; possible
|
|
values:
|
|
init
|
|
stop after the Distribution instance has been created and
|
|
populated with the keyword arguments to 'setup()'
|
|
config
|
|
stop after config files have been parsed (and their data
|
|
stored in the Distribution instance)
|
|
commandline
|
|
stop after the command-line ('sys.argv[1:]' or 'script_args')
|
|
have been parsed (and the data stored in the Distribution)
|
|
run [default]
|
|
stop after all commands have been run (the same as if 'setup()'
|
|
had been called in the usual way
|
|
|
|
Returns the Distribution instance, which provides all information
|
|
used to drive the Distutils.
|
|
"""
|
|
if stop_after not in ('init', 'config', 'commandline', 'run'):
|
|
raise ValueError("invalid value for 'stop_after': {!r}".format(stop_after))
|
|
|
|
global _setup_stop_after, _setup_distribution
|
|
_setup_stop_after = stop_after
|
|
|
|
save_argv = sys.argv.copy()
|
|
g = {'__file__': script_name, '__name__': '__main__'}
|
|
try:
|
|
try:
|
|
sys.argv[0] = script_name
|
|
if script_args is not None:
|
|
sys.argv[1:] = script_args
|
|
# tokenize.open supports automatic encoding detection
|
|
with tokenize.open(script_name) as f:
|
|
code = f.read().replace(r'\r\n', r'\n')
|
|
exec(code, g)
|
|
finally:
|
|
sys.argv = save_argv
|
|
_setup_stop_after = None
|
|
except SystemExit:
|
|
# Hmm, should we do something if exiting with a non-zero code
|
|
# (ie. error)?
|
|
pass
|
|
|
|
if _setup_distribution is None:
|
|
raise RuntimeError(
|
|
(
|
|
"'distutils.core.setup()' was never called -- "
|
|
"perhaps '%s' is not a Distutils setup script?"
|
|
)
|
|
% script_name
|
|
)
|
|
|
|
# I wonder if the setup script's namespace -- g and l -- would be of
|
|
# any interest to callers?
|
|
# print "_setup_distribution:", _setup_distribution
|
|
return _setup_distribution
|
|
|
|
|
|
# run_setup ()
|