8192986: Inconsistent handling of exploded modules in jlink
authorsundar
Fri, 08 Dec 2017 20:46:40 +0530
changeset 48223 962b4324320c
parent 48222 37d3e1a80c3b
child 48224 be0df5ab3093
8192986: Inconsistent handling of exploded modules in jlink Reviewed-by: redestad, jlaskey
src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java
src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java
src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java
src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties
test/jdk/tools/jlink/ExplodedModuleNameTest.java
test/jdk/tools/lib/tests/Helper.java
test/jdk/tools/lib/tests/JImageGenerator.java
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java	Fri Dec 08 14:28:51 2017 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java	Fri Dec 08 20:46:40 2017 +0530
@@ -267,15 +267,17 @@
                 assert !mainClassName.isEmpty();
             }
 
-            String path = "/" + module + "/module-info.class";
-            Optional<ResourcePoolEntry> res = imageContent.findEntry(path);
-            if (!res.isPresent()) {
-                throw new IOException("module-info.class not found for " + module + " module");
-            }
-            ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes());
-            Optional<String> mainClass = ModuleDescriptor.read(stream).mainClass();
-            if (mainClassName == null && mainClass.isPresent()) {
-                mainClassName = mainClass.get();
+            if (mainClassName == null) {
+                String path = "/" + module + "/module-info.class";
+                Optional<ResourcePoolEntry> res = imageContent.findEntry(path);
+                if (!res.isPresent()) {
+                    throw new IOException("module-info.class not found for " + module + " module");
+                }
+                ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes());
+                Optional<String> mainClass = ModuleDescriptor.read(stream).mainClass();
+                if (mainClass.isPresent()) {
+                    mainClassName = mainClass.get();
+                }
             }
 
             if (mainClassName != null) {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java	Fri Dec 08 14:28:51 2017 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java	Fri Dec 08 20:46:40 2017 +0530
@@ -85,17 +85,17 @@
     private static final Consumer<String> noopConsumer = (String t) -> {
     };
 
-    public DirArchive(Path dirPath) {
-        this(dirPath, noopConsumer);
+    public DirArchive(Path dirPath, String moduleName) {
+        this(dirPath, moduleName, noopConsumer);
     }
 
-    public DirArchive(Path dirPath, Consumer<String> log) {
+    public DirArchive(Path dirPath, String moduleName, Consumer<String> log) {
         Objects.requireNonNull(dirPath);
         if (!Files.isDirectory(dirPath)) {
             throw new IllegalArgumentException(dirPath + " is not a directory");
         }
         chop = dirPath.toString().length() + 1;
-        this.moduleName = Objects.requireNonNull(dirPath.getFileName()).toString();
+        this.moduleName = Objects.requireNonNull(moduleName);
         this.dirPath = dirPath;
         this.log = log;
     }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Fri Dec 08 14:28:51 2017 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Fri Dec 08 20:46:40 2017 +0530
@@ -24,6 +24,7 @@
  */
 package jdk.tools.jlink.internal;
 
+import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -824,13 +825,29 @@
 
                 return modularJarArchive;
             } else if (Files.isDirectory(path)) {
-                return new DirArchive(path);
+                Path modInfoPath = path.resolve("module-info.class");
+                if (Files.isRegularFile(modInfoPath)) {
+                    return new DirArchive(path, findModuleName(modInfoPath));
+                } else {
+                    throw new IllegalArgumentException(
+                        taskHelper.getMessage("err.not.a.module.directory", path));
+                }
             } else {
                 throw new IllegalArgumentException(
                     taskHelper.getMessage("err.not.modular.format", module, path));
             }
         }
 
+        private static String findModuleName(Path modInfoPath) {
+            try (BufferedInputStream bis = new BufferedInputStream(
+                    Files.newInputStream(modInfoPath))) {
+                return ModuleDescriptor.read(bis).name();
+            } catch (IOException exp) {
+                throw new IllegalArgumentException(taskHelper.getMessage(
+                    "err.cannot.read.module.info", modInfoPath), exp);
+            }
+        }
+
         @Override
         public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
             ExecutableImage image = ImageFileCreator.create(archives, order, stack);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties	Fri Dec 08 14:28:51 2017 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties	Fri Dec 08 20:46:40 2017 +0530
@@ -135,6 +135,8 @@
 err.config.defaults=property {0} is missing from configuration
 err.config.defaults.value=wrong value in defaults property: {0}
 err.bom.generation=bom file generation failed: {0}
+err.not.a.module.directory=directory {0} does not contain module-info.class file under it
+err.cannot.read.module.info=cannot read module descriptor from {0}
 err.not.modular.format=selected module {0} ({1}) not in jmod or modular JAR format
 err.signing=signed modular JAR {0} is currently not supported,\
 \ use --ignore-signing-information to suppress error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/tools/jlink/ExplodedModuleNameTest.java	Fri Dec 08 20:46:40 2017 +0530
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+import java.io.IOException;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.Files;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.util.spi.ToolProvider;
+
+import tests.Helper;
+import tests.JImageGenerator;
+import tests.Result;
+
+/*
+ * @test
+ * @bug 8192986
+ * @summary Inconsistent handling of exploded modules in jlink
+ * @library ../lib
+ * @modules java.base/jdk.internal.jimage
+ *          jdk.jdeps/com.sun.tools.classfile
+ *          jdk.jlink/jdk.tools.jlink.internal
+ *          jdk.jlink/jdk.tools.jmod
+ *          jdk.jlink/jdk.tools.jimage
+ *          jdk.compiler
+ * @build tests.*
+ * @run main ExplodedModuleNameTest
+ */
+public class ExplodedModuleNameTest {
+    static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
+        .orElseThrow(() ->
+            new RuntimeException("jlink tool not found")
+        );
+
+    public static void main(String[] args) throws Exception {
+        Helper helper = Helper.newHelper();
+        if (helper == null) {
+            System.err.println("Test not run");
+            return;
+        }
+
+        // generate a new exploded module
+        String modName = "mod8192986";
+        Path modDir = helper.generateDefaultExplodedModule(modName).getFile();
+        // rename the module containing directory
+        Path renamedModDir = modDir.resolveSibling("modified_mod8192986");
+        // copy the content from original directory to modified name directory
+        copyDir(modDir, renamedModDir);
+
+        Path outputDir = helper.createNewImageDir("image8192986");
+        JImageGenerator.getJLinkTask()
+                .modulePath(renamedModDir.toAbsolutePath().toString())
+                .output(outputDir)
+                .addMods(modName)
+                .launcher(modName + "=" + modName + "/" + modName +".Main")
+                .call().assertSuccess();
+    }
+
+    private static void copyDir(Path srcDir, Path destDir) throws IOException {
+        Files.walkFileTree(srcDir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+                Path target = destDir.resolve(srcDir.relativize(dir));
+                Files.createDirectory(target);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                Files.copy(file, destDir.resolve(srcDir.relativize(file)));
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+}
--- a/test/jdk/tools/lib/tests/Helper.java	Fri Dec 08 14:28:51 2017 +0000
+++ b/test/jdk/tools/lib/tests/Helper.java	Fri Dec 08 20:46:40 2017 +0530
@@ -219,7 +219,7 @@
         generateModuleCompiledClasses(explodedmodssrc, explodedmodsclasses,
                 moduleName, classNames, dependencies);
 
-        Path dir = explodedmods.resolve(moduleName);
+        Path dir = explodedmods.resolve("classes").resolve(moduleName);
         return new Result(0, "", dir);
     }
 
--- a/test/jdk/tools/lib/tests/JImageGenerator.java	Fri Dec 08 14:28:51 2017 +0000
+++ b/test/jdk/tools/lib/tests/JImageGenerator.java	Fri Dec 08 20:46:40 2017 +0530
@@ -110,6 +110,7 @@
     private static final String ADD_MODULES_OPTION = "--add-modules";
     private static final String LIMIT_MODULES_OPTION = "--limit-modules";
     private static final String PLUGIN_MODULE_PATH = "--plugin-module-path";
+    private static final String LAUNCHER = "--launcher";
 
     private static final String CMDS_OPTION = "--cmds";
     private static final String CONFIG_OPTION = "--config";
@@ -579,12 +580,18 @@
         private String repeatedLimitMods;
         private Path output;
         private Path existing;
+        private String launcher; // optional
 
         public JLinkTask modulePath(String modulePath) {
             this.modulePath = modulePath;
             return this;
         }
 
+        public JLinkTask launcher(String cmd) {
+            launcher = Objects.requireNonNull(cmd);
+            return this;
+        }
+
         public JLinkTask repeatedModulePath(String modulePath) {
             this.repeatedModulePath = modulePath;
             return this;
@@ -682,6 +689,10 @@
                 options.add(PLUGIN_MODULE_PATH);
                 options.add(toPath(pluginModulePath));
             }
+            if (launcher != null && !launcher.isEmpty()) {
+                options.add(LAUNCHER);
+                options.add(launcher);
+            }
             options.addAll(this.options);
             return options.toArray(new String[options.size()]);
         }