diff --git a/annotated_gclient.py b/annotated_gclient.py new file mode 100755 index 000000000..1ed0f719a --- /dev/null +++ b/annotated_gclient.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# Copyright 2013 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. + +"""Wraps gclient calls with annotated output. + +Note that you will have to use -- to stop option parsing for gclient flags. + +To run `gclient sync --gclientfile=.gclient` and annotate got_v8_revision: + `annotated_gclient.py --revision-mapping='{"src/v8": "got_v8_revision"}' -- + sync --gclientfile=.gclient` +""" + +import contextlib +import json +import optparse +import os +import subprocess +import sys +import tempfile + + +@contextlib.contextmanager +def temp_filename(suffix='', prefix='tmp'): + output_fd, output_file = tempfile.mkstemp(suffix=suffix, prefix=prefix) + os.close(output_fd) + + yield output_file + + try: + os.remove(output_file) + except OSError as e: + print 'Error cleaning up temp file %s: %s' % (output_file, e) + + +def parse_got_revision(filename, revision_mapping): + result = {} + with open(filename) as f: + data = json.load(f) + + for path, info in data['solutions'].iteritems(): + # gclient json paths always end with a slash + path = path.rstrip('/') + if path in revision_mapping: + propname = revision_mapping[path] + result[propname] = info['revision'] + + return result + + +def emit_buildprops(got_revisions): + for prop, revision in got_revisions.iteritems(): + print '@@@SET_BUILD_PROPERTY@%s@%s@@@' % (prop, revision) + + +def main(): + parser = optparse.OptionParser( + description=('Runs gclient and annotates the output with any ' + 'got_revisions.')) + parser.add_option('--revision-mapping', default='{}', + help='json dict of directory-to-property mappings.') + parser.add_option('--suffix', default='gclient', + help='tempfile suffix') + opts, args = parser.parse_args() + + revision_mapping = json.loads(opts.revision_mapping) + + if not args: + parser.error('Must provide arguments to gclient.') + + if any(a.startswith('--output-json') for a in args): + parser.error('Can\'t call annotated_gclient with --output-json.') + + with temp_filename(opts.suffix) as f: + cmd = ['gclient'] + cmd.extend(args) + cmd.extend(['--output-json', f]) + p = subprocess.Popen(cmd) + p.wait() + + if p.returncode == 0: + revisions = parse_got_revision(f, revision_mapping) + emit_buildprops(revisions) + return p.returncode + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/apply_issue.py b/apply_issue.py index 5a8f7b6ea..c8cbfec32 100755 --- a/apply_issue.py +++ b/apply_issue.py @@ -7,6 +7,7 @@ """ import getpass +import json import logging import optparse import os @@ -16,6 +17,7 @@ import urllib2 import breakpad # pylint: disable=W0611 +import annotated_gclient import checkout import fix_encoding import gclient_utils @@ -67,6 +69,9 @@ def main(): help='Rietveld server') parser.add_option('--no-auth', action='store_true', help='Do not attempt authenticated requests.') + parser.add_option('--revision-mapping', default='{}', + help='When running gclient, annotate the got_revisions ' + 'using the revision-mapping.') options, args = parser.parse_args() logging.basicConfig( format='%(levelname)5s %(module)11s(%(lineno)4d): %(message)s', @@ -80,6 +85,8 @@ def main(): if not options.server: parser.error('Require a valid server') + options.revision_mapping = json.loads(options.revision_mapping) + if options.password == '-': print('Reading password') options.password = sys.stdin.readline().strip() @@ -178,14 +185,24 @@ def main(): gclient_path = os.path.join(BASE_DIR, 'gclient') if sys.platform == 'win32': gclient_path += '.bat' - return subprocess.call( - [ + with annotated_gclient.temp_filename(suffix='gclient') as f: + cmd = [ gclient_path, 'sync', '--revision', base_rev, '--nohooks', '--delete_unversioned_trees', - ], - cwd=gclient_root) + ] + if options.revision_mapping: + cmd.extend(['--output-json', f]) + + retcode = subprocess.call(cmd, cwd=gclient_root) + + if retcode == 0 and options.revision_mapping: + revisions = annotated_gclient.parse_got_revision( + f, options.revision_mapping) + annotated_gclient.emit_buildprops(revisions) + + return retcode return 0