| 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 |