| Index: components/cronet/tools/update_api.py
|
| diff --git a/components/cronet/tools/update_api.py b/components/cronet/tools/update_api.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..fe5efb3f90d03f4934a4636475d6a52e60360958
|
| --- /dev/null
|
| +++ b/components/cronet/tools/update_api.py
|
| @@ -0,0 +1,141 @@
|
| +#!/usr/bin/python
|
| +# Copyright 2016 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.
|
| +
|
| +"""update_api.py - Update committed Cronet API."""
|
| +
|
| +import argparse
|
| +import filecmp
|
| +import fileinput
|
| +import os
|
| +import re
|
| +import shutil
|
| +import sys
|
| +import tempfile
|
| +
|
| +# Filename of dump of current API.
|
| +API_FILENAME = os.path.abspath(os.path.join(
|
| + os.path.dirname(__file__), '..', 'android', 'api.txt'))
|
| +# Filename of file containing API version number.
|
| +API_VERSION_FILENAME = os.path.abspath(os.path.join(
|
| + os.path.dirname(__file__), '..', 'android', 'api_version.txt'))
|
| +
|
| +# Regular expression that catches the beginning of lines that declare classes.
|
| +# The first group returned by a match is the class name.
|
| +CLASS_RE = re.compile(r'.*class ([^ ]*) .*\{')
|
| +
|
| +# Regular expression that matches a string containing an unnamed class name,
|
| +# for example 'Foo$1'.
|
| +UNNAMED_CLASS_RE = re.compile(r'.*\$[0-9]')
|
| +
|
| +
|
| +def generate_api(api_jar, output_filename):
|
| + # Dumps the API in |api_jar| into |outpuf_filename|.
|
| +
|
| + with open(output_filename, 'w') as output_file:
|
| + output_file.write(
|
| + 'DO NOT EDIT THIS FILE, USE update_api.py TO UPDATE IT\n\n')
|
| +
|
| + # Extract API class files from api_jar.
|
| + temp_dir = tempfile.mkdtemp()
|
| + old_cwd = os.getcwd()
|
| + api_jar_path = os.path.abspath(api_jar)
|
| + os.chdir(temp_dir)
|
| + if os.system('jar xf %s' % api_jar_path):
|
| + print 'ERROR: jar failed on ' + api_jar
|
| + return False
|
| + os.chdir(old_cwd)
|
| + shutil.rmtree(os.path.join(temp_dir, 'META-INF'))
|
| +
|
| + # Collect names of all API class files
|
| + api_class_files = []
|
| + for root, _, filenames in os.walk(temp_dir):
|
| + api_class_files += [os.path.join(root, f) for f in filenames]
|
| + api_class_files.sort()
|
| +
|
| + # Dump API class files into |output_filename|
|
| + javap_cmd = ('javap -protected %s >> %s' % (' '.join(api_class_files),
|
| + output_filename)).replace('$', '\\$')
|
| + if os.system(javap_cmd):
|
| + print 'ERROR: javap command failed: ' + javap_cmd
|
| + return False
|
| + shutil.rmtree(temp_dir)
|
| +
|
| + # Strip out pieces we don't need to compare.
|
| + output_file = fileinput.FileInput(output_filename, inplace=True)
|
| + skip_to_next_class = False
|
| + for line in output_file:
|
| + # Skip 'Compiled from ' lines as they're not part of the API.
|
| + if line.startswith('Compiled from "'):
|
| + continue
|
| + if CLASS_RE.match(line):
|
| + skip_to_next_class = (
|
| + # Skip internal classes, they aren't exposed.
|
| + UNNAMED_CLASS_RE.match(line) or
|
| + # Skip experimental classes, they can be modified.
|
| + 'Experimental' in line
|
| + )
|
| + if skip_to_next_class:
|
| + skip_to_next_class = line != '}'
|
| + continue
|
| + sys.stdout.write(line)
|
| + output_file.close()
|
| + return True
|
| +
|
| +
|
| +def check_up_to_date(api_jar):
|
| + # Returns True if API_FILENAME matches the API exposed by |api_jar|.
|
| +
|
| + [_, temp_filename] = tempfile.mkstemp()
|
| + if not generate_api(api_jar, temp_filename):
|
| + return False
|
| + ret = filecmp.cmp(API_FILENAME, temp_filename)
|
| + os.remove(temp_filename)
|
| + return ret
|
| +
|
| +
|
| +def check_api_update(old_api, new_api):
|
| + # Enforce that lines are only added when updating API.
|
| + with open(old_api, 'r') as old_api_file, open(new_api, 'r') as new_api_file:
|
| + for old_line in old_api_file:
|
| + while True:
|
| + new_line = new_api_file.readline()
|
| + if new_line == old_line:
|
| + break
|
| + if not new_line:
|
| + print 'ERROR: This API was modified or removed:'
|
| + print ' ' + old_line
|
| + print ' Cronet API methods and classes cannot be modified.'
|
| + return False
|
| + return True
|
| +
|
| +
|
| +def main(args):
|
| + parser = argparse.ArgumentParser(description='Update Cronet api.txt.')
|
| + parser.add_argument('--api_jar',
|
| + help='Path to API jar (i.e. cronet_api.jar)',
|
| + required=True,
|
| + metavar='path/to/cronet_api.jar')
|
| + opts = parser.parse_args(args)
|
| +
|
| + if check_up_to_date(opts.api_jar):
|
| + return True
|
| +
|
| + [_, temp_filename] = tempfile.mkstemp()
|
| + if (generate_api(opts.api_jar, temp_filename) and
|
| + check_api_update(API_FILENAME, temp_filename)):
|
| + # Update API version number to new version number
|
| + with open(API_VERSION_FILENAME,'r+') as f:
|
| + version = int(f.read())
|
| + f.seek(0)
|
| + f.write(str(version + 1))
|
| + # Update API file to new API
|
| + shutil.move(temp_filename, API_FILENAME)
|
| + return True
|
| + os.remove(temp_filename)
|
| + return False
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + sys.exit(0 if main(sys.argv[1:]) else -1)
|
|
|