OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """api_static_checks.py - Check Cronet implementation does not call through | 6 """api_static_checks.py - Enforce Cronet API requirements.""" |
7 API classes. | |
8 """ | |
9 | 7 |
10 import argparse | 8 import argparse |
11 import os | 9 import os |
12 import re | 10 import re |
13 import shutil | 11 import shutil |
14 import sys | 12 import sys |
15 import tempfile | 13 import tempfile |
16 | 14 |
17 REPOSITORY_ROOT = os.path.abspath(os.path.join( | 15 REPOSITORY_ROOT = os.path.abspath(os.path.join( |
18 os.path.dirname(__file__), '..', '..', '..')) | 16 os.path.dirname(__file__), '..', '..', '..')) |
19 | 17 |
20 sys.path.append(os.path.join(REPOSITORY_ROOT, 'build/android/gyp/util')) | 18 sys.path.append(os.path.join(REPOSITORY_ROOT, 'build/android/gyp/util')) |
21 import build_utils | 19 import build_utils |
22 | 20 |
21 sys.path.append(os.path.join(REPOSITORY_ROOT, 'components/cronet')) | |
22 from tools import update_api | |
23 | |
24 | |
23 # These regular expressions catch the beginning of lines that declare classes | 25 # These regular expressions catch the beginning of lines that declare classes |
24 # and methods. The first group returned by a match is the class or method name. | 26 # and methods. The first group returned by a match is the class or method name. |
25 CLASS_RE = re.compile(r'.*class ([^ ]*) .*\{') | 27 from tools.update_api import CLASS_RE |
26 METHOD_RE = re.compile(r'.* ([^ ]*)\(.*\);') | 28 METHOD_RE = re.compile(r'.* ([^ ]*)\(.*\);') |
27 | 29 |
28 # Allowed exceptions. Adding anything to this list is dangerous and should be | 30 # Allowed exceptions. Adding anything to this list is dangerous and should be |
29 # avoided if possible. For now these exceptions are for APIs that existed in | 31 # avoided if possible. For now these exceptions are for APIs that existed in |
30 # the first version of Cronet and will be supported forever. | 32 # the first version of Cronet and will be supported forever. |
31 # TODO(pauljensen): Remove these. | 33 # TODO(pauljensen): Remove these. |
32 ALLOWED_EXCEPTIONS = [ | 34 ALLOWED_EXCEPTIONS = [ |
33 'org.chromium.net.impl.CronetEngineBuilderImpl/build ->' | 35 'org.chromium.net.impl.CronetEngineBuilderImpl/build ->' |
34 ' org/chromium/net/ExperimentalCronetEngine/getVersionString:' | 36 ' org/chromium/net/ExperimentalCronetEngine/getVersionString:' |
35 '()Ljava/lang/String;', | 37 '()Ljava/lang/String;', |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
94 # Ignore VersionSafe calls | 96 # Ignore VersionSafe calls |
95 if 'VersionSafeCallbacks' in caller_class: | 97 if 'VersionSafeCallbacks' in caller_class: |
96 continue | 98 continue |
97 bad_call = '%s/%s -> %s/%s' % (caller_class, caller_method, | 99 bad_call = '%s/%s -> %s/%s' % (caller_class, caller_method, |
98 callee_class, callee_method) | 100 callee_class, callee_method) |
99 if bad_call in ALLOWED_EXCEPTIONS: | 101 if bad_call in ALLOWED_EXCEPTIONS: |
100 continue | 102 continue |
101 bad_calls += [bad_call] | 103 bad_calls += [bad_call] |
102 | 104 |
103 | 105 |
104 def main(args): | 106 def check_api_calls(opts): |
105 # Returns True if no calls through API classes in implementation. | 107 # Returns True if no calls through API classes in implementation. |
106 | 108 |
107 parser = argparse.ArgumentParser( | |
108 description='Check modules do not contain ARM Neon instructions.') | |
109 parser.add_argument('--api_jar', | |
110 help='Path to API jar (i.e. cronet_api.jar)', | |
111 required=True, | |
112 metavar='path/to/cronet_api.jar') | |
113 parser.add_argument('--impl_jar', | |
114 help='Path to implementation jar ' | |
115 '(i.e. cronet_impl_native_java.jar)', | |
116 required=True, | |
117 metavar='path/to/cronet_impl_native_java.jar', | |
118 action='append') | |
119 parser.add_argument('--stamp', help='Path to touch on success.') | |
120 opts = parser.parse_args(args) | |
121 | |
122 temp_dir = tempfile.mkdtemp() | 109 temp_dir = tempfile.mkdtemp() |
123 | 110 |
124 # Extract API class files from jar | 111 # Extract API class files from jar |
125 jar_cmd = ['jar', 'xf', os.path.abspath(opts.api_jar)] | 112 jar_cmd = ['jar', 'xf', os.path.abspath(opts.api_jar)] |
126 build_utils.CheckOutput(jar_cmd, cwd=temp_dir) | 113 build_utils.CheckOutput(jar_cmd, cwd=temp_dir) |
127 shutil.rmtree(os.path.join(temp_dir, 'META-INF')) | 114 shutil.rmtree(os.path.join(temp_dir, 'META-INF')) |
128 | 115 |
129 # Collect names of API classes | 116 # Collect names of API classes |
130 api_classes = [] | 117 api_classes = [] |
131 for dirpath, _, filenames in os.walk(temp_dir): | 118 for dirpath, _, filenames in os.walk(temp_dir): |
132 if not filenames: | 119 if not filenames: |
133 continue | 120 continue |
134 package = dirpath[len(temp_dir + '/'):] | 121 package = dirpath[len(temp_dir + '/'):] |
kapishnikov
2016/12/28 20:37:35
You probably can use
package = os.path.relpath(di
pauljensen
2017/01/03 15:01:48
Done.
| |
135 if package: | 122 if package: |
136 package += '/' | 123 package += '/' |
137 for filename in filenames: | 124 for filename in filenames: |
138 if filename.endswith('.class'): | 125 if filename.endswith('.class'): |
139 classname = filename[:-len('.class')] | 126 classname = filename[:-len('.class')] |
140 api_classes += [package + classname] | 127 api_classes += [package + classname] |
kapishnikov
2016/12/28 20:37:35
It is better to use os.path.join(package, classnam
pauljensen
2017/01/03 15:01:48
Done, I had to wrap it in os.path.normpath() becau
| |
141 | 128 |
142 shutil.rmtree(temp_dir) | 129 shutil.rmtree(temp_dir) |
143 temp_dir = tempfile.mkdtemp() | 130 temp_dir = tempfile.mkdtemp() |
144 | 131 |
145 # Extract impl class files from jars | 132 # Extract impl class files from jars |
146 for impl_jar in opts.impl_jar: | 133 for impl_jar in opts.impl_jar: |
147 jar_cmd = ['jar', 'xf', os.path.abspath(impl_jar)] | 134 jar_cmd = ['jar', 'xf', os.path.abspath(impl_jar)] |
148 build_utils.CheckOutput(jar_cmd, cwd=temp_dir) | 135 build_utils.CheckOutput(jar_cmd, cwd=temp_dir) |
149 shutil.rmtree(os.path.join(temp_dir, 'META-INF')) | 136 shutil.rmtree(os.path.join(temp_dir, 'META-INF')) |
150 | 137 |
(...skipping 15 matching lines...) Expand all Loading... | |
166 find_api_calls(dump, api_classes, bad_api_calls) | 153 find_api_calls(dump, api_classes, bad_api_calls) |
167 | 154 |
168 shutil.rmtree(temp_dir) | 155 shutil.rmtree(temp_dir) |
169 | 156 |
170 if bad_api_calls: | 157 if bad_api_calls: |
171 print 'ERROR: Found the following calls from implementation classes through' | 158 print 'ERROR: Found the following calls from implementation classes through' |
172 print ' API classes. These could fail if older API is used that' | 159 print ' API classes. These could fail if older API is used that' |
173 print ' does not contain newer methods. Please call through a' | 160 print ' does not contain newer methods. Please call through a' |
174 print ' wrapper class from VersionSafeCallbacks.' | 161 print ' wrapper class from VersionSafeCallbacks.' |
175 print '\n'.join(bad_api_calls) | 162 print '\n'.join(bad_api_calls) |
176 | |
177 if not bad_api_calls and opts.stamp: | |
178 build_utils.Touch(opts.stamp) | |
179 return not bad_api_calls | 163 return not bad_api_calls |
180 | 164 |
181 | 165 |
166 def check_api_version(opts): | |
167 if update_api.check_up_to_date(opts.api_jar): | |
168 return True | |
169 print 'ERROR: API file out of date. Please run this command:' | |
170 print ' components/cronet/tools/update_api.py --api_jar %s' % ( | |
171 os.path.abspath(opts.api_jar)) | |
172 return False | |
173 | |
174 | |
175 def main(args): | |
176 parser = argparse.ArgumentParser( | |
177 description='Enforce Cronet API requirements.') | |
178 parser.add_argument('--api_jar', | |
179 help='Path to API jar (i.e. cronet_api.jar)', | |
180 required=True, | |
181 metavar='path/to/cronet_api.jar') | |
182 parser.add_argument('--impl_jar', | |
183 help='Path to implementation jar ' | |
184 '(i.e. cronet_impl_native_java.jar)', | |
185 required=True, | |
186 metavar='path/to/cronet_impl_native_java.jar', | |
187 action='append') | |
188 parser.add_argument('--stamp', help='Path to touch on success.') | |
189 opts = parser.parse_args(args) | |
190 | |
191 ret = True | |
192 ret = check_api_calls(opts) and ret | |
193 ret = check_api_version(opts) and ret | |
194 if ret and opts.stamp: | |
195 build_utils.Touch(opts.stamp) | |
196 return ret | |
197 | |
198 | |
182 if __name__ == '__main__': | 199 if __name__ == '__main__': |
183 sys.exit(0 if main(sys.argv[1:]) else -1) | 200 sys.exit(0 if main(sys.argv[1:]) else -1) |
OLD | NEW |