gclient: implement conditions for hooks

Bug: 570091
Change-Id: I5a489f9f9cbc5384b720685264aa918573234cf5
Reviewed-on: https://chromium-review.googlesource.com/544965
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Commit-Queue: Paweł Hajdan Jr. <phajdan.jr@chromium.org>
diff --git a/gclient.py b/gclient.py
index 1f8a309..72c1e04 100755
--- a/gclient.py
+++ b/gclient.py
@@ -139,7 +139,8 @@
 class Hook(object):
   """Descriptor of command ran before/after sync or on demand."""
 
-  def __init__(self, action, pattern=None, name=None, cwd=None):
+  def __init__(self, action, pattern=None, name=None, cwd=None, condition=None,
+               variables=None):
     """Constructor.
 
     Arguments:
@@ -147,16 +148,26 @@
       pattern (basestring regex): noop with git; deprecated
       name (basestring): optional name; no effect on operation
       cwd (basestring): working directory to use
+      condition (basestring): condition when to run the hook
+      variables (dict): variables for evaluating the condition
     """
     self._action = gclient_utils.freeze(action)
     self._pattern = pattern
     self._name = name
     self._cwd = cwd
+    self._condition = condition
+    self._variables = variables
 
   @staticmethod
-  def from_dict(d):
+  def from_dict(d, variables=None):
     """Creates a Hook instance from a dict like in the DEPS file."""
-    return Hook(d['action'], d.get('pattern'), d.get('name'), d.get('cwd'))
+    return Hook(
+        d['action'],
+        d.get('pattern'),
+        d.get('name'),
+        d.get('cwd'),
+        d.get('condition'),
+        variables=variables)
 
   @property
   def action(self):
@@ -178,7 +189,11 @@
     return bool([f for f in file_list if pattern.search(f)])
 
   def run(self, root):
-    """Executes the hook's command."""
+    """Executes the hook's command (provided the condition is met)."""
+    if (self._condition and
+        not gclient_eval.EvaluateCondition(self._condition, self._variables)):
+      return
+
     cmd = list(self._action)
     if cmd[0] == 'python':
       # If the hook specified "python" as the first item, the action is a
@@ -782,8 +797,9 @@
         hooks_to_run.append(hook)
 
     if self.recursion_limit:
-      self._pre_deps_hooks = [Hook.from_dict(hook) for hook in
-                              local_scope.get('pre_deps_hooks', [])]
+      self._pre_deps_hooks = [
+          Hook.from_dict(hook, variables=self._vars) for hook in
+          local_scope.get('pre_deps_hooks', [])]
 
     self.add_dependencies_and_close(
         deps_to_add, hooks_to_run, orig_deps_to_add=orig_deps_to_add)
@@ -803,7 +819,8 @@
         self.add_dependency(dep)
     for dep in (orig_deps_to_add or deps_to_add):
       self.add_orig_dependency(dep)
-    self._mark_as_parsed([Hook.from_dict(h) for h in hooks])
+    self._mark_as_parsed(
+        [Hook.from_dict(h, variables=self._vars) for h in hooks])
 
   def findDepsFromNotAllowedHosts(self):
     """Returns a list of depenecies from not allowed hosts.
@@ -994,7 +1011,6 @@
     for hook in self.pre_deps_hooks:
       hook.run(self.root.root_dir)
 
-
   def subtree(self, include_all):
     """Breadth first recursion excluding root node."""
     dependencies = self.dependencies