Index: gclient.py |
diff --git a/gclient.py b/gclient.py |
index 4170f5fc62e716bfa8a024f5abfb227acf8a615c..db424cb2651885692df88eb07feb8a19b83c6c1f 100644 |
--- a/gclient.py |
+++ b/gclient.py |
@@ -49,7 +49,7 @@ Hooks |
] |
""" |
-__version__ = "0.5.1" |
+__version__ = "0.5.2" |
import logging |
import optparse |
@@ -244,7 +244,7 @@ class Dependency(GClientKeywords, WorkItem): |
DEPS_FILE = 'DEPS' |
def __init__(self, parent, name, url, safesync_url, custom_deps, |
- custom_vars, deps_file): |
+ custom_vars, deps_file, should_process): |
GClientKeywords.__init__(self) |
self.parent = parent |
self.name = name |
@@ -263,10 +263,8 @@ class Dependency(GClientKeywords, WorkItem): |
# If it is not set to True, the dependency wasn't processed for its child |
# dependency, i.e. its DEPS wasn't read. |
self.deps_parsed = False |
- # A direct reference is dependency that is referenced by a deps, deps_os or |
- # solution. A indirect one is one that was loaded with From() or that |
- # exceeded recursion limit. |
- self.direct_reference = False |
+ # This dependency should be processed, i.e. checked out |
+ self.should_process = should_process |
# This dependency has been processed, i.e. checked out |
self.processed = False |
# This dependency had its hook run |
@@ -295,6 +293,7 @@ class Dependency(GClientKeywords, WorkItem): |
Manages From() keyword accordingly. Do not touch self.parsed_url nor |
self.url because it may called with other urls due to From().""" |
+ assert self.parsed_url == None or not self.should_process, self.parsed_url |
overriden_url = self.get_custom_deps(self.name, url) |
if overriden_url != url: |
logging.info('%s, %s was overriden to %s' % (self.name, url, |
@@ -310,7 +309,7 @@ class Dependency(GClientKeywords, WorkItem): |
sub_target = url.sub_target_name or self.name |
# Make sure the referenced dependency DEPS file is loaded and file the |
# inner referenced dependency. |
- ref.ParseDepsFile(False) |
+ ref.ParseDepsFile() |
found_dep = None |
for d in ref.dependencies: |
if d.name == sub_target: |
@@ -351,12 +350,9 @@ class Dependency(GClientKeywords, WorkItem): |
else: |
raise gclient_utils.Error('Unkown url type') |
- def ParseDepsFile(self, direct_reference): |
+ def ParseDepsFile(self): |
"""Parses the DEPS file for this dependency.""" |
- if direct_reference: |
- # Maybe it was referenced earlier by a From() keyword but it's now |
- # directly referenced. |
- self.direct_reference = direct_reference |
+ assert self.processed == True |
if self.deps_parsed: |
logging.debug('%s was already parsed' % self.name) |
return |
@@ -423,14 +419,30 @@ class Dependency(GClientKeywords, WorkItem): |
raise gclient_utils.Error( |
'The same name "%s" appears multiple times in the deps section' % |
name) |
+ should_process = self.recursion_limit() and self.should_process |
+ if should_process: |
+ tree = dict((d.name, d) for d in self.tree(False)) |
+ if name in tree: |
+ if url == tree[name].url: |
+ logging.info('Won\'t process duplicate dependency %s' % tree[name]) |
+ # In theory we could keep it as a shadow of the other one. In |
+ # practice, simply ignore it. |
+ #should_process = False |
+ continue |
+ else: |
+ raise gclient_utils.Error( |
+ 'Dependency %s specified more than once:\n %s\nvs\n %s' % |
+ (name, tree[name].hierarchy(), self.hierarchy())) |
self.dependencies.append(Dependency(self, name, url, None, None, None, |
- None)) |
+ None, should_process)) |
logging.debug('Loaded: %s' % str(self)) |
def run(self, options, revision_overrides, command, args, work_queue): |
"""Runs 'command' before parsing the DEPS in case it's a initial checkout |
or a revert.""" |
assert self._file_list == [] |
+ if not self.should_process: |
+ return |
# When running runhooks, there's no need to consult the SCM. |
# All known hooks are expected to run unconditionally regardless of working |
# copy state, so skip the SCM status check. |
@@ -457,7 +469,7 @@ class Dependency(GClientKeywords, WorkItem): |
self.processed = True |
if self.recursion_limit(): |
# Then we can parse the DEPS file. |
- self.ParseDepsFile(True) |
+ self.ParseDepsFile() |
# Adjust the implicit dependency requirement; e.g. if a DEPS file contains |
# both src/foo and src/foo/bar, src/foo/bar is implicitly dependent of |
# src/foo. Yes, it's O(n^2)... It's important to do that before |
@@ -476,9 +488,12 @@ class Dependency(GClientKeywords, WorkItem): |
def RunHooksRecursively(self, options): |
"""Evaluates all hooks, running actions as needed. run() |
must have been called before to load the DEPS.""" |
+ assert self.hooks_ran == False |
+ if not self.should_process: |
+ return |
# If "--force" was specified, run all hooks regardless of what files have |
# changed. |
- if self.deps_hooks and self.direct_reference: |
+ if self.deps_hooks: |
# TODO(maruel): If the user is using git or git-svn, then we don't know |
# what files have changed so we always run all hooks. It'd be nice to fix |
# that. |
@@ -513,9 +528,8 @@ class Dependency(GClientKeywords, WorkItem): |
matching_file_list = [f for f in file_list if pattern.search(f)] |
if matching_file_list: |
self._RunHookAction(hook_dict, matching_file_list) |
- if self.recursion_limit(): |
- for s in self.dependencies: |
- s.RunHooksRecursively(options) |
+ for s in self.dependencies: |
+ s.RunHooksRecursively(options) |
def _RunHookAction(self, hook_dict, matching_file_list): |
"""Runs the action from a single hook.""" |
@@ -554,13 +568,13 @@ class Dependency(GClientKeywords, WorkItem): |
return self.parent.tree(include_all) |
def subtree(self, include_all): |
+ """Breadth first""" |
result = [] |
- # Add breadth-first. |
- if self.direct_reference or include_all: |
- for d in self.dependencies: |
+ for d in self.dependencies: |
+ if d.should_process or include_all: |
result.append(d) |
- for d in self.dependencies: |
- result.extend(d.subtree(include_all)) |
+ for d in self.dependencies: |
+ result.extend(d.subtree(include_all)) |
return result |
def get_custom_deps(self, name, url): |
@@ -579,8 +593,8 @@ class Dependency(GClientKeywords, WorkItem): |
def __str__(self): |
out = [] |
for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', |
- 'custom_vars', 'deps_hooks', '_file_list', 'processed', |
- 'hooks_ran', 'deps_parsed', 'requirements', 'direct_reference'): |
+ 'custom_vars', 'deps_hooks', '_file_list', 'should_process', |
+ 'processed', 'hooks_ran', 'deps_parsed', 'requirements'): |
# 'deps_file' |
if self.__dict__[i]: |
out.append('%s: %s' % (i, self.__dict__[i])) |
@@ -655,7 +669,7 @@ solutions = [ |
# Do not change previous behavior. Only solution level and immediate DEPS |
# are processed. |
self._recursion_limit = 2 |
- Dependency.__init__(self, None, None, None, None, None, None, None) |
+ Dependency.__init__(self, None, None, None, None, None, None, None, True) |
self._options = options |
if options.deps_os: |
enforced_os = options.deps_os.split(',') |
@@ -677,12 +691,17 @@ solutions = [ |
gclient_utils.SyntaxErrorToError('.gclient', e) |
for s in config_dict.get('solutions', []): |
try: |
+ tree = dict((d.name, d) for d in self.tree(False)) |
+ if s['name'] in tree: |
+ raise gclient_utils.Error( |
+ 'Dependency %s specified more than once in .gclient' % s['name']) |
self.dependencies.append(Dependency( |
self, s['name'], s['url'], |
s.get('safesync_url', None), |
s.get('custom_deps', {}), |
s.get('custom_vars', {}), |
- None)) |
+ None, |
+ True)) |
except KeyError: |
raise gclient_utils.Error('Invalid .gclient file. Solution is ' |
'incomplete: %s' % s) |
@@ -894,13 +913,10 @@ solutions = [ |
entries[d.name] = d.parsed_url |
keys = sorted(entries.keys()) |
for x in keys: |
- line = '%s: %s' % (x, entries[x]) |
- if x is not keys[-1]: |
- line += ';' |
- print line |
+ print('%s: %s' % (x, entries[x])) |
logging.info(str(self)) |
- def ParseDepsFile(self, direct_reference): |
+ def ParseDepsFile(self): |
"""No DEPS to parse for a .gclient file.""" |
raise gclient_utils.Error('Internal error') |