Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 2 # Copyright (c) 2014 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 | |
| 7 """Find and display recent changes in the given GM.""" | |
|
epoger
2014/04/01 17:39:02
Please paste your example run command line within
borenet
2014/04/01 18:11:38
Done.
| |
| 8 | |
| 9 | |
| 10 import argparse | |
| 11 import json | |
| 12 import os | |
| 13 import re | |
| 14 import subprocess | |
| 15 import sys | |
| 16 | |
| 17 | |
| 18 def _get_hash_and_last_change(gm_name, filepath): | |
| 19 """Find the current hash for the given GM and the last-changed revision. | |
| 20 | |
| 21 This function runs "svn blame", which is slow. | |
| 22 | |
| 23 Args: | |
| 24 gm_name: string; name of the GM in question. | |
| 25 filepath: string: path to the actual-results.json file. | |
|
epoger
2014/04/01 17:39:02
maybe semicolon after "string" just to be consiste
borenet
2014/04/01 18:11:38
Whoops. Done.
| |
| 26 Returns: | |
| 27 tuple of the form (last_changed_rev, hash), where last_changed_rev is an | |
| 28 int and hash is a string, or (None, None) if the file does not exist, the | |
| 29 GM is not found in the file, or some other problem occurs. | |
| 30 """ | |
| 31 if not os.path.isfile(filepath): | |
| 32 # If the file doesn't exist, we may have synced to before it was created. | |
| 33 return (None, None) | |
| 34 output = subprocess.check_output(['svn', 'blame', '--force', filepath]) | |
| 35 pattern = (r'^\s+\d+\s+.+\s+"%s.png" : {\s*\n\s+\d+\s+.+\s+"allowed-digests" ' | |
| 36 ': \[\s*\n\s+(\d+)\s+.+\s+\[ "bitmap-64bitMD5",\s+\n*(\d+)') | |
|
borenet
2014/04/01 13:49:57
This isn't quite right, since it will only find ch
epoger
2014/04/01 17:39:02
That's not a problem; see https://code.google.com/
borenet
2014/04/01 18:11:38
Agreed. I guess another way to do it would be to
| |
| 37 match = re.search(pattern % gm_name, output, re.MULTILINE) | |
| 38 if match: | |
| 39 try: | |
| 40 return (int(match.groups()[0]), match.groups()[1]) | |
| 41 except Exception: | |
| 42 # If there are any problems with the above (incorrect number of matches, | |
| 43 # inability to parse an integer), just return None. | |
| 44 return (None, None) | |
| 45 return (None, None) | |
| 46 | |
| 47 | |
| 48 def find_changes(builder_name, gm_name, autogen_path): | |
| 49 """Find and return recent changes in the given GM. | |
| 50 | |
| 51 This function runs "svn blame" and "svn update" numerous times and is | |
| 52 therefore very slow. | |
| 53 | |
| 54 Args: | |
| 55 builder_name: string; name of the builder. | |
| 56 gm_name: string; name of the GM. | |
| 57 autogen_path: string; path to skia-autogen checkout. | |
| 58 Yields: | |
| 59 tuples of the form: (autogen_revision, hash) | |
| 60 """ | |
| 61 actuals_path = os.path.join(autogen_path, builder_name, 'actual-results.json') | |
| 62 | |
| 63 # Capture the initial state of the skia-autogen checkout so that we can return | |
| 64 # to the same state later. | |
| 65 orig_rev = subprocess.check_output(['svnversion', '.'], | |
| 66 cwd=autogen_path).rstrip() | |
| 67 | |
| 68 try: | |
| 69 last_change_rev, hash = _get_hash_and_last_change(gm_name, actuals_path) | |
| 70 while last_change_rev: | |
| 71 yield (str(last_change_rev), hash) | |
| 72 # Sync to the revision just *before* the last change | |
| 73 subprocess.check_call(['svn', 'update', '-r', str(last_change_rev - 1)], | |
| 74 cwd=autogen_path, | |
| 75 stdout=subprocess.PIPE, | |
| 76 stderr=subprocess.PIPE) | |
| 77 last_change_rev, hash = _get_hash_and_last_change(gm_name, actuals_path) | |
| 78 finally: | |
| 79 # Return the repository to its initial state. | |
| 80 subprocess.check_call(['svn', 'update', '-r', orig_rev], | |
| 81 cwd=autogen_path, | |
| 82 stdout=subprocess.PIPE, | |
| 83 stderr=subprocess.PIPE) | |
| 84 | |
| 85 | |
| 86 def main(): | |
| 87 """Find and display recent changes in the given GM.""" | |
| 88 parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__) | |
| 89 parser.add_argument('builder_name', help='Name of the builder.') | |
| 90 parser.add_argument('gm_name', help='Name of the GM.') | |
| 91 parser.add_argument('--autogen-path', default=os.curdir, | |
| 92 help=('Path to a skia-autogen checkout. This checkout ' | |
| 93 'will be modified but the script will attempt to ' | |
| 94 'restore it to its original state. Default: ' | |
| 95 '"%(default)s"')) | |
| 96 args = parser.parse_args() | |
| 97 | |
| 98 print 'Rev\tHash' | |
| 99 for change in find_changes(args.builder_name, args.gm_name, | |
| 100 args.autogen_path): | |
| 101 print '\t'.join(change) | |
| 102 | |
| 103 | |
| 104 if __name__ == '__main__': | |
| 105 sys.exit(main()) | |
|
epoger
2014/04/01 17:39:02
Does this mean main() should return an int? Becau
borenet
2014/04/01 18:11:38
This is a convention I've seen in a number of plac
epoger
2014/04/01 18:18:55
I would buy that.
| |
| OLD | NEW |