From b9a78d31ba8cb820d1099d8b98c95623d1a8427a Mon Sep 17 00:00:00 2001 From: "szager@google.com" Date: Tue, 13 Mar 2012 18:46:21 +0000 Subject: [PATCH] Added `gclient hookinfo`. This will be used to convert hooks into repo post-sync hooks. Review URL: http://codereview.chromium.org/9232068 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@126426 0039d316-1c4b-4281-b951-d872f2087c98 --- gclient.py | 86 +++++++++++++++++++++++++------------------ tests/gclient_test.py | 32 +++++++++++++++- 2 files changed, 82 insertions(+), 36 deletions(-) diff --git a/gclient.py b/gclient.py index 3a4622ef6..fce686a95 100644 --- a/gclient.py +++ b/gclient.py @@ -604,13 +604,31 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): self._parsed_url = parsed_url self._processed = True - def RunHooksRecursively(self, options): - """Evaluates all hooks, running actions as needed. run() - must have been called before to load the DEPS.""" - assert self.hooks_ran == False + @staticmethod + def GetHookAction(hook_dict, matching_file_list): + """Turns a parsed 'hook' dict into an executable command.""" + logging.debug(hook_dict) + logging.debug(matching_file_list) + command = hook_dict['action'][:] + if command[0] == 'python': + # If the hook specified "python" as the first item, the action is a + # Python script. Run it by starting a new copy of the same + # interpreter. + command[0] = sys.executable + if '$matching_files' in command: + splice_index = command.index('$matching_files') + command[splice_index:splice_index + 1] = matching_file_list + return command + + def GetHooks(self, options): + """Evaluates all hooks, and return them in a flat list. + + RunOnDeps() must have been called before to load the DEPS. + """ + result = [] if not self.should_process or not self.recursion_limit: # Don't run the hook when it is above recursion_limit. - return + return result # If "--force" was specified, run all hooks regardless of what files have # changed. if self.deps_hooks: @@ -622,7 +640,7 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): gclient_scm.GetScmName(self.parsed_url) in ('git', None) or os.path.isdir(os.path.join(self.root.root_dir, self.name, '.git'))): for hook_dict in self.deps_hooks: - self._RunHookAction(hook_dict, []) + result.append(self.GetHookAction(hook_dict, [])) else: # Run hooks on the basis of whether the files from the gclient operation # match each hook's pattern. @@ -632,38 +650,24 @@ class Dependency(gclient_utils.WorkItem, DependencySettings): f for f in self.file_list_and_children if pattern.search(f) ] if matching_file_list: - self._RunHookAction(hook_dict, matching_file_list) + result.append(self.GetHookAction(hook_dict, matching_file_list)) for s in self.dependencies: - s.RunHooksRecursively(options) + result.extend(s.GetHooks(options)) + return result - def _RunHookAction(self, hook_dict, matching_file_list): - """Runs the action from a single hook.""" - # A single DEPS file can specify multiple hooks so this function can be - # called multiple times on a single Dependency. - #assert self.hooks_ran == False + def RunHooksRecursively(self, options): + assert self.hooks_ran == False self._hooks_ran = True - logging.debug(hook_dict) - logging.debug(matching_file_list) - command = hook_dict['action'][:] - if command[0] == 'python': - # If the hook specified "python" as the first item, the action is a - # Python script. Run it by starting a new copy of the same - # interpreter. - command[0] = sys.executable - - if '$matching_files' in command: - splice_index = command.index('$matching_files') - command[splice_index:splice_index + 1] = matching_file_list - - try: - gclient_utils.CheckCallAndFilterAndHeader( - command, cwd=self.root.root_dir, always=True) - except (gclient_utils.Error, subprocess2.CalledProcessError), e: - # 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. - print >> sys.stderr, 'Error: %s' % str(e) - sys.exit(2) + for hook in self.GetHooks(options): + try: + gclient_utils.CheckCallAndFilterAndHeader( + hook, cwd=self.root.root_dir, always=True) + except (gclient_utils.Error, subprocess2.CalledProcessError), e: + # 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. + print >> sys.stderr, 'Error: %s' % str(e) + sys.exit(2) def subtree(self, include_all): """Breadth first recursion excluding root node.""" @@ -1454,6 +1458,18 @@ def CMDrevinfo(parser, args): return 0 +def CMDhookinfo(parser, args): + """Output the hooks that would be run by `gclient runhooks`""" + (options, args) = parser.parse_args(args) + options.force = True + client = GClient.LoadCurrentConfig(options) + if not client: + raise gclient_utils.Error('client not configured; see \'gclient config\'') + client.RunOnDeps(None, []) + print '; '.join(' '.join(hook) for hook in client.GetHooks(options)) + return 0 + + def Command(name): return getattr(sys.modules[__name__], 'CMD' + name, None) diff --git a/tests/gclient_test.py b/tests/gclient_test.py index 657b9815f..c76f6d32d 100755 --- a/tests/gclient_test.py +++ b/tests/gclient_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Copyright (c) 2012 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -246,6 +246,36 @@ class GclientTest(trial_dir.TestCase): str_obj = str(obj) self.assertEquals(471, len(str_obj), '%d\n%s' % (len(str_obj), str_obj)) + def testHooks(self): + topdir = self.root_dir + gclient_fn = os.path.join(topdir, '.gclient') + fh = open(gclient_fn, 'w') + print >> fh, 'solutions = [{"name":"top","url":"svn://svn.top.com/top"}]' + fh.close() + subdir_fn = os.path.join(topdir, 'top') + os.mkdir(subdir_fn) + deps_fn = os.path.join(subdir_fn, 'DEPS') + fh = open(deps_fn, 'w') + hooks = [{'pattern':'.', 'action':['cmd1', 'arg1', 'arg2']}] + print >> fh, 'hooks = %s' % repr(hooks) + fh.close() + + fh = open(os.path.join(subdir_fn, 'fake.txt'), 'w') + print >> fh, 'bogus content' + fh.close() + + os.chdir(topdir) + + parser = gclient.Parser() + options, _ = parser.parse_args([]) + options.force = True + client = gclient.GClient.LoadCurrentConfig(options) + work_queue = gclient_utils.ExecutionQueue(options.jobs, None) + for s in client.dependencies: + work_queue.enqueue(s) + work_queue.flush({}, None, [], options=options) + self.assertEqual(client.GetHooks(options), [x['action'] for x in hooks]) + if __name__ == '__main__': sys.stdout = gclient_utils.MakeFileAutoFlush(sys.stdout)