commit-bot@chromium.org | 58af0c8 | 2014-04-01 19:03:19 +0000 | [diff] [blame] | 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()) |