diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java index 6c760d1fa34ef..fbdb4a54fedc9 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java @@ -432,6 +432,19 @@ private JlinkConfiguration initJlinkConfig() throws BadArgs { throw taskHelper.newBadArgs("err.empty.module.path", modPath); } + // Verify legitimacy of --limit-modules + Set depSet = newConfiguration(finder, initialRoots) + .modules() + .stream() + .map(ResolvedModule::name) + .collect(Collectors.toSet()); + for (String modLim: options.limitMods) { + // If the module limits aren't in the dep tree of the + // root set this is an error. + if (!depSet.contains(modLim)) { + throw taskHelper.newBadArgs("err.limit.modules", modLim); + } + } // Use a module finder with limited observability, as determined // by initialRoots, to find the observable modules from the // application module path (--module-path option) only. We must @@ -475,6 +488,11 @@ public static ModuleFinder newModuleFinder(List paths) { return ModulePath.of(version, true, entries); } + private Configuration newConfiguration(ModuleFinder finder, Set roots) { + return options.bindServices ? Configuration.empty().resolveAndBind(finder, ModuleFinder.of(), roots) : + Configuration.empty().resolve(finder, ModuleFinder.of(), roots); + } + private void createImage(JlinkConfiguration config) throws Exception { if (options.output == null) { throw taskHelper.newBadArgs("err.output.must.be.specified").showUsage(true); diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties index 2ec122ec8e22d..5fb798f626d50 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties @@ -129,6 +129,7 @@ err.runtime.link.patched.module=jlink does not support linking from the run-time \ when running on a patched runtime with --patch-module err.no.module.path=--module-path option must be specified with --add-modules ALL-MODULE-PATH err.empty.module.path=No module found in module path ''{0}'' with --add-modules ALL-MODULE-PATH +err.limit.modules={0} not in module dependency graph, but specified with --limit-modules err.jlink.version.mismatch=jlink version {0}.{1} does not match target java.base version {2}.{3} err.automatic.module:automatic module cannot be used with jlink: {0} from {1} err.unknown.byte.order:unknown byte order {0} diff --git a/test/jdk/tools/jlink/basic/AllModulePath.java b/test/jdk/tools/jlink/basic/AllModulePath.java index 12ad14365dc81..c5e065fb5282d 100644 --- a/test/jdk/tools/jlink/basic/AllModulePath.java +++ b/test/jdk/tools/jlink/basic/AllModulePath.java @@ -27,6 +27,11 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.lang.module.Configuration; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.lang.module.ResolvedModule; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -40,6 +45,7 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import jdk.internal.module.ModulePath; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; @@ -54,6 +60,7 @@ * @library ../../lib /test/lib * @modules jdk.compiler * java.base/jdk.internal.jimage + * java.base/jdk.internal.module * jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jimage * @build jdk.test.lib.process.ProcessTools @@ -235,6 +242,58 @@ public void modulePathWithLimitMods() throws Exception { verifyListModules(targetPath, expected); } + /* + * --add-modules ALL-MODULE-PATH with an existing module path and module + * limits applied. This case tests a module limit that does not exist in the + * module dependency graph. I.e. a module-limit specified on a module that + * would not get included into the image if that --limit-modules clause would + * be absent. This is an error. + */ + @Test + public void modulePathWithLimitModNotInDepTree() throws Exception { + if (isExplodedJDKImage()) { + return; + } + Path targetPath = HELPER.createNewImageDir("all-mods-limit-mods-error"); + String moduleName = "com.bar.testmod"; + Result result = HELPER.generateDefaultJModule(moduleName, "jdk.jdeps"); + Path customModulePath = result.getFile().getParent(); + ModuleFinder finder = moduleFinder(new Path[] { customModulePath }); + Set roots = finder.findAll().stream() + .map(ModuleReference::descriptor) + .map(ModuleDescriptor::name) + .collect(Collectors.toSet()); + Set depSet = Configuration.empty() + .resolve(finder, ModuleFinder.ofSystem(), roots) + .modules() + .stream() + .map(ResolvedModule::name) + .collect(Collectors.toSet()); + String moduleOutsideDepTree = "jdk.net"; + if (depSet.contains(moduleOutsideDepTree)) { + throw new AssertionError("Invalid test setup! " + moduleOutsideDepTree + " not " + + "expected in the dependency tree of " + moduleName); + } + List allArgs = List.of("--add-modules", "ALL-MODULE-PATH", + "--add-modules", moduleName, + // jdk.jfr doesn't exist in the module path + // and is no dependency of com.bar.testmod + "--limit-modules", moduleOutsideDepTree, + "--module-path", customModulePath.toString(), + "--output", targetPath.toString()); + JlinkOutput allOut = createImage(targetPath, allArgs, false /* success */); + String stdOut = allOut.stdout.trim(); + String expectedMsg = String.format("Error: %s not in module dependency graph," + + " but specified with --limit-modules", moduleOutsideDepTree); + assertEquals(stdOut, expectedMsg); + assertTrue(allOut.stderr.isEmpty()); + } + + private ModuleFinder moduleFinder(Path[] path) { + Runtime.Version version = Runtime.version(); + return ModulePath.of(version, true, path); + } + /* * check the modules linked in the image using m1/p.ListModules */