Add more glue code necessary for getting the DEPS dependencies as Dependency objects.

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@50988 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/gclient.py b/gclient.py
index 3a5a669..1b7da5c 100644
--- a/gclient.py
+++ b/gclient.py
@@ -311,8 +311,7 @@
     return deps
 
   def _RunHookAction(self, hook_dict, matching_file_list):
-    """Runs the action from a single hook.
-    """
+    """Runs the action from a single hook."""
     logging.info(hook_dict)
     logging.info(matching_file_list)
     command = hook_dict['action'][:]
@@ -329,7 +328,7 @@
     # Use a discrete exit status code of 2 to indicate that a hook action
     # failed.  Users of this script may wish to treat hook action failures
     # differently from VC failures.
-    gclient_utils.SubprocessCall(command, self.root_dir(), fail_status=2)
+    return gclient_utils.SubprocessCall(command, self.root_dir(), fail_status=2)
 
   def _RunHooks(self, command, file_list, is_using_git):
     """Evaluates all hooks, running actions as needed.
@@ -370,9 +369,39 @@
   def enforced_os(self):
     return self.parent.enforced_os()
 
+  def recursion_limit(self):
+    return self.parent.recursion_limit() - 1
+
+  def tree(self, force_all):
+    return self.parent.tree(force_all)
+
+  def get_custom_deps(self, name, url):
+    """Returns a custom deps if applicable."""
+    if self.parent:
+      url = self.parent.get_custom_deps(name, url)
+    # None is a valid return value to disable a dependency.
+    return self.custom_deps.get(name, url)
+
+  def __str__(self):
+    out = []
+    for i in ('name', 'url', 'safesync_url', 'custom_deps', 'custom_vars',
+              'deps_hooks'):
+      # 'deps_file'
+      if self.__dict__[i]:
+        out.append('%s: %s' % (i, self.__dict__[i]))
+
+    for d in self.dependencies:
+      out.extend(['  ' + x for x in str(d).splitlines()])
+      out.append('')
+    return '\n'.join(out)
+
+  def __repr__(self):
+    return '%s: %s' % (self.name, self.url)
+
 
 class GClient(Dependency):
-  """Main gclient checkout root where .gclient resides."""
+  """Object that represent a gclient checkout. A tree of Dependency(), one per
+  solution or DEPS entry."""
   SUPPORTED_COMMANDS = [
     'cleanup', 'diff', 'export', 'pack', 'revert', 'status', 'update',
     'runhooks'
@@ -429,6 +458,9 @@
     self._enforced_os = list(set(enforced_os))
     self._root_dir = root_dir
     self.config_content = None
+    # Do not change previous behavior. Only solution level and immediate DEPS
+    # are processed.
+    self._recursion_limit = 2
 
   def SetConfig(self, content):
     assert self.dependencies == []
@@ -674,13 +706,13 @@
           if not self._options.delete_unversioned_trees or modified_files:
             # There are modified files in this entry. Keep warning until
             # removed.
-            print(("\nWARNING: \"%s\" is no longer part of this client.  "
-                   "It is recommended that you manually remove it.\n") %
+            print(('\nWARNING: \'%s\' is no longer part of this client.  '
+                   'It is recommended that you manually remove it.\n') %
                       entry_fixed)
           else:
             # Delete the entry
-            print("\n________ deleting \'%s\' " +
-                  "in \'%s\'") % (entry_fixed, self.root_dir())
+            print('\n________ deleting \'%s\' ' +
+                  'in \'%s\'') % (entry_fixed, self.root_dir())
             gclient_utils.RemoveDirectory(e_dir)
       # record the current list of entries for next time
       self._SaveEntries(entries)
@@ -770,12 +802,36 @@
       snapclient.SetConfig(config)
       print(snapclient.config_content)
 
+  def ParseDepsFile(self, direct_reference):
+    """No DEPS to parse for a .gclient file."""
+    self.direct_reference = direct_reference
+    self.deps_parsed = True
+
   def root_dir(self):
+    """Root directory of gclient checkout."""
     return self._root_dir
 
   def enforced_os(self):
+    """What deps_os entries that are to be parsed."""
     return self._enforced_os
 
+  def recursion_limit(self):
+    """How recursive can each dependencies in DEPS file can load DEPS file."""
+    return self._recursion_limit
+
+  def tree(self, force_all):
+    """Returns a flat list of all the dependencies."""
+    def subtree(dep):
+      if not force_all and not dep.direct_reference:
+        # Was loaded from a From() keyword in a DEPS file, don't load all its
+        # dependencies.
+        return []
+      result = dep.dependencies[:]
+      for d in dep.dependencies:
+        result.extend(subtree(d))
+      return result
+    return subtree(self)
+
 
 #### gclient commands.