those described below."""
- # 'global_options' describes the command-line options that may
- # be supplied to the client (setup.py) prior to any actual
- # commands. Eg. "./setup.py -nv" or "./setup.py --verbose"
- # both take advantage of these global options.
- global_options = [('verbose', 'v', "run verbosely"),
- ('dry-run', 'n', "don't actually do anything"),
+ # 'global_options' describes the command-line options that may be
+ # supplied to the client (setup.py) prior to any actual commands.
+ # Eg. "./setup.py -nv" or "./setup.py --verbose" both take advantage of
+ # these global options. This list should be kept to a bare minimum,
+ # since every global option is also valid as a command option -- and we
+ # don't want to pollute the commands with too many options that they
+ # have minimal control over.
+ global_options = [('verbose', 'v',
+ "run verbosely"),
+ ('quiet=!verbose', 'q',
+ "run quietly (turns verbosity off)"),
+ ('dry-run', 'n',
+ "don't actually do anything"),
+ ('force', 'f',
+ "skip dependency checking between files"),
]
# Default values for our command-line options
self.verbose = 0
self.dry_run = 0
+ self.force = 0
# And the "distribution meta-data" options -- these can only
# come from setup.py (the caller), not the command line
def parse_command_line (self, args):
- """Parse the client's command line: set any Distribution
+ """Parse the setup script's command line: set any Distribution
attributes tied to command-line options, create all command
objects, and set their options from the command-line. 'args'
must be a list of command-line arguments, most likely
- 'sys.argv[1:]' (see the 'setup()' function). This list is
- first processed for "global options" -- options that set
- attributes of the Distribution instance. Then, it is
- alternately scanned for Distutils command and options for
- that command. Each new command terminates the options for
- the previous command. The allowed options for a command are
- determined by the 'options' attribute of the command object
- -- thus, we instantiate (and cache) every command object
- here, in order to access its 'options' attribute. Any error
- in that 'options' attribute raises DistutilsGetoptError; any
- error on the command-line raises DistutilsArgError. If no
- Distutils commands were found on the command line, raises
- DistutilsArgError."""
+ 'sys.argv[1:]' (see the 'setup()' function). This list is first
+ processed for "global options" -- options that set attributes of
+ the Distribution instance. Then, it is alternately scanned for
+ Distutils command and options for that command. Each new
+ command terminates the options for the previous command. The
+ allowed options for a command are determined by the 'options'
+ attribute of the command object -- thus, we instantiate (and
+ cache) every command object here, in order to access its
+ 'options' attribute. Any error in that 'options' attribute
+ raises DistutilsGetoptError; any error on the command-line
+ raises DistutilsArgError. If no Distutils commands were found
+ on the command line, raises DistutilsArgError."""
# We have to parse the command line a bit at a time -- global
# options, then the first command, then its options, and so on --
"(a list of tuples)") % \
cmd_obj.__class__
- args = fancy_getopt (cmd_obj.options, cmd_obj, args[1:])
+ # Poof! like magic, all commands support the global
+ # options too, just by adding in 'global_options'.
+ args = fancy_getopt (self.global_options + cmd_obj.options,
+ cmd_obj, args[1:])
self.command_obj[command] = cmd_obj
self.have_run[command] = 0
self.distribution = dist
self.set_default_options ()
+ # Per-command versions of the global flags, so that the user can
+ # customize Distutils' behaviour command-by-command and let some
+ # commands fallback on the Distribution's behaviour. None means
+ # "not defined, check self.distribution's copy", while 0 or 1 mean
+ # false and true (duh). Note that this means figuring out the real
+ # value of each flag is a touch complicatd -- hence "self.verbose"
+ # (etc.) will be handled by __getattr__, below.
+ self._verbose = None
+ self._dry_run = None
+ self._force = None
+
# 'ready' records whether or not 'set_final_options()' has been
# called. 'set_final_options()' itself should not pay attention to
# this flag: it is the business of 'ensure_ready()', which always
# end __init__ ()
+ def __getattr__ (self, attr):
+ if attr in ('verbose', 'dry_run', 'force'):
+ myval = getattr (self, "_" + attr)
+ if myval is None:
+ return getattr (self.distribution, attr)
+ else:
+ return myval
+ else:
+ raise AttributeError, attr
+
+
def ensure_ready (self):
if not self.ready:
self.set_final_options ()
"""If the Distribution instance to which this command belongs
has a verbosity level of greater than or equal to 'level'
print 'msg' to stdout."""
- if self.distribution.verbose >= level:
+
+ if self.verbose >= level:
print msg
def execute (self, func, args, msg=None, level=1):
"""Perform some action that affects the outside world (eg.
by writing to the filesystem). Such actions are special because
- they should be disabled by the "dry run" flag (carried around by
- the Command's Distribution), and should announce themselves if
- the current verbosity level is high enough. This method takes
- care of all that bureaucracy for you; all you have to do is
- supply the funtion to call and an argument tuple for it (to
- embody the "external action" being performed), a message to
- print if the verbosity level is high enough, and an optional
- verbosity threshold."""
-
+ they should be disabled by the "dry run" flag, and should
+ announce themselves if the current verbosity level is high
+ enough. This method takes care of all that bureaucracy for you;
+ all you have to do is supply the funtion to call and an argument
+ tuple for it (to embody the "external action" being performed),
+ a message to print if the verbosity level is high enough, and an
+ optional verbosity threshold."""
# Generate a message if we weren't passed one
if msg is None:
self.announce (msg, level)
# And do it, as long as we're not in dry-run mode
- if not self.distribution.dry_run:
+ if not self.dry_run:
apply (func, args)
# execute()
def mkpath (self, name, mode=0777):
util.mkpath (name, mode,
- self.distribution.verbose, self.distribution.dry_run)
+ self.verbose, self.dry_run)
def copy_file (self, infile, outfile,
- preserve_mode=1, preserve_times=1, update=1, level=1):
- """Copy a file respecting verbose and dry-run flags."""
+ preserve_mode=1, preserve_times=1, level=1):
+ """Copy a file respecting verbose, dry-run and force flags."""
return util.copy_file (infile, outfile,
preserve_mode, preserve_times,
- update, self.distribution.verbose >= level,
- self.distribution.dry_run)
+ not self.force,
+ self.verbose >= level,
+ self.dry_run)
def copy_tree (self, infile, outfile,
preserve_mode=1, preserve_times=1, preserve_symlinks=0,
- update=1, level=1):
- """Copy an entire directory tree respecting verbose and dry-run
- flags."""
+ level=1):
+ """Copy an entire directory tree respecting verbose, dry-run,
+ and force flags."""
return util.copy_tree (infile, outfile,
preserve_mode,preserve_times,preserve_symlinks,
- update, self.distribution.verbose >= level,
- self.distribution.dry_run)
+ not self.force,
+ self.verbose >= level,
+ self.dry_run)
def move_file (self, src, dst, level=1):
"""Move a file respecting verbose and dry-run flags."""
return util.move_file (src, dst,
- self.distribution.verbose >= level,
- self.distribution.dry_run)
+ self.verbose >= level,
+ self.dry_run)
def spawn (self, cmd, search_path=1, level=1):
from distutils.spawn import spawn
spawn (cmd, search_path,
- self.distribution.verbose >= level,
- self.distribution.dry_run)
+ self.verbose >= level,
+ self.dry_run)
def make_file (self, infiles, outfile, func, args,
raise TypeError, \
"'infiles' must be a string, or a list or tuple of strings"
- # XXX this stuff should probably be moved off to a function
- # in 'distutils.util'
- from stat import *
-
- if os.path.exists (outfile):
- out_mtime = os.stat (outfile)[ST_MTIME]
-
- # Loop over all infiles. If any infile is newer than outfile,
- # then we'll have to regenerate outfile
- for f in infiles:
- in_mtime = os.stat (f)[ST_MTIME]
- if in_mtime > out_mtime:
- runit = 1
- break
- else:
- runit = 0
-
- else:
- runit = 1
-
- # If we determined that 'outfile' must be regenerated, then
+ # If 'outfile' must be regenerated (either because it doesn't
+ # exist, is out-of-date, or the 'force' flag is true) then
# perform the action that presumably regenerates it
- if runit:
+ if self.force or newer_group (infiles, outfile):
self.execute (func, args, exec_msg, level)
# Otherwise, print the "skip" message
# make_file ()
-
-# def make_files (self, infiles, outfiles, func, args,
-# exec_msg=None, skip_msg=None, level=1):
-
-# """Special case of 'execute()' for operations that process one or
-# more input files and generate one or more output files. Works
-# just like 'execute()', except the operation is skipped and a
-# different message printed if all files listed in 'outfiles'
-# already exist and are newer than all files listed in
-# 'infiles'."""
-
-# pass
-
-
-
# end class Command