| Index: gclient.py
|
| diff --git a/gclient.py b/gclient.py
|
| index 9ea7ce08c5b27382836d94aaa5d0622434d23e8c..9721c55ff50db194276929c98a02c467dee42916 100644
|
| --- a/gclient.py
|
| +++ b/gclient.py
|
| @@ -155,10 +155,38 @@ class GClientKeywords(object):
|
| raise gclient_utils.Error("Var is not defined: %s" % var_name)
|
|
|
|
|
| -class GClient(GClientKeywords):
|
| - """Object that represent a gclient checkout."""
|
| +class Dependency(GClientKeywords):
|
| + """Object that represents a dependency checkout."""
|
| DEPS_FILE = 'DEPS'
|
| + def __init__(self, parent, name, url, safesync_url=None, custom_deps=None,
|
| + custom_vars=None, deps_file=None):
|
| + GClientKeywords.__init__(self)
|
| + self.parent = parent
|
| + self.name = name
|
| + self.url = url
|
| + # These 2 are only set in .gclient and not in DEPS files.
|
| + self.safesync_url = safesync_url
|
| + self.custom_vars = custom_vars or {}
|
| + self.custom_deps = custom_deps or {}
|
| + self.dependencies = []
|
| + self.deps_file = deps_file or self.DEPS_FILE
|
| + self._deps_hooks = []
|
|
|
| + # Sanity checks
|
| + if not self.name and self.parent:
|
| + raise gclient_utils.Error('Dependency without name')
|
| + if not isinstance(self.url,
|
| + (basestring, self.FromImpl, self.FileImpl, None.__class__)):
|
| + raise gclient_utils.Error('dependency url must be either a string, None, '
|
| + 'File() or From() instead of %s' %
|
| + self.url.__class__.__name__)
|
| + if '/' in self.deps_file or '\\' in self.deps_file:
|
| + raise gclient_utils.Error('deps_file name must not be a path, just a '
|
| + 'filename. %s' % self.deps_file)
|
| +
|
| +
|
| +class GClient(Dependency):
|
| + """Main gclient checkout root where .gclient resides."""
|
| SUPPORTED_COMMANDS = [
|
| 'cleanup', 'diff', 'export', 'pack', 'revert', 'status', 'update',
|
| 'runhooks'
|
| @@ -204,17 +232,16 @@ solutions = [
|
| """)
|
|
|
| def __init__(self, root_dir, options):
|
| + Dependency.__init__(self, None, None, None)
|
| self._root_dir = root_dir
|
| self._options = options
|
| self.config_content = None
|
| - self._config_dict = {}
|
| - self._deps_hooks = []
|
|
|
| def SetConfig(self, content):
|
| - self._config_dict = {}
|
| + config_dict = {}
|
| self.config_content = content
|
| try:
|
| - exec(content, self._config_dict)
|
| + exec(content, config_dict)
|
| except SyntaxError, e:
|
| try:
|
| # Try to construct a human readable error message
|
| @@ -228,6 +255,14 @@ solutions = [
|
| else:
|
| # Raise a new exception with the human readable message:
|
| raise gclient_utils.Error('\n'.join(error_message))
|
| + for s in config_dict.get('solutions', []):
|
| + self.dependencies.append(Dependency(
|
| + self, s['name'], s['url'],
|
| + s.get('safesync_url', None),
|
| + s.get('custom_deps', {}),
|
| + s.get('custom_vars', {})))
|
| + # .gclient can have hooks.
|
| + self._deps_hooks = config_dict.get('hooks', [])
|
|
|
| def SaveConfig(self):
|
| gclient_utils.FileWrite(os.path.join(self.root_dir(),
|
| @@ -239,9 +274,6 @@ solutions = [
|
| os.path.join(self.root_dir(), self._options.config_filename))
|
| self.SetConfig(client_source)
|
|
|
| - def GetVar(self, key, default=None):
|
| - return self._config_dict.get(key, default)
|
| -
|
| @staticmethod
|
| def LoadCurrentConfig(options, from_dir=None):
|
| """Searches for and loads a .gclient file relative to the current working
|
| @@ -355,7 +387,12 @@ solutions = [
|
| deps.update(os_deps)
|
|
|
| if 'hooks' in local_scope and parse_hooks:
|
| - self._deps_hooks.extend(local_scope['hooks'])
|
| + # TODO(maruel): Temporary Hack. Since this function is misplaced, find the
|
| + # right 'self' to add the hooks.
|
| + for d in self.dependencies:
|
| + if d.name == solution_name:
|
| + d._deps_hooks.extend(local_scope['hooks'])
|
| + break
|
|
|
| # If use_relative_paths is set in the DEPS file, regenerate
|
| # the dictionary using paths relative to the directory containing
|
| @@ -388,25 +425,23 @@ solutions = [
|
| Error: If a dependency conflicts with another dependency or of a solution.
|
| """
|
| deps = {}
|
| - for solution in self.GetVar("solutions"):
|
| - custom_vars = solution.get("custom_vars", {})
|
| + for solution in self.dependencies:
|
| solution_deps = self._ParseSolutionDeps(
|
| - solution["name"],
|
| - solution_deps_content[solution["name"]],
|
| - custom_vars,
|
| + solution.name,
|
| + solution_deps_content[solution.name],
|
| + solution.custom_vars,
|
| True)
|
|
|
| # If a line is in custom_deps, but not in the solution, we want to append
|
| # this line to the solution.
|
| - if "custom_deps" in solution:
|
| - for d in solution["custom_deps"]:
|
| - if d not in solution_deps:
|
| - solution_deps[d] = solution["custom_deps"][d]
|
| + for d in solution.custom_deps:
|
| + if d not in solution_deps:
|
| + solution_deps[d] = solution.custom_deps[d]
|
|
|
| for d in solution_deps:
|
| - if "custom_deps" in solution and d in solution["custom_deps"]:
|
| + if d in solution.custom_deps:
|
| # Dependency is overriden.
|
| - url = solution["custom_deps"][d]
|
| + url = solution.custom_deps[d]
|
| if url is None:
|
| continue
|
| else:
|
| @@ -434,8 +469,8 @@ solutions = [
|
| raise gclient_utils.Error(
|
| "relative DEPS entry \"%s\" must begin with a slash" % d)
|
| # Create a scm just to query the full url.
|
| - scm = gclient_scm.CreateSCM(solution["url"], self.root_dir(),
|
| - None)
|
| + scm = gclient_scm.CreateSCM(solution.url, self.root_dir(),
|
| + None)
|
| url = scm.FullUrlForRelativeUrl(url)
|
| if d in deps and deps[d] != url:
|
| raise gclient_utils.Error(
|
| @@ -480,9 +515,10 @@ solutions = [
|
| return
|
|
|
| # Get any hooks from the .gclient file.
|
| - hooks = self.GetVar("hooks", [])
|
| + hooks = self._deps_hooks[:]
|
| # Add any hooks found in DEPS files.
|
| - hooks.extend(self._deps_hooks)
|
| + for d in self.dependencies:
|
| + hooks.extend(d._deps_hooks)
|
|
|
| # 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
|
| @@ -500,29 +536,29 @@ solutions = [
|
| if matching_file_list:
|
| self._RunHookAction(hook_dict, matching_file_list)
|
|
|
| - def _EnforceRevisions(self, solutions):
|
| + def _EnforceRevisions(self):
|
| """Checks for revision overrides."""
|
| revision_overrides = {}
|
| if self._options.head:
|
| return revision_overrides
|
| - for s in solutions:
|
| - if not s.get('safesync_url', None):
|
| + for s in self.dependencies:
|
| + if not s.safesync_url:
|
| continue
|
| - handle = urllib.urlopen(s['safesync_url'])
|
| + handle = urllib.urlopen(s.safesync_url)
|
| rev = handle.read().strip()
|
| handle.close()
|
| if len(rev):
|
| - self._options.revisions.append('%s@%s' % (s['name'], rev))
|
| + self._options.revisions.append('%s@%s' % (s.name, rev))
|
| if not self._options.revisions:
|
| return revision_overrides
|
| # --revision will take over safesync_url.
|
| - solutions_names = [s['name'] for s in solutions]
|
| + solutions_names = [s.name for s in self.dependencies]
|
| index = 0
|
| for revision in self._options.revisions:
|
| if not '@' in revision:
|
| # Support for --revision 123
|
| revision = '%s@%s' % (solutions_names[index], revision)
|
| - sol, rev = revision.split("@", 1)
|
| + sol, rev = revision.split('@', 1)
|
| if not sol in solutions_names:
|
| #raise gclient_utils.Error('%s is not a valid solution.' % sol)
|
| print >> sys.stderr, ('Please fix your script, having invalid '
|
| @@ -547,10 +583,9 @@ solutions = [
|
| if not command in self.SUPPORTED_COMMANDS:
|
| raise gclient_utils.Error("'%s' is an unsupported command" % command)
|
|
|
| - solutions = self.GetVar("solutions")
|
| - if not solutions:
|
| + if not self.dependencies:
|
| raise gclient_utils.Error("No solution specified")
|
| - revision_overrides = self._EnforceRevisions(solutions)
|
| + revision_overrides = self._EnforceRevisions()
|
|
|
| # When running runhooks --force, there's no need to consult the SCM.
|
| # All known hooks are expected to run unconditionally regardless of working
|
| @@ -561,15 +596,11 @@ solutions = [
|
| entries_deps_content = {}
|
| file_list = []
|
| # Run on the base solutions first.
|
| - for solution in solutions:
|
| - name = solution["name"]
|
| - deps_file = solution.get("deps_file", self.DEPS_FILE)
|
| - if '/' in deps_file or '\\' in deps_file:
|
| - raise gclient_utils.Error('deps_file name must not be a path, just a '
|
| - 'filename.')
|
| + for solution in self.dependencies:
|
| + name = solution.name
|
| if name in entries:
|
| raise gclient_utils.Error("solution %s specified more than once" % name)
|
| - url = solution["url"]
|
| + url = solution.url
|
| entries[name] = url
|
| if run_scm and url:
|
| self._options.revision = revision_overrides.get(name)
|
| @@ -579,7 +610,7 @@ solutions = [
|
| self._options.revision = None
|
| try:
|
| deps_content = gclient_utils.FileRead(
|
| - os.path.join(self.root_dir(), name, deps_file))
|
| + os.path.join(self.root_dir(), name, solution.deps_file))
|
| except IOError, e:
|
| if e.errno != errno.ENOENT:
|
| raise
|
| @@ -703,8 +734,7 @@ solutions = [
|
|
|
| The --snapshot option allows creating a .gclient file to reproduce the tree.
|
| """
|
| - solutions = self.GetVar("solutions")
|
| - if not solutions:
|
| + if not self.dependencies:
|
| raise gclient_utils.Error("No solution specified")
|
|
|
| # Inner helper to generate base url and rev tuple
|
| @@ -720,15 +750,15 @@ solutions = [
|
| entries = {}
|
| entries_deps_content = {}
|
| # Run on the base solutions first.
|
| - for solution in solutions:
|
| + for solution in self.dependencies:
|
| # Dictionary of { path : SCM url } to describe the gclient checkout
|
| - name = solution["name"]
|
| + name = solution.name
|
| if name in solution_names:
|
| raise gclient_utils.Error("solution %s specified more than once" % name)
|
| - (url, rev) = GetURLAndRev(name, solution["url"])
|
| + (url, rev) = GetURLAndRev(name, solution.url)
|
| entries[name] = "%s@%s" % (url, rev)
|
| solution_names[name] = "%s@%s" % (url, rev)
|
| - deps_file = solution.get("deps_file", self.DEPS_FILE)
|
| + deps_file = solution.deps_file
|
| if '/' in deps_file or '\\' in deps_file:
|
| raise gclient_utils.Error('deps_file name must not be a path, just a '
|
| 'filename.')
|
|
|