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.util.FileUtils;
  36 
  37 import static jdk.testlibrary.ProcessTools.*;
  38 
  39 import org.testng.annotations.BeforeTest;
  40 import org.testng.annotations.Test;
  41 import static org.testng.Assert.*;
  42 
  43 /**
  44  * @test
  45  * @bug 8142968 8173381 8174740
  46  * @library /lib/testlibrary /test/lib
  47  * @modules jdk.compiler jdk.jlink
  48  * @modules java.base/jdk.internal.module
  49  * @modules java.base/jdk.internal.org.objectweb.asm
  50  * @build ModuleTargetHelper UserModuleTest CompilerUtils jdk.testlibrary.ProcessTools
  51  * @run testng UserModuleTest
  52  */
  53 
  54 public class UserModuleTest {
  55     private static final String JAVA_HOME = System.getProperty("java.home");
  56     private static final String TEST_SRC = System.getProperty("test.src");
  57 
  58     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
  59     private static final Path MODS_DIR = Paths.get("mods");
  60     private static final Path JMODS_DIR = Paths.get("jmods");
  61 
  62     private static final Path IMAGE = Paths.get("image");
  63     private static final String MAIN_MID = "m1/p1.Main";
  64 
  65     // the names of the modules in this test
  66     private static String[] modules = new String[] {"m1", "m2", "m3", "m4", "m5"};
  67 
  68 
  69     private static boolean hasJmods() {
  70         if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) {
  71             System.err.println("Test skipped. NO jmods directory");
  72             return false;
  73         }
  74         return true;
  75     }
  76 
  77     /*
  78      * Compiles all modules used by the test
  79      */
  80     @BeforeTest
  81     public void compileAll() throws Throwable {
  82         if (!hasJmods()) return;
  83 
  84         for (String mn : modules) {
  85             Path msrc = SRC_DIR.resolve(mn);
  86             assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
  87                 "--module-source-path", SRC_DIR.toString(),
  88                 "--add-exports", "java.base/jdk.internal.module=" + mn,
  89                 "--add-exports", "java.base/jdk.internal.org.objectweb.asm=" + mn));
  90         }
  91 
  92         if (Files.exists(IMAGE)) {
  93             FileUtils.deleteFileTreeUnchecked(IMAGE);
  94         }
  95 
  96         createImage(IMAGE, "m1", "m3");
  97 
  98         createJmods("m1", "m4");
  99     }
 100 
 101     /*
 102      * Test the image created when linking with a module with
 103      * no Packages attribute
 104      */
 105     @Test
 106     public void testPackagesAttribute() throws Throwable {
 107         if (!hasJmods()) return;
 108 
 109         Path java = IMAGE.resolve("bin").resolve("java");
 110         assertTrue(executeProcess(java.toString(),
 111                         "--add-exports", "java.base/jdk.internal.module=m1,m4",
 112                         "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 113                         "-m", MAIN_MID)
 114                         .outputTo(System.out)
 115                         .errorTo(System.out)
 116                         .getExitValue() == 0);
 117     }
 118 
 119     /*
 120      * Test the image created when linking with an open module
 121     */
 122     @Test
 123     public void testOpenModule() throws Throwable {
 124         if (!hasJmods()) return;
 125 
 126         Path java = IMAGE.resolve("bin").resolve("java");
 127         assertTrue(executeProcess(java.toString(), "-m", "m3/p3.Main")
 128                         .outputTo(System.out)
 129                         .errorTo(System.out)
 130                         .getExitValue() == 0);
 131     }
 132 
 133     /*
 134      * Disable the fast loading of system modules.
 135      * Parsing module-info.class
 136      */
 137     @Test
 138     public void disableSystemModules() throws Throwable {
 139         if (!hasJmods()) return;
 140 
 141         Path java = IMAGE.resolve("bin").resolve("java");
 142         assertTrue(executeProcess(java.toString(),
 143                                   "--add-exports", "java.base/jdk.internal.module=m1,m4",
 144                                   "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 145                                   "-Djdk.system.module.finder.disabledFastPath",
 146                                   "-m", MAIN_MID)
 147                         .outputTo(System.out)
 148                         .errorTo(System.out)
 149                         .getExitValue() == 0);
 150     }
 151 
 152     /*
 153      * Test the optimization that deduplicates Set<String> on targets of exports,
 154      * uses, provides.
 155      */
 156     @Test
 157     public void testDedupSet() throws Throwable {
 158         if (!hasJmods()) return;
 159 
 160         Path dir = Paths.get("dedupSetTest");
 161         createImage(dir, "m1", "m2", "m3", "m4");
 162         Path java = dir.resolve("bin").resolve("java");
 163         assertTrue(executeProcess(java.toString(),
 164                          "--add-exports", "java.base/jdk.internal.module=m1,m4",
 165                          "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 166                          "-m", MAIN_MID)
 167                         .outputTo(System.out)
 168                         .errorTo(System.out)
 169                         .getExitValue() == 0);
 170     }
 171 
 172     @Test
 173     public void testRequiresStatic() throws Throwable {
 174         if (!hasJmods()) return;
 175 
 176         Path dir = Paths.get("requiresStatic");
 177         createImage(dir, "m5");
 178         Path java = dir.resolve("bin").resolve("java");
 179         assertTrue(executeProcess(java.toString(), "-m", "m5/p5.Main")
 180                         .outputTo(System.out)
 181                         .errorTo(System.out)
 182                         .getExitValue() == 0);
 183 
 184         // run with m3 present
 185         assertTrue(executeProcess(java.toString(),
 186                                   "--module-path", MODS_DIR.toString(),
 187                                   "--add-modules", "m3",
 188                                   "-m", "m5/p5.Main")
 189                         .outputTo(System.out)
 190                         .errorTo(System.out)
 191                         .getExitValue() == 0);
 192     }
 193 
 194     @Test
 195     public void testRequiresStatic2() throws Throwable {
 196         if (!hasJmods()) return;
 197 
 198         Path dir = Paths.get("requiresStatic2");
 199         createImage(dir, "m3", "m5");
 200 
 201         Path java = dir.resolve("bin").resolve("java");
 202         assertTrue(executeProcess(java.toString(), "-m", "m5/p5.Main")
 203                         .outputTo(System.out)
 204                         .errorTo(System.out)
 205                         .getExitValue() == 0);
 206 
 207         // boot layer with m3 and m5
 208         assertTrue(executeProcess(java.toString(),
 209                                   "--add-modules", "m3",
 210                                   "-m", "m5/p5.Main")
 211                         .outputTo(System.out)
 212                         .errorTo(System.out)
 213                         .getExitValue() == 0);
 214     }
 215 
 216     private void createJmods(String... modules) throws IOException {
 217         ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.getJavaBaseTarget();
 218         if (mt == null) {
 219             throw new RuntimeException("ModuleTarget is missing for java.base");
 220         }
 221 
 222         String[] values = mt.targetPlatform().split("-");
 223         String osName = values[0];
 224         String osArch = values[1];
 225 
 226         // create JMOD files
 227         Files.createDirectories(JMODS_DIR);
 228         Stream.of(modules).forEach(mn ->
 229             assertTrue(jmod("create",
 230                 "--class-path", MODS_DIR.resolve(mn).toString(),
 231                 "--target-platform", mt.targetPlatform(),
 232                 "--main-class", mn.replace('m', 'p') + ".Main",
 233                 JMODS_DIR.resolve(mn + ".jmod").toString()) == 0)
 234         );
 235     }
 236 
 237 
 238     /**
 239      * Verify the module descriptor if package p4.dummy is excluded at link time.
 240      */
 241     @Test
 242     public void testModulePackagesAttribute() throws Throwable {
 243         if (!hasJmods()) return;
 244 
 245         // create an image using JMOD files
 246         Path dir = Paths.get("packagesTest");
 247         String mp = Paths.get(JAVA_HOME, "jmods").toString() +
 248             File.pathSeparator + JMODS_DIR.toString();
 249 
 250         Set<String> modules = Set.of("m1", "m4");
 251         assertTrue(JLINK_TOOL.run(System.out, System.out,
 252             "--output", dir.toString(),
 253             "--exclude-resources", "m4/p4/dummy/*",
 254             "--add-modules", modules.stream().collect(Collectors.joining(",")),
 255             "--module-path", mp) == 0);
 256 
 257         // verify ModuleDescriptor
 258         Path java = dir.resolve("bin").resolve("java");
 259         assertTrue(executeProcess(java.toString(),
 260                         "--add-exports", "java.base/jdk.internal.module=m1,m4",
 261                         "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 262                         "--add-modules=m1", "-m", "m4")
 263             .outputTo(System.out)
 264             .errorTo(System.out)
 265             .getExitValue() == 0);
 266     }
 267 
 268     /**
 269      * Verify the plugin to retain ModuleTarget attribute
 270      */
 271     @Test
 272     public void testRetainModuleTarget() throws Throwable {
 273         if (!hasJmods()) return;
 274 
 275         // create an image using JMOD files
 276         Path dir = Paths.get("retainModuleTargetTest");
 277         String mp = Paths.get(JAVA_HOME, "jmods").toString() +
 278             File.pathSeparator + JMODS_DIR.toString();
 279 
 280         Set<String> modules = Set.of("m1", "m4");
 281         assertTrue(JLINK_TOOL.run(System.out, System.out,
 282             "--output", dir.toString(),
 283             "--system-modules", "retainModuleTarget",
 284             "--exclude-resources", "m4/p4/dummy/*",
 285             "--add-modules", modules.stream().collect(Collectors.joining(",")),
 286             "--module-path", mp) == 0);
 287 
 288         // verify ModuleDescriptor
 289         Path java = dir.resolve("bin").resolve("java");
 290         assertTrue(executeProcess(java.toString(),
 291                         "--add-exports", "java.base/jdk.internal.module=m1,m4",
 292                         "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 293                         "--add-modules=m1", "-m", "m4", "retainModuleTarget")
 294             .outputTo(System.out)
 295             .errorTo(System.out)
 296             .getExitValue() == 0);
 297     }
 298 
 299     static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
 300         .orElseThrow(() ->
 301             new RuntimeException("jlink tool not found")
 302         );
 303 
 304     static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
 305         .orElseThrow(() ->
 306             new RuntimeException("jmod tool not found")
 307         );
 308 
 309     static final String MODULE_PATH = Paths.get(JAVA_HOME, "jmods").toString()
 310         + File.pathSeparator + MODS_DIR.toString();
 311 
 312     private void createImage(Path outputDir, String... modules) throws Throwable {
 313         assertTrue(JLINK_TOOL.run(System.out, System.out,
 314             "--output", outputDir.toString(),
 315             "--add-modules", Arrays.stream(modules).collect(Collectors.joining(",")),
 316             "--module-path", MODULE_PATH) == 0);
 317     }
 318 
 319     private static int jmod(String... options) {
 320         System.out.println("jmod " + Arrays.asList(options));
 321         return JMOD_TOOL.run(System.out, System.out, options);
 322     }
 323 }