Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 1 | // Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | package com.android.tools.apiusagesample; |
| 5 | |
| 6 | import com.android.tools.r8.ArchiveClassFileProvider; |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 7 | import com.android.tools.r8.ArchiveProgramResourceProvider; |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 8 | import com.android.tools.r8.AssertionsConfiguration; |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 9 | import com.android.tools.r8.CompilationFailedException; |
| 10 | import com.android.tools.r8.CompilationMode; |
| 11 | import com.android.tools.r8.DexIndexedConsumer; |
| 12 | import com.android.tools.r8.DiagnosticsHandler; |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 13 | import com.android.tools.r8.ProgramResource; |
| 14 | import com.android.tools.r8.ProgramResource.Kind; |
| 15 | import com.android.tools.r8.ProgramResourceProvider; |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 16 | import com.android.tools.r8.R8; |
| 17 | import com.android.tools.r8.R8Command; |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 18 | import com.android.tools.r8.ResourceException; |
Ian Zerny | de09d14 | 2019-06-07 11:59:19 +0200 | [diff] [blame] | 19 | import com.android.tools.r8.StringConsumer; |
Ian Zerny | 3dd1e4c | 2019-11-26 15:50:43 +0100 | [diff] [blame] | 20 | import com.android.tools.r8.Version; |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 21 | import com.android.tools.r8.origin.ArchiveEntryOrigin; |
| 22 | import com.android.tools.r8.origin.Origin; |
| 23 | import com.android.tools.r8.origin.PathOrigin; |
| 24 | import com.android.tools.r8.utils.StringDiagnostic; |
| 25 | import java.io.ByteArrayOutputStream; |
| 26 | import java.io.IOException; |
| 27 | import java.io.InputStream; |
Ian Zerny | 1b7bb37 | 2020-06-19 12:48:37 +0200 | [diff] [blame] | 28 | import java.lang.reflect.Field; |
| 29 | import java.lang.reflect.Modifier; |
Mads Ager | a26659a | 2018-06-11 08:44:58 +0200 | [diff] [blame] | 30 | import java.nio.charset.StandardCharsets; |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 31 | import java.nio.file.Files; |
| 32 | import java.nio.file.Path; |
| 33 | import java.nio.file.Paths; |
| 34 | import java.util.ArrayList; |
| 35 | import java.util.Collection; |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 36 | import java.util.Collections; |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 37 | import java.util.List; |
| 38 | import java.util.Set; |
| 39 | import java.util.zip.ZipEntry; |
| 40 | import java.util.zip.ZipInputStream; |
| 41 | |
| 42 | public class R8ApiUsageSample { |
| 43 | |
| 44 | private static final Origin origin = |
| 45 | new Origin(Origin.root()) { |
| 46 | @Override |
| 47 | public String part() { |
| 48 | return "R8ApiUsageSample"; |
| 49 | } |
| 50 | }; |
| 51 | |
| 52 | private static final DiagnosticsHandler handler = new D8DiagnosticsHandler(); |
| 53 | |
| 54 | /** |
| 55 | * Example invocation: |
| 56 | * |
| 57 | * <pre> |
| 58 | * java -jar r8-api-uses.jar \ |
| 59 | * --output path/to/output/dir \ |
| 60 | * --min-api minApiLevel \ |
| 61 | * --lib path/to/library.jar \ |
| 62 | * path/to/input{1,2,3}.{jar,class} |
| 63 | * </pre> |
| 64 | */ |
| 65 | public static void main(String[] args) { |
Ian Zerny | 3dd1e4c | 2019-11-26 15:50:43 +0100 | [diff] [blame] | 66 | // Check version API |
| 67 | checkVersionApi(); |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 68 | // Parse arguments with the commandline parser to make use of its API. |
| 69 | R8Command.Builder cmd = R8Command.parse(args, origin); |
| 70 | CompilationMode mode = cmd.getMode(); |
| 71 | Path temp = cmd.getOutputPath(); |
| 72 | int minApiLevel = cmd.getMinApiLevel(); |
| 73 | // The Builder API does not provide access to the concrete paths |
| 74 | // (everything is put into providers) so manually parse them here. |
| 75 | List<Path> libraries = new ArrayList<>(1); |
| 76 | List<Path> mainDexList = new ArrayList<>(1); |
| 77 | List<Path> mainDexRules = new ArrayList<>(1); |
| 78 | List<Path> pgConf = new ArrayList<>(1); |
| 79 | List<Path> inputs = new ArrayList<>(args.length); |
| 80 | for (int i = 0; i < args.length; i++) { |
| 81 | if (args[i].equals("--lib")) { |
| 82 | libraries.add(Paths.get(args[++i])); |
| 83 | } else if (args[i].equals("--main-dex-list")) { |
| 84 | mainDexList.add(Paths.get(args[++i])); |
| 85 | } else if (args[i].equals("--main-dex-rules")) { |
| 86 | mainDexRules.add(Paths.get(args[++i])); |
| 87 | } else if (args[i].equals("--pg-conf")) { |
| 88 | pgConf.add(Paths.get(args[++i])); |
| 89 | } else if (isArchive(args[i]) || isClassFile(args[i])) { |
| 90 | inputs.add(Paths.get(args[i])); |
| 91 | } |
| 92 | } |
| 93 | if (!Files.exists(temp) || !Files.isDirectory(temp)) { |
| 94 | throw new RuntimeException("Must supply a temp/output directory"); |
| 95 | } |
| 96 | if (inputs.isEmpty()) { |
| 97 | throw new RuntimeException("Must supply program inputs"); |
| 98 | } |
| 99 | if (libraries.isEmpty()) { |
| 100 | throw new RuntimeException("Must supply library inputs"); |
| 101 | } |
| 102 | if (mainDexList.isEmpty()) { |
| 103 | throw new RuntimeException("Must supply main-dex-list inputs"); |
| 104 | } |
| 105 | if (mainDexRules.isEmpty()) { |
| 106 | throw new RuntimeException("Must supply main-dex-rules inputs"); |
| 107 | } |
| 108 | if (pgConf.isEmpty()) { |
| 109 | throw new RuntimeException("Must supply pg-conf inputs"); |
| 110 | } |
| 111 | |
Ian Zerny | 1a3ebcd | 2018-01-04 11:06:21 +0100 | [diff] [blame] | 112 | useProgramFileList(CompilationMode.DEBUG, minApiLevel, libraries, inputs); |
| 113 | useProgramFileList(CompilationMode.RELEASE, minApiLevel, libraries, inputs); |
| 114 | useProgramData(minApiLevel, libraries, inputs); |
| 115 | useProgramResourceProvider(minApiLevel, libraries, inputs); |
| 116 | useLibraryResourceProvider(minApiLevel, libraries, inputs); |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 117 | useMainDexListFiles(minApiLevel, libraries, inputs, mainDexList); |
| 118 | useMainDexClasses(minApiLevel, libraries, inputs, mainDexList); |
| 119 | useMainDexRulesFiles(minApiLevel, libraries, inputs, mainDexRules); |
| 120 | useMainDexRules(minApiLevel, libraries, inputs, mainDexRules); |
| 121 | useProguardConfigFiles(minApiLevel, libraries, inputs, mainDexList, pgConf); |
| 122 | useProguardConfigLines(minApiLevel, libraries, inputs, mainDexList, pgConf); |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 123 | useAssertionConfig(minApiLevel, libraries, inputs); |
Ian Zerny | 1a3ebcd | 2018-01-04 11:06:21 +0100 | [diff] [blame] | 124 | useVArgVariants(minApiLevel, libraries, inputs, mainDexList, mainDexRules, pgConf); |
Ian Zerny | de09d14 | 2019-06-07 11:59:19 +0200 | [diff] [blame] | 125 | useProguardConfigConsumers(minApiLevel, libraries, inputs, pgConf); |
| 126 | } |
| 127 | |
| 128 | private static class InMemoryStringConsumer implements StringConsumer { |
| 129 | public String value = null; |
| 130 | |
| 131 | @Override |
| 132 | public void accept(String string, DiagnosticsHandler handler) { |
| 133 | value = string; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | private static void useProguardConfigConsumers( |
| 138 | int minApiLevel, Collection<Path> libraries, Collection<Path> inputs, List<Path> pgConf) { |
| 139 | InMemoryStringConsumer usageConsumer = new InMemoryStringConsumer(); |
| 140 | InMemoryStringConsumer seedsConsumer = new InMemoryStringConsumer(); |
| 141 | InMemoryStringConsumer configConsumer = new InMemoryStringConsumer(); |
| 142 | try { |
| 143 | R8.run( |
| 144 | R8Command.builder(handler) |
| 145 | .setMinApiLevel(minApiLevel) |
| 146 | .setProgramConsumer(new EnsureOutputConsumer()) |
| 147 | .addLibraryFiles(libraries) |
| 148 | .addProgramFiles(inputs) |
| 149 | .addProguardConfigurationFiles(pgConf) |
| 150 | .setProguardUsageConsumer(usageConsumer) |
| 151 | .setProguardSeedsConsumer(seedsConsumer) |
| 152 | .setProguardConfigurationConsumer(configConsumer) |
| 153 | .build()); |
| 154 | } catch (CompilationFailedException e) { |
| 155 | throw new RuntimeException("Unexpected compilation exception", e); |
| 156 | } |
| 157 | if (usageConsumer.value == null) { |
| 158 | throw new RuntimeException("Expected usage info but had none"); |
| 159 | } |
| 160 | if (seedsConsumer.value == null) { |
| 161 | throw new RuntimeException("Expected seeds info but had none"); |
| 162 | } |
| 163 | if (configConsumer.value == null) { |
| 164 | throw new RuntimeException("Expected config info but had none"); |
| 165 | } |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | // Check API support for compiling Java class-files from the file system. |
Ian Zerny | 1a3ebcd | 2018-01-04 11:06:21 +0100 | [diff] [blame] | 169 | private static void useProgramFileList( |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 170 | CompilationMode mode, int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) { |
| 171 | try { |
| 172 | R8.run( |
| 173 | R8Command.builder(handler) |
| 174 | .setMode(mode) |
| 175 | .setMinApiLevel(minApiLevel) |
Søren Gjesse | 22ddf66 | 2018-12-20 09:56:42 +0100 | [diff] [blame] | 176 | .setDisableTreeShaking(true) |
| 177 | .setDisableMinification(true) |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 178 | .setProgramConsumer(new EnsureOutputConsumer()) |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 179 | .addLibraryFiles(libraries) |
| 180 | .addProgramFiles(inputs) |
| 181 | .build()); |
| 182 | } catch (CompilationFailedException e) { |
| 183 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | // Check API support for compiling Java class-files from byte content. |
Ian Zerny | 1a3ebcd | 2018-01-04 11:06:21 +0100 | [diff] [blame] | 188 | private static void useProgramData( |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 189 | int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) { |
| 190 | try { |
| 191 | R8Command.Builder builder = |
| 192 | R8Command.builder(handler) |
| 193 | .setMinApiLevel(minApiLevel) |
Søren Gjesse | 22ddf66 | 2018-12-20 09:56:42 +0100 | [diff] [blame] | 194 | .setDisableTreeShaking(true) |
| 195 | .setDisableMinification(true) |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 196 | .setProgramConsumer(new EnsureOutputConsumer()) |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 197 | .addLibraryFiles(libraries); |
| 198 | for (ClassFileContent classfile : readClassFiles(inputs)) { |
| 199 | builder.addClassProgramData(classfile.data, classfile.origin); |
| 200 | } |
| 201 | R8.run(builder.build()); |
| 202 | } catch (CompilationFailedException e) { |
| 203 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 204 | } catch (IOException e) { |
| 205 | throw new RuntimeException("Unexpected IO exception", e); |
| 206 | } |
| 207 | } |
| 208 | |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 209 | // Check API support for compiling Java class-files from a program provider abstraction. |
Ian Zerny | 1a3ebcd | 2018-01-04 11:06:21 +0100 | [diff] [blame] | 210 | private static void useProgramResourceProvider( |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 211 | int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) { |
| 212 | try { |
| 213 | R8Command.Builder builder = |
| 214 | R8Command.builder(handler) |
| 215 | .setMinApiLevel(minApiLevel) |
Søren Gjesse | 22ddf66 | 2018-12-20 09:56:42 +0100 | [diff] [blame] | 216 | .setDisableTreeShaking(true) |
| 217 | .setDisableMinification(true) |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 218 | .setProgramConsumer(new EnsureOutputConsumer()) |
| 219 | .addLibraryFiles(libraries); |
| 220 | for (Path input : inputs) { |
| 221 | if (isArchive(input)) { |
| 222 | builder.addProgramResourceProvider( |
| 223 | ArchiveProgramResourceProvider.fromArchive( |
| 224 | input, ArchiveProgramResourceProvider::includeClassFileEntries)); |
| 225 | } else { |
| 226 | builder.addProgramResourceProvider( |
| 227 | new ProgramResourceProvider() { |
| 228 | @Override |
| 229 | public Collection<ProgramResource> getProgramResources() throws ResourceException { |
| 230 | return Collections.singleton(ProgramResource.fromFile(Kind.CF, input)); |
| 231 | } |
| 232 | }); |
| 233 | } |
| 234 | } |
| 235 | R8.run(builder.build()); |
| 236 | } catch (CompilationFailedException e) { |
| 237 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 238 | } |
| 239 | } |
| 240 | |
Ian Zerny | 1a3ebcd | 2018-01-04 11:06:21 +0100 | [diff] [blame] | 241 | private static void useLibraryResourceProvider( |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 242 | int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) { |
| 243 | try { |
| 244 | R8Command.Builder builder = |
| 245 | R8Command.builder(handler) |
| 246 | .setMinApiLevel(minApiLevel) |
Søren Gjesse | 22ddf66 | 2018-12-20 09:56:42 +0100 | [diff] [blame] | 247 | .setDisableTreeShaking(true) |
| 248 | .setDisableMinification(true) |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 249 | .setProgramConsumer(new EnsureOutputConsumer()) |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 250 | .addProgramFiles(inputs); |
| 251 | for (Path library : libraries) { |
| 252 | builder.addLibraryResourceProvider(new ArchiveClassFileProvider(library)); |
| 253 | } |
| 254 | R8.run(builder.build()); |
| 255 | } catch (CompilationFailedException e) { |
| 256 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 257 | } catch (IOException e) { |
| 258 | throw new RuntimeException("Unexpected IO exception", e); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | private static void useMainDexListFiles( |
Ian Zerny | 7d1591f | 2018-01-03 12:38:07 +0100 | [diff] [blame] | 263 | int minApiLevel, |
| 264 | Collection<Path> libraries, |
| 265 | Collection<Path> inputs, |
| 266 | Collection<Path> mainDexList) { |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 267 | try { |
| 268 | R8.run( |
| 269 | R8Command.builder(handler) |
| 270 | .setMinApiLevel(minApiLevel) |
Søren Gjesse | 22ddf66 | 2018-12-20 09:56:42 +0100 | [diff] [blame] | 271 | .setDisableTreeShaking(true) |
| 272 | .setDisableMinification(true) |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 273 | .setProgramConsumer(new EnsureOutputConsumer()) |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 274 | .addLibraryFiles(libraries) |
| 275 | .addProgramFiles(inputs) |
| 276 | .addMainDexListFiles(mainDexList) |
| 277 | .build()); |
| 278 | } catch (CompilationFailedException e) { |
| 279 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | private static void useMainDexClasses( |
Ian Zerny | 7d1591f | 2018-01-03 12:38:07 +0100 | [diff] [blame] | 284 | int minApiLevel, |
| 285 | Collection<Path> libraries, |
| 286 | Collection<Path> inputs, |
| 287 | Collection<Path> mainDexList) { |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 288 | try { |
| 289 | List<String> mainDexClasses = new ArrayList<>(1); |
| 290 | for (Path path : mainDexList) { |
| 291 | for (String line : Files.readAllLines(path)) { |
| 292 | String entry = line.trim(); |
| 293 | if (entry.isEmpty() || entry.startsWith("#") || !entry.endsWith(".class")) { |
| 294 | continue; |
| 295 | } |
| 296 | mainDexClasses.add(entry.replace(".class", "").replace("/", ".")); |
| 297 | } |
| 298 | } |
| 299 | R8.run( |
| 300 | R8Command.builder(handler) |
| 301 | .setMinApiLevel(minApiLevel) |
Søren Gjesse | 22ddf66 | 2018-12-20 09:56:42 +0100 | [diff] [blame] | 302 | .setDisableTreeShaking(true) |
| 303 | .setDisableMinification(true) |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 304 | .setProgramConsumer(new EnsureOutputConsumer()) |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 305 | .addLibraryFiles(libraries) |
| 306 | .addProgramFiles(inputs) |
| 307 | .addMainDexClasses(mainDexClasses) |
| 308 | .build()); |
| 309 | } catch (CompilationFailedException e) { |
| 310 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 311 | } catch (IOException e) { |
| 312 | throw new RuntimeException("Unexpected IO exception", e); |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | private static void useMainDexRulesFiles( |
Ian Zerny | 7d1591f | 2018-01-03 12:38:07 +0100 | [diff] [blame] | 317 | int minApiLevel, |
| 318 | Collection<Path> libraries, |
| 319 | Collection<Path> inputs, |
| 320 | Collection<Path> mainDexRules) { |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 321 | try { |
| 322 | R8.run( |
| 323 | R8Command.builder(handler) |
| 324 | .setMinApiLevel(minApiLevel) |
Søren Gjesse | 22ddf66 | 2018-12-20 09:56:42 +0100 | [diff] [blame] | 325 | .setDisableTreeShaking(true) |
| 326 | .setDisableMinification(true) |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 327 | .setProgramConsumer(new EnsureOutputConsumer()) |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 328 | .addLibraryFiles(libraries) |
| 329 | .addProgramFiles(inputs) |
| 330 | .addMainDexRulesFiles(mainDexRules) |
| 331 | .build()); |
| 332 | } catch (CompilationFailedException e) { |
| 333 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | private static void useMainDexRules( |
Ian Zerny | 7d1591f | 2018-01-03 12:38:07 +0100 | [diff] [blame] | 338 | int minApiLevel, |
| 339 | Collection<Path> libraries, |
| 340 | Collection<Path> inputs, |
| 341 | Collection<Path> mainDexRulesFiles) { |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 342 | try { |
| 343 | R8Command.Builder builder = |
| 344 | R8Command.builder(handler) |
| 345 | .setMinApiLevel(minApiLevel) |
Søren Gjesse | 22ddf66 | 2018-12-20 09:56:42 +0100 | [diff] [blame] | 346 | .setDisableTreeShaking(true) |
| 347 | .setDisableMinification(true) |
Ian Zerny | 7e2e18d | 2018-01-03 15:15:37 +0100 | [diff] [blame] | 348 | .setProgramConsumer(new EnsureOutputConsumer()) |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 349 | .addLibraryFiles(libraries) |
| 350 | .addProgramFiles(inputs); |
| 351 | for (Path mainDexRulesFile : mainDexRulesFiles) { |
| 352 | builder.addMainDexRules( |
| 353 | Files.readAllLines(mainDexRulesFile), new PathOrigin(mainDexRulesFile)); |
| 354 | } |
| 355 | R8.run(builder.build()); |
| 356 | } catch (CompilationFailedException e) { |
| 357 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 358 | } catch (IOException e) { |
| 359 | throw new RuntimeException("Unexpected IO exception", e); |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | private static void useProguardConfigFiles( |
| 364 | int minApiLevel, |
Ian Zerny | 7d1591f | 2018-01-03 12:38:07 +0100 | [diff] [blame] | 365 | Collection<Path> libraries, |
| 366 | Collection<Path> inputs, |
| 367 | Collection<Path> mainDexList, |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 368 | List<Path> pgConf) { |
| 369 | try { |
| 370 | R8.run( |
| 371 | R8Command.builder(handler) |
| 372 | .setMinApiLevel(minApiLevel) |
| 373 | .setProgramConsumer(new EnsureOutputConsumer()) |
| 374 | .addLibraryFiles(libraries) |
| 375 | .addProgramFiles(inputs) |
| 376 | .addMainDexListFiles(mainDexList) |
| 377 | .addProguardConfigurationFiles(pgConf) |
| 378 | .build()); |
| 379 | } catch (CompilationFailedException e) { |
| 380 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | private static void useProguardConfigLines( |
| 385 | int minApiLevel, |
Ian Zerny | 7d1591f | 2018-01-03 12:38:07 +0100 | [diff] [blame] | 386 | Collection<Path> libraries, |
| 387 | Collection<Path> inputs, |
| 388 | Collection<Path> mainDexList, |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 389 | List<Path> pgConf) { |
| 390 | try { |
| 391 | R8Command.Builder builder = |
| 392 | R8Command.builder(handler) |
| 393 | .setMinApiLevel(minApiLevel) |
| 394 | .setProgramConsumer(new EnsureOutputConsumer()) |
| 395 | .addLibraryFiles(libraries) |
| 396 | .addProgramFiles(inputs) |
| 397 | .addMainDexListFiles(mainDexList); |
| 398 | for (Path file : pgConf) { |
| 399 | builder.addProguardConfiguration(Files.readAllLines(file), new PathOrigin(file)); |
| 400 | } |
| 401 | R8.run(builder.build()); |
| 402 | } catch (CompilationFailedException e) { |
| 403 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 404 | } catch (IOException e) { |
| 405 | throw new RuntimeException("Unexpected IO exception", e); |
| 406 | } |
| 407 | } |
| 408 | |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 409 | private static void useAssertionConfig( |
| 410 | int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) { |
| 411 | try { |
| 412 | R8.run( |
| 413 | R8Command.builder(handler) |
Ian Zerny | faa17d3 | 2020-06-19 12:15:50 +0200 | [diff] [blame] | 414 | .setDisableTreeShaking(true) |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 415 | .setMinApiLevel(minApiLevel) |
Ian Zerny | faa17d3 | 2020-06-19 12:15:50 +0200 | [diff] [blame] | 416 | .setProgramConsumer(new EnsureOutputConsumer()) |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 417 | .addLibraryFiles(libraries) |
| 418 | .addProgramFiles(inputs) |
Søren Gjesse | c393db5 | 2022-04-28 09:45:29 +0200 | [diff] [blame] | 419 | .addAssertionsConfiguration(b -> b.setScopeAll().setCompileTimeEnable().build()) |
| 420 | .addAssertionsConfiguration(b -> b.setScopeAll().setCompileTimeDisable().build()) |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 421 | .addAssertionsConfiguration( |
Søren Gjesse | c393db5 | 2022-04-28 09:45:29 +0200 | [diff] [blame] | 422 | b -> |
| 423 | b.setScopePackage("com.android.tools.apiusagesample") |
| 424 | .setCompileTimeEnable() |
| 425 | .build()) |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 426 | .addAssertionsConfiguration( |
| 427 | b -> |
| 428 | b.setScopePackage("com.android.tools.apiusagesample") |
| 429 | .setPassthrough() |
| 430 | .build()) |
| 431 | .addAssertionsConfiguration( |
Søren Gjesse | c393db5 | 2022-04-28 09:45:29 +0200 | [diff] [blame] | 432 | b -> |
| 433 | b.setScopePackage("com.android.tools.apiusagesample") |
| 434 | .setCompileTimeDisable() |
| 435 | .build()) |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 436 | .addAssertionsConfiguration( |
| 437 | b -> |
| 438 | b.setScopeClass("com.android.tools.apiusagesample.D8ApiUsageSample") |
Søren Gjesse | c393db5 | 2022-04-28 09:45:29 +0200 | [diff] [blame] | 439 | .setCompileTimeEnable() |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 440 | .build()) |
| 441 | .addAssertionsConfiguration( |
| 442 | b -> |
| 443 | b.setScopeClass("com.android.tools.apiusagesample.D8ApiUsageSample") |
| 444 | .setPassthrough() |
| 445 | .build()) |
| 446 | .addAssertionsConfiguration( |
| 447 | b -> |
| 448 | b.setScopeClass("com.android.tools.apiusagesample.D8ApiUsageSample") |
Søren Gjesse | c393db5 | 2022-04-28 09:45:29 +0200 | [diff] [blame] | 449 | .setCompileTimeDisable() |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 450 | .build()) |
Søren Gjesse | c393db5 | 2022-04-28 09:45:29 +0200 | [diff] [blame] | 451 | .addAssertionsConfiguration( |
| 452 | AssertionsConfiguration.Builder::compileTimeEnableAllAssertions) |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 453 | .addAssertionsConfiguration(AssertionsConfiguration.Builder::passthroughAllAssertions) |
Søren Gjesse | c393db5 | 2022-04-28 09:45:29 +0200 | [diff] [blame] | 454 | .addAssertionsConfiguration( |
| 455 | AssertionsConfiguration.Builder::compileTimeDisableAllAssertions) |
Søren Gjesse | 33a8c5b | 2020-01-14 12:58:13 +0100 | [diff] [blame] | 456 | .build()); |
| 457 | } catch (CompilationFailedException e) { |
| 458 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 459 | } |
| 460 | } |
| 461 | |
Ian Zerny | 1a3ebcd | 2018-01-04 11:06:21 +0100 | [diff] [blame] | 462 | // Check API support for all the varg variants. |
| 463 | private static void useVArgVariants( |
| 464 | int minApiLevel, |
| 465 | List<Path> libraries, |
| 466 | List<Path> inputs, |
| 467 | List<Path> mainDexList, |
| 468 | List<Path> mainDexRules, |
| 469 | List<Path> pgConf) { |
| 470 | try { |
| 471 | R8.run( |
| 472 | R8Command.builder(handler) |
| 473 | .setMinApiLevel(minApiLevel) |
| 474 | .setProgramConsumer(new EnsureOutputConsumer()) |
| 475 | .addLibraryFiles(libraries.get(0)) |
| 476 | .addLibraryFiles(libraries.stream().skip(1).toArray(Path[]::new)) |
| 477 | .addProgramFiles(inputs.get(0)) |
| 478 | .addProgramFiles(inputs.stream().skip(1).toArray(Path[]::new)) |
| 479 | .addMainDexListFiles(mainDexList.get(0)) |
| 480 | .addMainDexListFiles(mainDexList.stream().skip(1).toArray(Path[]::new)) |
| 481 | .addMainDexRulesFiles(mainDexRules.get(0)) |
| 482 | .addMainDexRulesFiles(mainDexRules.stream().skip(1).toArray(Path[]::new)) |
| 483 | .addProguardConfigurationFiles(pgConf.get(0)) |
| 484 | .addProguardConfigurationFiles(pgConf.stream().skip(1).toArray(Path[]::new)) |
| 485 | .build()); |
| 486 | } catch (CompilationFailedException e) { |
| 487 | throw new RuntimeException("Unexpected compilation exceptions", e); |
| 488 | } |
| 489 | } |
| 490 | |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 491 | // Helpers for tests. |
| 492 | // Some of this reimplements stuff in R8 utils, but that is not public API and we should not |
| 493 | // rely on it. |
| 494 | |
| 495 | private static List<ClassFileContent> readClassFiles(Collection<Path> files) throws IOException { |
| 496 | List<ClassFileContent> classfiles = new ArrayList<>(); |
| 497 | for (Path file : files) { |
| 498 | if (isArchive(file)) { |
| 499 | Origin zipOrigin = new PathOrigin(file); |
Mads Ager | a26659a | 2018-06-11 08:44:58 +0200 | [diff] [blame] | 500 | ZipInputStream zip = new ZipInputStream(Files.newInputStream(file), StandardCharsets.UTF_8); |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 501 | ZipEntry entry; |
| 502 | while (null != (entry = zip.getNextEntry())) { |
Mads Ager | 45e6e7b | 2018-06-15 11:28:17 +0200 | [diff] [blame] | 503 | String name = entry.getName(); |
| 504 | if (isClassFile(name)) { |
| 505 | Origin origin = new ArchiveEntryOrigin(name, zipOrigin); |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 506 | classfiles.add(new ClassFileContent(origin, readBytes(zip))); |
| 507 | } |
| 508 | } |
| 509 | } else if (isClassFile(file)) { |
| 510 | classfiles.add(new ClassFileContent(new PathOrigin(file), Files.readAllBytes(file))); |
| 511 | } |
| 512 | } |
| 513 | return classfiles; |
| 514 | } |
| 515 | |
| 516 | private static byte[] readBytes(InputStream stream) throws IOException { |
| 517 | try (ByteArrayOutputStream bytes = new ByteArrayOutputStream()) { |
| 518 | byte[] buffer = new byte[0xffff]; |
| 519 | for (int length; (length = stream.read(buffer)) != -1; ) { |
| 520 | bytes.write(buffer, 0, length); |
| 521 | } |
| 522 | return bytes.toByteArray(); |
| 523 | } |
| 524 | } |
| 525 | |
| 526 | private static boolean isClassFile(Path file) { |
| 527 | return isClassFile(file.toString()); |
| 528 | } |
| 529 | |
| 530 | private static boolean isClassFile(String file) { |
| 531 | file = file.toLowerCase(); |
| 532 | return file.endsWith(".class"); |
| 533 | } |
| 534 | |
| 535 | private static boolean isArchive(Path file) { |
| 536 | return isArchive(file.toString()); |
| 537 | } |
| 538 | |
| 539 | private static boolean isArchive(String file) { |
| 540 | file = file.toLowerCase(); |
| 541 | return file.endsWith(".zip") || file.endsWith(".jar"); |
| 542 | } |
| 543 | |
| 544 | private static class ClassFileContent { |
| 545 | final Origin origin; |
| 546 | final byte[] data; |
| 547 | |
| 548 | public ClassFileContent(Origin origin, byte[] data) { |
| 549 | this.origin = origin; |
| 550 | this.data = data; |
| 551 | } |
| 552 | } |
| 553 | |
| 554 | private static class EnsureOutputConsumer implements DexIndexedConsumer { |
| 555 | boolean hasOutput = false; |
| 556 | |
| 557 | @Override |
| 558 | public synchronized void accept( |
| 559 | int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) { |
| 560 | hasOutput = true; |
| 561 | } |
| 562 | |
| 563 | @Override |
| 564 | public void finished(DiagnosticsHandler handler) { |
| 565 | if (!hasOutput) { |
| 566 | handler.error(new StringDiagnostic("Expected to produce output but had none")); |
| 567 | } |
| 568 | } |
| 569 | } |
Ian Zerny | 3dd1e4c | 2019-11-26 15:50:43 +0100 | [diff] [blame] | 570 | |
| 571 | private static void checkVersionApi() { |
Ian Zerny | 1b7bb37 | 2020-06-19 12:48:37 +0200 | [diff] [blame] | 572 | String labelValue; |
| 573 | int labelAccess; |
| 574 | try { |
| 575 | Field field = Version.class.getDeclaredField("LABEL"); |
| 576 | labelAccess = field.getModifiers(); |
| 577 | labelValue = (String) field.get(Version.class); |
| 578 | } catch (Exception e) { |
| 579 | throw new RuntimeException(e); |
| 580 | } |
| 581 | if (!Modifier.isPublic(labelAccess) |
| 582 | || !Modifier.isStatic(labelAccess) |
| 583 | || !Modifier.isFinal(labelAccess)) { |
| 584 | throw new RuntimeException("Expected public static final LABEL"); |
| 585 | } |
| 586 | if (labelValue.isEmpty()) { |
| 587 | throw new RuntimeException("Expected LABEL constant"); |
| 588 | } |
| 589 | if (Version.LABEL.isEmpty()) { |
| 590 | throw new RuntimeException("Expected LABEL constant"); |
| 591 | } |
Ian Zerny | 3dd1e4c | 2019-11-26 15:50:43 +0100 | [diff] [blame] | 592 | if (Version.getVersionString() == null) { |
| 593 | throw new RuntimeException("Expected getVersionString API"); |
| 594 | } |
| 595 | if (Version.getMajorVersion() < -1) { |
| 596 | throw new RuntimeException("Expected getMajorVersion API"); |
| 597 | } |
| 598 | if (Version.getMinorVersion() < -1) { |
| 599 | throw new RuntimeException("Expected getMinorVersion API"); |
| 600 | } |
| 601 | if (Version.getPatchVersion() < -1) { |
| 602 | throw new RuntimeException("Expected getPatchVersion API"); |
| 603 | } |
| 604 | if (Version.getPreReleaseString() == null && false) { |
| 605 | throw new RuntimeException("Expected getPreReleaseString API"); |
| 606 | } |
| 607 | if (Version.isDevelopmentVersion() && false) { |
| 608 | throw new RuntimeException("Expected isDevelopmentVersion API"); |
| 609 | } |
| 610 | } |
Ian Zerny | 923a0c1 | 2018-01-03 10:59:18 +0100 | [diff] [blame] | 611 | } |