1 /*
   2  * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.File;
  25 import java.io.IOException;
  26 import java.nio.file.Files;
  27 import java.nio.file.Path;
  28 import java.nio.file.Paths;
  29 import java.util.Arrays;
  30 import java.util.Set;
  31 import java.util.spi.ToolProvider;
  32 import java.util.stream.Collectors;
  33 import java.util.stream.Stream;
  34 
  35 import jdk.test.lib.compiler.CompilerUtils;
  36 import jdk.test.lib.util.FileUtils;
  37 
  38 import static jdk.testlibrary.ProcessTools.*;
  39 
  40 import org.testng.annotations.BeforeTest;
  41 import org.testng.annotations.Test;
  42 import static org.testng.Assert.*;
  43 
  44 /**
  45  * @test
  46  * @bug 8142968 8173381 8174740
  47  * @library /lib/testlibrary /test/lib
  48  * @modules jdk.compiler jdk.jlink
  49  * @modules java.base/jdk.internal.module
  50  * @modules java.base/jdk.internal.org.objectweb.asm
  51  * @build ModuleTargetHelper UserModuleTest jdk.test.lib.compiler.CompilerUtils jdk.testlibrary.ProcessTools
  52  * @run testng UserModuleTest
  53  */
  54 
  55 public class UserModuleTest {
  56     private static final String JAVA_HOME = System.getProperty("java.home");
  57     private static final String TEST_SRC = System.getProperty("test.src");
  58 
  59     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
  60     private static final Path MODS_DIR = Paths.get("mods");
  61     private static final Path JMODS_DIR = Paths.get("jmods");
  62 
  63     private static final Path IMAGE = Paths.get("image");
  64     private static final String MAIN_MID = "m1/p1.Main";
  65 
  66     // the names of the modules in this test
  67     private static String[] modules = new String[] {"m1", "m2", "m3", "m4", "m5"};
  68 
  69 
  70     private static boolean hasJmods() {
  71         if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) {
  72             System.err.println("Test skipped. NO jmods directory");
  73             return false;
  74         }
  75         return true;
  76     }
  77 
  78     /*
  79      * Compiles all modules used by the test
  80      */
  81     @BeforeTest
  82     public void compileAll() throws Throwable {
  83         if (!hasJmods()) return;
  84 
  85         for (String mn : modules) {
  86             Path msrc = SRC_DIR.resolve(mn);
  87             assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
  88                 "--module-source-path", SRC_DIR.toString(),
  89                 "--add-exports", "java.base/jdk.internal.module=" + mn,
  90                 "--add-exports", "java.base/jdk.internal.org.objectweb.asm=" + mn));
  91         }
  92 
  93         if (Files.exists(IMAGE)) {
  94             FileUtils.deleteFileTreeUnchecked(IMAGE);
  95         }
  96 
  97         createImage(IMAGE, "m1", "m3");
  98 
  99         createJmods("m1", "m4");
 100     }
 101 
 102     /*
 103      * Test the image created when linking with a module with
 104      * no Packages attribute
 105      */
 106     @Test
 107     public void testPackagesAttribute() throws Throwable {
 108         if (!hasJmods()) return;
 109 
 110         Path java = IMAGE.resolve("bin").resolve("java");
 111         assertTrue(executeProcess(java.toString(),
 112                         "--add-exports", "java.base/jdk.internal.module=m1,m4",
 113                         "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 114                         "-m", MAIN_MID)
 115                         .outputTo(System.out)
 116                         .errorTo(System.out)
 117                         .getExitValue() == 0);
 118     }
 119 
 120     /*
 121      * Test the image created when linking with an open module
 122     */
 123     @Test
 124     public void testOpenModule() throws Throwable {
 125         if (!hasJmods()) return;
 126 
 127         Path java = IMAGE.resolve("bin").resolve("java");
 128         assertTrue(executeProcess(java.toString(), "-m", "m3/p3.Main")
 129                         .outputTo(System.out)
 130                         .errorTo(System.out)
 131                         .getExitValue() == 0);
 132     }
 133 
 134     /*
 135      * Disable the fast loading of system modules.
 136      * Parsing module-info.class
 137      */
 138     @Test
 139     public void disableSystemModules() throws Throwable {
 140         if (!hasJmods()) return;
 141 
 142         Path java = IMAGE.resolve("bin").resolve("java");
 143         assertTrue(executeProcess(java.toString(),
 144                                   "--add-exports", "java.base/jdk.internal.module=m1,m4",
 145                                   "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 146                                   "-Djdk.system.module.finder.disabledFastPath",
 147                                   "-m", MAIN_MID)
 148                         .outputTo(System.out)
 149                         .errorTo(System.out)
 150                         .getExitValue() == 0);
 151     }
 152 
 153     /*
 154      * Test the optimization that deduplicates Set<String> on targets of exports,
 155      * uses, provides.
 156      */
 157     @Test
 158     public void testDedupSet() throws Throwable {
 159         if (!hasJmods()) return;
 160 
 161         Path dir = Paths.get("dedupSetTest");
 162         createImage(dir, "m1", "m2", "m3", "m4");
 163         Path java = dir.resolve("bin").resolve("java");
 164         assertTrue(executeProcess(java.toString(),
 165                          "--add-exports", "java.base/jdk.internal.module=m1,m4",
 166                          "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 167                          "-m", MAIN_MID)
 168                         .outputTo(System.out)
 169                         .errorTo(System.out)
 170                         .getExitValue() == 0);
 171     }
 172 
 173     @Test
 174     public void testRequiresStatic() throws Throwable {
 175         if (!hasJmods()) return;
 176 
 177         Path dir = Paths.get("requiresStatic");
 178         createImage(dir, "m5");
 179         Path java = dir.resolve("bin").resolve("java");
 180         assertTrue(executeProcess(java.toString(), "-m", "m5/p5.Main")
 181                         .outputTo(System.out)
 182                         .errorTo(System.out)
 183                         .getExitValue() == 0);
 184 
 185         // run with m3 present
 186         assertTrue(executeProcess(java.toString(),
 187                                   "--module-path", MODS_DIR.toString(),
 188                                   "--add-modules", "m3",
 189                                   "-m", "m5/p5.Main")
 190                         .outputTo(System.out)
 191                         .errorTo(System.out)
 192                         .getExitValue() == 0);
 193     }
 194 
 195     @Test
 196     public void testRequiresStatic2() throws Throwable {
 197         if (!hasJmods()) return;
 198 
 199         Path dir = Paths.get("requiresStatic2");
 200         createImage(dir, "m3", "m5");
 201 
 202         Path java = dir.resolve("bin").resolve("java");
 203         assertTrue(executeProcess(java.toString(), "-m", "m5/p5.Main")
 204                         .outputTo(System.out)
 205                         .errorTo(System.out)
 206                         .getExitValue() == 0);
 207 
 208         // boot layer with m3 and m5
 209         assertTrue(executeProcess(java.toString(),
 210                                   "--add-modules", "m3",
 211                                   "-m", "m5/p5.Main")
 212                         .outputTo(System.out)
 213                         .errorTo(System.out)
 214                         .getExitValue() == 0);
 215     }
 216 
 217     private void createJmods(String... modules) throws IOException {
 218         ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.getJavaBaseTarget();
 219         if (mt == null) {
 220             throw new RuntimeException("ModuleTarget is missing for java.base");
 221         }
 222 
 223         String[] values = mt.targetPlatform().split("-");
 224         String osName = values[0];
 225         String osArch = values[1];
 226 
 227         // create JMOD files
 228         Files.createDirectories(JMODS_DIR);
 229         Stream.of(modules).forEach(mn ->
 230             assertTrue(jmod("create",
 231                 "--class-path", MODS_DIR.resolve(mn).toString(),
 232                 "--target-platform", mt.targetPlatform(),
 233                 "--main-class", mn.replace('m', 'p') + ".Main",
 234                 JMODS_DIR.resolve(mn + ".jmod").toString()) == 0)
 235         );
 236     }
 237 
 238 
 239     /**
 240      * Verify the module descriptor if package p4.dummy is excluded at link time.
 241      */
 242     @Test
 243     public void testModulePackagesAttribute() throws Throwable {
 244         if (!hasJmods()) return;
 245 
 246         // create an image using JMOD files
 247         Path dir = Paths.get("packagesTest");
 248         String mp = Paths.get(JAVA_HOME, "jmods").toString() +
 249             File.pathSeparator + JMODS_DIR.toString();
 250 
 251         Set<String> modules = Set.of("m1", "m4");
 252         assertTrue(JLINK_TOOL.run(System.out, System.out,
 253             "--output", dir.toString(),
 254             "--exclude-resources", "m4/p4/dummy/*",
 255             "--add-modules", modules.stream().collect(Collectors.joining(",")),
 256             "--module-path", mp) == 0);
 257 
 258         // verify ModuleDescriptor
 259         Path java = dir.resolve("bin").resolve("java");
 260         assertTrue(executeProcess(java.toString(),
 261                         "--add-exports", "java.base/jdk.internal.module=m1,m4",
 262                         "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 263                         "--add-modules=m1", "-m", "m4")
 264             .outputTo(System.out)
 265             .errorTo(System.out)
 266             .getExitValue() == 0);
 267     }
 268 
 269     /**
 270      * Verify the plugin to retain ModuleTarget attribute
 271      */
 272     @Test
 273     public void testRetainModuleTarget() throws Throwable {
 274         if (!hasJmods()) return;
 275 
 276         // create an image using JMOD files
 277         Path dir = Paths.get("retainModuleTargetTest");
 278         String mp = Paths.get(JAVA_HOME, "jmods").toString() +
 279             File.pathSeparator + JMODS_DIR.toString();
 280 
 281         Set<String> modules = Set.of("m1", "m4");
 282         assertTrue(JLINK_TOOL.run(System.out, System.out,
 283             "--output", dir.toString(),
 284             "--system-modules", "retainModuleTarget",
 285             "--exclude-resources", "m4/p4/dummy/*",
 286             "--add-modules", modules.stream().collect(Collectors.joining(",")),
 287             "--module-path", mp) == 0);
 288 
 289         // verify ModuleDescriptor
 290         Path java = dir.resolve("bin").resolve("java");
 291         assertTrue(executeProcess(java.toString(),
 292                         "--add-exports", "java.base/jdk.internal.module=m1,m4",
 293                         "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 294                         "--add-modules=m1", "-m", "m4", "retainModuleTarget")
 295             .outputTo(System.out)
 296             .errorTo(System.out)
 297             .getExitValue() == 0);
 298     }
 299 
 300     static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
 301         .orElseThrow(() ->
 302             new RuntimeException("jlink tool not found")
 303         );
 304 
 305     static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
 306         .orElseThrow(() ->
 307             new RuntimeException("jmod tool not found")
 308         );
 309 
 310     static final String MODULE_PATH = Paths.get(JAVA_HOME, "jmods").toString()
 311         + File.pathSeparator + MODS_DIR.toString();
 312 
 313     private void createImage(Path outputDir, String... modules) throws Throwable {
 314         assertTrue(JLINK_TOOL.run(System.out, System.out,
 315             "--output", outputDir.toString(),
 316             "--add-modules", Arrays.stream(modules).collect(Collectors.joining(",")),
 317             "--module-path", MODULE_PATH) == 0);
 318     }
 319 
 320     private static int jmod(String... options) {
 321         System.out.println("jmod " + Arrays.asList(options));
 322         return JMOD_TOOL.run(System.out, System.out, options);
 323     }
 324 }