blob: 4b86cea5cbc275ba7bb1f4980aad724eb06bfefb [file] [log] [blame]
Ian Zerny923a0c12018-01-03 10:59:18 +01001// 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.
4package com.android.tools.apiusagesample;
5
6import com.android.tools.r8.ArchiveClassFileProvider;
Ian Zerny7e2e18d2018-01-03 15:15:37 +01007import com.android.tools.r8.ArchiveProgramResourceProvider;
Søren Gjesse33a8c5b2020-01-14 12:58:13 +01008import com.android.tools.r8.AssertionsConfiguration;
Ian Zerny923a0c12018-01-03 10:59:18 +01009import com.android.tools.r8.CompilationFailedException;
10import com.android.tools.r8.CompilationMode;
11import com.android.tools.r8.DexIndexedConsumer;
12import com.android.tools.r8.DiagnosticsHandler;
Ian Zerny7e2e18d2018-01-03 15:15:37 +010013import com.android.tools.r8.ProgramResource;
14import com.android.tools.r8.ProgramResource.Kind;
15import com.android.tools.r8.ProgramResourceProvider;
Ian Zerny923a0c12018-01-03 10:59:18 +010016import com.android.tools.r8.R8;
17import com.android.tools.r8.R8Command;
Ian Zerny7e2e18d2018-01-03 15:15:37 +010018import com.android.tools.r8.ResourceException;
Ian Zernyde09d142019-06-07 11:59:19 +020019import com.android.tools.r8.StringConsumer;
Ian Zerny3dd1e4c2019-11-26 15:50:43 +010020import com.android.tools.r8.Version;
Ian Zerny923a0c12018-01-03 10:59:18 +010021import com.android.tools.r8.origin.ArchiveEntryOrigin;
22import com.android.tools.r8.origin.Origin;
23import com.android.tools.r8.origin.PathOrigin;
24import com.android.tools.r8.utils.StringDiagnostic;
25import java.io.ByteArrayOutputStream;
26import java.io.IOException;
27import java.io.InputStream;
Ian Zerny1b7bb372020-06-19 12:48:37 +020028import java.lang.reflect.Field;
29import java.lang.reflect.Modifier;
Mads Agera26659a2018-06-11 08:44:58 +020030import java.nio.charset.StandardCharsets;
Ian Zerny923a0c12018-01-03 10:59:18 +010031import java.nio.file.Files;
32import java.nio.file.Path;
33import java.nio.file.Paths;
34import java.util.ArrayList;
35import java.util.Collection;
Ian Zerny7e2e18d2018-01-03 15:15:37 +010036import java.util.Collections;
Ian Zerny923a0c12018-01-03 10:59:18 +010037import java.util.List;
38import java.util.Set;
39import java.util.zip.ZipEntry;
40import java.util.zip.ZipInputStream;
41
42public 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 Zerny3dd1e4c2019-11-26 15:50:43 +010066 // Check version API
67 checkVersionApi();
Ian Zerny923a0c12018-01-03 10:59:18 +010068 // 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 Zerny1a3ebcd2018-01-04 11:06:21 +0100112 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 Zerny923a0c12018-01-03 10:59:18 +0100117 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 Gjesse33a8c5b2020-01-14 12:58:13 +0100123 useAssertionConfig(minApiLevel, libraries, inputs);
Ian Zerny1a3ebcd2018-01-04 11:06:21 +0100124 useVArgVariants(minApiLevel, libraries, inputs, mainDexList, mainDexRules, pgConf);
Ian Zernyde09d142019-06-07 11:59:19 +0200125 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 Zerny923a0c12018-01-03 10:59:18 +0100166 }
167
168 // Check API support for compiling Java class-files from the file system.
Ian Zerny1a3ebcd2018-01-04 11:06:21 +0100169 private static void useProgramFileList(
Ian Zerny923a0c12018-01-03 10:59:18 +0100170 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 Gjesse22ddf662018-12-20 09:56:42 +0100176 .setDisableTreeShaking(true)
177 .setDisableMinification(true)
Ian Zerny7e2e18d2018-01-03 15:15:37 +0100178 .setProgramConsumer(new EnsureOutputConsumer())
Ian Zerny923a0c12018-01-03 10:59:18 +0100179 .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 Zerny1a3ebcd2018-01-04 11:06:21 +0100188 private static void useProgramData(
Ian Zerny923a0c12018-01-03 10:59:18 +0100189 int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) {
190 try {
191 R8Command.Builder builder =
192 R8Command.builder(handler)
193 .setMinApiLevel(minApiLevel)
Søren Gjesse22ddf662018-12-20 09:56:42 +0100194 .setDisableTreeShaking(true)
195 .setDisableMinification(true)
Ian Zerny7e2e18d2018-01-03 15:15:37 +0100196 .setProgramConsumer(new EnsureOutputConsumer())
Ian Zerny923a0c12018-01-03 10:59:18 +0100197 .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 Zerny7e2e18d2018-01-03 15:15:37 +0100209 // Check API support for compiling Java class-files from a program provider abstraction.
Ian Zerny1a3ebcd2018-01-04 11:06:21 +0100210 private static void useProgramResourceProvider(
Ian Zerny7e2e18d2018-01-03 15:15:37 +0100211 int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) {
212 try {
213 R8Command.Builder builder =
214 R8Command.builder(handler)
215 .setMinApiLevel(minApiLevel)
Søren Gjesse22ddf662018-12-20 09:56:42 +0100216 .setDisableTreeShaking(true)
217 .setDisableMinification(true)
Ian Zerny7e2e18d2018-01-03 15:15:37 +0100218 .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 Zerny1a3ebcd2018-01-04 11:06:21 +0100241 private static void useLibraryResourceProvider(
Ian Zerny923a0c12018-01-03 10:59:18 +0100242 int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) {
243 try {
244 R8Command.Builder builder =
245 R8Command.builder(handler)
246 .setMinApiLevel(minApiLevel)
Søren Gjesse22ddf662018-12-20 09:56:42 +0100247 .setDisableTreeShaking(true)
248 .setDisableMinification(true)
Ian Zerny7e2e18d2018-01-03 15:15:37 +0100249 .setProgramConsumer(new EnsureOutputConsumer())
Ian Zerny923a0c12018-01-03 10:59:18 +0100250 .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 Zerny7d1591f2018-01-03 12:38:07 +0100263 int minApiLevel,
264 Collection<Path> libraries,
265 Collection<Path> inputs,
266 Collection<Path> mainDexList) {
Ian Zerny923a0c12018-01-03 10:59:18 +0100267 try {
268 R8.run(
269 R8Command.builder(handler)
270 .setMinApiLevel(minApiLevel)
Søren Gjesse22ddf662018-12-20 09:56:42 +0100271 .setDisableTreeShaking(true)
272 .setDisableMinification(true)
Ian Zerny7e2e18d2018-01-03 15:15:37 +0100273 .setProgramConsumer(new EnsureOutputConsumer())
Ian Zerny923a0c12018-01-03 10:59:18 +0100274 .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 Zerny7d1591f2018-01-03 12:38:07 +0100284 int minApiLevel,
285 Collection<Path> libraries,
286 Collection<Path> inputs,
287 Collection<Path> mainDexList) {
Ian Zerny923a0c12018-01-03 10:59:18 +0100288 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 Gjesse22ddf662018-12-20 09:56:42 +0100302 .setDisableTreeShaking(true)
303 .setDisableMinification(true)
Ian Zerny7e2e18d2018-01-03 15:15:37 +0100304 .setProgramConsumer(new EnsureOutputConsumer())
Ian Zerny923a0c12018-01-03 10:59:18 +0100305 .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 Zerny7d1591f2018-01-03 12:38:07 +0100317 int minApiLevel,
318 Collection<Path> libraries,
319 Collection<Path> inputs,
320 Collection<Path> mainDexRules) {
Ian Zerny923a0c12018-01-03 10:59:18 +0100321 try {
322 R8.run(
323 R8Command.builder(handler)
324 .setMinApiLevel(minApiLevel)
Søren Gjesse22ddf662018-12-20 09:56:42 +0100325 .setDisableTreeShaking(true)
326 .setDisableMinification(true)
Ian Zerny7e2e18d2018-01-03 15:15:37 +0100327 .setProgramConsumer(new EnsureOutputConsumer())
Ian Zerny923a0c12018-01-03 10:59:18 +0100328 .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 Zerny7d1591f2018-01-03 12:38:07 +0100338 int minApiLevel,
339 Collection<Path> libraries,
340 Collection<Path> inputs,
341 Collection<Path> mainDexRulesFiles) {
Ian Zerny923a0c12018-01-03 10:59:18 +0100342 try {
343 R8Command.Builder builder =
344 R8Command.builder(handler)
345 .setMinApiLevel(minApiLevel)
Søren Gjesse22ddf662018-12-20 09:56:42 +0100346 .setDisableTreeShaking(true)
347 .setDisableMinification(true)
Ian Zerny7e2e18d2018-01-03 15:15:37 +0100348 .setProgramConsumer(new EnsureOutputConsumer())
Ian Zerny923a0c12018-01-03 10:59:18 +0100349 .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 Zerny7d1591f2018-01-03 12:38:07 +0100365 Collection<Path> libraries,
366 Collection<Path> inputs,
367 Collection<Path> mainDexList,
Ian Zerny923a0c12018-01-03 10:59:18 +0100368 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 Zerny7d1591f2018-01-03 12:38:07 +0100386 Collection<Path> libraries,
387 Collection<Path> inputs,
388 Collection<Path> mainDexList,
Ian Zerny923a0c12018-01-03 10:59:18 +0100389 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 Gjesse33a8c5b2020-01-14 12:58:13 +0100409 private static void useAssertionConfig(
410 int minApiLevel, Collection<Path> libraries, Collection<Path> inputs) {
411 try {
412 R8.run(
413 R8Command.builder(handler)
Ian Zernyfaa17d32020-06-19 12:15:50 +0200414 .setDisableTreeShaking(true)
Søren Gjesse33a8c5b2020-01-14 12:58:13 +0100415 .setMinApiLevel(minApiLevel)
Ian Zernyfaa17d32020-06-19 12:15:50 +0200416 .setProgramConsumer(new EnsureOutputConsumer())
Søren Gjesse33a8c5b2020-01-14 12:58:13 +0100417 .addLibraryFiles(libraries)
418 .addProgramFiles(inputs)
Søren Gjessec393db52022-04-28 09:45:29 +0200419 .addAssertionsConfiguration(b -> b.setScopeAll().setCompileTimeEnable().build())
420 .addAssertionsConfiguration(b -> b.setScopeAll().setCompileTimeDisable().build())
Søren Gjesse33a8c5b2020-01-14 12:58:13 +0100421 .addAssertionsConfiguration(
Søren Gjessec393db52022-04-28 09:45:29 +0200422 b ->
423 b.setScopePackage("com.android.tools.apiusagesample")
424 .setCompileTimeEnable()
425 .build())
Søren Gjesse33a8c5b2020-01-14 12:58:13 +0100426 .addAssertionsConfiguration(
427 b ->
428 b.setScopePackage("com.android.tools.apiusagesample")
429 .setPassthrough()
430 .build())
431 .addAssertionsConfiguration(
Søren Gjessec393db52022-04-28 09:45:29 +0200432 b ->
433 b.setScopePackage("com.android.tools.apiusagesample")
434 .setCompileTimeDisable()
435 .build())
Søren Gjesse33a8c5b2020-01-14 12:58:13 +0100436 .addAssertionsConfiguration(
437 b ->
438 b.setScopeClass("com.android.tools.apiusagesample.D8ApiUsageSample")
Søren Gjessec393db52022-04-28 09:45:29 +0200439 .setCompileTimeEnable()
Søren Gjesse33a8c5b2020-01-14 12:58:13 +0100440 .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 Gjessec393db52022-04-28 09:45:29 +0200449 .setCompileTimeDisable()
Søren Gjesse33a8c5b2020-01-14 12:58:13 +0100450 .build())
Søren Gjessec393db52022-04-28 09:45:29 +0200451 .addAssertionsConfiguration(
452 AssertionsConfiguration.Builder::compileTimeEnableAllAssertions)
Søren Gjesse33a8c5b2020-01-14 12:58:13 +0100453 .addAssertionsConfiguration(AssertionsConfiguration.Builder::passthroughAllAssertions)
Søren Gjessec393db52022-04-28 09:45:29 +0200454 .addAssertionsConfiguration(
455 AssertionsConfiguration.Builder::compileTimeDisableAllAssertions)
Søren Gjesse33a8c5b2020-01-14 12:58:13 +0100456 .build());
457 } catch (CompilationFailedException e) {
458 throw new RuntimeException("Unexpected compilation exceptions", e);
459 }
460 }
461
Ian Zerny1a3ebcd2018-01-04 11:06:21 +0100462 // 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 Zerny923a0c12018-01-03 10:59:18 +0100491 // 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 Agera26659a2018-06-11 08:44:58 +0200500 ZipInputStream zip = new ZipInputStream(Files.newInputStream(file), StandardCharsets.UTF_8);
Ian Zerny923a0c12018-01-03 10:59:18 +0100501 ZipEntry entry;
502 while (null != (entry = zip.getNextEntry())) {
Mads Ager45e6e7b2018-06-15 11:28:17 +0200503 String name = entry.getName();
504 if (isClassFile(name)) {
505 Origin origin = new ArchiveEntryOrigin(name, zipOrigin);
Ian Zerny923a0c12018-01-03 10:59:18 +0100506 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 Zerny3dd1e4c2019-11-26 15:50:43 +0100570
571 private static void checkVersionApi() {
Ian Zerny1b7bb372020-06-19 12:48:37 +0200572 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 Zerny3dd1e4c2019-11-26 15:50:43 +0100592 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 Zerny923a0c12018-01-03 10:59:18 +0100611}