diff --git a/gclient.py b/gclient.py index 1f8a309a9..72c1e0484 100755 --- a/gclient.py +++ b/gclient.py @@ -139,7 +139,8 @@ def ToGNString(value, allow_dicts = True): 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 @@ class Hook(object): 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 @@ class Hook(object): 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 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): 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 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): 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 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): 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 diff --git a/gclient_eval.py b/gclient_eval.py index 2f27634ae..2ce4f9cb2 100644 --- a/gclient_eval.py +++ b/gclient_eval.py @@ -23,6 +23,10 @@ _GCLIENT_HOOKS_SCHEMA = [{ # Working directory where to execute the hook. schema.Optional('cwd'): basestring, + + # Optional condition string. The hook will only be run + # if the condition evaluates to True. + schema.Optional('condition'): basestring, }] _GCLIENT_SCHEMA = schema.Schema({ diff --git a/testing_support/fake_repos.py b/testing_support/fake_repos.py index ed7dac258..fda77da77 100755 --- a/testing_support/fake_repos.py +++ b/testing_support/fake_repos.py @@ -298,7 +298,7 @@ class FakeReposBase(object): class FakeRepos(FakeReposBase): """Implements populateGit().""" - NB_GIT_REPOS = 6 + NB_GIT_REPOS = 7 def populateGit(self): # Testing: @@ -507,6 +507,27 @@ recursedeps = [ 'origin': 'git/repo_6@1\n', }) + self._commit_git('repo_7', { + 'DEPS': """ +vars = { + 'true_var': 'True', + 'false_var': 'true_var and False', +} +hooks = [ + { + 'action': ['python', '-c', + 'open(\\'src/should_run\\', \\'w\\').write(\\'should_run\\')'], + 'condition': 'true_var or True', + }, + { + 'action': ['python', '-c', + 'open(\\'src/should_not_run\\', \\'w\\').write(\\'should_not_run\\')'], + 'condition': 'false_var', + }, +]""", + 'origin': 'git/repo_7@1\n', + }) + class FakeRepoSkiaDEPS(FakeReposBase): """Simulates the Skia DEPS transition in Chrome.""" diff --git a/tests/gclient_smoketest.py b/tests/gclient_smoketest.py index a0b7341f1..bf59678af 100755 --- a/tests/gclient_smoketest.py +++ b/tests/gclient_smoketest.py @@ -461,6 +461,15 @@ class GClientSmokeGIT(GClientSmokeBase): tree['src/git_hooked2'] = 'git_hooked2' self.assertTree(tree) + def testRunHooksCondition(self): + if not self.enabled: + return + self.gclient(['config', self.git_base + 'repo_7', '--name', 'src']) + self.gclient(['sync', '--deps', 'mac']) + tree = self.mangle_git_tree(('repo_7@1', 'src')) + tree['src/should_run'] = 'should_run' + self.assertTree(tree) + def testPreDepsHooks(self): if not self.enabled: return