OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Gathers information about APKs.""" | 5 """Gathers information about APKs.""" |
6 | 6 |
7 import collections | 7 import collections |
| 8 import logging |
8 import os | 9 import os |
| 10 import pickle |
9 import re | 11 import re |
10 | 12 |
11 import cmd_helper | 13 import cmd_helper |
12 | 14 |
| 15 # If you change the cached output of proguard, increment this number |
| 16 PICKLE_FORMAT_VERSION = 1 |
13 | 17 |
14 def GetPackageNameForApk(apk_path): | 18 def GetPackageNameForApk(apk_path): |
15 """Returns the package name of the apk file.""" | 19 """Returns the package name of the apk file.""" |
16 aapt_output = cmd_helper.GetCmdOutput( | 20 aapt_output = cmd_helper.GetCmdOutput( |
17 ['aapt', 'dump', 'badging', apk_path]).split('\n') | 21 ['aapt', 'dump', 'badging', apk_path]).split('\n') |
18 package_name_re = re.compile(r'package: .*name=\'(\S*)\'') | 22 package_name_re = re.compile(r'package: .*name=\'(\S*)\'') |
19 for line in aapt_output: | 23 for line in aapt_output: |
20 m = package_name_re.match(line) | 24 m = package_name_re.match(line) |
21 if m: | 25 if m: |
22 return m.group(1) | 26 return m.group(1) |
(...skipping 16 matching lines...) Expand all Loading... |
39 re.compile(r'\s*?- Constant element value.*$')) | 43 re.compile(r'\s*?- Constant element value.*$')) |
40 self._PROGUARD_ANNOTATION_VALUE_RE = re.compile(r'\s*?- \S+? \[(.*)\]$') | 44 self._PROGUARD_ANNOTATION_VALUE_RE = re.compile(r'\s*?- \S+? \[(.*)\]$') |
41 | 45 |
42 if not os.path.exists(apk_path): | 46 if not os.path.exists(apk_path): |
43 raise Exception('%s not found, please build it' % apk_path) | 47 raise Exception('%s not found, please build it' % apk_path) |
44 self._apk_path = apk_path | 48 self._apk_path = apk_path |
45 if not os.path.exists(jar_path): | 49 if not os.path.exists(jar_path): |
46 raise Exception('%s not found, please build it' % jar_path) | 50 raise Exception('%s not found, please build it' % jar_path) |
47 self._jar_path = jar_path | 51 self._jar_path = jar_path |
48 self._annotation_map = collections.defaultdict(list) | 52 self._annotation_map = collections.defaultdict(list) |
| 53 self._pickled_proguard_name = self._jar_path + '-proguard.pickle' |
49 self._test_methods = [] | 54 self._test_methods = [] |
50 self._Initialize() | 55 self._Initialize() |
51 | 56 |
52 def _Initialize(self): | 57 def _Initialize(self): |
| 58 if not self._GetCachedProguardData(): |
| 59 self._GetProguardData() |
| 60 |
| 61 def _GetCachedProguardData(self): |
| 62 if (os.path.exists(self._pickled_proguard_name) and |
| 63 (os.path.getmtime(self._pickled_proguard_name) > |
| 64 os.path.getmtime(self._jar_path))): |
| 65 logging.info('Loading cached proguard output from %s', |
| 66 self._pickled_proguard_name) |
| 67 try: |
| 68 with open(self._pickled_proguard_name, 'r') as r: |
| 69 d = pickle.loads(r.read()) |
| 70 if d['VERSION'] == PICKLE_FORMAT_VERSION: |
| 71 self._annotation_map = d['ANNOTATION_MAP'] |
| 72 self._test_methods = d['TEST_METHODS'] |
| 73 return True |
| 74 except: |
| 75 logging.warning('PICKLE_FORMAT_VERSION has changed, ignoring cache') |
| 76 return False |
| 77 |
| 78 def _GetProguardData(self): |
53 proguard_output = cmd_helper.GetCmdOutput([self._PROGUARD_PATH, | 79 proguard_output = cmd_helper.GetCmdOutput([self._PROGUARD_PATH, |
54 '-injars', self._jar_path, | 80 '-injars', self._jar_path, |
55 '-dontshrink', | 81 '-dontshrink', |
56 '-dontoptimize', | 82 '-dontoptimize', |
57 '-dontobfuscate', | 83 '-dontobfuscate', |
58 '-dontpreverify', | 84 '-dontpreverify', |
59 '-dump', | 85 '-dump', |
60 ]).split('\n') | 86 ]).split('\n') |
61 clazz = None | 87 clazz = None |
62 method = None | 88 method = None |
(...skipping 28 matching lines...) Expand all Loading... |
91 if m: | 117 if m: |
92 has_value = True | 118 has_value = True |
93 else: | 119 else: |
94 m = self._PROGUARD_ANNOTATION_VALUE_RE.match(line) | 120 m = self._PROGUARD_ANNOTATION_VALUE_RE.match(line) |
95 if m: | 121 if m: |
96 value = m.group(1) | 122 value = m.group(1) |
97 self._annotation_map[qualified_method].append( | 123 self._annotation_map[qualified_method].append( |
98 annotation + ':' + value) | 124 annotation + ':' + value) |
99 has_value = False | 125 has_value = False |
100 | 126 |
| 127 logging.info('Storing proguard output to %s', self._pickled_proguard_name) |
| 128 d = {'VERSION': PICKLE_FORMAT_VERSION, |
| 129 'ANNOTATION_MAP': self._annotation_map, |
| 130 'TEST_METHODS': self._test_methods} |
| 131 with open(self._pickled_proguard_name, 'w') as f: |
| 132 f.write(pickle.dumps(d)) |
| 133 |
101 def _GetAnnotationMap(self): | 134 def _GetAnnotationMap(self): |
102 return self._annotation_map | 135 return self._annotation_map |
103 | 136 |
104 def _IsTestMethod(self, test): | 137 def _IsTestMethod(self, test): |
105 class_name, method = test.split('#') | 138 class_name, method = test.split('#') |
106 return class_name.endswith('Test') and method.startswith('test') | 139 return class_name.endswith('Test') and method.startswith('test') |
107 | 140 |
108 def GetApkPath(self): | 141 def GetApkPath(self): |
109 return self._apk_path | 142 return self._apk_path |
110 | 143 |
(...skipping 29 matching lines...) Expand all Loading... |
140 if self._IsTestMethod(test) and self._AnnotationsMatchFilters( | 173 if self._IsTestMethod(test) and self._AnnotationsMatchFilters( |
141 annotation_filter_list, annotations)] | 174 annotation_filter_list, annotations)] |
142 | 175 |
143 def GetTestMethods(self): | 176 def GetTestMethods(self): |
144 """Returns a list of all test methods in this apk as Class#testMethod.""" | 177 """Returns a list of all test methods in this apk as Class#testMethod.""" |
145 return self._test_methods | 178 return self._test_methods |
146 | 179 |
147 @staticmethod | 180 @staticmethod |
148 def IsPythonDrivenTest(test): | 181 def IsPythonDrivenTest(test): |
149 return 'pythonDrivenTests' in test | 182 return 'pythonDrivenTests' in test |
OLD | NEW |