1 /*
   2  * Copyright (c) 2015, 2016, 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 /**
  25  * @test
  26  * @bug 8156998
  27  * @summary Test --inherit-runtime-environment
  28  * @library /tools/lib
  29  * @modules
  30  *      jdk.compiler/com.sun.tools.javac.api
  31  *      jdk.compiler/com.sun.tools.javac.main
  32  * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask toolbox.JavaTask ModuleTestBase
  33  * @run main InheritRuntimeEnvironmentTest
  34  */
  35 
  36 import java.io.IOException;
  37 import java.nio.file.Files;
  38 import java.nio.file.Path;
  39 import java.util.Arrays;
  40 import java.util.Collections;
  41 import java.util.List;
  42 import java.util.stream.Collectors;
  43 
  44 import toolbox.ModuleBuilder;
  45 import toolbox.JavaTask;
  46 import toolbox.JavacTask;
  47 import toolbox.Task;
  48 
  49 /**
  50  * Tests that javac picks up runtime options with --inherit-runtime-environment.
  51  * For each option, javac is first run using the option directly, as a control.
  52  * javac is then run again, with the same option(s) being passed to the runtime,
  53  * and --inherit-runtime-environment being used by javac.
  54  * @author jjg
  55  */
  56 public class InheritRuntimeEnvironmentTest extends ModuleTestBase {
  57     public static void main(String... args) throws Exception {
  58         InheritRuntimeEnvironmentTest t = new InheritRuntimeEnvironmentTest();
  59         t.runTests();
  60     }
  61 
  62     /**
  63      * Tests that code being compiled can access JDK-internal API using -add-exports.
  64      * @param base
  65      * @throws Exception
  66      */
  67     @Test
  68     public void testAddExports(Path base) throws Exception {
  69         Path src = base.resolve("src");
  70         tb.writeJavaFiles(src,
  71                 "class C { com.sun.tools.javac.main.Main main; }");
  72 
  73         new TestCase(base)
  74                 .testOpts("--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED")
  75                 .files(findJavaFiles(src))
  76                 .run();
  77     }
  78 
  79     /**
  80      * Tests that code in the unnamed module can access a module on the module path using --add-modules.
  81      */
  82     @Test
  83     public void testAddModules(Path base) throws Exception {
  84         Path modules = base.resolve("modules");
  85         new ModuleBuilder(tb, "m1")
  86                 .exports("pkg1")
  87                 .classes("package pkg1; public class C1 { }")
  88                 .build(modules);
  89 
  90         Path src = base.resolve("src");
  91         tb.writeJavaFiles(src,
  92                 "class C { pkg1.C1 c1; }");
  93 
  94         new TestCase(base)
  95                 .testOpts("--module-path", modules.toString(), "--add-modules", "m1")
  96                 .files(findJavaFiles(src))
  97                 .run();
  98     }
  99 
 100     /**
 101      * Tests that a module on the module path is not visible when --limit-modules is used to
 102      * restrict the set of observable modules.
 103      */
 104     @Test
 105     public void testLimitModules(Path base) throws Exception {
 106         Path modules = base.resolve("modules");
 107         new ModuleBuilder(tb, "m1")
 108                 .exports("pkg1")
 109                 .classes("package pkg1; public class C1 { }")
 110                 .build(modules);
 111 
 112         Path src = base.resolve("src");
 113         new ModuleBuilder(tb, "m2")
 114                 .requires("m1")
 115                 .classes("package pkg2; public class C2 { pkg1.C1 c1; }")
 116                 .write(src);
 117 
 118         // This is the control, to verify that by default, the module being compiled will
 119         // be able to read modules on the module path
 120         new TestCase(base)
 121                 .testOpts("--module-path", modules.toString())
 122                 .otherOpts("--module-source-path", src.toString())
 123                 .files(findJavaFiles(src))
 124                 .run();
 125 
 126         Path emptyClassPath = base.resolve("emptyClassPath");
 127 
 128         Files.createDirectories(emptyClassPath);
 129 
 130         // This is the test, to verify that the module being compiled will not be able to read
 131         // modules on the module path when a --limit-modules is used
 132         new TestCase(base)
 133                 .testOpts("--module-path", modules.toString(), "--limit-modules", "jdk.compiler")
 134                 .otherOpts("-XDrawDiagnostics",
 135                         "--module-source-path", src.toString(),
 136                         "-classpath", emptyClassPath.toString())
 137                 .files(findJavaFiles(src))
 138                 .expect(Task.Expect.FAIL, "compiler.err.module.not.found")
 139                 .run();
 140     }
 141 
 142     /**
 143      * Tests that a module being compiled can see another module on the module path
 144      * using --module-path.
 145      */
 146     @Test
 147     public void testModulePath(Path base) throws Exception {
 148         Path modules = base.resolve("modules");
 149         new ModuleBuilder(tb, "m1")
 150                 .exports("pkg1")
 151                 .classes("package pkg1; public class C1 { }")
 152                 .build(modules);
 153 
 154         Path src = base.resolve("src");
 155         new ModuleBuilder(tb, "m2")
 156                 .requires("m1")
 157                 .classes("package pkg2; public class C2 { pkg1.C1 c1; }")
 158                 .write(src);
 159 
 160         new TestCase(base)
 161                 .testOpts("--module-path", modules.toString())
 162                 .otherOpts("--module-source-path", src.toString())
 163                 .files(findJavaFiles(src))
 164                 .run();
 165     }
 166 
 167     /**
 168      * Tests that a module being compiled can see classes patches into an existing module
 169      * with --patch-module
 170      */
 171     @Test
 172     public void testPatchModule(Path base) throws Exception {
 173         Path patchSrc = base.resolve("patchSrc");
 174         tb.writeJavaFiles(patchSrc,
 175                 "package java.util; public class Xyzzy { }");
 176         Path patch = base.resolve("patch");
 177         Files.createDirectories(patch);
 178 
 179         new JavacTask(tb)
 180                 .options("--patch-module", "java.base=" + patchSrc.toString())
 181                 .outdir(patch)
 182                 .sourcepath(patchSrc)
 183                 .files(findJavaFiles(patchSrc))
 184                 .run()
 185                 .writeAll();
 186 
 187         Path src = base.resolve("src");
 188         tb.writeJavaFiles(src,
 189                 "public class C { java.util.Xyzzy x; }");
 190 
 191         new TestCase(base)
 192                 .testOpts("--patch-module", "java.base=" + patch)
 193                 .files(findJavaFiles(src))
 194                 .run();
 195     }
 196 
 197     /**
 198      * Tests that options in @files are also effective.
 199      * The test is similar to testModulePath, except that the test options are provided in an @-file.
 200      */
 201     @Test
 202     public void testAtFile(Path base) throws Exception {
 203         Path modules = base.resolve("modules");
 204         new ModuleBuilder(tb, "m1")
 205                 .exports("pkg1")
 206                 .classes("package pkg1; public class C1 { }")
 207                 .build(modules);
 208 
 209         Path src = base.resolve("src");
 210         new ModuleBuilder(tb, "m2")
 211                 .requires("m1")
 212                 .classes("package pkg2; public class C2 { pkg1.C1 c1; }")
 213                 .write(src);
 214 
 215         Path atFile = base.resolve("atFile");
 216         tb.writeFile(atFile, "--module-path " + modules);
 217 
 218         new TestCase(base)
 219                 .testOpts("@" + atFile)
 220                 .otherOpts("--module-source-path", src.toString())
 221                 .files(findJavaFiles(src))
 222                 .run();
 223     }
 224 
 225     /**
 226      * Tests that --inherit-runtime-environment works in conjunction with
 227      * environment variables.
 228      * This is a variant of testAddExports.
 229      * The use of environment variables is sufficiently custom that it is
 230      * not easy to do this directly with a simple TestCase.
 231      */
 232     @Test
 233     public void testEnvVars(Path base) throws Exception {
 234         Path src = base.resolve("src");
 235         tb.writeJavaFiles(src,
 236                 "class C { com.sun.tools.javac.main.Main main; }");
 237         List<String> testOpts =
 238                 Arrays.asList("--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED");
 239         List<Path> files = Arrays.asList(findJavaFiles(src));
 240 
 241         String envName = "_JAVAC_OPTIONS";
 242         String envValue = String.join(" ", testOpts);
 243 
 244         out.println("  javac:");
 245         Path javacOutDir = base.resolve("out-javac");
 246         Files.createDirectories(javacOutDir);
 247 
 248         out.println("    env: " + envName + "=" + envValue);
 249         out.println("    outdir: " + javacOutDir);
 250         out.println("    files: " + files);
 251 
 252         new JavacTask(tb, Task.Mode.EXEC)
 253                 .envVar(envName, envValue)
 254                 .outdir(javacOutDir)
 255                 .files(files)
 256                 .run()
 257                 .writeAll()
 258                 .getOutput(Task.OutputKind.DIRECT);
 259 
 260         out.println("  java:");
 261         Path javaOutDir = base.resolve("out-java");
 262         Files.createDirectories(javaOutDir);
 263 
 264         Path atFile = base.resolve("atFile");
 265         tb.writeFile(atFile, String.join(" ", testOpts));
 266 
 267         List<String> vmOpts = Arrays.asList(
 268                 "@" + atFile,
 269                 "--module", "jdk.compiler/com.sun.tools.javac.Main"
 270         );
 271 
 272         List<String> classArgs = join(
 273                 Arrays.asList("-d", javaOutDir.toString()),
 274                 files.stream()
 275                         .map(p -> p.toString())
 276                         .collect(Collectors.toList())
 277         );
 278 
 279         envValue = "--inherit-runtime-environment";
 280 
 281         out.println("    env: " + envName + "=" + envValue);
 282         out.println("    vmOpts: " + vmOpts);
 283         out.println("    classArgs: " + classArgs);
 284 
 285         new JavaTask(tb)
 286                 .envVar(envName, envValue)
 287                 .vmOptions(vmOpts)
 288                 .classArgs(classArgs)
 289                 .run()
 290                 .writeAll()
 291                 .getOutput(Task.OutputKind.STDERR);
 292     }
 293 
 294     /**
 295      * Runs javac with given test options,  first directly, and then again, specifying the
 296      * options to the runtime, and using --inherit-runtime-environment.
 297      */
 298     class TestCase {
 299         final Path base;
 300         List<String> testOpts = Collections.emptyList();
 301         List<String> otherOpts = Collections.emptyList();
 302         List<Path> files = Collections.emptyList();
 303         Task.Expect expect = Task.Expect.SUCCESS;
 304         String expectedText;
 305 
 306         /**
 307          * Creates a test case, specifying a base directory for work files.
 308          */
 309         TestCase(Path base) {
 310             this.base = base;
 311         }
 312 
 313         /**
 314          * Set the "test options" to be passed to javac or to the runtime.
 315          */
 316         TestCase testOpts(String... testOpts) {
 317             this.testOpts = Arrays.asList(testOpts);
 318             return this;
 319         }
 320 
 321         /**
 322          * Sets additional options required for the compilation.
 323          */
 324         TestCase otherOpts(String... otherOpts) {
 325             this.otherOpts = Arrays.asList(otherOpts);
 326             return this;
 327         }
 328 
 329         /**
 330          * Sets the files to be compiled.
 331          */
 332         TestCase files(Path... files) {
 333             this.files = Arrays.asList(files);
 334             return this;
 335         }
 336 
 337         /**
 338          * Sets the expected output, and any expected output from javac.
 339          * The default is {@code Expect.SUCCESS} and no specific output expected.
 340          */
 341         TestCase expect(Task.Expect expect, String expectedText) {
 342             this.expect = expect;
 343             this.expectedText = expectedText;
 344             return this;
 345         }
 346 
 347         /**
 348          * Runs the test case.
 349          * First, javac is run passing the test options directly to javac.
 350          * Then, javac is run again, passing the test options to the runtime,
 351          * and using --inherit-runtime-environment.
 352          */
 353         void run() throws IOException {
 354             runJavac();
 355             runJava();
 356         }
 357 
 358         private void runJavac() throws IOException {
 359             out.println("  javac:");
 360             Path javacOutDir = base.resolve("out-javac");
 361             Files.createDirectories(javacOutDir);
 362 
 363             List<String> options = join(testOpts, otherOpts);
 364 
 365             out.println("    options: " + options);
 366             out.println("    outdir: " + javacOutDir);
 367             out.println("    files: " + files);
 368 
 369             String log = new JavacTask(tb, Task.Mode.CMDLINE)
 370                     .options(options)
 371                     .outdir(javacOutDir)
 372                     .files(files)
 373                     .run(expect)
 374                     .writeAll()
 375                     .getOutput(Task.OutputKind.DIRECT);
 376 
 377             if (expectedText != null && !log.contains(expectedText))
 378                 error("expected text not found");
 379         }
 380 
 381         private void runJava() throws IOException {
 382             out.println("  java:");
 383             Path javaOutDir = base.resolve("out-java");
 384             Files.createDirectories(javaOutDir);
 385 
 386             List<String> vmOpts = join(
 387                     testOpts,
 388                     Arrays.asList("--module", "jdk.compiler/com.sun.tools.javac.Main")
 389             );
 390 
 391             List<String> classArgs = join(
 392                     Arrays.asList("--inherit-runtime-environment",
 393                             "-d", javaOutDir.toString()),
 394                     otherOpts,
 395                     files.stream()
 396                             .map(p -> p.toString())
 397                             .collect(Collectors.toList())
 398             );
 399 
 400             out.println("    vmOpts: " + vmOpts);
 401             out.println("    classArgs: " + classArgs);
 402 
 403             String log = new JavaTask(tb)
 404                     .vmOptions(vmOpts)
 405                     .classArgs(classArgs)
 406                     .run(expect)
 407                     .writeAll()
 408                     .getOutput(Task.OutputKind.STDERR);
 409 
 410             if (expectedText != null && !log.contains(expectedText))
 411                 error("expected text not found");
 412         }
 413     }
 414 
 415     /**
 416      * Join a series of lists.
 417      */
 418     @SafeVarargs
 419     private <T> List<T> join(List<T>... lists) {
 420         return Arrays.stream(lists)
 421             .flatMap(list -> list.stream())
 422             .collect(Collectors.toList());
 423     }
 424 
 425 }
 426