Ian Zerny | dcb172e | 2022-02-22 15:36:45 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Christoffer Quist Adamsen | 10b7db8 | 2018-12-13 14:50:38 +0100 | [diff] [blame] | 2 | # Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file |
| 3 | # for details. All rights reserved. Use of this source code is governed by a |
| 4 | # BSD-style license that can be found in the LICENSE file. |
| 5 | |
Christoffer Quist Adamsen | 10b7db8 | 2018-12-13 14:50:38 +0100 | [diff] [blame] | 6 | import os |
Christoffer Quist Adamsen | 1d87ca6 | 2019-01-11 12:22:26 +0100 | [diff] [blame] | 7 | import shutil |
Ian Zerny | 161ff74 | 2022-01-20 12:39:40 +0100 | [diff] [blame] | 8 | from distutils.version import LooseVersion |
| 9 | |
| 10 | import utils |
Christoffer Quist Adamsen | 10b7db8 | 2018-12-13 14:50:38 +0100 | [diff] [blame] | 11 | |
Christoffer Quist Adamsen | 5c9ded1 | 2021-01-14 14:29:37 +0100 | [diff] [blame] | 12 | if utils.is_python3(): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 13 | from html.parser import HTMLParser |
Christoffer Quist Adamsen | 5c9ded1 | 2021-01-14 14:29:37 +0100 | [diff] [blame] | 14 | else: |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 15 | from HTMLParser import HTMLParser |
Christoffer Quist Adamsen | 5c9ded1 | 2021-01-14 14:29:37 +0100 | [diff] [blame] | 16 | |
| 17 | |
Morten Krogh-Jespersen | d45d95e | 2019-02-07 13:27:17 +0100 | [diff] [blame] | 18 | def add_r8_dependency(checkout_dir, temp_dir, minified): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 19 | build_file = os.path.join(checkout_dir, 'build.gradle') |
| 20 | assert os.path.isfile(build_file), ( |
| 21 | 'Expected a file to be present at {}'.format(build_file)) |
Christoffer Quist Adamsen | 10b7db8 | 2018-12-13 14:50:38 +0100 | [diff] [blame] | 22 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 23 | with open(build_file) as f: |
| 24 | lines = f.readlines() |
Christoffer Quist Adamsen | 10b7db8 | 2018-12-13 14:50:38 +0100 | [diff] [blame] | 25 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 26 | added_r8_dependency = False |
| 27 | is_inside_dependencies = False |
Christoffer Quist Adamsen | 10b7db8 | 2018-12-13 14:50:38 +0100 | [diff] [blame] | 28 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 29 | with open(build_file, 'w') as f: |
| 30 | gradle_version = None |
| 31 | for line in lines: |
| 32 | stripped = line.strip() |
| 33 | if stripped == 'dependencies {': |
| 34 | assert not is_inside_dependencies, ( |
| 35 | 'Unexpected line with \'dependencies {\'') |
| 36 | is_inside_dependencies = True |
| 37 | if is_inside_dependencies: |
| 38 | if '/r8.jar' in stripped or '/r8lib.jar' in stripped: |
| 39 | # Skip line to avoid dependency on r8.jar |
| 40 | continue |
| 41 | elif 'com.android.tools.build:gradle:' in stripped: |
| 42 | gradle_version = stripped[stripped.rindex(':') + 1:-1] |
| 43 | indent = ''.ljust(line.index('classpath')) |
| 44 | jar = os.path.join(temp_dir, |
| 45 | 'r8lib.jar' if minified else 'r8.jar') |
| 46 | f.write('{}classpath files(\'{}\')\n'.format(indent, jar)) |
| 47 | added_r8_dependency = True |
| 48 | elif stripped == '}': |
| 49 | is_inside_dependencies = False |
| 50 | f.write(line) |
Christoffer Quist Adamsen | 10b7db8 | 2018-12-13 14:50:38 +0100 | [diff] [blame] | 51 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 52 | assert added_r8_dependency, 'Unable to add R8 as a dependency' |
| 53 | assert gradle_version |
| 54 | assert LooseVersion(gradle_version) >= LooseVersion('3.2'), ( |
| 55 | 'Unsupported gradle version: {} (must use at least gradle ' + |
| 56 | 'version 3.2)').format(gradle_version) |
| 57 | |
Christoffer Quist Adamsen | 10b7db8 | 2018-12-13 14:50:38 +0100 | [diff] [blame] | 58 | |
Morten Krogh-Jespersen | de566ea | 2019-02-18 11:59:48 +0100 | [diff] [blame] | 59 | def add_settings_gradle(checkout_dir, name): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 60 | settings_file = os.path.join(checkout_dir, 'settings.gradle') |
| 61 | if os.path.isfile(settings_file): |
| 62 | return |
Morten Krogh-Jespersen | de566ea | 2019-02-18 11:59:48 +0100 | [diff] [blame] | 63 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 64 | with open(settings_file, "w+") as f: |
| 65 | f.write("rootProject.name = '{}'\n".format(name)) |
| 66 | |
Morten Krogh-Jespersen | de566ea | 2019-02-18 11:59:48 +0100 | [diff] [blame] | 67 | |
Christoffer Quist Adamsen | 10b7db8 | 2018-12-13 14:50:38 +0100 | [diff] [blame] | 68 | def remove_r8_dependency(checkout_dir): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 69 | build_file = os.path.join(checkout_dir, 'build.gradle') |
| 70 | assert os.path.isfile(build_file), ( |
| 71 | 'Expected a file to be present at {}'.format(build_file)) |
| 72 | with open(build_file) as f: |
| 73 | lines = f.readlines() |
| 74 | with open(build_file, 'w') as f: |
| 75 | for line in lines: |
| 76 | if ('/r8.jar' not in line) and ('/r8lib.jar' not in line): |
| 77 | f.write(line) |
| 78 | |
Christoffer Quist Adamsen | 1d87ca6 | 2019-01-11 12:22:26 +0100 | [diff] [blame] | 79 | |
Christoffer Quist Adamsen | b899c11 | 2019-03-06 15:25:00 +0100 | [diff] [blame] | 80 | def GetMinAndCompileSdk(app, checkout_dir, apk_reference): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 81 | compile_sdk = app.compile_sdk |
| 82 | min_sdk = app.min_sdk |
Christoffer Quist Adamsen | e59840b | 2019-01-22 15:25:15 +0100 | [diff] [blame] | 83 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 84 | if not compile_sdk or not min_sdk: |
| 85 | build_gradle_file = os.path.join(checkout_dir, app.module, |
| 86 | 'build.gradle') |
| 87 | assert os.path.isfile(build_gradle_file), ( |
| 88 | 'Expected to find build.gradle file at {}'.format(build_gradle_file) |
| 89 | ) |
Christoffer Quist Adamsen | e59840b | 2019-01-22 15:25:15 +0100 | [diff] [blame] | 90 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 91 | # Attempt to find the sdk values from build.gradle. |
| 92 | with open(build_gradle_file) as f: |
| 93 | for line in f.readlines(): |
| 94 | stripped = line.strip() |
| 95 | if stripped.startswith('compileSdkVersion '): |
| 96 | if not app.compile_sdk: |
| 97 | assert not compile_sdk |
| 98 | compile_sdk = int(stripped[len('compileSdkVersion '):]) |
| 99 | elif stripped.startswith('minSdkVersion '): |
| 100 | if not app.min_sdk: |
| 101 | assert not min_sdk |
| 102 | min_sdk = int(stripped[len('minSdkVersion '):]) |
Christoffer Quist Adamsen | e59840b | 2019-01-22 15:25:15 +0100 | [diff] [blame] | 103 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 104 | assert min_sdk, ( |
| 105 | 'Expected to find `minSdkVersion` in {}'.format(build_gradle_file)) |
| 106 | assert compile_sdk, ( |
| 107 | 'Expected to find `compileSdkVersion` in {}'.format(build_gradle_file)) |
Christoffer Quist Adamsen | e59840b | 2019-01-22 15:25:15 +0100 | [diff] [blame] | 108 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 109 | return (min_sdk, compile_sdk) |
| 110 | |
Christoffer Quist Adamsen | e59840b | 2019-01-22 15:25:15 +0100 | [diff] [blame] | 111 | |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 112 | def IsGradleTaskName(x): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 113 | # Check that it is non-empty. |
| 114 | if not x: |
| 115 | return False |
| 116 | # Check that there is no whitespace. |
| 117 | for c in x: |
| 118 | if c.isspace(): |
| 119 | return False |
| 120 | # Check that the first character following an optional ':' is a lower-case |
| 121 | # alphabetic character. |
| 122 | c = x[0] |
| 123 | if c == ':' and len(x) >= 2: |
| 124 | c = x[1] |
| 125 | return c.isalpha() and c.islower() |
| 126 | |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 127 | |
| 128 | def IsGradleCompilerTask(x, shrinker): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 129 | if 'r8' in shrinker: |
| 130 | assert 'transformClassesWithDexBuilderFor' not in x |
| 131 | assert 'transformDexArchiveWithDexMergerFor' not in x |
| 132 | return 'transformClassesAndResourcesWithR8For' in x |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 133 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 134 | assert shrinker == 'pg' |
| 135 | return ('transformClassesAndResourcesWithProguard' in x or |
| 136 | 'transformClassesWithDexBuilderFor' in x or |
| 137 | 'transformDexArchiveWithDexMergerFor' in x) |
| 138 | |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 139 | |
Christoffer Quist Adamsen | 7d74ef7 | 2019-04-12 10:33:38 +0200 | [diff] [blame] | 140 | def ListFiles(directory, predicate=None): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 141 | files = [] |
| 142 | for root, directories, filenames in os.walk(directory): |
| 143 | for filename in filenames: |
| 144 | file = os.path.join(root, filename) |
| 145 | if predicate is None or predicate(file): |
| 146 | files.append(file) |
| 147 | return files |
| 148 | |
Christoffer Quist Adamsen | 7d74ef7 | 2019-04-12 10:33:38 +0200 | [diff] [blame] | 149 | |
Christoffer Quist Adamsen | b899c11 | 2019-03-06 15:25:00 +0100 | [diff] [blame] | 150 | def SetPrintConfigurationDirective(app, checkout_dir, destination): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 151 | proguard_config_file = FindProguardConfigurationFile(app, checkout_dir) |
| 152 | with open(proguard_config_file) as f: |
| 153 | lines = f.readlines() |
| 154 | with open(proguard_config_file, 'w') as f: |
| 155 | for line in lines: |
| 156 | if '-printconfiguration' not in line: |
| 157 | f.write(line) |
| 158 | # Check that there is a line-break at the end of the file or insert one. |
| 159 | if len(lines) and lines[-1].strip(): |
| 160 | f.write('\n') |
| 161 | f.write('-printconfiguration {}\n'.format(destination)) |
| 162 | |
Christoffer Quist Adamsen | 4038bbe | 2019-01-15 14:31:46 +0100 | [diff] [blame] | 163 | |
Christoffer Quist Adamsen | b899c11 | 2019-03-06 15:25:00 +0100 | [diff] [blame] | 164 | def FindProguardConfigurationFile(app, checkout_dir): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 165 | candidates = [ |
| 166 | 'proguard.cfg', 'proguard-rules.pro', 'proguard-rules.txt', |
| 167 | 'proguard-project.txt' |
| 168 | ] |
| 169 | for candidate in candidates: |
| 170 | proguard_config_file = os.path.join(checkout_dir, app.module, candidate) |
| 171 | if os.path.isfile(proguard_config_file): |
| 172 | return proguard_config_file |
| 173 | # Currently assuming that the Proguard configuration file can be found at |
| 174 | # one of the predefined locations. |
| 175 | assert False, 'Unable to find Proguard configuration file' |
| 176 | |
Christoffer Quist Adamsen | 4038bbe | 2019-01-15 14:31:46 +0100 | [diff] [blame] | 177 | |
Christoffer Quist Adamsen | 1de3dde | 2019-01-24 13:17:46 +0100 | [diff] [blame] | 178 | def Move(src, dst, quiet=False): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 179 | if not quiet: |
| 180 | print('Moving `{}` to `{}`'.format(src, dst)) |
| 181 | dst_parent = os.path.dirname(dst) |
| 182 | if not os.path.isdir(dst_parent): |
| 183 | os.makedirs(dst_parent) |
| 184 | elif os.path.isdir(dst): |
| 185 | shutil.rmtree(dst) |
| 186 | elif os.path.isfile(dst): |
| 187 | os.remove(dst) |
| 188 | shutil.move(src, dst) |
| 189 | |
Christoffer Quist Adamsen | 1d87ca6 | 2019-01-11 12:22:26 +0100 | [diff] [blame] | 190 | |
Christoffer Quist Adamsen | 1de3dde | 2019-01-24 13:17:46 +0100 | [diff] [blame] | 191 | def MoveDir(src, dst, quiet=False): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 192 | assert os.path.isdir(src) |
| 193 | Move(src, dst, quiet=quiet) |
| 194 | |
Christoffer Quist Adamsen | 1d87ca6 | 2019-01-11 12:22:26 +0100 | [diff] [blame] | 195 | |
Christoffer Quist Adamsen | 1de3dde | 2019-01-24 13:17:46 +0100 | [diff] [blame] | 196 | def MoveFile(src, dst, quiet=False): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 197 | assert os.path.isfile(src), "Expected a file to be present at " + src |
| 198 | Move(src, dst, quiet=quiet) |
| 199 | |
Christoffer Quist Adamsen | 1d87ca6 | 2019-01-11 12:22:26 +0100 | [diff] [blame] | 200 | |
Christoffer Quist Adamsen | 1de3dde | 2019-01-24 13:17:46 +0100 | [diff] [blame] | 201 | def MoveProfileReportTo(dest_dir, build_stdout, quiet=False): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 202 | html_file = None |
| 203 | profile_message = 'See the profiling report at: ' |
| 204 | # We are not interested in the profiling report for buildSrc. |
| 205 | for line in build_stdout: |
| 206 | if (profile_message in line) and ('buildSrc' not in line): |
| 207 | assert not html_file, "Only one report should be created" |
| 208 | html_file = line[len(profile_message):] |
| 209 | if html_file.startswith('file://'): |
| 210 | html_file = html_file[len('file://'):] |
Christoffer Quist Adamsen | 1d87ca6 | 2019-01-11 12:22:26 +0100 | [diff] [blame] | 211 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 212 | if not html_file: |
| 213 | return |
Christoffer Quist Adamsen | 1d87ca6 | 2019-01-11 12:22:26 +0100 | [diff] [blame] | 214 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 215 | assert os.path.isfile(html_file), 'Expected to find HTML file at {}'.format( |
| 216 | html_file) |
| 217 | MoveFile(html_file, os.path.join(dest_dir, 'index.html'), quiet=quiet) |
Christoffer Quist Adamsen | 1d87ca6 | 2019-01-11 12:22:26 +0100 | [diff] [blame] | 218 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 219 | html_dir = os.path.dirname(html_file) |
| 220 | for dir_name in ['css', 'js']: |
| 221 | MoveDir(os.path.join(html_dir, dir_name), |
| 222 | os.path.join(dest_dir, dir_name), |
| 223 | quiet=quiet) |
| 224 | |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 225 | |
Christoffer Quist Adamsen | 7cf4c56 | 2019-03-07 10:57:33 +0100 | [diff] [blame] | 226 | def MoveXMLTestResultFileTo(xml_test_result_dest, test_stdout, quiet=False): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 227 | xml_test_result_file = None |
| 228 | xml_result_reporter_message = 'XML test result file generated at ' |
| 229 | for line in test_stdout: |
| 230 | if xml_result_reporter_message in line: |
| 231 | index_from = (line.index(xml_result_reporter_message) + |
| 232 | len(xml_result_reporter_message)) |
| 233 | index_to = line.index('.xml') + len('.xml') |
| 234 | xml_test_result_file = line[index_from:index_to] |
| 235 | break |
Christoffer Quist Adamsen | 7cf4c56 | 2019-03-07 10:57:33 +0100 | [diff] [blame] | 236 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 237 | assert os.path.isfile(xml_test_result_file), ( |
| 238 | 'Expected to find XML file at {}'.format(xml_test_result_file)) |
Christoffer Quist Adamsen | 7cf4c56 | 2019-03-07 10:57:33 +0100 | [diff] [blame] | 239 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 240 | MoveFile(xml_test_result_file, xml_test_result_dest, quiet=quiet) |
| 241 | |
Christoffer Quist Adamsen | 7cf4c56 | 2019-03-07 10:57:33 +0100 | [diff] [blame] | 242 | |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 243 | def ParseProfileReport(profile_dir): |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 244 | html_file = os.path.join(profile_dir, 'index.html') |
| 245 | assert os.path.isfile(html_file) |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 246 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 247 | parser = ProfileReportParser() |
| 248 | with open(html_file) as f: |
| 249 | for line in f.readlines(): |
| 250 | parser.feed(line) |
| 251 | return parser.result |
| 252 | |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 253 | |
| 254 | # A simple HTML parser that recognizes the following pattern: |
| 255 | # |
| 256 | # <tr> |
| 257 | # <td class="indentPath">:app:transformClassesAndResourcesWithR8ForRelease</td> |
| 258 | # <td class="numeric">3.490s</td> |
| 259 | # <td></td> |
| 260 | # </tr> |
| 261 | class ProfileReportParser(HTMLParser): |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 262 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 263 | def __init__(self): |
| 264 | HTMLParser.__init__(self) |
| 265 | self.entered_table_row = False |
| 266 | self.entered_task_name_cell = False |
| 267 | self.entered_duration_cell = False |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 268 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 269 | self.current_task_name = None |
| 270 | self.current_duration = None |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 271 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 272 | self.result = {} |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 273 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 274 | def handle_starttag(self, tag, attrs): |
| 275 | entered_table_row_before = self.entered_table_row |
| 276 | entered_task_name_cell_before = self.entered_task_name_cell |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 277 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 278 | self.entered_table_row = (tag == 'tr') |
| 279 | self.entered_task_name_cell = (tag == 'td' and entered_table_row_before) |
| 280 | self.entered_duration_cell = (self.current_task_name and tag == 'td' and |
| 281 | entered_task_name_cell_before) |
Christoffer Quist Adamsen | 81321b6 | 2019-01-15 14:39:53 +0100 | [diff] [blame] | 282 | |
Christoffer Quist Adamsen | 2434a4d | 2023-10-16 11:29:03 +0200 | [diff] [blame] | 283 | def handle_endtag(self, tag): |
| 284 | if tag == 'tr': |
| 285 | if self.current_task_name and self.current_duration: |
| 286 | self.result[self.current_task_name] = self.current_duration |
| 287 | self.current_task_name = None |
| 288 | self.current_duration = None |
| 289 | self.entered_table_row = False |
| 290 | |
| 291 | def handle_data(self, data): |
| 292 | stripped = data.strip() |
| 293 | if not stripped: |
| 294 | return |
| 295 | if self.entered_task_name_cell: |
| 296 | if IsGradleTaskName(stripped): |
| 297 | self.current_task_name = stripped |
| 298 | elif self.entered_duration_cell and stripped.endswith('s'): |
| 299 | duration = stripped[:-1] |
| 300 | if 'm' in duration: |
| 301 | tmp = duration.split('m') |
| 302 | minutes = int(tmp[0]) |
| 303 | seconds = float(tmp[1]) |
| 304 | else: |
| 305 | minutes = 0 |
| 306 | seconds = float(duration) |
| 307 | self.current_duration = 60 * minutes + seconds |
| 308 | self.entered_table_row = False |