diff --git a/README.md b/README.md index e2ff38e7..6adff942 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,16 @@ Configurable system properties: Alternative to retrolambda.includedFiles for avoiding the command line length limit. The file must list one file per line with UTF-8 encoding. + retrolambda.jars + List of jars to process. + This is useful for including libraries. + Uses ; or : as the path separator, see java.io.File#pathSeparatorChar + + retrolambda.jarsFile (alternative) + File listing the jars to process. + Alternative to retrolambda.jars for avoiding the command line + length limit. The file must list one file per line with UTF-8 encoding. + retrolambda.javacHacks Attempts to fix javac bugs (type-annotation emission for local variables). Disabled by default. Enable by setting to "true" diff --git a/end-to-end-tests/pom.xml b/end-to-end-tests/pom.xml index 1cdf9652..751f512d 100644 --- a/end-to-end-tests/pom.xml +++ b/end-to-end-tests/pom.xml @@ -6,7 +6,7 @@ net.orfjackal.retrolambda parent - 2.5.8-SNAPSHOT + 2.6.0-SNAPSHOT ../parent/pom.xml @@ -54,7 +54,6 @@ system ${basedir}/src/test/lib/java-lang-dummies.jar - diff --git a/parent/pom.xml b/parent/pom.xml index 9e43dc49..6aaac0ba 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -12,7 +12,7 @@ net.orfjackal.retrolambda parent - 2.5.8-SNAPSHOT + 2.6.0-SNAPSHOT pom Backport of Java 8 lambda expressions to Java 7 diff --git a/pom.xml b/pom.xml index e8265b5f..e5f4b900 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ net.orfjackal.retrolambda parent - 2.5.8-SNAPSHOT + 2.6.0-SNAPSHOT parent/pom.xml diff --git a/retrolambda-api/pom.xml b/retrolambda-api/pom.xml index d82d4b7d..9e517e66 100644 --- a/retrolambda-api/pom.xml +++ b/retrolambda-api/pom.xml @@ -6,7 +6,7 @@ net.orfjackal.retrolambda parent - 2.5.8-SNAPSHOT + 2.6.0-SNAPSHOT ../parent/pom.xml diff --git a/retrolambda-api/src/main/java/net/orfjackal/retrolambda/api/RetrolambdaApi.java b/retrolambda-api/src/main/java/net/orfjackal/retrolambda/api/RetrolambdaApi.java index 7276b978..8a0203e3 100644 --- a/retrolambda-api/src/main/java/net/orfjackal/retrolambda/api/RetrolambdaApi.java +++ b/retrolambda-api/src/main/java/net/orfjackal/retrolambda/api/RetrolambdaApi.java @@ -12,6 +12,8 @@ public class RetrolambdaApi { public static final String INCLUDED_FILES_FILE = INCLUDED_FILES + "File"; public static final String CLASSPATH = PREFIX + "classpath"; public static final String CLASSPATH_FILE = CLASSPATH + "File"; + public static final String JARS = "jars"; + public static final String JARS_FILE = JARS + "File"; public static final String OUTPUT_DIR = PREFIX + "outputDir"; public static final String INPUT_DIR = PREFIX + "inputDir"; public static final String DEFAULT_METHODS = PREFIX + "defaultMethods"; diff --git a/retrolambda-maven-plugin/pom.xml b/retrolambda-maven-plugin/pom.xml index 951a593c..3fd1ae1f 100644 --- a/retrolambda-maven-plugin/pom.xml +++ b/retrolambda-maven-plugin/pom.xml @@ -5,7 +5,7 @@ net.orfjackal.retrolambda parent - 2.5.8-SNAPSHOT + 2.6.0-SNAPSHOT ../parent/pom.xml diff --git a/retrolambda/pom.xml b/retrolambda/pom.xml index bd0df18e..70fda115 100644 --- a/retrolambda/pom.xml +++ b/retrolambda/pom.xml @@ -6,7 +6,7 @@ net.orfjackal.retrolambda parent - 2.5.8-SNAPSHOT + 2.6.0-SNAPSHOT ../parent/pom.xml diff --git a/retrolambda/src/main/java/net/orfjackal/retrolambda/Config.java b/retrolambda/src/main/java/net/orfjackal/retrolambda/Config.java index e5d2c983..8ba20865 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/Config.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/Config.java @@ -21,6 +21,8 @@ public interface Config { List getIncludedFiles(); + List getJars(); + boolean isJavacHacksEnabled(); boolean isQuiet(); diff --git a/retrolambda/src/main/java/net/orfjackal/retrolambda/Retrolambda.java b/retrolambda/src/main/java/net/orfjackal/retrolambda/Retrolambda.java index 07d292f0..0453910a 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/Retrolambda.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/Retrolambda.java @@ -5,6 +5,7 @@ package net.orfjackal.retrolambda; import com.esotericsoftware.minlog.Log; +import com.google.common.collect.ImmutableMap; import net.orfjackal.retrolambda.files.*; import net.orfjackal.retrolambda.interfaces.ClassInfo; import net.orfjackal.retrolambda.lambdas.*; @@ -32,6 +33,7 @@ public static void run(Config config) throws Throwable { Path outputDir = config.getOutputDir(); List classpath = config.getClasspath(); List includedFiles = config.getIncludedFiles(); + List jars = config.getJars(); boolean isJavacHacksEnabled = config.isJavacHacksEnabled(); if (config.isQuiet()) { Log.WARN(); @@ -44,6 +46,7 @@ public static void run(Config config) throws Throwable { Log.info("Output directory: " + outputDir); Log.info("Classpath: " + classpath); Log.info("Included files: " + (includedFiles != null ? includedFiles.size() : "all")); + Log.info("Jars: " + (jars != null ? jars.size() : 0)); Log.info("JVM version: " + System.getProperty("java.version")); Log.info("Agent enabled: " + Agent.isEnabled()); Log.info("javac hacks: " + isJavacHacksEnabled); @@ -67,7 +70,7 @@ public static void run(Config config) throws Throwable { dumper.install(); } - visitFiles(inputDir, includedFiles, new ClasspathVisitor() { + visitFiles(inputDir, includedFiles, jars, new ClasspathVisitor() { @Override protected void visitClass(byte[] bytecode) { analyzer.analyze(bytecode, isJavacHacksEnabled); @@ -102,10 +105,18 @@ protected void visitResource(Path relativePath, byte[] content) throws IOExcepti } } - static void visitFiles(Path inputDir, List includedFiles, FileVisitor visitor) throws IOException { + static void visitFiles(Path inputDir, List includedFiles, List jars, FileVisitor visitor) throws IOException { if (includedFiles != null) { visitor = new FilteringFileVisitor(includedFiles, visitor); } + if (jars != null) { + for (Path jar : jars) { + URI uri = URI.create("jar:file:" + jar.toUri().getPath()); + try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + Files.walkFileTree(fileSystem.getPath("/"), visitor); + } + } + } Files.walkFileTree(inputDir, visitor); } diff --git a/retrolambda/src/main/java/net/orfjackal/retrolambda/SystemPropertiesConfig.java b/retrolambda/src/main/java/net/orfjackal/retrolambda/SystemPropertiesConfig.java index 18afb719..05529cbe 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/SystemPropertiesConfig.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/SystemPropertiesConfig.java @@ -190,6 +190,31 @@ public List getIncludedFiles() { return null; } + // jars + + static { + optionalParameterHelp(JARS, + "List of jars to process.", + "This is useful for including libraries.", + "Uses ; or : as the path separator, see java.io.File#pathSeparatorChar"); + alternativeParameterHelp(JARS_FILE, JARS, + "File listing the files to process, instead of processing all files.", + "Alternative to " + JARS + " for avoiding the command line", + "length limit. The file must list one file per line with UTF-8 encoding."); + } + + @Override + public List getJars() { + String files = p.getProperty(JARS); + if (files != null) { + return parsePathList(files); + } + String filesFile = p.getProperty(JARS_FILE); + if (filesFile != null) { + return readPathList(Paths.get(filesFile)); + } + return null; + } // useJavac8ReadLabelHack diff --git a/retrolambda/src/main/java/net/orfjackal/retrolambda/files/ClasspathVisitor.java b/retrolambda/src/main/java/net/orfjackal/retrolambda/files/ClasspathVisitor.java index 72dfad58..56088839 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/files/ClasspathVisitor.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/files/ClasspathVisitor.java @@ -22,7 +22,7 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Path relativePath = baseDir.relativize(file); + Path relativePath = safeRelativize(baseDir, file); byte[] content = Files.readAllBytes(file); if (isJavaClass(relativePath)) { @@ -33,6 +33,18 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO return FileVisitResult.CONTINUE; } + /** + * The path might point into a jar which will throw an IllegalArgumentException. In that case, just return a path + * relative to the root of the jar. + */ + private static Path safeRelativize(Path baseDir, Path file) { + try { + return baseDir.relativize(file); + } catch (IllegalArgumentException e) { + return file.subpath(1, file.getNameCount()); + } + } + protected abstract void visitClass(byte[] bytecode) throws IOException; protected abstract void visitResource(Path relativePath, byte[] content) throws IOException; diff --git a/retrolambda/src/main/java/net/orfjackal/retrolambda/files/OutputDirectory.java b/retrolambda/src/main/java/net/orfjackal/retrolambda/files/OutputDirectory.java index 085effa1..a5e9e3e8 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/files/OutputDirectory.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/files/OutputDirectory.java @@ -28,7 +28,7 @@ public void writeClass(byte[] bytecode, boolean isJavacHacksEnabled) throws IOEx } public void writeFile(Path relativePath, byte[] content) throws IOException { - Path outputFile = outputDir.resolve(relativePath); + Path outputFile = outputDir.resolve(relativePath.toString()); Files.createDirectories(outputFile.getParent()); Files.write(outputFile, content); } diff --git a/retrolambda/src/test/java/net/orfjackal/retrolambda/RetrolambdaTest.java b/retrolambda/src/test/java/net/orfjackal/retrolambda/RetrolambdaTest.java index 8e74d823..2051d375 100644 --- a/retrolambda/src/test/java/net/orfjackal/retrolambda/RetrolambdaTest.java +++ b/retrolambda/src/test/java/net/orfjackal/retrolambda/RetrolambdaTest.java @@ -4,14 +4,18 @@ package net.orfjackal.retrolambda; +import com.google.common.collect.ImmutableMap; import net.orfjackal.retrolambda.api.RetrolambdaApi; import org.junit.*; import org.junit.rules.TemporaryFolder; -import java.io.IOException; +import java.io.*; +import java.net.URI; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.*; +import java.util.jar.*; +import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -37,6 +41,8 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { private Path file2; private Path fileInSubdir; private Path outsider; + private Path jar; + private Path fileInJar; @Before public void setup() throws IOException { @@ -48,11 +54,15 @@ public void setup() throws IOException { Files.createDirectory(subdir); fileInSubdir = Files.createFile(subdir.resolve("file.txt")); outsider = tempDir.newFile("outsider.txt").toPath(); + jar = new File(tempDir.getRoot(), "file.jar").toPath(); + try (FileSystem jarFileSystem = FileSystems.newFileSystem(URI.create("jar:file:" + jar.toUri().getPath()), ImmutableMap.of("create", "true"))) { + fileInJar = Files.createFile(jarFileSystem.getPath("/").resolve("file.txt")); + } } @Test public void by_default_visits_all_files_recursively() throws IOException { - Retrolambda.visitFiles(inputDir, null, visitor); + Retrolambda.visitFiles(inputDir, null, null, visitor); assertThat(visitedFiles, containsInAnyOrder(file1, file2, fileInSubdir)); } @@ -61,7 +71,7 @@ public void by_default_visits_all_files_recursively() throws IOException { public void when_included_files_is_set_then_visits_only_those_files() throws IOException { List includedFiles = Arrays.asList(file1, fileInSubdir); - Retrolambda.visitFiles(inputDir, includedFiles, visitor); + Retrolambda.visitFiles(inputDir, includedFiles, null, visitor); assertThat(visitedFiles, containsInAnyOrder(file1, fileInSubdir)); } @@ -70,11 +80,21 @@ public void when_included_files_is_set_then_visits_only_those_files() throws IOE public void ignores_included_files_that_are_outside_the_input_directory() throws IOException { List includedFiles = Arrays.asList(file1, outsider); - Retrolambda.visitFiles(inputDir, includedFiles, visitor); + Retrolambda.visitFiles(inputDir, includedFiles, null, visitor); assertThat(visitedFiles, containsInAnyOrder(file1)); } + @Test + public void visits_files_in_jar() throws IOException { + List jars = Arrays.asList(jar); + + Retrolambda.visitFiles(inputDir, null, jars, visitor); + List uris = visitedFiles.stream().map(Path::toUri).collect(Collectors.toList()); + + assertThat(uris, containsInAnyOrder(file1.toUri(), file2.toUri(), fileInSubdir.toUri(), fileInJar.toUri())); + } + @Test public void copies_resources_to_output_directory() throws Throwable { Properties p = new Properties(); diff --git a/retrolambda/src/test/java/net/orfjackal/retrolambda/SystemPropertiesConfigTest.java b/retrolambda/src/test/java/net/orfjackal/retrolambda/SystemPropertiesConfigTest.java index 1c9a403e..d3b5735b 100644 --- a/retrolambda/src/test/java/net/orfjackal/retrolambda/SystemPropertiesConfigTest.java +++ b/retrolambda/src/test/java/net/orfjackal/retrolambda/SystemPropertiesConfigTest.java @@ -149,4 +149,36 @@ public void included_files_file() throws IOException { systemProperties.setProperty(RetrolambdaApi.INCLUDED_FILES_FILE, file.toString()); assertThat("multiple values", config().getIncludedFiles(), is(Arrays.asList(Paths.get("one.class"), Paths.get("two.class")))); } + + @Test + public void jars() throws IOException { + assertThat("not set", config().getJars(), is(nullValue())); + + systemProperties.setProperty(SystemPropertiesConfig.JARS, ""); + assertThat("zero values", config().getJars(), is(empty())); + + systemProperties.setProperty(SystemPropertiesConfig.JARS, "/foo/one.jar"); + assertThat("one value", config().getJars(), is(Arrays.asList(Paths.get("/foo/one.jar")))); + + systemProperties.setProperty(SystemPropertiesConfig.JARS, "/foo/one.jar" + File.pathSeparator + "/foo/two.jar"); + assertThat("multiple values", config().getJars(), is(Arrays.asList(Paths.get("/foo/one.jar"), Paths.get("/foo/two.jar")))); + } + + @Test + public void jars_file() throws IOException { + Path file = tempDir.newFile("jars.txt").toPath(); + assertThat("not set", config().getJars(), is(nullValue())); + + Files.write(file, Arrays.asList("", "", "")); // empty lines are ignored + systemProperties.setProperty(SystemPropertiesConfig.JARS_FILE, file.toString()); + assertThat("zero values", config().getJars(), is(empty())); + + Files.write(file, Arrays.asList("one.jar")); + systemProperties.setProperty(SystemPropertiesConfig.JARS_FILE, file.toString()); + assertThat("one value", config().getJars(), is(Arrays.asList(Paths.get("one.jar")))); + + Files.write(file, Arrays.asList("one.jar", "two.jar")); + systemProperties.setProperty(SystemPropertiesConfig.JARS_FILE, file.toString()); + assertThat("multiple values", config().getJars(), is(Arrays.asList(Paths.get("one.jar"), Paths.get("two.jar")))); + } }