8237467: jlink plugin to save the argument files as input to jlink in the output image
Reviewed-by: mchung
This commit is contained in:
parent
edfb18a724
commit
4f44fd6308
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
110
test/jdk/tools/jlink/plugins/SaveJlinkArgfilesPluginTest.java
Normal file
110
test/jdk/tools/jlink/plugins/SaveJlinkArgfilesPluginTest.java
Normal 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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user