From 69c309f700c131f00e4fe25059c6c43809f1f7c1 Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Thu, 25 Aug 2022 14:28:35 +0200 Subject: [PATCH 1/2] add CopyFilesPlugin (JDK-8294394) --- .../internal/plugins/CopyFilesPlugin.java | 106 ++++++++++++++++++ .../tools/jlink/resources/plugins.properties | 7 ++ src/jdk.jlink/share/classes/module-info.java | 1 + .../jlink/plugins/CopyFilesPluginTest.java | 98 ++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CopyFilesPlugin.java create mode 100644 test/jdk/tools/jlink/plugins/CopyFilesPluginTest.java diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CopyFilesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CopyFilesPlugin.java new file mode 100644 index 00000000000..ea860767b99 --- /dev/null +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CopyFilesPlugin.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ +package jdk.tools.jlink.internal.plugins; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import jdk.tools.jlink.plugin.*; +import jdk.tools.jlink.plugin.ResourcePoolEntry.Type; + +/** + * Jlink plugin to copy files in the current runtime image into the output image. + * The files to copy are specified as {@link File#pathSeparatorChar} separated + * paths relative to the image root directory. + */ +public final class CopyFilesPlugin implements Plugin { + + public static final String NAME = "copy-files"; + + /** + * List of relative path names for the files to copy. + */ + private final List files = new ArrayList<>(); + + @Override + public String getName() { + return NAME; + } + + @Override + public Category getType() { + return Category.ADDER; + } + + @Override + public String getDescription() { + return PluginsResourceBundle.getDescription(NAME); + } + + @Override + public String getArgumentsDescription() { + return PluginsResourceBundle.getArgument(NAME); + } + + @Override + public boolean hasArguments() { + return true; + } + + @Override + public boolean hasRawArgument() { + return true; + } + + @Override + public void configure(Map config) { + String arg = config.get(getName()); + if (arg == null) { + throw new AssertionError(); + } + for (String relativePath : arg.split(File.pathSeparator)) { + files.add(relativePath); + } + } + + @Override + public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { + if (!files.isEmpty()) { + in.transformAndCopy(Function.identity(), out); + + String javaHome = System.getProperty("java.home"); + for (String relativePath : files) { + Path file = Paths.get(javaHome, relativePath); + out.add(ResourcePoolEntry.create("/java.base/top/" + relativePath, Type.TOP, file)); + } + } + return out.build(); + } +} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties index 365606b3d2a..fc63e3821e5 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties @@ -59,6 +59,13 @@ compact-cp.description=Constant Pool strings sharing.\n\ By default, all resources are compressed. You can express the set \n\ of resources to compress or not compress (use ^ for negation). +copy-files.argument= + +copy-files.description=\ +Copies the specified files from the current image to the output image. \n\ + is a ':' (';' on Windows) separated list of file paths \n\ +relative to the image root. + dedup-legal-notices.argument=[error-if-not-same-content] dedup-legal-notices.description=\ diff --git a/src/jdk.jlink/share/classes/module-info.java b/src/jdk.jlink/share/classes/module-info.java index fe3bca9ecd0..271922f09b4 100644 --- a/src/jdk.jlink/share/classes/module-info.java +++ b/src/jdk.jlink/share/classes/module-info.java @@ -69,6 +69,7 @@ jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin, jdk.tools.jlink.internal.plugins.LegalNoticeFilePlugin, jdk.tools.jlink.internal.plugins.SystemModulesPlugin, + jdk.tools.jlink.internal.plugins.CopyFilesPlugin, jdk.tools.jlink.internal.plugins.StripNativeCommandsPlugin, jdk.tools.jlink.internal.plugins.OrderResourcesPlugin, jdk.tools.jlink.internal.plugins.DefaultCompressPlugin, diff --git a/test/jdk/tools/jlink/plugins/CopyFilesPluginTest.java b/test/jdk/tools/jlink/plugins/CopyFilesPluginTest.java new file mode 100644 index 00000000000..e8eb54fc98a --- /dev/null +++ b/test/jdk/tools/jlink/plugins/CopyFilesPluginTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2022, 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. + */ + +/* + * @test + * @summary Test copy files plugin + * @library ../../lib + * @library /test/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 CopyFilesPluginTest + */ + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.Path; + +import jdk.test.lib.process.ProcessTools; + +import tests.Helper; + +public class CopyFilesPluginTest { + + public static void main(String[] args) throws Throwable { + + Helper helper = Helper.newHelper(); + if (helper == null) { + System.err.println("Test not run"); + return; + } + + var module = "copyfiles"; + helper.generateDefaultJModule(module); + var image = helper.generateDefaultImage(new String[] { + "--add-modules", "jdk.jlink,jdk.jdeps,jdk.internal.opt,jdk.compiler,java.compiler", + "--keep-packaged-modules", "images/copyfiles.image/jmods" + }, module) + .assertSuccess(); + helper.checkImage(image, module, null, null); + + String[] files = { + "lib/foo", + "bin/bar", + "foreign/baz" + }; + + for (String file : files) { + Path path = image.resolve(file); + Files.createDirectories(path.getParent()); + Files.writeString(image.resolve(file), file); + } + + var launcher = image.resolve("bin/jlink" + + (System.getProperty("os.name").startsWith("Windows") + ? ".exe" : "")); + + + var oa = ProcessTools.executeProcess(launcher.toString(), + "--add-modules", "ALL-MODULE-PATH", + "--copy-files=" + String.join(File.pathSeparator, files), + "--output", "image2"); + oa.shouldHaveExitValue(0); + + for (String file : files) { + Path path = Paths.get("image2", file); + String content = Files.readString(path); + if (!file.equals(content)) { + throw new AssertionError(path + ": expected \"" + file + "\", got \"" + content + "\""); + } + } + } +} From b2f8a87461ba2efd3cb3487ab3c60eadaa20fdbb Mon Sep 17 00:00:00 2001 From: Doug Simon Date: Mon, 26 Sep 2022 00:17:29 +0200 Subject: [PATCH 2/2] add SaveJlinkArgfilesPlugin (JDK-8237467) --- ci.jsonnet | 2 +- .../jdk/tools/jlink/internal/CommandLine.java | 166 ++++++++++++++++++ .../jdk/tools/jlink/internal/JlinkTask.java | 15 ++ .../plugins/SaveJlinkArgfilesPlugin.java | 117 ++++++++++++ .../tools/jlink/resources/plugins.properties | 7 + src/jdk.jlink/share/classes/module-info.java | 3 +- .../plugins/SaveJlinkArgfilesPluginTest.java | 110 ++++++++++++ 7 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 src/jdk.jlink/share/classes/jdk/tools/jlink/internal/CommandLine.java create mode 100644 src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SaveJlinkArgfilesPlugin.java create mode 100644 test/jdk/tools/jlink/plugins/SaveJlinkArgfilesPluginTest.java diff --git a/ci.jsonnet b/ci.jsonnet index 3c86133a71c..65239aee6c7 100644 --- a/ci.jsonnet +++ b/ci.jsonnet @@ -1,5 +1,5 @@ # https://github.com/graalvm/labs-openjdk-11/blob/master/doc/testing.md -local run_test_spec = "test/hotspot/jtreg/compiler/jvmci"; +local run_test_spec = "test/hotspot/jtreg/compiler/jvmci test/jdk/tools/jlink/plugins"; local labsjdk_builder_version = "e9c60b5174490f2012c7c5d60a20aace93209a56"; diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/CommandLine.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/CommandLine.java new file mode 100644 index 00000000000..f504d7a5739 --- /dev/null +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/CommandLine.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2022, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.tools.jlink.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; + +/** + * This file was originally a copy of CommandLine.java in + * com.sun.tools.javac.main. + * It should track changes made to that file. + */ + +/** + * Various utility methods for processing Java tool command line arguments. + */ +public class CommandLine { + + static void loadCmdFile(InputStream in, List args) + throws IOException { + try (Reader r = new InputStreamReader(in)) { + Tokenizer t = new Tokenizer(r); + String s; + while ((s = t.nextToken()) != null) { + args.add(s); + } + } + } + public static class Tokenizer { + private final Reader in; + private int ch; + + public Tokenizer(Reader in) throws IOException { + this.in = in; + ch = in.read(); + } + + public String nextToken() throws IOException { + skipWhite(); + if (ch == -1) { + return null; + } + + StringBuilder sb = new StringBuilder(); + char quoteChar = 0; + + while (ch != -1) { + switch (ch) { + case ' ': + case '\t': + case '\f': + if (quoteChar == 0) { + return sb.toString(); + } + sb.append((char) ch); + break; + + case '\n': + case '\r': + return sb.toString(); + + case '\'': + case '"': + if (quoteChar == 0) { + quoteChar = (char) ch; + } else if (quoteChar == ch) { + quoteChar = 0; + } else { + sb.append((char) ch); + } + break; + + case '\\': + if (quoteChar != 0) { + ch = in.read(); + switch (ch) { + case '\n': + case '\r': + while (ch == ' ' || ch == '\n' + || ch == '\r' || ch == '\t' + || ch == '\f') { + ch = in.read(); + } + continue; + + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case 'f': + ch = '\f'; + break; + default: + break; + } + } + sb.append((char) ch); + break; + + default: + sb.append((char) ch); + } + + ch = in.read(); + } + + return sb.toString(); + } + + void skipWhite() throws IOException { + while (ch != -1) { + switch (ch) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\f': + break; + + case '#': + ch = in.read(); + while (ch != '\n' && ch != '\r' && ch != -1) { + ch = in.read(); + } + break; + + default: + return; + } + + ch = in.read(); + } + } + } +} 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 beba588a8c2..22dae6c9304 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 @@ -27,6 +27,7 @@ import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.io.UncheckedIOException; import java.lang.module.Configuration; @@ -221,12 +222,26 @@ static class OptionsValues { boolean suggestProviders = false; } + public static final String OPTIONS_RESOURCE = "jdk/tools/jlink/internal/options"; + int run(String[] args) { if (log == null) { setLog(new PrintWriter(System.out, true), new PrintWriter(System.err, true)); } try { + Module m = JlinkTask.class.getModule(); + try (InputStream savedOptions = m.getResourceAsStream(OPTIONS_RESOURCE)) { + if (savedOptions != null) { + List prependArgs = new ArrayList<>(); + CommandLine.loadCmdFile(savedOptions, prependArgs); + if (!prependArgs.isEmpty()) { + prependArgs.addAll(Arrays.asList(args)); + args = prependArgs.toArray(new String[prependArgs.size()]); + } + } + } + List remaining = optionsHelper.handleOptions(this, args); if (remaining.size() > 0 && !options.suggestProviders) { throw taskHelper.newBadArgs("err.orphan.arguments", diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SaveJlinkArgfilesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SaveJlinkArgfilesPlugin.java new file mode 100644 index 00000000000..b9f04c6c5e3 --- /dev/null +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SaveJlinkArgfilesPlugin.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2022, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.tools.jlink.internal.plugins; + +import static jdk.tools.jlink.internal.JlinkTask.OPTIONS_RESOURCE; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import jdk.tools.jlink.plugin.*; + +/** + * Saves the arguments in the specified argument files to a resource that's read + * by jlink in the output image. The saved arguments are prepended to the arguments + * specified on the jlink command line. + */ +public final class SaveJlinkArgfilesPlugin implements Plugin { + + public static final String NAME = "save-jlink-argfiles"; + + private List argfiles = new ArrayList<>(); + + @Override + public String getName() { + return NAME; + } + + @Override + public Category getType() { + return Category.ADDER; + } + + @Override + public String getDescription() { + return PluginsResourceBundle.getDescription(NAME); + } + + @Override + public String getArgumentsDescription() { + return PluginsResourceBundle.getArgument(NAME); + } + + @Override + public boolean hasArguments() { + return true; + } + + @Override + public boolean hasRawArgument() { + return true; + } + + @Override + public void configure(Map config) { + var v = config.get(getName()); + + if (v == null) + throw new AssertionError(); + + for (String argfile : v.split(File.pathSeparator)) { + argfiles.add(readArgfile(argfile)); + } + } + + private static String readArgfile(String argfile) { + try { + return Files.readString(Path.of(argfile)); + } catch (IOException e) { + throw new PluginException("Argfile " + argfile + " is not readable"); + } + } + + @Override + public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { + in.transformAndCopy(Function.identity(), out); + if (!in.moduleView().findModule("jdk.jlink").isPresent()) { + throw new PluginException("--save-jlink-argfiles requires jdk.jlink to be in the output image"); + } + byte[] savedOptions = argfiles.stream() + .collect(Collectors.joining("\n")) + .getBytes(StandardCharsets.UTF_8); + out.add(ResourcePoolEntry.create("/jdk.jlink/" + OPTIONS_RESOURCE, + savedOptions)); + return out.build(); + } +} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties index fc63e3821e5..4a10bcea802 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties @@ -110,6 +110,13 @@ is specified, then each line should be an exact match for the path to be ordered order-resources.description=\ Order resources. e.g.: **/module-info.class,@classlist,/java.base/java/lang/** +save-jlink-argfiles.argument= + +save-jlink-argfiles.description=\ +Save the specified argument files that contain the arguments \n\ +to be prepended to the execution of jlink in the output image. \n\ + is a ':' (';' on Windows) separated list of command-line argument files. + strip-debug.description=\ Strip debug information from the output image diff --git a/src/jdk.jlink/share/classes/module-info.java b/src/jdk.jlink/share/classes/module-info.java index 271922f09b4..50096953532 100644 --- a/src/jdk.jlink/share/classes/module-info.java +++ b/src/jdk.jlink/share/classes/module-info.java @@ -80,6 +80,7 @@ jdk.tools.jlink.internal.plugins.AddOptionsPlugin, jdk.tools.jlink.internal.plugins.VendorBugURLPlugin, jdk.tools.jlink.internal.plugins.VendorVMBugURLPlugin, - jdk.tools.jlink.internal.plugins.VendorVersionPlugin; + jdk.tools.jlink.internal.plugins.VendorVersionPlugin, + jdk.tools.jlink.internal.plugins.SaveJlinkArgfilesPlugin; } diff --git a/test/jdk/tools/jlink/plugins/SaveJlinkArgfilesPluginTest.java b/test/jdk/tools/jlink/plugins/SaveJlinkArgfilesPluginTest.java new file mode 100644 index 00000000000..80b3cd04b61 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/SaveJlinkArgfilesPluginTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022, 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. + */ + +/* + * @test + * @summary Test --save-jlink-argfiles plugin + * @library ../../lib + * @library /test/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 SaveJlinkArgfilesPluginTest + */ + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; + +import jdk.test.lib.process.ProcessTools; + +import tests.Helper; + +public class SaveJlinkArgfilesPluginTest { + + public static void main(String[] args) throws Throwable { + + Helper helper = Helper.newHelper(); + if (helper == null) { + System.err.println("Test not run"); + return; + } + + String exe = System.getProperty("os.name").startsWith("Windows") ? ".exe" : ""; + Path argfile1 = Path.of("argfile1"); + Path argfile2 = Path.of("argfile2"); + + Files.writeString(argfile1, "--add-modules jdk.internal.vm.ci --add-options=-Dfoo=xyzzy"); + Files.writeString(argfile2, "--vendor-version=\"XyzzyVM 3.14.15\" --vendor-bug-url=https://bugs.xyzzy.com/"); + + var module = "base"; + helper.generateDefaultJModule(module); + var image = helper.generateDefaultImage(new String[] { + "--add-modules", "jdk.jlink,jdk.jdeps,jdk.internal.opt,jdk.compiler,java.compiler,jdk.internal.vm.ci", + "--keep-packaged-modules", "images/base.image/jmods", + "--save-jlink-argfiles", argfile1 + File.pathSeparator + argfile2 + }, module) + .assertSuccess(); + helper.checkImage(image, module, null, null); + + String launcher = image.resolve("bin/java" + exe).toString(); + var oa = ProcessTools.executeProcess(launcher, "-XshowSettings:properties", "--version"); + + // Check that the primary image creation ignored the saved args + oa.shouldHaveExitValue(0); + oa.shouldNotMatch("java.vendor.url.bug = https://bugs.xyzzy.com/"); + oa.shouldNotMatch("java.vendor.version = XyzzyVM 3.14.15"); + oa.shouldNotMatch("foo = xyzzy"); + + // Check that --save-jlink-argfiles fails if jdk.jlink not in the output image + launcher = image.resolve("bin/jlink" + exe).toString(); + oa = ProcessTools.executeProcess(launcher, "--output=ignore", "--save-jlink-argfiles=" + argfile1); + oa.shouldHaveExitValue(1); + oa.stdoutShouldMatch("--save-jlink-argfiles requires jdk.jlink to be in the output image"); + + // Create a secondary image + Path image2 = Path.of("image2").toAbsolutePath(); + launcher = image.resolve("bin/jlink" + exe).toString(); + oa = ProcessTools.executeProcess(launcher, "--output", image2.toString(), "--add-modules=java.base"); + oa.shouldHaveExitValue(0); + + // Ensure the saved `--add-options` and `--vendor-*` options + // were applied when creating the secondary image. + launcher = image2.resolve(Path.of("bin", "java" + exe)).toString(); + oa = ProcessTools.executeProcess(launcher, "-XshowSettings:properties", "--version"); + oa.shouldHaveExitValue(0); + oa.stdoutShouldMatch(" XyzzyVM 3.14.15 "); + oa.stderrShouldMatch("java.vendor.url.bug = https://bugs.xyzzy.com/"); + oa.stderrShouldMatch("java.vendor.version = XyzzyVM 3.14.15"); + oa.stderrShouldMatch("foo = xyzzy"); + + // Ensure the saved `--add-modules` option + // was applied when creating the secondary image. + oa = ProcessTools.executeProcess(launcher.toString(), "-d", "jdk.internal.vm.ci"); + oa.shouldHaveExitValue(0); + } +}