Commit 0b6a0840 authored by maruel@chromium.org's avatar maruel@chromium.org

More work toward refactoring. Simplify LoadCurrentConfig() and convert most " with '.

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@49789 0039d316-1c4b-4281-b951-d872f2087c98
parent 953586a1
...@@ -3,13 +3,7 @@ ...@@ -3,13 +3,7 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
"""A wrapper script to manage a set of client modules in different SCM. """Meta checkout manager supporting both Subversion and GIT.
This script is intended to be used to help basic management of client
program sources residing in one or more Subversion modules and Git
repositories, along with other modules it depends on, also in Subversion or Git,
but possibly on multiple respositories, making a wrapper system apparently
necessary.
Files Files
.gclient : Current client configuration, written by 'config' command. .gclient : Current client configuration, written by 'config' command.
...@@ -28,7 +22,7 @@ Hooks ...@@ -28,7 +22,7 @@ Hooks
.gclient and DEPS files may optionally contain a list named "hooks" to .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 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 working copy as a result of a "sync"/"update" or "revert" operation. This
could be prevented by using --nohooks (hooks run by default). Hooks can also 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 be forced to run with the "runhooks" operation. If "sync" is run with
--force, all known hooks will run regardless of the state of the working --force, all known hooks will run regardless of the state of the working
copy. copy.
...@@ -55,7 +49,7 @@ Hooks ...@@ -55,7 +49,7 @@ Hooks
] ]
""" """
__version__ = "0.4" __version__ = "0.4.1"
import errno import errno
import logging import logging
...@@ -158,6 +152,7 @@ class GClientKeywords(object): ...@@ -158,6 +152,7 @@ class GClientKeywords(object):
class Dependency(GClientKeywords): class Dependency(GClientKeywords):
"""Object that represents a dependency checkout.""" """Object that represents a dependency checkout."""
DEPS_FILE = 'DEPS' DEPS_FILE = 'DEPS'
def __init__(self, parent, name, url, safesync_url=None, custom_deps=None, def __init__(self, parent, name, url, safesync_url=None, custom_deps=None,
custom_vars=None, deps_file=None): custom_vars=None, deps_file=None):
GClientKeywords.__init__(self) GClientKeywords.__init__(self)
...@@ -168,9 +163,9 @@ class Dependency(GClientKeywords): ...@@ -168,9 +163,9 @@ class Dependency(GClientKeywords):
self.safesync_url = safesync_url self.safesync_url = safesync_url
self.custom_vars = custom_vars or {} self.custom_vars = custom_vars or {}
self.custom_deps = custom_deps or {} self.custom_deps = custom_deps or {}
self.deps_hooks = []
self.dependencies = [] self.dependencies = []
self.deps_file = deps_file or self.DEPS_FILE self.deps_file = deps_file or self.DEPS_FILE
self._deps_hooks = []
# Sanity checks # Sanity checks
if not self.name and self.parent: if not self.name and self.parent:
...@@ -238,6 +233,7 @@ solutions = [ ...@@ -238,6 +233,7 @@ solutions = [
self.config_content = None self.config_content = None
def SetConfig(self, content): def SetConfig(self, content):
assert self.dependencies == []
config_dict = {} config_dict = {}
self.config_content = content self.config_content = content
try: try:
...@@ -262,37 +258,23 @@ solutions = [ ...@@ -262,37 +258,23 @@ solutions = [
s.get('custom_deps', {}), s.get('custom_deps', {}),
s.get('custom_vars', {}))) s.get('custom_vars', {})))
# .gclient can have hooks. # .gclient can have hooks.
self._deps_hooks = config_dict.get('hooks', []) self.deps_hooks = config_dict.get('hooks', [])
def SaveConfig(self): def SaveConfig(self):
gclient_utils.FileWrite(os.path.join(self.root_dir(), gclient_utils.FileWrite(os.path.join(self.root_dir(),
self._options.config_filename), self._options.config_filename),
self.config_content) self.config_content)
def _LoadConfig(self):
client_source = gclient_utils.FileRead(
os.path.join(self.root_dir(), self._options.config_filename))
self.SetConfig(client_source)
@staticmethod @staticmethod
def LoadCurrentConfig(options, from_dir=None): def LoadCurrentConfig(options):
"""Searches for and loads a .gclient file relative to the current working """Searches for and loads a .gclient file relative to the current working
dir. dir. Returns a GClient object."""
path = gclient_utils.FindGclientRoot(os.getcwd(), options.config_filename)
Returns: if not path:
A dict representing the contents of the .gclient file or an empty dict if
the .gclient file doesn't exist.
"""
if not from_dir:
from_dir = os.curdir
path = os.path.realpath(from_dir)
while not os.path.exists(os.path.join(path, options.config_filename)):
split_path = os.path.split(path)
if not split_path[1]:
return None return None
path = split_path[0]
client = GClient(path, options) client = GClient(path, options)
client._LoadConfig() client.SetConfig(gclient_utils.FileRead(
os.path.join(path, options.config_filename)))
return client return client
def SetDefaultConfig(self, solution_name, solution_url, safesync_url): def SetDefaultConfig(self, solution_name, solution_url, safesync_url):
...@@ -322,9 +304,6 @@ solutions = [ ...@@ -322,9 +304,6 @@ solutions = [
def _ReadEntries(self): def _ReadEntries(self):
"""Read the .gclient_entries file for the given client. """Read the .gclient_entries file for the given client.
Args:
client: The client for which the entries file should be read.
Returns: Returns:
A sequence of solution names, which will be empty if there is the A sequence of solution names, which will be empty if there is the
entries file hasn't been created yet. entries file hasn't been created yet.
...@@ -334,7 +313,7 @@ solutions = [ ...@@ -334,7 +313,7 @@ solutions = [
if not os.path.exists(filename): if not os.path.exists(filename):
return [] return []
exec(gclient_utils.FileRead(filename), scope) exec(gclient_utils.FileRead(filename), scope)
return scope["entries"] return scope['entries']
def _ParseSolutionDeps(self, solution_name, solution_deps_content, def _ParseSolutionDeps(self, solution_name, solution_deps_content,
custom_vars, parse_hooks): custom_vars, parse_hooks):
...@@ -391,7 +370,7 @@ solutions = [ ...@@ -391,7 +370,7 @@ solutions = [
# right 'self' to add the hooks. # right 'self' to add the hooks.
for d in self.dependencies: for d in self.dependencies:
if d.name == solution_name: if d.name == solution_name:
d._deps_hooks.extend(local_scope['hooks']) d.deps_hooks.extend(local_scope['hooks'])
break break
# If use_relative_paths is set in the DEPS file, regenerate # If use_relative_paths is set in the DEPS file, regenerate
...@@ -515,10 +494,10 @@ solutions = [ ...@@ -515,10 +494,10 @@ solutions = [
return return
# Get any hooks from the .gclient file. # Get any hooks from the .gclient file.
hooks = self._deps_hooks[:] hooks = self.deps_hooks[:]
# Add any hooks found in DEPS files. # Add any hooks found in DEPS files.
for d in self.dependencies: for d in self.dependencies:
hooks.extend(d._deps_hooks) hooks.extend(d.deps_hooks)
# If "--force" was specified, run all hooks regardless of what files have # If "--force" was specified, run all hooks regardless of what files have
# changed. If the user is using git, then we don't know what files have # changed. If the user is using git, then we don't know what files have
...@@ -571,14 +550,9 @@ solutions = [ ...@@ -571,14 +550,9 @@ solutions = [
def RunOnDeps(self, command, args): def RunOnDeps(self, command, args):
"""Runs a command on each dependency in a client and its dependencies. """Runs a command on each dependency in a client and its dependencies.
The module's dependencies are specified in its top-level DEPS files.
Args: Args:
command: The command to use (e.g., 'status' or 'diff') command: The command to use (e.g., 'status' or 'diff')
args: list of str - extra arguments to add to the command line. args: list of str - extra arguments to add to the command line.
Raises:
Error: If the client has conflicting entries.
""" """
if not command in self.SUPPORTED_COMMANDS: if not command in self.SUPPORTED_COMMANDS:
raise gclient_utils.Error("'%s' is an unsupported command" % command) raise gclient_utils.Error("'%s' is an unsupported command" % command)
...@@ -834,14 +808,14 @@ def CMDcleanup(parser, args): ...@@ -834,14 +808,14 @@ def CMDcleanup(parser, args):
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", parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help="override deps for the specified (comma-separated) " help='override deps for the specified (comma-separated) '
"platform(s); 'all' will process all deps_os " 'platform(s); \'all\' will process all deps_os '
"references") 'references')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options) client = GClient.LoadCurrentConfig(options)
if not client: if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'") raise gclient_utils.Error('client not configured; see \'gclient config\'')
if options.verbose: if options.verbose:
# Print out the .gclient file. This is longer than if we just printed the # Print out the .gclient file. This is longer than if we just printed the
# client dict, but more legible, and it might contain helpful comments. # client dict, but more legible, and it might contain helpful comments.
...@@ -859,19 +833,19 @@ modules to operate on as well. If optional [url] parameter is ...@@ -859,19 +833,19 @@ modules to operate on as well. If optional [url] parameter is
provided, then configuration is read from a specified Subversion server provided, then configuration is read from a specified Subversion server
URL. URL.
""" """
parser.add_option("--spec", parser.add_option('--spec',
help="create a gclient file containing the provided " help='create a gclient file containing the provided '
"string. Due to Cygwin/Python brokenness, it " 'string. Due to Cygwin/Python brokenness, it '
"probably can't contain any newlines.") 'probably can\'t contain any newlines.')
parser.add_option("--name", parser.add_option('--name',
help="overrides the default name for the solution") help='overrides the default name for the solution')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
if ((options.spec and args) or len(args) > 2 or if ((options.spec and args) or len(args) > 2 or
(not options.spec and not args)): (not options.spec and not args)):
parser.error('Inconsistent arguments. Use either --spec or one or 2 args') parser.error('Inconsistent arguments. Use either --spec or one or 2 args')
if os.path.exists(options.config_filename): if os.path.exists(options.config_filename):
raise gclient_utils.Error("%s file already exists in the current directory" raise gclient_utils.Error('%s file already exists in the current directory'
% options.config_filename) % options.config_filename)
client = GClient('.', options) client = GClient('.', options)
if options.spec: if options.spec:
...@@ -879,11 +853,11 @@ URL. ...@@ -879,11 +853,11 @@ URL.
else: else:
base_url = args[0].rstrip('/') base_url = args[0].rstrip('/')
if not options.name: if not options.name:
name = base_url.split("/")[-1] name = base_url.split('/')[-1]
else: else:
# specify an alternate relpath for the given URL. # specify an alternate relpath for the given URL.
name = options.name name = options.name
safesync_url = "" safesync_url = ''
if len(args) > 1: if len(args) > 1:
safesync_url = args[1] safesync_url = args[1]
client.SetDefaultConfig(name, base_url, safesync_url) client.SetDefaultConfig(name, base_url, safesync_url)
...@@ -893,17 +867,17 @@ URL. ...@@ -893,17 +867,17 @@ URL.
def CMDexport(parser, args): def CMDexport(parser, args):
"""Wrapper for svn export for all managed directories.""" """Wrapper for svn export for all managed directories."""
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help="override deps for the specified (comma-separated) " help='override deps for the specified (comma-separated) '
"platform(s); 'all' will process all deps_os " 'platform(s); \'all\' will process all deps_os '
"references") 'references')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
if len(args) != 1: if len(args) != 1:
raise gclient_utils.Error("Need directory name") raise gclient_utils.Error('Need directory name')
client = GClient.LoadCurrentConfig(options) client = GClient.LoadCurrentConfig(options)
if not client: if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'") raise gclient_utils.Error('client not configured; see \'gclient config\'')
if options.verbose: if options.verbose:
# Print out the .gclient file. This is longer than if we just printed the # Print out the .gclient file. This is longer than if we just printed the
...@@ -924,14 +898,14 @@ dependencies, and performs minimal postprocessing of the output. The ...@@ -924,14 +898,14 @@ dependencies, and performs minimal postprocessing of the output. The
resulting patch is printed to stdout and can be applied to a freshly resulting patch is printed to stdout and can be applied to a freshly
checked out tree via 'patch -p0 < patchfile'. checked out tree via 'patch -p0 < patchfile'.
""" """
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help="override deps for the specified (comma-separated) " help='override deps for the specified (comma-separated) '
"platform(s); 'all' will process all deps_os " 'platform(s); \'all\' will process all deps_os '
"references") 'references')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options) client = GClient.LoadCurrentConfig(options)
if not client: if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'") raise gclient_utils.Error('client not configured; see \'gclient config\'')
if options.verbose: if options.verbose:
# Print out the .gclient file. This is longer than if we just printed the # Print out the .gclient file. This is longer than if we just printed the
# client dict, but more legible, and it might contain helpful comments. # client dict, but more legible, and it might contain helpful comments.
...@@ -941,14 +915,14 @@ checked out tree via 'patch -p0 < patchfile'. ...@@ -941,14 +915,14 @@ checked out tree via 'patch -p0 < patchfile'.
def CMDstatus(parser, args): def CMDstatus(parser, args):
"""Show modification status for every dependencies.""" """Show modification status for every dependencies."""
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help="override deps for the specified (comma-separated) " help='override deps for the specified (comma-separated) '
"platform(s); 'all' will process all deps_os " 'platform(s); \'all\' will process all deps_os '
"references") 'references')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options) client = GClient.LoadCurrentConfig(options)
if not client: if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'") raise gclient_utils.Error('client not configured; see \'gclient config\'')
if options.verbose: if options.verbose:
# Print out the .gclient file. This is longer than if we just printed the # Print out the .gclient file. This is longer than if we just printed the
# client dict, but more legible, and it might contain helpful comments. # client dict, but more legible, and it might contain helpful comments.
...@@ -968,41 +942,41 @@ def CMDstatus(parser, args): ...@@ -968,41 +942,41 @@ def CMDstatus(parser, args):
""") """)
def CMDsync(parser, args): def CMDsync(parser, args):
"""Checkout/update all modules.""" """Checkout/update all modules."""
parser.add_option("-f", "--force", action="store_true", parser.add_option('-f', '--force', action='store_true',
help="force update even for unchanged modules") help='force update even for unchanged modules')
parser.add_option("-n", "--nohooks", action="store_true", parser.add_option('-n', '--nohooks', action='store_true',
help="don't run hooks after the update is complete") help='don\'t run hooks after the update is complete')
parser.add_option("-r", "--revision", action="append", parser.add_option('-r', '--revision', action='append',
dest="revisions", metavar="REV", default=[], dest='revisions', metavar='REV', default=[],
help="Enforces revision/hash for the solutions with the " help='Enforces revision/hash for the solutions with the '
"format src@rev. The src@ part is optional and can be " 'format src@rev. The src@ part is optional and can be '
"skipped. -r can be used multiple times when .gclient " 'skipped. -r can be used multiple times when .gclient '
"has multiple solutions configured and will work even " 'has multiple solutions configured and will work even '
"if the src@ part is skipped.") 'if the src@ part is skipped.')
parser.add_option("-H", "--head", action="store_true", parser.add_option('-H', '--head', action='store_true',
help="skips any safesync_urls specified in " help='skips any safesync_urls specified in '
"configured solutions and sync to head instead") 'configured solutions and sync to head instead')
parser.add_option("-D", "--delete_unversioned_trees", action="store_true", parser.add_option('-D', '--delete_unversioned_trees', action='store_true',
help="delete any unexpected unversioned trees " help='delete any unexpected unversioned trees '
"that are in the checkout") 'that are in the checkout')
parser.add_option("-R", "--reset", action="store_true", parser.add_option('-R', '--reset', action='store_true',
help="resets any local changes before updating (git only)") help='resets any local changes before updating (git only)')
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help="override deps for the specified (comma-separated) " help='override deps for the specified (comma-separated) '
"platform(s); 'all' will process all deps_os " 'platform(s); \'all\' will process all deps_os '
"references") 'references')
parser.add_option("-m", "--manually_grab_svn_rev", action="store_true", parser.add_option('-m', '--manually_grab_svn_rev', action='store_true',
help="Skip svn up whenever possible by requesting " help='Skip svn up whenever possible by requesting '
"actual HEAD revision from the repository") 'actual HEAD revision from the repository')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options) client = GClient.LoadCurrentConfig(options)
if not client: if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'") raise gclient_utils.Error('client not configured; see \'gclient config\'')
if options.revisions and options.head: if options.revisions and options.head:
# TODO(maruel): Make it a parser.error if it doesn't break any builder. # TODO(maruel): Make it a parser.error if it doesn't break any builder.
print("Warning: you cannot use both --head and --revision") print('Warning: you cannot use both --head and --revision')
if options.verbose: if options.verbose:
# Print out the .gclient file. This is longer than if we just printed the # Print out the .gclient file. This is longer than if we just printed the
...@@ -1017,14 +991,14 @@ def CMDupdate(parser, args): ...@@ -1017,14 +991,14 @@ def CMDupdate(parser, args):
def CMDdiff(parser, args): def CMDdiff(parser, args):
"""Displays local diff for every dependencies.""" """Displays local diff for every dependencies."""
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help="override deps for the specified (comma-separated) " help='override deps for the specified (comma-separated) '
"platform(s); 'all' will process all deps_os " 'platform(s); \'all\' will process all deps_os '
"references") 'references')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options) client = GClient.LoadCurrentConfig(options)
if not client: if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'") raise gclient_utils.Error('client not configured; see \'gclient config\'')
if options.verbose: if options.verbose:
# Print out the .gclient file. This is longer than if we just printed the # Print out the .gclient file. This is longer than if we just printed the
# client dict, but more legible, and it might contain helpful comments. # client dict, but more legible, and it might contain helpful comments.
...@@ -1034,33 +1008,33 @@ def CMDdiff(parser, args): ...@@ -1034,33 +1008,33 @@ def CMDdiff(parser, args):
def CMDrevert(parser, args): def CMDrevert(parser, args):
"""Revert all modifications in every dependencies.""" """Revert all modifications in every dependencies."""
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help="override deps for the specified (comma-separated) " help='override deps for the specified (comma-separated) '
"platform(s); 'all' will process all deps_os " 'platform(s); \'all\' will process all deps_os '
"references") 'references')
parser.add_option("-n", "--nohooks", action="store_true", parser.add_option('-n', '--nohooks', action='store_true',
help="don't run hooks after the revert is complete") help='don\'t run hooks after the revert is complete')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
# --force is implied. # --force is implied.
options.force = True options.force = True
client = GClient.LoadCurrentConfig(options) client = GClient.LoadCurrentConfig(options)
if not client: if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'") raise gclient_utils.Error('client not configured; see \'gclient config\'')
return client.RunOnDeps('revert', args) return client.RunOnDeps('revert', args)
def CMDrunhooks(parser, args): def CMDrunhooks(parser, args):
"""Runs hooks for files that have been modified in the local working copy.""" """Runs hooks for files that have been modified in the local working copy."""
parser.add_option("--deps", dest="deps_os", metavar="OS_LIST", parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
help="override deps for the specified (comma-separated) " help='override deps for the specified (comma-separated) '
"platform(s); 'all' will process all deps_os " 'platform(s); \'all\' will process all deps_os '
"references") 'references')
parser.add_option("-f", "--force", action="store_true", default=True, parser.add_option('-f', '--force', action='store_true', default=True,
help="Deprecated. No effect.") help='Deprecated. No effect.')
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options) client = GClient.LoadCurrentConfig(options)
if not client: if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'") raise gclient_utils.Error('client not configured; see \'gclient config\'')
if options.verbose: if options.verbose:
# Print out the .gclient file. This is longer than if we just printed the # Print out the .gclient file. This is longer than if we just printed the
# client dict, but more legible, and it might contain helpful comments. # client dict, but more legible, and it might contain helpful comments.
...@@ -1073,10 +1047,10 @@ def CMDrunhooks(parser, args): ...@@ -1073,10 +1047,10 @@ def CMDrunhooks(parser, args):
def CMDrevinfo(parser, args): def CMDrevinfo(parser, args):
"""Output revision info mapping for the client and its dependencies. """Output revision info mapping for the client and its dependencies.
This allows the capture of an overall "revision" for the source tree that 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 can be used to reproduce the same tree in the future. It is only useful for
"unpinned dependencies", i.e. DEPS/deps references without a svn revision 'unpinned dependencies', i.e. DEPS/deps references without a svn revision
number or a git hash. A git branch name isn't "pinned" since the actual number or a git hash. A git branch name isn't 'pinned' since the actual
commit can change. commit can change.
""" """
parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', parser.add_option('--deps', dest='deps_os', metavar='OS_LIST',
...@@ -1090,7 +1064,7 @@ def CMDrevinfo(parser, args): ...@@ -1090,7 +1064,7 @@ def CMDrevinfo(parser, args):
(options, args) = parser.parse_args(args) (options, args) = parser.parse_args(args)
client = GClient.LoadCurrentConfig(options) client = GClient.LoadCurrentConfig(options)
if not client: if not client:
raise gclient_utils.Error("client not configured; see 'gclient config'") raise gclient_utils.Error('client not configured; see \'gclient config\'')
client.PrintRevInfo() client.PrintRevInfo()
return 0 return 0
......
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