Commit ddff62df authored by maruel@chromium.org's avatar maruel@chromium.org

Goes further down the rabbit hole.

Enable automatic command and one-liner doc. Reformat pydoc accordingly.
Add parser as an argument and parse_args hook in preparation to move parse_args at the right place, inside the CMDxx functions.
Update unit tests

Review URL: http://codereview.chromium.org/2129005

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@47449 0039d316-1c4b-4281-b951-d872f2087c98
parent a761b60c
......@@ -55,7 +55,7 @@ Hooks
]
"""
__version__ = "0.3.5"
__version__ = "0.3.7"
import errno
import logging
......@@ -75,23 +75,10 @@ from third_party.repo.progress import Progress
# default help text
DEFAULT_USAGE_TEXT = (
"""usage: %prog <subcommand> [options] [--] [SCM options/args...]
"""%prog <subcommand> [options] [--] [SCM options/args...]
a wrapper for managing a set of svn client modules and/or git repositories.
Version """ + __version__ + """
subcommands:
cleanup
config
diff
export
pack
revert
status
sync
update
runhooks
revinfo
Options and extra arguments can be passed to invoked SCM commands by
appending them to the command line. Note that if the first such
appended option starts with a dash (-) then the options must be
......@@ -829,8 +816,9 @@ solutions = [
## gclient commands.
def CMDcleanup(options, args):
def CMDcleanup(parser, options, args):
"""Clean up all working copies, using 'svn cleanup' for each module.
Additional options and args may be passed to 'svn cleanup'.
usage: cleanup [options] [--] [svn cleanup args/options]
......@@ -848,9 +836,10 @@ Valid options:
return client.RunOnDeps('cleanup', args)
def CMDconfig(options, args):
"""Create a .gclient file in the current directory; this
specifies the configuration for further commands. After update/sync,
def CMDconfig(parser, options, args):
"""Create a .gclient file in the current directory.
This specifies the configuration for further commands. After update/sync,
top-level DEPS files in each module are read to determine dependent
modules to operate on as well. If optional [url] parameter is
provided, then configuration is read from a specified Subversion server
......@@ -897,9 +886,8 @@ Examples:
return 0
def CMDexport(options, args):
"""Wrapper for svn export for all managed directories
"""
def CMDexport(parser, options, args):
"""Wrapper for svn export for all managed directories."""
if len(args) != 1:
raise gclient_utils.Error("Need directory name")
client = GClient.LoadCurrentConfig(options)
......@@ -914,27 +902,9 @@ def CMDexport(options, args):
return client.RunOnDeps('export', args)
def CMDhelp(options, args):
"""Describe the usage of this program or its subcommands.
usage: help [options] [subcommand]
Valid options:
--verbose : output additional diagnostics
"""
__pychecker__ = 'unusednames=options'
module = sys.modules[__name__]
commands = [x[3:] for x in dir(module) if x.startswith('CMD')]
if len(args) == 1 and args[0] in commands:
print getattr(module, 'CMD' + args[0]).__doc__
else:
raise gclient_utils.Error("unknown subcommand '%s'; see 'gclient help'" %
args[0])
return 0
def CMDpack(options, args):
def CMDpack(parser, options, args):
"""Generate a patch which can be applied at the root of the tree.
Internally, runs 'svn diff' on each checked out module and
dependencies, and performs minimal postprocessing of the output. The
resulting patch is printed to stdout and can be applied to a freshly
......@@ -966,9 +936,10 @@ Examples:
return client.RunOnDeps('pack', args)
def CMDstatus(options, args):
"""Show the status of client and dependent modules, using 'svn diff'
for each module. Additional options and args may be passed to 'svn diff'.
def CMDstatus(parser, options, args):
"""Show the modification status of for every dependencies.
Additional options and args may be passed to 'svn status'.
usage: status [options] [--] [svn diff args/options]
......@@ -986,17 +957,17 @@ Valid options:
return client.RunOnDeps('status', args)
def CMDsync(options, args):
"""Perform a checkout/update of the modules specified by the gclient
configuration; see 'help config'. Unless --revision is specified,
then the latest revision of the root solutions is checked out, with
dependent submodule versions updated according to DEPS files.
If --revision is specified, then the given revision is used in place
def CMDsync(parser, options, args):
"""Checkout/update the modules specified by the gclient configuration.
Unless --revision is specified, then the latest revision of the root solutions
is checked out, with dependent submodule versions updated according to DEPS
files. If --revision is specified, then the given revision is used in place
of the latest, either for a single solution or for all solutions.
Unless the --force option is provided, solutions and modules whose
local revision matches the one to update (i.e., they have not changed
in the repository) are *not* modified. Unless --nohooks is provided,
the hooks are run.
the hooks are run. See 'help config' for more information.
usage: gclient sync [options] [--] [SCM update options/args]
......@@ -1052,14 +1023,14 @@ Examples:
return client.RunOnDeps('update', args)
def CMDupdate(options, args):
"""Alias for the sync command. Deprecated.
"""
return CMDsync(options, args)
def CMDupdate(parser, options, args):
"""Alias for the sync command. Deprecated."""
return CMDsync(parser, options, args)
def CMDdiff(options, args):
def CMDdiff(parser, options, args):
"""Display the differences between two revisions of modules.
(Does 'svn diff' for each checked out module and dependences.)
Additional args and options to 'svn diff' can be passed after
gclient options.
......@@ -1087,18 +1058,18 @@ Examples:
return client.RunOnDeps('diff', args)
def CMDrevert(options, args):
"""Revert every file in every managed directory in the client view.
"""
def CMDrevert(parser, options, args):
"""Revert every file in every managed directory in the client view."""
client = GClient.LoadCurrentConfig(options)
if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'")
return client.RunOnDeps('revert', args)
def CMDrunhooks(options, args):
"""Runs hooks for files that have been modified in the local working copy,
according to 'svn status'. Implies --force.
def CMDrunhooks(parser, options, args):
"""Runs hooks for files that have been modified in the local working copy.
Implies --force.
usage: runhooks [options]
......@@ -1116,8 +1087,10 @@ Valid options:
return client.RunOnDeps('runhooks', args)
def CMDrevinfo(options, args):
"""Outputs source path, server URL and revision information for every
def CMDrevinfo(parser, options, args):
"""Outputs defails for every dependencies.
This includes source path, server URL and revision information for every
dependency in all solutions.
usage: revinfo [options]
......@@ -1130,102 +1103,102 @@ usage: revinfo [options]
return 0
def DispatchCommand(command, options, args):
"""Dispatches the appropriate subcommand based on command line arguments.
"""
module = sys.modules[__name__]
command = getattr(module, 'CMD' + command, None)
if command:
return command(options, args)
else:
raise gclient_utils.Error("unknown subcommand '%s'; see 'gclient help'" %
command)
def CMDhelp(parser, options, args):
"""Prints general help or command-specific documentation."""
if len(args) == 1:
command = Command(args[0])
if command:
print getattr(sys.modules[__name__], 'CMD' + args[0]).__doc__
return 0
parser.usage = (DEFAULT_USAGE_TEXT + '\nCommands are:\n' + '\n'.join([
' %-10s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip())
for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')]))
parser.print_help()
return 0
def Command(command):
return getattr(sys.modules[__name__], 'CMD' + command, CMDhelp)
def Main(argv):
option_parser = optparse.OptionParser(usage=DEFAULT_USAGE_TEXT,
version=__version__)
option_parser.add_option("--force", action="store_true",
help="(update/sync only) force update even "
"for modules which haven't changed")
option_parser.add_option("--nohooks", action="store_true",
help="(update/sync/revert only) prevent the hooks "
"from running")
option_parser.add_option("--revision", action="append", dest="revisions",
metavar="REV", default=[],
help="(update/sync only) sync to a specific "
"revision, can be used multiple times for "
"each solution, e.g. --revision=src@123, "
"--revision=internal@32")
option_parser.add_option("--deps", dest="deps_os", metavar="OS_LIST",
help="(update/sync only) sync deps for the "
"specified (comma-separated) platform(s); "
"'all' will sync all platforms")
option_parser.add_option("--reset", action="store_true",
help="(update/sync only) resets any local changes "
"before updating (git only)")
option_parser.add_option("--spec",
help="(config only) create a gclient file "
"containing the provided string")
option_parser.add_option("-v", "--verbose", action="count", default=0,
help="produce additional output for diagnostics")
option_parser.add_option("--manually_grab_svn_rev", action="store_true",
help="Skip svn up whenever possible by requesting "
"actual HEAD revision from the repository")
option_parser.add_option("--head", action="store_true",
help="skips any safesync_urls specified in "
"configured solutions")
option_parser.add_option("--delete_unversioned_trees", action="store_true",
help="on update, delete any unexpected "
"unversioned trees that are in the checkout")
option_parser.add_option("--snapshot", action="store_true",
help="(revinfo only), create a snapshot file "
"of the current version of all repositories")
option_parser.add_option("--name",
help="specify alternate relative solution path")
option_parser.add_option("--gclientfile", metavar="FILENAME",
help="specify an alternate .gclient file")
if len(argv) < 2:
# Users don't need to be told to use the 'help' command.
option_parser.print_help()
return 1
parser = optparse.OptionParser(usage=DEFAULT_USAGE_TEXT,
version='%prog ' + __version__)
parser.add_option("-v", "--verbose", action="count", default=0,
help="Produces additional output for diagnostics. Can be "
"used up to three times for more logging info.")
parser.add_option("--gclientfile", metavar="FILENAME", dest="config_filename",
default=os.environ.get("GCLIENT_FILE", ".gclient"),
help="Specify an alternate .gclient file")
# The other options will be moved eventually.
parser.add_option("--force", action="store_true",
help="(update/sync only) force update even "
"for modules which haven't changed")
parser.add_option("--nohooks", action="store_true",
help="(update/sync/revert only) prevent the hooks "
"from running")
parser.add_option("--revision", action="append", dest="revisions",
metavar="REV", default=[],
help="(update/sync only) sync to a specific "
"revision, can be used multiple times for "
"each solution, e.g. --revision=src@123, "
"--revision=internal@32")
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST",
help="(update/sync only) sync deps for the "
"specified (comma-separated) platform(s); "
"'all' will sync all platforms")
parser.add_option("--reset", action="store_true",
help="(update/sync only) resets any local changes "
"before updating (git only)")
parser.add_option("--spec",
help="(config only) create a gclient file "
"containing the provided string")
parser.add_option("--manually_grab_svn_rev", action="store_true",
help="Skip svn up whenever possible by requesting "
"actual HEAD revision from the repository")
parser.add_option("--head", action="store_true",
help="skips any safesync_urls specified in "
"configured solutions")
parser.add_option("--delete_unversioned_trees", action="store_true",
help="on update, delete any unexpected "
"unversioned trees that are in the checkout")
parser.add_option("--snapshot", action="store_true",
help="(revinfo only), create a snapshot file "
"of the current version of all repositories")
parser.add_option("--name",
help="specify alternate relative solution path")
# Integrate standard options processing.
old_parser = parser.parse_args
def Parse(args):
(options, args) = old_parser(args)
if options.verbose == 2:
logging.basicConfig(level=logging.INFO)
elif options.verbose > 2:
logging.basicConfig(level=logging.DEBUG)
options.entries_filename = options.config_filename + "_entries"
return (options, args)
parser.parse_args = Parse
# We don't want wordwrapping in epilog (usually examples)
parser.format_epilog = lambda _: parser.epilog or ''
if not len(argv):
argv = ['help']
# Add manual support for --version as first argument.
if argv[1] == '--version':
option_parser.print_version()
if argv[0] == '--version':
parser.print_version()
return 0
# Add manual support for --help as first argument.
if argv[1] == '--help':
argv[1] = 'help'
command = argv[1]
options, args = option_parser.parse_args(argv[2:])
if len(argv) < 3 and command == "help":
option_parser.print_help()
return 0
if options.verbose > 1:
logging.basicConfig(level=logging.DEBUG)
# Files used for configuration and state saving.
options.config_filename = os.environ.get("GCLIENT_FILE", ".gclient")
if options.gclientfile:
options.config_filename = options.gclientfile
options.entries_filename = options.config_filename + "_entries"
options.deps_file = "DEPS"
options.platform = sys.platform
return DispatchCommand(command, options, args)
if argv[0] == '--help':
argv[0] = 'help'
options, args = parser.parse_args(argv[1:])
return Command(argv[0])(parser, options, args)
if "__main__" == __name__:
try:
result = Main(sys.argv)
sys.exit(Main(sys.argv[1:]))
except gclient_utils.Error, e:
print >> sys.stderr, "Error: %s" % str(e)
result = 1
sys.exit(result)
sys.exit(1)
# vim: ts=2:sw=2:tw=80:et:
......@@ -97,7 +97,8 @@ class TestCMDconfig(GclientTestCase):
exception_msg = "required argument missing; see 'gclient help config'"
self.mox.ReplayAll()
self.assertRaisesError(exception_msg, gclient.CMDconfig, self.Options(), ())
self.assertRaisesError(exception_msg, gclient.CMDconfig, None,
self.Options(), ())
def testExistingClientFile(self):
options = self.Options()
......@@ -106,7 +107,8 @@ class TestCMDconfig(GclientTestCase):
gclient.os.path.exists(options.config_filename).AndReturn(True)
self.mox.ReplayAll()
self.assertRaisesError(exception_msg, gclient.CMDconfig, options, (1,))
self.assertRaisesError(exception_msg, gclient.CMDconfig, None, options,
(1,))
def testFromText(self):
options = self.Options(spec='config_source_content')
......@@ -116,7 +118,7 @@ class TestCMDconfig(GclientTestCase):
gclient.GClient.SaveConfig()
self.mox.ReplayAll()
gclient.CMDconfig(options, (1,),)
gclient.CMDconfig(None, options, (1,),)
def testCreateClientFile(self):
options = self.Options()
......@@ -127,7 +129,7 @@ class TestCMDconfig(GclientTestCase):
gclient.GClient.SaveConfig()
self.mox.ReplayAll()
gclient.CMDconfig(options,
gclient.CMDconfig(None, options,
('http://svn/url/the_name', 'other', 'args', 'ignored'))
......@@ -138,7 +140,7 @@ class GenericCommandTestCase(GclientTestCase):
gclient.GClient.RunOnDeps(command, self.args).AndReturn(return_value)
self.mox.ReplayAll()
result = function(options, self.args)
result = function(None, options, self.args)
self.assertEquals(result, return_value)
def BadClient(self, function):
......@@ -148,7 +150,7 @@ class GenericCommandTestCase(GclientTestCase):
self.mox.ReplayAll()
self.assertRaisesError(
"client not configured; see 'gclient config'",
function, options, self.args)
function, None, options, self.args)
def Verbose(self, command, function):
options = self.Options(verbose=True)
......@@ -159,7 +161,7 @@ class GenericCommandTestCase(GclientTestCase):
gclient.GClient.RunOnDeps(command, self.args).AndReturn(0)
self.mox.ReplayAll()
result = function(options, self.args)
result = function(None, options, self.args)
self.assertEquals(result, 0)
......@@ -201,7 +203,7 @@ class TestCMDupdate(GenericCommandTestCase):
gclient.GClient.RunOnDeps(command, self.args).AndReturn(return_value)
self.mox.ReplayAll()
result = function(options, self.args)
result = function(None, options, self.args)
self.assertEquals(result, return_value)
def Verbose(self, command, function):
......@@ -214,7 +216,7 @@ class TestCMDupdate(GenericCommandTestCase):
gclient.GClient.RunOnDeps(command, self.args).AndReturn(0)
self.mox.ReplayAll()
result = function(options, self.args)
result = function(None, options, self.args)
self.assertEquals(result, 0)
def Options(self, verbose=False, *args, **kwargs):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment