Revert "gclient: remove support for From()"

This reverts commit bf72b593a2fac54e0ac7961be2706581ea47f7f9.

Reason for revert: maybe caused outage.

Original change's description:
> gclient: remove support for From()
> 
> This feature appears unused, and removing it will simplify the codebase.
> 
> Bug: 661382
> Change-Id: I545befb2c592eea53c54552018ce2d3dda7670f5
> Reviewed-on: https://chromium-review.googlesource.com/509693
> Commit-Queue: Paweł Hajdan Jr. <phajdan.jr@chromium.org>
> Reviewed-by: Dirk Pranke <dpranke@chromium.org>
> 

TBR=phajdan.jr@chromium.org,dpranke@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
Bug: 661382

Change-Id: I4db9554a0a3a64a3a69908560b6da2a9963518f2
Reviewed-on: https://chromium-review.googlesource.com/512343
Reviewed-by: Andrii Shyshkalov <tandrii@chromium.org>
Commit-Queue: Andrii Shyshkalov <tandrii@chromium.org>
diff --git a/gclient.py b/gclient.py
index 6873406..600c83d 100755
--- a/gclient.py
+++ b/gclient.py
@@ -158,6 +158,22 @@
 
 
 class GClientKeywords(object):
+  class FromImpl(object):
+    """Used to implement the From() syntax."""
+
+    def __init__(self, module_name, sub_target_name=None):
+      """module_name is the dep module we want to include from.  It can also be
+      the name of a subdirectory to include from.
+
+      sub_target_name is an optional parameter if the module name in the other
+      DEPS file is different. E.g., you might want to map src/net to net."""
+      self.module_name = module_name
+      self.sub_target_name = sub_target_name
+
+    def __str__(self):
+      return 'From(%s, %s)' % (repr(self.module_name),
+                               repr(self.sub_target_name))
+
   class VarImpl(object):
     def __init__(self, custom_vars, local_scope):
       self._custom_vars = custom_vars
@@ -210,10 +226,10 @@
       # urls are sometime incorrectly written as proto://host/path/@rev. Replace
       # it to proto://host/path@rev.
       self._url = self._url.replace('/@', '@')
-    elif not isinstance(self._url, (None.__class__)):
+    elif not isinstance(self._url, (self.FromImpl, None.__class__)):
       raise gclient_utils.Error(
-          ('dependency url must be either string or None, '
-           'instead of %s') % self._url.__class__.__name__)
+          ('dependency url must be either a string, None, '
+           'or From() instead of %s') % self._url.__class__.__name__)
     # Make any deps_file path platform-appropriate.
     for sep in ['/', '\\']:
       self._deps_file = self._deps_file.replace(sep, os.sep)
@@ -361,6 +377,9 @@
     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):
+      requirements.add(self.url.module_name)
+
     if self.name:
       requirements |= set(
           obj.name for obj in self.root.subtree(False)
@@ -430,7 +449,10 @@
     return True
 
   def LateOverride(self, url):
-    """Resolves the parsed url from url."""
+    """Resolves the parsed url from url.
+
+    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
     parsed_url = self.get_custom_deps(self.name, url)
     if parsed_url != url:
@@ -439,6 +461,32 @@
           (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
+      ]
+      if not ref:
+        raise gclient_utils.Error('Failed to find one reference to %s. %s' % (
+            url.module_name, ref))
+      # It may happen that len(ref) > 1 but it's no big deal.
+      ref = ref[0]
+      sub_target = url.sub_target_name or self.name
+      found_deps = [d for d in ref.dependencies if d.name == sub_target]
+      if len(found_deps) != 1:
+        raise gclient_utils.Error(
+            'Couldn\'t find %s in %s, referenced by %s (parent: %s)\n%s' % (
+                sub_target, ref.name, self.name, self.parent.name,
+                str(self.root)))
+
+      # Call LateOverride() again.
+      found_dep = found_deps[0]
+      parsed_url = found_dep.LateOverride(found_dep.url)
+      logging.info(
+          'Dependency(%s).LateOverride(%s) -> %s (From)' %
+          (self.name, url, parsed_url))
+      return parsed_url
+
     if isinstance(url, basestring):
       parsed_url = urlparse.urlparse(url)
       if (not parsed_url[0] and
@@ -550,6 +598,7 @@
         }
       else:
         global_scope = {
+          'From': self.FromImpl,
           'Var': var.Lookup,
           'deps_os': {},
         }