diff -r d489081c5650 src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java Sat Mar 02 18:09:18 2019 -0500 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java Wed Mar 13 22:31:17 2019 +0100 @@ -26,7 +26,10 @@ package com.sun.tools.javac.file; import java.io.IOException; -import java.nio.file.FileSystems; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.spi.FileSystemProvider; @@ -90,7 +93,6 @@ } public List getJarClassPath(Path file) throws IOException { - Path parent = file.getParent(); try (JarFile jarFile = new JarFile(file.toFile())) { Manifest man = jarFile.getManifest(); if (man == null) @@ -100,22 +102,27 @@ if (attr == null) return Collections.emptyList(); - String path = attr.getValue(Attributes.Name.CLASS_PATH); - if (path == null) + String classpath = attr.getValue(Attributes.Name.CLASS_PATH); + if (classpath == null) return Collections.emptyList(); - List list = new ArrayList<>(); + // TODO: Must use the same code as jdk.internal.loader.URLClassPath.JarLoader.parseClassPath(base, path) + StringTokenizer st = new StringTokenizer(classpath); + List paths = new ArrayList<>(st.countTokens()); + while (st.hasMoreTokens()) { + String path = st.nextToken(); + try { + // Use getCanonicalFile just as jdk.internal.loader.URLClassPath.toFileURL + URL base = file.toFile().getCanonicalFile().toURI().toURL(); + URI uri = new URL(base, path).toURI(); - for (StringTokenizer st = new StringTokenizer(path); - st.hasMoreTokens(); ) { - String elt = st.nextToken(); - Path f = FileSystems.getDefault().getPath(elt); - if (!f.isAbsolute() && parent != null) - f = parent.resolve(f).toAbsolutePath(); - list.add(f); + // Should return uri, see comment on com.sun.tools.javac.file.Locations.SearchPath.addJarClassPath + paths.add(Path.of(uri)); + } catch (MalformedURLException | URISyntaxException e) { + System.err.println("Class-Path entry: \"" + path + "\" ignored in JAR file \"" + file + "\": " + e.getMessage()); + } } - - return list; + return paths; } } diff -r d489081c5650 test/langtools/tools/javac/8218268/ManifestClassPathTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/langtools/tools/javac/8218268/ManifestClassPathTest.java Wed Mar 13 22:31:17 2019 +0100 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8821826 + * @summary : Test javac handles Manifest Class-Path entries the same as java + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.TestRunner toolbox.JavacTask toolbox.JavaTask toolbox.JarTask + * @run main ManifestClassPathTest + */ + +import static java.io.File.pathSeparator; +import static java.io.File.separator; +import static java.nio.file.Files.createLink; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import toolbox.JarTask; +import toolbox.JavaTask; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.Task.Expect; +import toolbox.TestRunner; +import toolbox.ToolBox; + +public class ManifestClassPathTest extends TestRunner { + private static final String JAR_NAME = "j.jar"; + + private ToolBox tb; + + public static void main(String... args) throws Exception { + new ManifestClassPathTest().runTests(); + } + + public ManifestClassPathTest() { + super(System.err); + tb = new ToolBox(); + } + + @Override + public void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void test(Path base) throws Exception { + String dirName = "x y"; + String jarOutDirectory = "x%20y"; + + Path current = base.resolve("out"); + Path jarPath = createJar(current, dirName); + + Path src = current.resolve("src"); + tb.writeJavaFiles(src, "public class A { public static void main(String[] args) { J.test(); }}"); + + // Test 1: Compile and run with jar on classpath + doTest(jarPath.toString(), src); + + // Test 2: Compile and run with jar as relative path via classpath manifest + String jarRelativePath = jarOutDirectory + separator + JAR_NAME; + doTest(createManifestJar(current, jarRelativePath), src); + + // Test 3: Compile and run with jar as absolute (url) path via classpath + // manifest + String path = current.toFile().getAbsolutePath() + separator + jarRelativePath; + doTest(createManifestJar(current, path), src); + + // Test 4: Compile and run with jar as url via classpath manifest + String urlPath = path.replace(separator, "/"); + doTest(createManifestJar(current, "file:" + urlPath), src); + doTest(createManifestJar(current, "file://" + urlPath), src); + + // Test 5: garbage before, right jar must still be used + doTest(createManifestJar(current, "notexisting://garbage " + jarRelativePath), src); + + // Test 6: linked jar + String linkJar = "l.jar"; + createLink(current.resolve(linkJar), current.resolve(dirName + separator + JAR_NAME)); + doTest(createManifestJar(current, linkJar), src); + + // Test 7: wrong classpath + doTest(createManifestJar(current, "notexisting://garbage"), src, Task.Expect.FAIL); + } + + private void doTest(String classpath, Path src) { + doTest(classpath, src, Task.Expect.SUCCESS); + } + + private void doTest(String classpath, Path src, Expect expectedExit) { + String cp = classpath + pathSeparator + src; + System.err.println("javac -cp " + cp + src.resolve("A.java")); + + new JavacTask(tb, Task.Mode.EXEC).classpath(cp).files(src.resolve("A.java")).run(expectedExit); + // Bug: jdk.net.URLClassPath.disableClassPathURLCheck meaning is inverted and + // should be default enabled according to spec + new JavaTask(tb).vmOptions("-Djdk.net.URLClassPath.disableClassPathURLCheck=false").classpath(cp).className("A") + .run(expectedExit); + } + + private Path createJar(Path current, String jarOutDirectory) throws IOException { + Path jarSrc = current.resolve("jarSrc"); + tb.writeJavaFiles(jarSrc, "public class J { public static void test() { System.out.println(\"OK\"); }}"); + Path jarOut = current.resolve(jarOutDirectory); + tb.createDirectories(jarOut); + Path jarFile = jarOut.resolve(JAR_NAME); + + new JarTask(tb, jarFile).baseDir(jarSrc).files("J.java").run().writeAll(); + return jarFile; + } + + private String createManifestJar(Path current, String classpath) throws IOException { + Path jarCpPath = current.resolve("jcp.jar"); + new JarTask(tb, jarCpPath).manifest("Class-Path: " + classpath + "\n\n").run().writeAll(); + return jarCpPath.toString(); + } +} +