summaryrefslogtreecommitdiff
path: root/java/com/google/devtools/build/android/desugar/Desugar.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/devtools/build/android/desugar/Desugar.java')
-rw-r--r--java/com/google/devtools/build/android/desugar/Desugar.java471
1 files changed, 290 insertions, 181 deletions
diff --git a/java/com/google/devtools/build/android/desugar/Desugar.java b/java/com/google/devtools/build/android/desugar/Desugar.java
index 22780ea..797cbc4 100644
--- a/java/com/google/devtools/build/android/desugar/Desugar.java
+++ b/java/com/google/devtools/build/android/desugar/Desugar.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.google.devtools.build.android.desugar;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
@@ -20,12 +21,16 @@ import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
import com.google.common.io.Closer;
import com.google.devtools.build.android.Converters.ExistingPathConverter;
import com.google.devtools.build.android.Converters.PathConverter;
+import com.google.devtools.build.android.desugar.CoreLibraryRewriter.UnprefixingClassWriter;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
+import com.google.devtools.common.options.OptionsParser.OptionUsageRestrictions;
+import com.google.errorprone.annotations.MustBeClosed;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitResult;
@@ -57,8 +62,8 @@ class Desugar {
converter = ExistingPathConverter.class,
abbrev = 'i',
help =
- "Input Jar or directory with classes to desugar (required, the n-th input is paired with"
- + "the n-th output)."
+ "Input Jar or directory with classes to desugar (required, the n-th input is paired with"
+ + "the n-th output)."
)
public List<Path> inputJars;
@@ -68,7 +73,9 @@ class Desugar {
defaultValue = "",
category = "input",
converter = ExistingPathConverter.class,
- help = "Ordered classpath to resolve symbols in the --input Jar, like javac's -cp flag."
+ help =
+ "Ordered classpath (Jar or directory) to resolve symbols in the --input Jar, like "
+ + "javac's -cp flag."
)
public List<Path> classpath;
@@ -78,15 +85,16 @@ class Desugar {
defaultValue = "",
category = "input",
converter = ExistingPathConverter.class,
- help = "Bootclasspath that was used to compile the --input Jar with, like javac's "
- + "-bootclasspath flag (required)."
+ help =
+ "Bootclasspath that was used to compile the --input Jar with, like javac's "
+ + "-bootclasspath flag (required)."
)
public List<Path> bootclasspath;
@Option(
name = "allow_empty_bootclasspath",
defaultValue = "false",
- category = "undocumented"
+ optionUsageRestrictions = OptionUsageRestrictions.UNDOCUMENTED
)
public boolean allowEmptyBootclasspath;
@@ -95,11 +103,19 @@ class Desugar {
defaultValue = "false",
help =
"A temporary flag specifically for android lint, subject to removal anytime (DO NOT USE)",
- category = "undocumented"
+ optionUsageRestrictions = OptionUsageRestrictions.UNDOCUMENTED
)
public boolean onlyDesugarJavac9ForLint;
@Option(
+ name = "rewrite_calls_to_long_compare",
+ defaultValue = "true",
+ help = "rewrite calls to Long.compare(long, long) to the JVM instruction lcmp",
+ category = "misc"
+ )
+ public boolean enableRewritingOfLongCompare;
+
+ @Option(
name = "output",
allowMultiple = true,
defaultValue = "",
@@ -140,26 +156,261 @@ class Desugar {
@Option(
name = "core_library",
defaultValue = "false",
- category = "undocumented",
+ optionUsageRestrictions = OptionUsageRestrictions.UNDOCUMENTED,
implicitRequirements = "--allow_empty_bootclasspath",
help = "Enables rewriting to desugar java.* classes."
)
public boolean coreLibrary;
}
+ private final Options options;
+ private final Path dumpDirectory;
+ private final CoreLibraryRewriter rewriter;
+ private final LambdaClassMaker lambdas;
+ private final boolean allowDefaultMethods;
+ private final boolean allowCallsToObjectsNonNull;
+ /** An instance of Desugar is expected to be used ONLY ONCE */
+ private boolean used;
+
+ private Desugar(Options options, Path dumpDirectory) {
+ this.options = options;
+ this.dumpDirectory = dumpDirectory;
+ this.rewriter = new CoreLibraryRewriter(options.coreLibrary ? "__desugar__/" : "");
+ this.lambdas = new LambdaClassMaker(dumpDirectory);
+ this.allowDefaultMethods = options.minSdkVersion >= 24;
+ this.allowCallsToObjectsNonNull = options.minSdkVersion >= 19;
+ this.used = false;
+ }
+
+ private void desugar() throws Exception {
+ checkState(!this.used, "This Desugar instance has been used. Please create another one.");
+ this.used = true;
+
+ try (Closer closer = Closer.create()) {
+ IndexedInputs indexedClasspath =
+ new IndexedInputs(toRegisteredInputFileProvider(closer, options.classpath));
+ // Use a classloader that as much as possible uses the provided bootclasspath instead of
+ // the tool's system classloader. Unfortunately we can't do that for java. classes.
+ ClassLoader bootclassloader =
+ options.bootclasspath.isEmpty()
+ ? new ThrowingClassLoader()
+ : new HeaderClassLoader(
+ new IndexedInputs(toRegisteredInputFileProvider(closer, options.bootclasspath)),
+ rewriter,
+ new ThrowingClassLoader());
+
+ // Process each input separately
+ for (InputOutputPair inputOutputPair : toInputOutputPairs(options)) {
+ desugarOneInput(inputOutputPair, indexedClasspath, bootclassloader);
+ }
+ }
+ }
+
+ private void desugarOneInput(
+ InputOutputPair inputOutputPair, IndexedInputs indexedClasspath, ClassLoader bootclassloader)
+ throws Exception {
+ Path inputPath = inputOutputPair.getInput();
+ Path outputPath = inputOutputPair.getOutput();
+ checkArgument(
+ Files.isDirectory(inputPath) || !Files.isDirectory(outputPath),
+ "Input jar file requires an output jar file");
+
+ try (OutputFileProvider outputFileProvider = toOutputFileProvider(outputPath);
+ InputFileProvider inputFiles = toInputFileProvider(inputPath)) {
+ IndexedInputs indexedInputFiles = new IndexedInputs(ImmutableList.of(inputFiles));
+ // Prepend classpath with input file itself so LambdaDesugaring can load classes with
+ // lambdas.
+ IndexedInputs indexedClasspathAndInputFiles = indexedClasspath.withParent(indexedInputFiles);
+ // Note that input file and classpath need to be in the same classloader because
+ // we typically get the header Jar for inputJar on the classpath and having the header
+ // Jar in a parent loader means the header version is preferred over the real thing.
+ ClassLoader loader =
+ new HeaderClassLoader(indexedClasspathAndInputFiles, rewriter, bootclassloader);
+
+ ClassReaderFactory readerFactory =
+ new ClassReaderFactory(
+ (options.copyBridgesFromClasspath && !allowDefaultMethods)
+ ? indexedClasspathAndInputFiles
+ : indexedInputFiles,
+ rewriter);
+
+ ImmutableSet.Builder<String> interfaceLambdaMethodCollector = ImmutableSet.builder();
+
+ desugarClassesInInput(
+ inputFiles, outputFileProvider, loader, readerFactory, interfaceLambdaMethodCollector);
+
+ desugarAndWriteDumpedLambdaClassesToOutput(
+ outputFileProvider, loader, readerFactory, interfaceLambdaMethodCollector);
+ }
+
+ ImmutableMap<Path, LambdaInfo> leftBehind = lambdas.drain();
+ checkState(leftBehind.isEmpty(), "Didn't process %s", leftBehind);
+ }
+
+ /** Desugar the classes that are in the inputs specified in the command line arguments. */
+ private void desugarClassesInInput(
+ InputFileProvider inputFiles,
+ OutputFileProvider outputFileProvider,
+ ClassLoader loader,
+ ClassReaderFactory readerFactory,
+ Builder<String> interfaceLambdaMethodCollector)
+ throws IOException {
+ for (String filename : inputFiles) {
+ try (InputStream content = inputFiles.getInputStream(filename)) {
+ // We can write classes uncompressed since they need to be converted to .dex format
+ // for Android anyways. Resources are written as they were in the input jar to avoid
+ // any danger of accidentally uncompressed resources ending up in an .apk.
+ if (filename.endsWith(".class")) {
+ ClassReader reader = rewriter.reader(content);
+ UnprefixingClassWriter writer =
+ rewriter.writer(ClassWriter.COMPUTE_MAXS /*for bridge methods*/);
+ ClassVisitor visitor =
+ createClassVisitorsForClassesInInputs(
+ loader, readerFactory, interfaceLambdaMethodCollector, writer);
+ reader.accept(visitor, 0);
+
+ outputFileProvider.write(filename, writer.toByteArray());
+ } else {
+ outputFileProvider.copyFrom(filename, inputFiles);
+ }
+ }
+ }
+ }
+
+ /**
+ * Desugar the classes that are generated on the fly when we are desugaring the classes in the
+ * specified inputs.
+ */
+ private void desugarAndWriteDumpedLambdaClassesToOutput(
+ OutputFileProvider outputFileProvider,
+ ClassLoader loader,
+ ClassReaderFactory readerFactory,
+ Builder<String> interfaceLambdaMethodCollector)
+ throws IOException {
+ ImmutableSet<String> interfaceLambdaMethods = interfaceLambdaMethodCollector.build();
+ checkState(
+ !allowDefaultMethods || interfaceLambdaMethods.isEmpty(),
+ "Desugaring with default methods enabled moved interface lambdas");
+
+ // Write out the lambda classes we generated along the way
+ ImmutableMap<Path, LambdaInfo> lambdaClasses = lambdas.drain();
+ checkState(
+ !options.onlyDesugarJavac9ForLint || lambdaClasses.isEmpty(),
+ "There should be no lambda classes generated: %s",
+ lambdaClasses.keySet());
+
+ for (Map.Entry<Path, LambdaInfo> lambdaClass : lambdaClasses.entrySet()) {
+ try (InputStream bytecode =
+ Files.newInputStream(dumpDirectory.resolve(lambdaClass.getKey()))) {
+ ClassReader reader = rewriter.reader(bytecode);
+ UnprefixingClassWriter writer =
+ rewriter.writer(ClassWriter.COMPUTE_MAXS /*for invoking bridges*/);
+ ClassVisitor visitor =
+ createClassVisitorsForDumpedLambdaClasses(
+ loader, readerFactory, interfaceLambdaMethods, lambdaClass.getValue(), writer);
+ reader.accept(visitor, 0);
+ String filename =
+ rewriter.unprefix(lambdaClass.getValue().desiredInternalName()) + ".class";
+ outputFileProvider.write(filename, writer.toByteArray());
+ }
+ }
+ }
+
+ /**
+ * Create the class visitors for the lambda classes that are generated on the fly. If no new class
+ * visitors are not generated, then the passed-in {@code writer} will be returned.
+ */
+ private ClassVisitor createClassVisitorsForDumpedLambdaClasses(
+ ClassLoader loader,
+ ClassReaderFactory readerFactory,
+ ImmutableSet<String> interfaceLambdaMethods,
+ LambdaInfo lambdaClass,
+ UnprefixingClassWriter writer) {
+ ClassVisitor visitor = writer;
+
+ if (!allowDefaultMethods) {
+ // null ClassReaderFactory b/c we don't expect to need it for lambda classes
+ visitor = new Java7Compatibility(visitor, (ClassReaderFactory) null);
+ }
+
+ visitor =
+ new LambdaClassFixer(
+ visitor, lambdaClass, readerFactory, interfaceLambdaMethods, allowDefaultMethods);
+ // Send lambda classes through desugaring to make sure there's no invokedynamic
+ // instructions in generated lambda classes (checkState below will fail)
+ visitor = new LambdaDesugaring(visitor, loader, lambdas, null, allowDefaultMethods);
+ if (!allowCallsToObjectsNonNull) {
+ // Not sure whether there will be implicit null check emitted by javac, so we rerun
+ // the inliner again
+ visitor = new ObjectsRequireNonNullMethodRewriter(visitor);
+ }
+ if (options.enableRewritingOfLongCompare) {
+ visitor = new LongCompareMethodRewriter(visitor);
+ }
+ return visitor;
+ }
+
+ /**
+ * Create the class visitors for the classes which are in the inputs. If new visitors are created,
+ * then all these visitors and the passed-in writer will be chained together. If no new visitor is
+ * created, then the passed-in {@code writer} will be returned.
+ */
+ private ClassVisitor createClassVisitorsForClassesInInputs(
+ ClassLoader loader,
+ ClassReaderFactory readerFactory,
+ Builder<String> interfaceLambdaMethodCollector,
+ UnprefixingClassWriter writer) {
+ checkArgument(writer != null, "The class writer cannot be null");
+ ClassVisitor visitor = writer;
+
+ if (!options.onlyDesugarJavac9ForLint) {
+ if (!allowDefaultMethods) {
+ visitor = new Java7Compatibility(visitor, readerFactory);
+ }
+
+ visitor =
+ new LambdaDesugaring(
+ visitor, loader, lambdas, interfaceLambdaMethodCollector, allowDefaultMethods);
+ }
+
+ if (!allowCallsToObjectsNonNull) {
+ visitor = new ObjectsRequireNonNullMethodRewriter(visitor);
+ }
+ if (options.enableRewritingOfLongCompare) {
+ visitor = new LongCompareMethodRewriter(visitor);
+ }
+ return visitor;
+ }
+
+
public static void main(String[] args) throws Exception {
- // LambdaClassMaker generates lambda classes for us, but it does so by essentially simulating
- // the call to LambdaMetafactory that the JVM would make when encountering an invokedynamic.
- // LambdaMetafactory is in the JDK and its implementation has a property to write out ("dump")
- // generated classes, which we take advantage of here. Set property before doing anything else
- // since the property is read in the static initializer; if this breaks we can investigate
- // setting the property when calling the tool.
+ // It is important that this method is called first. See its javadoc.
+ Path dumpDirectory = createAndRegisterLambdaDumpDirectory();
+ Options options = parseCommandLineOptions(args);
+ if (options.verbose) {
+ System.out.printf("Lambda classes will be written under %s%n", dumpDirectory);
+ }
+ new Desugar(options, dumpDirectory).desugar();
+ }
+
+ /**
+ * LambdaClassMaker generates lambda classes for us, but it does so by essentially simulating the
+ * call to LambdaMetafactory that the JVM would make when encountering an invokedynamic.
+ * LambdaMetafactory is in the JDK and its implementation has a property to write out ("dump")
+ * generated classes, which we take advantage of here. Set property before doing anything else
+ * since the property is read in the static initializer; if this breaks we can investigate setting
+ * the property when calling the tool.
+ */
+ private static Path createAndRegisterLambdaDumpDirectory() throws IOException {
Path dumpDirectory = Files.createTempDirectory("lambdas");
System.setProperty(
LambdaClassMaker.LAMBDA_METAFACTORY_DUMPER_PROPERTY, dumpDirectory.toString());
deleteTreeOnExit(dumpDirectory);
+ return dumpDirectory;
+ }
+ private static Options parseCommandLineOptions(String[] args) throws IOException {
if (args.length == 1 && args[0].startsWith("@")) {
args = Files.readAllLines(Paths.get(args[0].substring(1)), ISO_8859_1).toArray(new String[0]);
}
@@ -167,155 +418,25 @@ class Desugar {
OptionsParser optionsParser = OptionsParser.newOptionsParser(Options.class);
optionsParser.setAllowResidue(false);
optionsParser.parseAndExitUponError(args);
+
Options options = optionsParser.getOptions(Options.class);
- checkState(!options.inputJars.isEmpty(), "--input is required");
- checkState(
+ checkArgument(!options.inputJars.isEmpty(), "--input is required");
+ checkArgument(
options.inputJars.size() == options.outputJars.size(),
- "Desugar requires the same number of inputs and outputs to pair them");
- checkState(!options.bootclasspath.isEmpty() || options.allowEmptyBootclasspath,
+ "Desugar requires the same number of inputs and outputs to pair them. #input=%s,#output=%s",
+ options.inputJars.size(),
+ options.outputJars.size());
+ checkArgument(
+ !options.bootclasspath.isEmpty() || options.allowEmptyBootclasspath,
"At least one --bootclasspath_entry is required");
- for (Path path : options.classpath) {
- checkState(!Files.isDirectory(path), "Classpath entry must be a jar file: %s", path);
- }
for (Path path : options.bootclasspath) {
- checkState(!Files.isDirectory(path), "Bootclasspath entry must be a jar file: %s", path);
- }
- if (options.verbose) {
- System.out.printf("Lambda classes will be written under %s%n", dumpDirectory);
- }
-
- CoreLibraryRewriter rewriter =
- new CoreLibraryRewriter(options.coreLibrary ? "__desugar__/" : "");
-
- boolean allowDefaultMethods = options.minSdkVersion >= 24;
- boolean allowCallsToObjectsNonNull = options.minSdkVersion >= 19;
-
- LambdaClassMaker lambdas = new LambdaClassMaker(dumpDirectory);
-
- // Process each input separately
- for (InputOutputPair inputOutputPair : toInputOutputPairs(options)) {
- Path inputPath = inputOutputPair.getInput();
- Path outputPath = inputOutputPair.getOutput();
- checkState(
- Files.isDirectory(inputPath) || !Files.isDirectory(outputPath),
- "Input jar file requires an output jar file");
-
- try (Closer closer = Closer.create();
- OutputFileProvider outputFileProvider = toOutputFileProvider(outputPath)) {
- InputFileProvider appInputFiles = toInputFileProvider(closer, inputPath);
- List<InputFileProvider> classpathInputFiles =
- toInputFileProvider(closer, options.classpath);
- IndexedInputs appIndexedInputs = new IndexedInputs(ImmutableList.of(appInputFiles));
- IndexedInputs appAndClasspathIndexedInputs =
- new IndexedInputs(classpathInputFiles, appIndexedInputs);
- ClassLoader loader =
- createClassLoader(
- rewriter,
- toInputFileProvider(closer, options.bootclasspath),
- appAndClasspathIndexedInputs);
-
- ClassReaderFactory readerFactory =
- new ClassReaderFactory(
- (options.copyBridgesFromClasspath && !allowDefaultMethods)
- ? appAndClasspathIndexedInputs
- : appIndexedInputs,
- rewriter);
-
- ImmutableSet.Builder<String> interfaceLambdaMethodCollector = ImmutableSet.builder();
-
- // Process inputs, desugaring as we go
- for (String filename : appInputFiles) {
- try (InputStream content = appInputFiles.getInputStream(filename)) {
- // We can write classes uncompressed since they need to be converted to .dex format
- // for Android anyways. Resources are written as they were in the input jar to avoid
- // any danger of accidentally uncompressed resources ending up in an .apk.
- if (filename.endsWith(".class")) {
- ClassReader reader = rewriter.reader(content);
- CoreLibraryRewriter.UnprefixingClassWriter writer =
- rewriter.writer(ClassWriter.COMPUTE_MAXS /*for bridge methods*/);
- ClassVisitor visitor = writer;
-
- if (!options.onlyDesugarJavac9ForLint) {
- if (!allowDefaultMethods) {
- visitor = new Java7Compatibility(visitor, readerFactory);
- }
-
- visitor =
- new LambdaDesugaring(
- visitor,
- loader,
- lambdas,
- interfaceLambdaMethodCollector,
- allowDefaultMethods);
- }
-
- if (!allowCallsToObjectsNonNull) {
- visitor = new ObjectsRequireNonNullMethodInliner(visitor);
- }
- reader.accept(visitor, 0);
-
- outputFileProvider.write(filename, writer.toByteArray());
- } else {
- outputFileProvider.copyFrom(filename, appInputFiles);
- }
- }
- }
-
- ImmutableSet<String> interfaceLambdaMethods = interfaceLambdaMethodCollector.build();
- checkState(
- !allowDefaultMethods || interfaceLambdaMethods.isEmpty(),
- "Desugaring with default methods enabled moved interface lambdas");
-
- // Write out the lambda classes we generated along the way
- ImmutableMap<Path, LambdaInfo> lambdaClasses = lambdas.drain();
- checkState(
- !options.onlyDesugarJavac9ForLint || lambdaClasses.isEmpty(),
- "There should be no lambda classes generated: %s",
- lambdaClasses.keySet());
-
- for (Map.Entry<Path, LambdaInfo> lambdaClass : lambdaClasses.entrySet()) {
- try (InputStream bytecode =
- Files.newInputStream(dumpDirectory.resolve(lambdaClass.getKey()))) {
- ClassReader reader = rewriter.reader(bytecode);
- CoreLibraryRewriter.UnprefixingClassWriter writer =
- rewriter.writer(ClassWriter.COMPUTE_MAXS /*for invoking bridges*/);
- ClassVisitor visitor = writer;
-
- if (!allowDefaultMethods) {
- // null ClassReaderFactory b/c we don't expect to need it for lambda classes
- visitor = new Java7Compatibility(visitor, (ClassReaderFactory) null);
- }
-
- visitor =
- new LambdaClassFixer(
- visitor,
- lambdaClass.getValue(),
- readerFactory,
- interfaceLambdaMethods,
- allowDefaultMethods);
- // Send lambda classes through desugaring to make sure there's no invokedynamic
- // instructions in generated lambda classes (checkState below will fail)
- visitor = new LambdaDesugaring(visitor, loader, lambdas, null, allowDefaultMethods);
- if (!allowCallsToObjectsNonNull) {
- // Not sure whether there will be implicit null check emitted by javac, so we rerun
- // the inliner again
- visitor = new ObjectsRequireNonNullMethodInliner(visitor);
- }
- reader.accept(visitor, 0);
- String filename =
- rewriter.unprefix(lambdaClass.getValue().desiredInternalName()) + ".class";
- outputFileProvider.write(filename, writer.toByteArray());
- }
- }
-
- Map<Path, LambdaInfo> leftBehind = lambdas.drain();
- checkState(leftBehind.isEmpty(), "Didn't process %s", leftBehind);
- }
+ checkArgument(!Files.isDirectory(path), "Bootclasspath entry must be a jar file: %s", path);
}
+ return options;
}
- private static List<InputOutputPair> toInputOutputPairs(Options options) {
+ private static ImmutableList<InputOutputPair> toInputOutputPairs(Options options) {
final ImmutableList.Builder<InputOutputPair> ioPairListbuilder = ImmutableList.builder();
for (Iterator<Path> inputIt = options.inputJars.iterator(),
outputIt = options.outputJars.iterator();
@@ -325,24 +446,6 @@ class Desugar {
return ioPairListbuilder.build();
}
- private static ClassLoader createClassLoader(
- CoreLibraryRewriter rewriter,
- List<InputFileProvider> bootclasspath,
- IndexedInputs appAndClasspathIndexedInputs)
- throws IOException {
- // Use a classloader that as much as possible uses the provided bootclasspath instead of
- // the tool's system classloader. Unfortunately we can't do that for java. classes.
- ClassLoader parent = new ThrowingClassLoader();
- if (!bootclasspath.isEmpty()) {
- parent = new HeaderClassLoader(new IndexedInputs(bootclasspath), rewriter, parent);
- }
- // Prepend classpath with input jar itself so LambdaDesugaring can load classes with lambdas.
- // Note that inputJar and classpath need to be in the same classloader because we typically get
- // the header Jar for inputJar on the classpath and having the header Jar in a parent loader
- // means the header version is preferred over the real thing.
- return new HeaderClassLoader(appAndClasspathIndexedInputs, rewriter, parent);
- }
-
private static class ThrowingClassLoader extends ClassLoader {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
@@ -394,6 +497,7 @@ class Desugar {
}
/** Transform a Path to an {@link OutputFileProvider} */
+ @MustBeClosed
private static OutputFileProvider toOutputFileProvider(Path path)
throws IOException {
if (Files.isDirectory(path)) {
@@ -403,26 +507,31 @@ class Desugar {
}
}
- /** Transform a Path to an InputFileProvider and register it to close it at the end of desugar */
- private static InputFileProvider toInputFileProvider(Closer closer, Path path)
+ /** Transform a Path to an InputFileProvider that needs to be closed by the caller. */
+ @MustBeClosed
+ private static InputFileProvider toInputFileProvider(Path path)
throws IOException {
if (Files.isDirectory(path)) {
- return closer.register(new DirectoryInputFileProvider(path));
+ return new DirectoryInputFileProvider(path);
} else {
- return closer.register(new ZipInputFileProvider(path));
+ return new ZipInputFileProvider(path);
}
}
- private static ImmutableList<InputFileProvider> toInputFileProvider(
+ /**
+ * Transform a list of Path to a list of InputFileProvider and register them with the given
+ * closer.
+ */
+ @SuppressWarnings("MustBeClosedChecker")
+ private static ImmutableList<InputFileProvider> toRegisteredInputFileProvider(
Closer closer, List<Path> paths) throws IOException {
ImmutableList.Builder<InputFileProvider> builder = new ImmutableList.Builder<>();
for (Path path : paths) {
- checkState(!Files.isDirectory(path), "Directory is not supported: %s", path);
- builder.add(closer.register(new ZipInputFileProvider(path)));
+ builder.add(closer.register(toInputFileProvider(path)));
}
return builder.build();
}
-
+
/**
* Pair input and output.
*/