| Index: gclient.py
 | 
| diff --git a/gclient.py b/gclient.py
 | 
| index 22e5f990a635b2da696922bf41862c2fa6c1fc6d..cbba142e60ab1c16d1519fdd68f7be49b50942ae 100644
 | 
| --- a/gclient.py
 | 
| +++ b/gclient.py
 | 
| @@ -261,33 +261,14 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
 | 
|      if not self.name and self.parent:
 | 
|        raise gclient_utils.Error('Dependency without name')
 | 
|  
 | 
| -  def setup_requirements(self):
 | 
| -    """Setup self.requirements and find any other dependency who would have self
 | 
| -    as a requirement.
 | 
| -
 | 
| -    Returns True if this entry should be added, False if it is a duplicate of
 | 
| -    another entry.
 | 
| -    """
 | 
| -    if self.name in [s.name for s in self.parent.dependencies]:
 | 
| -      raise gclient_utils.Error(
 | 
| -          'The same name "%s" appears multiple times in the deps section' %
 | 
| -              self.name)
 | 
| -    if self.should_process:
 | 
| -      siblings = [d for d in self.root.subtree(False) if d.name == self.name]
 | 
| -      for sibling in siblings:
 | 
| -        if self.url != sibling.url:
 | 
| -          raise gclient_utils.Error(
 | 
| -              'Dependency %s specified more than once:\n  %s\nvs\n  %s' %
 | 
| -              (self.name, sibling.hierarchy(), self.hierarchy()))
 | 
| -        # In theory we could keep it as a shadow of the other one. In
 | 
| -        # practice, simply ignore it.
 | 
| -        logging.warn('Won\'t process duplicate dependency %s' % sibling)
 | 
| -        return False
 | 
| -
 | 
| +  @property
 | 
| +  def requirements(self):
 | 
| +    """Calculate the list of requirements."""
 | 
| +    requirements = set()
 | 
|      # self.parent is implicitly a requirement. This will be recursive by
 | 
|      # definition.
 | 
|      if self.parent and self.parent.name:
 | 
| -      self.add_requirement(self.parent.name)
 | 
| +      requirements.add(self.parent.name)
 | 
|  
 | 
|      # For a tree with at least 2 levels*, the leaf node needs to depend
 | 
|      # on the level higher up in an orderly way.
 | 
| @@ -300,27 +281,48 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
 | 
|      # Interestingly enough, the following condition only works in the case we
 | 
|      # want: self is a 2nd level node. 3nd level node wouldn't need this since
 | 
|      # they already have their parent as a requirement.
 | 
| -    root_deps = self.root.dependencies
 | 
| -    if self.parent in root_deps:
 | 
| -      for i in root_deps:
 | 
| -        if i is self.parent:
 | 
| -          break
 | 
| -        if i.name:
 | 
| -          self.add_requirement(i.name)
 | 
| +    if self.parent and self.parent.parent and not self.parent.parent.parent:
 | 
| +      requirements |= set(i.name for i in self.root.dependencies if i.name)
 | 
|  
 | 
|      if isinstance(self.url, self.FromImpl):
 | 
| -      self.add_requirement(self.url.module_name)
 | 
| +      requirements.add(self.url.module_name)
 | 
|  
 | 
| -    if self.name and self.should_process:
 | 
| -      for obj in self.root.depth_first_tree():
 | 
| -        if obj is self or not obj.name:
 | 
| -          continue
 | 
| -        # Step 1: Find any requirements self may need.
 | 
| -        if self.name.startswith(posixpath.join(obj.name, '')):
 | 
| -          self.add_requirement(obj.name)
 | 
| -        # Step 2: Find any requirements self may impose.
 | 
| -        if obj.name.startswith(posixpath.join(self.name, '')):
 | 
| -          obj.add_requirement(self.name)
 | 
| +    if self.name:
 | 
| +      requirements |= set(
 | 
| +          obj.name for obj in self.root.subtree(False)
 | 
| +          if (obj is not self
 | 
| +              and obj.name and
 | 
| +              self.name.startswith(posixpath.join(obj.name, ''))))
 | 
| +    requirements = tuple(sorted(requirements))
 | 
| +    logging.info('Dependency(%s).requirements = %s' % (self.name, requirements))
 | 
| +    return requirements
 | 
| +
 | 
| +  def verify_validity(self):
 | 
| +    """Verifies that this Dependency is fine to add as a child of another one.
 | 
| +
 | 
| +    Returns True if this entry should be added, False if it is a duplicate of
 | 
| +    another entry.
 | 
| +    """
 | 
| +    logging.info('Dependency(%s).verify_validity()' % self.name)
 | 
| +    if self.name in [s.name for s in self.parent.dependencies]:
 | 
| +      raise gclient_utils.Error(
 | 
| +          'The same name "%s" appears multiple times in the deps section' %
 | 
| +              self.name)
 | 
| +    if not self.should_process:
 | 
| +      # Return early, no need to set requirements.
 | 
| +      return True
 | 
| +
 | 
| +    # This require a full tree traversal with locks.
 | 
| +    siblings = [d for d in self.root.subtree(False) if d.name == self.name]
 | 
| +    for sibling in siblings:
 | 
| +      if self.url != sibling.url:
 | 
| +        raise gclient_utils.Error(
 | 
| +            'Dependency %s specified more than once:\n  %s\nvs\n  %s' %
 | 
| +            (self.name, sibling.hierarchy(), self.hierarchy()))
 | 
| +      # In theory we could keep it as a shadow of the other one. In
 | 
| +      # practice, simply ignore it.
 | 
| +      logging.warn('Won\'t process duplicate dependency %s' % sibling)
 | 
| +      return False
 | 
|      return True
 | 
|  
 | 
|    def LateOverride(self, url):
 | 
| @@ -331,10 +333,13 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
 | 
|      assert self.parsed_url == None or not self.should_process, self.parsed_url
 | 
|      parsed_url = self.get_custom_deps(self.name, url)
 | 
|      if parsed_url != url:
 | 
| -      logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, parsed_url))
 | 
| +      logging.info(
 | 
| +          'Dependency(%s).LateOverride(%s) -> %s' %
 | 
| +          (self.name, url, parsed_url))
 | 
|        return parsed_url
 | 
|  
 | 
|      if isinstance(url, self.FromImpl):
 | 
| +      # Requires tree traversal.
 | 
|        ref = [
 | 
|            dep for dep in self.root.subtree(True) if url.module_name == dep.name
 | 
|        ]
 | 
| @@ -355,7 +360,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
 | 
|        found_dep = found_deps[0]
 | 
|        parsed_url = found_dep.LateOverride(found_dep.url)
 | 
|        logging.info(
 | 
| -          'LateOverride(%s, %s) -> %s (From)' % (self.name, url, parsed_url))
 | 
| +          'Dependency(%s).LateOverride(%s) -> %s (From)' %
 | 
| +          (self.name, url, parsed_url))
 | 
|        return parsed_url
 | 
|  
 | 
|      if isinstance(url, basestring):
 | 
| @@ -374,15 +380,20 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
 | 
|          parsed_url = scm.FullUrlForRelativeUrl(url)
 | 
|        else:
 | 
|          parsed_url = url
 | 
| -      logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, parsed_url))
 | 
| +      logging.info(
 | 
| +          'Dependency(%s).LateOverride(%s) -> %s' %
 | 
| +          (self.name, url, parsed_url))
 | 
|        return parsed_url
 | 
|  
 | 
|      if isinstance(url, self.FileImpl):
 | 
| -      logging.info('LateOverride(%s, %s) -> %s (File)' % (self.name, url, url))
 | 
| +      logging.info(
 | 
| +          'Dependency(%s).LateOverride(%s) -> %s (File)' %
 | 
| +          (self.name, url, url))
 | 
|        return url
 | 
|  
 | 
|      if url is None:
 | 
| -      logging.info('LateOverride(%s, %s) -> %s' % (self.name, url, url))
 | 
| +      logging.info(
 | 
| +          'Dependency(%s).LateOverride(%s) -> %s' % (self.name, url, url))
 | 
|        return url
 | 
|  
 | 
|      raise gclient_utils.Error('Unknown url type')
 | 
| @@ -459,8 +470,8 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
 | 
|  
 | 
|    def add_dependencies_and_close(self, deps_to_add, hooks):
 | 
|      """Adds the dependencies, hooks and mark the parsing as done."""
 | 
| -    for dep in deps_to_add:
 | 
| -      if dep.setup_requirements():
 | 
| +    for dep in sorted(deps_to_add, key=lambda x: x.name):
 | 
| +      if dep.verify_validity():
 | 
|          self.add_dependency(dep)
 | 
|      self._mark_as_parsed(hooks)
 | 
|  
 | 
| @@ -505,6 +516,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings):
 | 
|    # pylint: disable=W0221
 | 
|    def run(self, revision_overrides, command, args, work_queue, options):
 | 
|      """Runs |command| then parse the DEPS file."""
 | 
| +    logging.info('Dependency(%s).run()' % self.name)
 | 
|      assert self._file_list == []
 | 
|      if not self.should_process:
 | 
|        return
 | 
| 
 |