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. | |
8 | |
9 Example usage: | |
10 | |
11 $ python gm/show_gm_changes.py Test-Mac10.7-MacMini4.1-GeForce320M-x86-Debug \ | |
12 shadertext_gpu --autogen-path .gm-actuals | |
13 | |
14 Rev Hash | |
15 15990 10904734222736193002 | |
16 10729 10752292282035416719 | |
17 8504 2915063876615374518 | |
18 71 7546128203733045901 | |
19 """ | |
20 | |
21 | |
22 import argparse | |
23 import json | |
24 import os | |
25 import re | |
26 import subprocess | |
27 import sys | |
28 | |
29 | |
30 def _get_hash_and_last_change(gm_name, filepath): | |
31 """Find the current hash for the given GM and the last-changed revision. | |
32 | |
33 This function runs "svn blame", which is slow. | |
34 | |
35 Args: | |
36 gm_name: string; name of the GM in question. | |
37 filepath: string; path to the actual-results.json file. | |
38 Returns: | |
39 tuple of the form (last_changed_rev, hash), where last_changed_rev is an | |
40 int and hash is a string, or (None, None) if the file does not exist, the | |
41 GM is not found in the file, or some other problem occurs. | |
42 """ | |
43 if not os.path.isfile(filepath): | |
44 # If the file doesn't exist, we may have synced to before it was created. | |
45 return (None, None) | |
46 output = subprocess.check_output(['svn', 'blame', '--force', filepath]) | |
47 pattern = (r'^\s+\d+\s+.+\s+"%s.png" : {\s*\n\s+\d+\s+.+\s+"allowed-digests" ' | |
48 ': \[\s*\n\s+(\d+)\s+.+\s+\[ "bitmap-64bitMD5",\s+\n*(\d+)') | |
49 match = re.search(pattern % gm_name, output, re.MULTILINE) | |
50 if match: | |
51 try: | |
52 return (int(match.groups()[0]), match.groups()[1]) | |
53 except Exception: | |
54 # If there are any problems with the above (incorrect number of matches, | |
55 # inability to parse an integer), just return None. | |
56 return (None, None) | |
57 return (None, None) | |
58 | |
59 | |
60 def find_changes(builder_name, gm_name, autogen_path): | |
61 """Find and return recent changes in the given GM. | |
62 | |
63 This function runs "svn blame" and "svn update" numerous times and is | |
64 therefore very slow. | |
65 | |
66 Args: | |
67 builder_name: string; name of the builder. | |
68 gm_name: string; name of the GM. | |
69 autogen_path: string; path to skia-autogen checkout. | |
70 Yields: | |
71 tuples of the form: (autogen_revision, hash) | |
72 """ | |
73 actuals_path = os.path.join(autogen_path, builder_name, 'actual-results.json') | |
74 | |
75 # Capture the initial state of the skia-autogen checkout so that we can return | |
76 # to the same state later. | |
77 orig_rev = subprocess.check_output(['svnversion', '.'], | |
78 cwd=autogen_path).rstrip() | |
79 | |
80 try: | |
81 last_change_rev, hash = _get_hash_and_last_change(gm_name, actuals_path) | |
82 while last_change_rev: | |
83 yield (str(last_change_rev), hash) | |
84 # Sync to the revision just *before* the last change | |
85 subprocess.check_call(['svn', 'update', '-r', str(last_change_rev - 1)], | |
86 cwd=autogen_path, | |
87 stdout=subprocess.PIPE, | |
88 stderr=subprocess.PIPE) | |
89 last_change_rev, hash = _get_hash_and_last_change(gm_name, actuals_path) | |
90 finally: | |
91 # Return the repository to its initial state. | |
92 subprocess.check_call(['svn', 'update', '-r', orig_rev], | |
93 cwd=autogen_path, | |
94 stdout=subprocess.PIPE, | |
95 stderr=subprocess.PIPE) | |
96 | |
97 | |
98 def main(): | |
99 """Find and display recent changes in the given GM.""" | |
100 parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__) | |
101 parser.add_argument('builder_name', help='Name of the builder.') | |
102 parser.add_argument('gm_name', help='Name of the GM.') | |
103 parser.add_argument('--autogen-path', default=os.curdir, | |
104 help=('Path to a skia-autogen checkout. This checkout ' | |
105 'will be modified but the script will attempt to ' | |
106 'restore it to its original state. Default: ' | |
107 '"%(default)s"')) | |
108 args = parser.parse_args() | |
109 | |
110 print 'Rev\tHash' | |
111 for change in find_changes(args.builder_name, args.gm_name, | |
112 args.autogen_path): | |
113 print '\t'.join(change) | |
114 | |
115 | |
116 if __name__ == '__main__': | |
117 sys.exit(main()) | |
OLD | NEW |