8237467: jlink plugin to save the argument files as input to jlink in the output image

Reviewed-by: mchung
This commit is contained in:
Doug Simon 2022-10-03 16:24:43 +00:00
parent edfb18a724
commit 4f44fd6308
6 changed files with 414 additions and 1 deletions

View File

@ -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<String> 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();
}
}
}
}

View File

@ -27,6 +27,7 @@ package jdk.tools.jlink.internal;
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;
@ -223,6 +224,8 @@ public class JlinkTask {
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),
@ -230,6 +233,18 @@ public class JlinkTask {
}
Path outputPath = null;
try {
Module m = JlinkTask.class.getModule();
try (InputStream savedOptions = m.getResourceAsStream(OPTIONS_RESOURCE)) {
if (savedOptions != null) {
List<String> prependArgs = new ArrayList<>();
CommandLine.loadCmdFile(savedOptions, prependArgs);
if (!prependArgs.isEmpty()) {
prependArgs.addAll(Arrays.asList(args));
args = prependArgs.toArray(new String[prependArgs.size()]);
}
}
}
List<String> remaining = optionsHelper.handleOptions(this, args);
if (remaining.size() > 0 && !options.suggestProviders) {
throw taskHelper.newBadArgs("err.orphan.arguments",

View File

@ -0,0 +1,107 @@
/*
* 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.PluginException;
import jdk.tools.jlink.plugin.ResourcePool;
import jdk.tools.jlink.plugin.ResourcePoolBuilder;
import jdk.tools.jlink.plugin.ResourcePoolEntry;
/**
* 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 extends AbstractPlugin {
public SaveJlinkArgfilesPlugin() {
super("save-jlink-argfiles");
}
private List<String> argfiles = new ArrayList<>();
@Override
public Category getType() {
return Category.ADDER;
}
@Override
public boolean hasArguments() {
return true;
}
@Override
public boolean hasRawArgument() {
return true;
}
@Override
public void configure(Map<String, String> 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();
}
}

View File

@ -187,6 +187,20 @@ order-resources.usage=\
\ e.g.: **/module-info.class,@classlist,\n\
\ /java.base/java/lang/**
save-jlink-argfiles.argument=<filenames>
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\
<filenames> is a ':' (';' on Windows) separated list of command-line argument files.
save-jlink-argfiles.usage=\
\ --save-jlink-argfiles <filenames>\n\
\ Save the specified argument files that contain \n\
\ the arguments to be prepended to the execution of \n\
\ jlink in the output image. <filenames> is a \n\
\ ':' (';' on Windows) separated list of command-line argument files.
strip-debug.description=\
Strip debug information from the output image

View File

@ -80,6 +80,7 @@ module jdk.jlink {
jdk.tools.jlink.internal.plugins.VendorBugURLPlugin,
jdk.tools.jlink.internal.plugins.VendorVMBugURLPlugin,
jdk.tools.jlink.internal.plugins.VendorVersionPlugin,
jdk.tools.jlink.internal.plugins.CDSPlugin;
jdk.tools.jlink.internal.plugins.CDSPlugin,
jdk.tools.jlink.internal.plugins.SaveJlinkArgfilesPlugin;
}

View File

@ -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.zipfs,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);
}
}