blob: febd5e6e8eb680167c661d3c28e169c0e9aee9b1 [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)
419 .addAssertionsConfiguration(b -> b.setScopeAll().setEnable().build())
420 .addAssertionsConfiguration(b -> b.setScopeAll().setDisable().build())
421 .addAssertionsConfiguration(
422 b -> b.setScopePackage("com.android.tools.apiusagesample").setEnable().build())
423 .addAssertionsConfiguration(
424 b ->
425 b.setScopePackage("com.android.tools.apiusagesample")
426 .setPassthrough()
427 .build())
428 .addAssertionsConfiguration(
429 b -> b.setScopePackage("com.android.tools.apiusagesample").setDisable().build())
430 .addAssertionsConfiguration(
431 b ->
432 b.setScopeClass("com.android.tools.apiusagesample.D8ApiUsageSample")
433 .setEnable()
434 .build())
435 .addAssertionsConfiguration(
436 b ->
437 b.setScopeClass("com.android.tools.apiusagesample.D8ApiUsageSample")
438 .setPassthrough()
439 .build())
440 .addAssertionsConfiguration(
441 b ->
442 b.setScopeClass("com.android.tools.apiusagesample.D8ApiUsageSample")
443 .setDisable()
444 .build())
445 .addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions)
446 .addAssertionsConfiguration(AssertionsConfiguration.Builder::passthroughAllAssertions)
447 .addAssertionsConfiguration(AssertionsConfiguration.Builder::disableAllAssertions)
448 .build());
449 } catch (CompilationFailedException e) {
450 throw new RuntimeException("Unexpected compilation exceptions", e);
451 }
452 }
453
Ian Zerny1a3ebcd2018-01-04 11:06:21 +0100454 // Check API support for all the varg variants.
455 private static void useVArgVariants(
456 int minApiLevel,
457 List<Path> libraries,
458 List<Path> inputs,
459 List<Path> mainDexList,
460 List<Path> mainDexRules,
461 List<Path> pgConf) {
462 try {
463 R8.run(
464 R8Command.builder(handler)
465 .setMinApiLevel(minApiLevel)
466 .setProgramConsumer(new EnsureOutputConsumer())
467 .addLibraryFiles(libraries.get(0))
468 .addLibraryFiles(libraries.stream().skip(1).toArray(Path[]::new))
469 .addProgramFiles(inputs.get(0))
470 .addProgramFiles(inputs.stream().skip(1).toArray(Path[]::new))
471 .addMainDexListFiles(mainDexList.get(0))
472 .addMainDexListFiles(mainDexList.stream().skip(1).toArray(Path[]::new))
473 .addMainDexRulesFiles(mainDexRules.get(0))
474 .addMainDexRulesFiles(mainDexRules.stream().skip(1).toArray(Path[]::new))
475 .addProguardConfigurationFiles(pgConf.get(0))
476 .addProguardConfigurationFiles(pgConf.stream().skip(1).toArray(Path[]::new))
477 .build());
478 } catch (CompilationFailedException e) {
479 throw new RuntimeException("Unexpected compilation exceptions", e);
480 }
481 }
482
Ian Zerny923a0c12018-01-03 10:59:18 +0100483 // Helpers for tests.
484 // Some of this reimplements stuff in R8 utils, but that is not public API and we should not
485 // rely on it.
486
487 private static List<ClassFileContent> readClassFiles(Collection<Path> files) throws IOException {
488 List<ClassFileContent> classfiles = new ArrayList<>();
489 for (Path file : files) {
490 if (isArchive(file)) {
491 Origin zipOrigin = new PathOrigin(file);
Mads Agera26659a2018-06-11 08:44:58 +0200492 ZipInputStream zip = new ZipInputStream(Files.newInputStream(file), StandardCharsets.UTF_8);
Ian Zerny923a0c12018-01-03 10:59:18 +0100493 ZipEntry entry;
494 while (null != (entry = zip.getNextEntry())) {
Mads Ager45e6e7b2018-06-15 11:28:17 +0200495 String name = entry.getName();
496 if (isClassFile(name)) {
497 Origin origin = new ArchiveEntryOrigin(name, zipOrigin);
Ian Zerny923a0c12018-01-03 10:59:18 +0100498 classfiles.add(new ClassFileContent(origin, readBytes(zip)));
499 }
500 }
501 } else if (isClassFile(file)) {
502 classfiles.add(new ClassFileContent(new PathOrigin(file), Files.readAllBytes(file)));
503 }
504 }
505 return classfiles;
506 }
507
508 private static byte[] readBytes(InputStream stream) throws IOException {
509 try (ByteArrayOutputStream bytes = new ByteArrayOutputStream()) {
510 byte[] buffer = new byte[0xffff];
511 for (int length; (length = stream.read(buffer)) != -1; ) {
512 bytes.write(buffer, 0, length);
513 }
514 return bytes.toByteArray();
515 }
516 }
517
518 private static boolean isClassFile(Path file) {
519 return isClassFile(file.toString());
520 }
521
522 private static boolean isClassFile(String file) {
523 file = file.toLowerCase();
524 return file.endsWith(".class");
525 }
526
527 private static boolean isArchive(Path file) {
528 return isArchive(file.toString());
529 }
530
531 private static boolean isArchive(String file) {
532 file = file.toLowerCase();
533 return file.endsWith(".zip") || file.endsWith(".jar");
534 }
535
536 private static class ClassFileContent {
537 final Origin origin;
538 final byte[] data;
539
540 public ClassFileContent(Origin origin, byte[] data) {
541 this.origin = origin;
542 this.data = data;
543 }
544 }
545
546 private static class EnsureOutputConsumer implements DexIndexedConsumer {
547 boolean hasOutput = false;
548
549 @Override
550 public synchronized void accept(
551 int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
552 hasOutput = true;
553 }
554
555 @Override
556 public void finished(DiagnosticsHandler handler) {
557 if (!hasOutput) {
558 handler.error(new StringDiagnostic("Expected to produce output but had none"));
559 }
560 }
561 }
Ian Zerny3dd1e4c2019-11-26 15:50:43 +0100562
563 private static void checkVersionApi() {
Ian Zerny1b7bb372020-06-19 12:48:37 +0200564 String labelValue;
565 int labelAccess;
566 try {
567 Field field = Version.class.getDeclaredField("LABEL");
568 labelAccess = field.getModifiers();
569 labelValue = (String) field.get(Version.class);
570 } catch (Exception e) {
571 throw new RuntimeException(e);
572 }
573 if (!Modifier.isPublic(labelAccess)
574 || !Modifier.isStatic(labelAccess)
575 || !Modifier.isFinal(labelAccess)) {
576 throw new RuntimeException("Expected public static final LABEL");
577 }
578 if (labelValue.isEmpty()) {
579 throw new RuntimeException("Expected LABEL constant");
580 }
581 if (Version.LABEL.isEmpty()) {
582 throw new RuntimeException("Expected LABEL constant");
583 }
Ian Zerny3dd1e4c2019-11-26 15:50:43 +0100584 if (Version.getVersionString() == null) {
585 throw new RuntimeException("Expected getVersionString API");
586 }
587 if (Version.getMajorVersion() < -1) {
588 throw new RuntimeException("Expected getMajorVersion API");
589 }
590 if (Version.getMinorVersion() < -1) {
591 throw new RuntimeException("Expected getMinorVersion API");
592 }
593 if (Version.getPatchVersion() < -1) {
594 throw new RuntimeException("Expected getPatchVersion API");
595 }
596 if (Version.getPreReleaseString() == null && false) {
597 throw new RuntimeException("Expected getPreReleaseString API");
598 }
599 if (Version.isDevelopmentVersion() && false) {
600 throw new RuntimeException("Expected isDevelopmentVersion API");
601 }
602 }
Ian Zerny923a0c12018-01-03 10:59:18 +0100603}