Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(480)

Unified Diff: gclient.py

Issue 22824018: Convert gclient to use subcommand.py (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: fix non-determinism in gclient_smoketest.py Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | git_cl.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gclient.py
diff --git a/gclient.py b/gclient.py
index a92b8e110e70f34f7b57097326047c48a25a0699..407b9ad4e99f1efd3f31270f3695e18f458b1fcb 100755
--- a/gclient.py
+++ b/gclient.py
@@ -3,74 +3,74 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-"""Meta checkout manager supporting both Subversion and GIT.
-
-Files
- .gclient : Current client configuration, written by 'config' command.
- Format is a Python script defining 'solutions', a list whose
- entries each are maps binding the strings "name" and "url"
- to strings specifying the name and location of the client
- module, as well as "custom_deps" to a map similar to the deps
- section of the DEPS file below, as well as "custom_hooks" to
- a list similar to the hooks sections of the DEPS file below.
- .gclient_entries : A cache constructed by 'update' command. Format is a
- Python script defining 'entries', a list of the names
- of all modules in the client
- <module>/DEPS : Python script defining var 'deps' as a map from each requisite
- submodule name to a URL where it can be found (via one SCM)
-
-Hooks
- .gclient and DEPS files may optionally contain a list named "hooks" to
- allow custom actions to be performed based on files that have changed in the
- working copy as a result of a "sync"/"update" or "revert" operation. This
- can be prevented by using --nohooks (hooks run by default). Hooks can also
- be forced to run with the "runhooks" operation. If "sync" is run with
- --force, all known but not suppressed hooks will run regardless of the state
- of the working copy.
-
- Each item in a "hooks" list is a dict, containing these two keys:
- "pattern" The associated value is a string containing a regular
- expression. When a file whose pathname matches the expression
- is checked out, updated, or reverted, the hook's "action" will
- run.
- "action" A list describing a command to run along with its arguments, if
- any. An action command will run at most one time per gclient
- invocation, regardless of how many files matched the pattern.
- The action is executed in the same directory as the .gclient
- file. If the first item in the list is the string "python",
- the current Python interpreter (sys.executable) will be used
- to run the command. If the list contains string "$matching_files"
- it will be removed from the list and the list will be extended
- by the list of matching files.
- "name" An optional string specifying the group to which a hook belongs
- for overriding and organizing.
-
- Example:
- hooks = [
- { "pattern": "\\.(gif|jpe?g|pr0n|png)$",
- "action": ["python", "image_indexer.py", "--all"]},
- { "pattern": ".",
- "name": "gyp",
- "action": ["python", "src/build/gyp_chromium"]},
- ]
-
-Specifying a target OS
- An optional key named "target_os" may be added to a gclient file to specify
- one or more additional operating systems that should be considered when
- processing the deps_os dict of a DEPS file.
-
- Example:
- target_os = [ "android" ]
-
- If the "target_os_only" key is also present and true, then *only* the
- operating systems listed in "target_os" will be used.
-
- Example:
- target_os = [ "ios" ]
- target_os_only = True
-"""
-
-__version__ = "0.6.4"
+"""Meta checkout manager supporting both Subversion and GIT."""
+# Files
+# .gclient : Current client configuration, written by 'config' command.
+# Format is a Python script defining 'solutions', a list whose
+# entries each are maps binding the strings "name" and "url"
+# to strings specifying the name and location of the client
+# module, as well as "custom_deps" to a map similar to the
+# deps section of the DEPS file below, as well as
+# "custom_hooks" to a list similar to the hooks sections of
+# the DEPS file below.
+# .gclient_entries : A cache constructed by 'update' command. Format is a
+# Python script defining 'entries', a list of the names
+# of all modules in the client
+# <module>/DEPS : Python script defining var 'deps' as a map from each
+# requisite submodule name to a URL where it can be found (via
+# one SCM)
+#
+# Hooks
+# .gclient and DEPS files may optionally contain a list named "hooks" to
+# allow custom actions to be performed based on files that have changed in the
+# working copy as a result of a "sync"/"update" or "revert" operation. This
+# can be prevented by using --nohooks (hooks run by default). Hooks can also
+# be forced to run with the "runhooks" operation. If "sync" is run with
+# --force, all known but not suppressed hooks will run regardless of the state
+# of the working copy.
+#
+# Each item in a "hooks" list is a dict, containing these two keys:
+# "pattern" The associated value is a string containing a regular
+# expression. When a file whose pathname matches the expression
+# is checked out, updated, or reverted, the hook's "action" will
+# run.
+# "action" A list describing a command to run along with its arguments, if
+# any. An action command will run at most one time per gclient
+# invocation, regardless of how many files matched the pattern.
+# The action is executed in the same directory as the .gclient
+# file. If the first item in the list is the string "python",
+# the current Python interpreter (sys.executable) will be used
+# to run the command. If the list contains string
+# "$matching_files" it will be removed from the list and the list
+# will be extended by the list of matching files.
+# "name" An optional string specifying the group to which a hook belongs
+# for overriding and organizing.
+#
+# Example:
+# hooks = [
+# { "pattern": "\\.(gif|jpe?g|pr0n|png)$",
+# "action": ["python", "image_indexer.py", "--all"]},
+# { "pattern": ".",
+# "name": "gyp",
+# "action": ["python", "src/build/gyp_chromium"]},
+# ]
+#
+# Specifying a target OS
+# An optional key named "target_os" may be added to a gclient file to specify
+# one or more additional operating systems that should be considered when
+# processing the deps_os dict of a DEPS file.
+#
+# Example:
+# target_os = [ "android" ]
+#
+# If the "target_os_only" key is also present and true, then *only* the
+# operating systems listed in "target_os" will be used.
+#
+# Example:
+# target_os = [ "ios" ]
+# target_os_only = True
+
+__version__ = '0.7'
import copy
import logging
@@ -91,21 +91,9 @@ import fix_encoding
import gclient_scm
import gclient_utils
from third_party.repo.progress import Progress
+import subcommand
import subprocess2
from third_party import colorama
-# Import shortcut.
-from third_party.colorama import Fore
-
-
-def attr(attribute, data):
- """Sets an attribute on a function."""
- def hook(fn):
- setattr(fn, attribute, data)
- return fn
- return hook
-
-
-## GClient implementation.
class GClientKeywords(object):
@@ -1314,8 +1302,8 @@ solutions = [
def CMDcleanup(parser, args):
"""Cleans up all working copies.
-Mostly svn-specific. Simply runs 'svn cleanup' for each module.
-"""
+ Mostly svn-specific. Simply runs 'svn cleanup' for each module.
+ """
parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help='override deps for the specified (comma-separated) '
'platform(s); \'all\' will process all deps_os '
@@ -1331,9 +1319,9 @@ Mostly svn-specific. Simply runs 'svn cleanup' for each module.
return client.RunOnDeps('cleanup', args)
-@attr('usage', '[command] [args ...]')
+@subcommand.usage('[command] [args ...]')
def CMDrecurse(parser, args):
- """Operates on all the entries.
+ """Operates [command args ...] on all the dependencies.
Runs a shell command on all entries.
Sets GCLIENT_DEP_PATH enviroment variable as the dep's relative location to
@@ -1372,12 +1360,12 @@ def CMDrecurse(parser, args):
progress=not options.no_progress)
-@attr('usage', '[args ...]')
+@subcommand.usage('[args ...]')
def CMDfetch(parser, args):
"""Fetches upstream commits for all modules.
-Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
-"""
+ Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
+ """
(options, args) = parser.parse_args(args)
return CMDrecurse(OptionParser(), [
'--jobs=%d' % options.jobs, '--scm=git', 'git', 'fetch'] + args)
@@ -1386,9 +1374,8 @@ Completely git-specific. Simply runs 'git fetch [args ...]' for each module.
def CMDgrep(parser, args):
"""Greps through git repos managed by gclient.
-Runs 'git grep [args...]' for each module.
-"""
-
+ Runs 'git grep [args...]' for each module.
+ """
# We can't use optparse because it will try to parse arguments sent
# to git grep and throw an error. :-(
if not args or re.match('(-h|--help)$', args[0]):
@@ -1413,17 +1400,16 @@ Runs 'git grep [args...]' for each module.
'git', 'grep', '--null', '--color=Always'] + args)
-@attr('usage', '[url] [safesync url]')
+@subcommand.usage('[url] [safesync url]')
def CMDconfig(parser, 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
-URL.
-"""
+ """Creates 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
+ URL.
+ """
# We do a little dance with the --gclientfile option. 'gclient config' is the
# only command where it's acceptable to have both '--gclientfile' and '--spec'
# arguments. So, we temporarily stash any --gclientfile parameter into
@@ -1481,18 +1467,18 @@ URL.
return 0
-@attr('epilog', """Example:
+@subcommand.epilog("""Example:
gclient pack > patch.txt
generate simple patch for configured client and dependences
""")
def CMDpack(parser, args):
- """Generate a patch which can be applied at the root of the tree.
+ """Generates a patch which can be applied at the root of the tree.
-Internally, runs 'svn diff'/'git 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
-checked out tree via 'patch -p0 < patchfile'.
-"""
+ Internally, runs 'svn diff'/'git 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
+ checked out tree via 'patch -p0 < patchfile'.
+ """
parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help='override deps for the specified (comma-separated) '
'platform(s); \'all\' will process all deps_os '
@@ -1512,7 +1498,7 @@ checked out tree via 'patch -p0 < patchfile'.
def CMDstatus(parser, args):
- """Show modification status for every dependencies."""
+ """Shows modification status for every dependencies."""
parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help='override deps for the specified (comma-separated) '
'platform(s); \'all\' will process all deps_os '
@@ -1528,7 +1514,7 @@ def CMDstatus(parser, args):
return client.RunOnDeps('status', args)
-@attr('epilog', """Examples:
+@subcommand.epilog("""Examples:
gclient sync
update files from SCM according to current configuration,
*for modules which have changed since last update or sync*
@@ -1604,9 +1590,8 @@ def CMDsync(parser, args):
return client.RunOnDeps('update', args)
-def CMDupdate(parser, args):
- """Alias for the sync command. Deprecated."""
- return CMDsync(parser, args)
+CMDupdate = CMDsync
+
def CMDdiff(parser, args):
"""Displays local diff for every dependencies."""
@@ -1626,7 +1611,7 @@ def CMDdiff(parser, args):
def CMDrevert(parser, args):
- """Revert all modifications in every dependencies.
+ """Reverts all modifications in every dependencies.
That's the nuclear option to get back to a 'clean' state. It removes anything
that shows up in svn status."""
@@ -1671,7 +1656,7 @@ def CMDrunhooks(parser, args):
def CMDrevinfo(parser, args):
- """Output revision info mapping for the client and its dependencies.
+ """Outputs revision info mapping for the client and its dependencies.
This allows the capture of an overall 'revision' for the source tree that
can be used to reproduce the same tree in the future. It is only useful for
@@ -1699,7 +1684,7 @@ def CMDrevinfo(parser, args):
def CMDhookinfo(parser, args):
- """Output the hooks that would be run by `gclient runhooks`"""
+ """Outputs the hooks that would be run by `gclient runhooks`."""
(options, args) = parser.parse_args(args)
options.force = True
client = GClient.LoadCurrentConfig(options)
@@ -1710,31 +1695,6 @@ def CMDhookinfo(parser, args):
return 0
-def Command(name):
- return getattr(sys.modules[__name__], 'CMD' + name, None)
-
-
-def CMDhelp(parser, args):
- """Prints list of commands or help for a specific command."""
- (_, args) = parser.parse_args(args)
- if len(args) == 1:
- return Main(args + ['--help'])
- parser.print_help()
- return 0
-
-
-def GenUsage(parser, command):
- """Modify an OptParse object with the function's documentation."""
- obj = Command(command)
- if command == 'help':
- command = '<command>'
- # OptParser.description prefer nicely non-formatted strings.
- parser.description = re.sub('[\r\n ]{2,}', ' ', obj.__doc__)
- usage = getattr(obj, 'usage', '')
- parser.set_usage('%%prog %s [options] %s' % (command, usage))
- parser.epilog = getattr(obj, 'epilog', None)
-
-
class OptionParser(optparse.OptionParser):
gclientfile_default = os.environ.get('GCLIENT_FILE', '.gclient')
@@ -1805,9 +1765,13 @@ class OptionParser(optparse.OptionParser):
gclient_scm.SCMWrapper.nag_max = None
return (options, args)
- def format_epilog(self, _):
- """Disables wordwrapping in epilog (usually examples)."""
- return self.epilog or ''
+
+def disable_buffering():
+ # Make stdout auto-flush so buildbot doesn't kill us during lengthy
+ # operations. Python as a strong tendency to buffer sys.stdout.
+ sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
+ # Make stdout annotated with the thread ids.
+ sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
def Main(argv):
@@ -1822,34 +1786,12 @@ def Main(argv):
print >> sys.stderr, (
'\nPython cannot find the location of it\'s own executable.\n')
return 2
+ fix_encoding.fix_encoding()
+ disable_buffering()
colorama.init()
+ dispatcher = subcommand.CommandDispatcher(__name__)
try:
- # Make stdout auto-flush so buildbot doesn't kill us during lengthy
- # operations. Python as a strong tendency to buffer sys.stdout.
- sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)
- # Make stdout annotated with the thread ids.
- sys.stdout = gclient_utils.MakeFileAnnotated(sys.stdout)
- # Do it late so all commands are listed.
- # Unused variable 'usage'
- # pylint: disable=W0612
- def to_str(fn):
- return (
- ' %s%-10s%s' % (Fore.GREEN, fn[3:], Fore.RESET) +
- ' %s' % Command(fn[3:]).__doc__.split('\n')[0].strip())
- cmds = (
- to_str(fn) for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')
- )
- CMDhelp.usage = '\n\nCommands are:\n' + '\n'.join(cmds)
- parser = OptionParser()
- if argv:
- command = Command(argv[0])
- if command:
- # 'fix' the usage and the description now that we know the subcommand.
- GenUsage(parser, argv[0])
- return command(parser, argv[1:])
- # Not a known command. Default to help.
- GenUsage(parser, 'help')
- return CMDhelp(parser, argv)
+ return dispatcher.execute(OptionParser(), argv)
except KeyboardInterrupt:
gclient_utils.GClientChildren.KillAllRemainingChildren()
raise
@@ -1859,7 +1801,6 @@ def Main(argv):
if '__main__' == __name__:
- fix_encoding.fix_encoding()
sys.exit(Main(sys.argv[1:]))
# vim: ts=2:sw=2:tw=80:et:
« no previous file with comments | « no previous file | git_cl.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698