--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Sun Dec 18 18:09:05 2016 -0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Mon Dec 19 09:48:59 2016 +0530
@@ -130,6 +130,7 @@
}
private final Path root;
+ private final Map<String, String> launchers;
private final Path mdir;
private final Set<String> modules = new HashSet<>();
private String targetOsName;
@@ -140,10 +141,9 @@
* @param root The image root directory.
* @throws IOException
*/
- public DefaultImageBuilder(Path root) throws IOException {
- Objects.requireNonNull(root);
-
- this.root = root;
+ public DefaultImageBuilder(Path root, Map<String, String> launchers) throws IOException {
+ this.root = Objects.requireNonNull(root);
+ this.launchers = Objects.requireNonNull(launchers);
this.mdir = root.resolve("lib");
Files.createDirectories(mdir);
}
@@ -235,7 +235,7 @@
// If native files are stripped completely, <root>/bin dir won't exist!
// So, don't bother generating launcher scripts.
if (Files.isDirectory(bin)) {
- prepareApplicationFiles(files, modules);
+ prepareApplicationFiles(files);
}
} catch (IOException ex) {
throw new PluginException(ex);
@@ -246,22 +246,44 @@
* Generates launcher scripts.
*
* @param imageContent The image content.
- * @param modules The set of modules that the runtime image contains.
* @throws IOException
*/
- protected void prepareApplicationFiles(ResourcePool imageContent, Set<String> modules) throws IOException {
+ protected void prepareApplicationFiles(ResourcePool imageContent) throws IOException {
// generate launch scripts for the modules with a main class
- for (String module : modules) {
+ for (Map.Entry<String, String> entry : launchers.entrySet()) {
+ String launcherEntry = entry.getValue();
+ int slashIdx = launcherEntry.indexOf("/");
+ String module, mainClassName;
+ if (slashIdx == -1) {
+ module = launcherEntry;
+ mainClassName = null;
+ } else {
+ module = launcherEntry.substring(0, slashIdx);
+ assert !module.isEmpty();
+ mainClassName = launcherEntry.substring(slashIdx + 1);
+ assert !mainClassName.isEmpty();
+ }
+
String path = "/" + module + "/module-info.class";
Optional<ResourcePoolEntry> res = imageContent.findEntry(path);
if (!res.isPresent()) {
throw new IOException("module-info.class not found for " + module + " module");
}
- Optional<String> mainClass;
ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes());
- mainClass = ModuleDescriptor.read(stream).mainClass();
- if (mainClass.isPresent()) {
- Path cmd = root.resolve("bin").resolve(module);
+ Optional<String> mainClass = ModuleDescriptor.read(stream).mainClass();
+ if (mainClassName == null && mainClass.isPresent()) {
+ mainClassName = mainClass.get();
+ }
+
+ if (mainClassName != null) {
+ // make sure main class exists!
+ if (!imageContent.findEntry("/" + module + "/" +
+ mainClassName.replace('.', '/') + ".class").isPresent()) {
+ throw new IllegalArgumentException(module + " does not have main class: " + mainClassName);
+ }
+
+ String launcherFile = entry.getKey();
+ Path cmd = root.resolve("bin").resolve(launcherFile);
// generate shell script for Unix platforms
StringBuilder sb = new StringBuilder();
sb.append("#!/bin/sh")
@@ -272,7 +294,7 @@
.append("\n");
sb.append("$DIR/java $JLINK_VM_OPTIONS -m ")
.append(module).append('/')
- .append(mainClass.get())
+ .append(mainClassName)
.append(" $@\n");
try (BufferedWriter writer = Files.newBufferedWriter(cmd,
@@ -286,7 +308,7 @@
}
// generate .bat file for Windows
if (isWindows()) {
- Path bat = root.resolve(BIN_DIRNAME).resolve(module + ".bat");
+ Path bat = root.resolve(BIN_DIRNAME).resolve(launcherFile + ".bat");
sb = new StringBuilder();
sb.append("@echo off")
.append("\r\n");
@@ -296,7 +318,7 @@
.append("\r\n");
sb.append("\"%DIR%\\java\" %JLINK_VM_OPTIONS% -m ")
.append(module).append('/')
- .append(mainClass.get())
+ .append(mainClassName)
.append(" %*\r\n");
try (BufferedWriter writer = Files.newBufferedWriter(bat,
@@ -305,6 +327,8 @@
writer.write(sb.toString());
}
}
+ } else {
+ throw new IllegalArgumentException(module + " doesn't contain main class & main not specified in command line");
}
}
}
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Sun Dec 18 18:09:05 2016 -0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Mon Dec 19 09:48:59 2016 +0530
@@ -111,6 +111,27 @@
task.options.output = path;
}, "--output"),
new Option<JlinkTask>(true, (task, opt, arg) -> {
+ String[] values = arg.split("=");
+ // check values
+ if (values.length != 2 || values[0].isEmpty() || values[1].isEmpty()) {
+ throw taskHelper.newBadArgs("err.launcher.value.format", arg);
+ } else {
+ String commandName = values[0];
+ String moduleAndMain = values[1];
+ int idx = moduleAndMain.indexOf("/");
+ if (idx != -1) {
+ if (moduleAndMain.substring(0, idx).isEmpty()) {
+ throw taskHelper.newBadArgs("err.launcher.module.name.empty", arg);
+ }
+
+ if (moduleAndMain.substring(idx + 1).isEmpty()) {
+ throw taskHelper.newBadArgs("err.launcher.main.class.empty", arg);
+ }
+ }
+ task.options.launchers.put(commandName, moduleAndMain);
+ }
+ }, "--launcher"),
+ new Option<JlinkTask>(true, (task, opt, arg) -> {
if ("little".equals(arg)) {
task.options.endian = ByteOrder.LITTLE_ENDIAN;
} else if ("big".equals(arg)) {
@@ -170,6 +191,7 @@
final Set<String> limitMods = new HashSet<>();
final Set<String> addMods = new HashSet<>();
Path output;
+ final Map<String, String> launchers = new HashMap<>();
Path packagedModulesPath;
ByteOrder endian = ByteOrder.nativeOrder();
boolean ignoreSigning = false;
@@ -287,7 +309,7 @@
}
private void postProcessOnly(Path existingImage) throws Exception {
- PluginsConfiguration config = taskHelper.getPluginsConfig(null);
+ PluginsConfiguration config = taskHelper.getPluginsConfig(null, null);
ExecutableImage img = DefaultImageBuilder.getExecutableImage(existingImage);
if (img == null) {
throw taskHelper.newBadArgs("err.existing.image.invalid");
@@ -336,7 +358,7 @@
// Then create the Plugin Stack
ImagePluginStack stack = ImagePluginConfiguration.
- parseConfiguration(taskHelper.getPluginsConfig(options.output));
+ parseConfiguration(taskHelper.getPluginsConfig(options.output, options.launchers));
//Ask the stack to proceed
stack.operate(imageProvider);
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java Sun Dec 18 18:09:05 2016 -0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java Mon Dec 19 09:48:59 2016 +0530
@@ -403,7 +403,7 @@
return null;
}
- private PluginsConfiguration getPluginsConfig(Path output
+ private PluginsConfiguration getPluginsConfig(Path output, Map<String, String> launchers
) throws IOException, BadArgs {
if (output != null) {
if (Files.exists(output)) {
@@ -440,9 +440,9 @@
// recreate or postprocessing don't require an output directory.
ImageBuilder builder = null;
if (output != null) {
- builder = new DefaultImageBuilder(output);
+ builder = new DefaultImageBuilder(output, launchers);
+ }
- }
return new Jlink.PluginsConfiguration(pluginsList,
builder, lastSorter);
}
@@ -745,9 +745,9 @@
+ bundleHelper.getMessage(key, args));
}
- public PluginsConfiguration getPluginsConfig(Path output)
+ public PluginsConfiguration getPluginsConfig(Path output, Map<String, String> launchers)
throws IOException, BadArgs {
- return pluginOptions.getPluginsConfig(output);
+ return pluginOptions.getPluginsConfig(output, launchers);
}
public Path getExistingImage() {
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java Sun Dec 18 18:09:05 2016 -0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java Mon Dec 19 09:48:59 2016 +0530
@@ -49,6 +49,7 @@
*/
public final class AppRuntimeImageBuilder {
private Path outputDir = null;
+ private Map<String, String> launchers = Collections.emptyMap();
private List<Path> modulePath = null;
private Set<String> addModules = null;
private Set<String> limitModules = null;
@@ -62,6 +63,10 @@
outputDir = value;
}
+ public void setLaunchers(Map<String, String> value) {
+ launchers = value;
+ }
+
public void setModulePath(List<Path> value) {
modulePath = value;
}
@@ -120,7 +125,7 @@
// build the image
Jlink.PluginsConfiguration pluginConfig = new Jlink.PluginsConfiguration(
- plugins, new DefaultImageBuilder(outputDir), null);
+ plugins, new DefaultImageBuilder(outputDir, launchers), null);
Jlink jlink = new Jlink();
jlink.build(jlinkConfig, pluginConfig);
}
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties Sun Dec 18 18:09:05 2016 -0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties Mon Dec 19 09:48:59 2016 +0530
@@ -53,6 +53,11 @@
main.opt.output=\
\ --output <path> Location of output path
+main.opt.launcher=\
+\ --launcher <command>=<module> Launcher command name for the module\n\
+\ --launcher <command>=<module>/<main>\n\
+\ Launcher command name for the module and the main class
+
main.command.files=\
\ @<filename> Read options from file
@@ -91,6 +96,9 @@
err.unknown.byte.order:unknown byte order {0}
+err.launcher.main.class.empty:launcher main class name cannot be empty: {0}
+err.launcher.module.name.empty:launcher module name cannot be empty: {0}
+err.launcher.value.format:launcher value should be of form <command>=<module>[/<main-class>]: {0}
err.output.must.be.specified:--output must be specified
err.modulepath.must.be.specified:--module-path must be specified
err.mods.must.be.specified:no modules specified to {0}
--- a/jdk/test/tools/jlink/IntegrationTest.java Sun Dec 18 18:09:05 2016 -0800
+++ b/jdk/test/tools/jlink/IntegrationTest.java Mon Dec 19 09:48:59 2016 +0530
@@ -186,7 +186,7 @@
lst.add(new MyPostProcessor());
}
// Image builder
- DefaultImageBuilder builder = new DefaultImageBuilder(output);
+ DefaultImageBuilder builder = new DefaultImageBuilder(output, Collections.emptyMap());
PluginsConfiguration plugins
= new Jlink.PluginsConfiguration(lst, builder, null);
--- a/jdk/test/tools/jlink/basic/BasicTest.java Sun Dec 18 18:09:05 2016 -0800
+++ b/jdk/test/tools/jlink/basic/BasicTest.java Mon Dec 19 09:48:59 2016 +0530
@@ -87,20 +87,29 @@
JarUtils.createJarFile(jarfile, classes);
Path image = Paths.get("mysmallimage");
- runJmod(jarfile.toString(), TEST_MODULE);
- runJlink(image, TEST_MODULE, "--compress", "2");
- execute(image, TEST_MODULE);
+ runJmod(jarfile.toString(), TEST_MODULE, true);
+ runJlink(image, TEST_MODULE, "--compress", "2", "--launcher", "foo=" + TEST_MODULE);
+ execute(image, "foo");
Files.delete(jmods.resolve(TEST_MODULE + ".jmod"));
image = Paths.get("myimage");
- runJmod(classes.toString(), TEST_MODULE);
- runJlink(image, TEST_MODULE);
- execute(image, TEST_MODULE);
+ runJmod(classes.toString(), TEST_MODULE, true);
+ runJlink(image, TEST_MODULE, "--launcher", "bar=" + TEST_MODULE);
+ execute(image, "bar");
+
+ Files.delete(jmods.resolve(TEST_MODULE + ".jmod"));
+
+ image = Paths.get("myimage2");
+ runJmod(classes.toString(), TEST_MODULE, false /* no ModuleMainClass! */);
+ // specify main class in --launcher command line
+ runJlink(image, TEST_MODULE, "--launcher", "bar2=" + TEST_MODULE + "/jdk.test.Test");
+ execute(image, "bar2");
+
}
- private void execute(Path image, String moduleName) throws Throwable {
- String cmd = image.resolve("bin").resolve(moduleName).toString();
+ private void execute(Path image, String scriptName) throws Throwable {
+ String cmd = image.resolve("bin").resolve(scriptName).toString();
OutputAnalyzer analyzer;
if (System.getProperty("os.name").startsWith("Windows")) {
analyzer = ProcessTools.executeProcess("sh.exe", cmd, "1", "2", "3");
@@ -127,14 +136,25 @@
}
}
- private void runJmod(String cp, String modName) {
- int rc = JMOD_TOOL.run(System.out, System.out, new String[] {
+ private void runJmod(String cp, String modName, boolean main) {
+ int rc;
+ if (main) {
+ rc = JMOD_TOOL.run(System.out, System.out, new String[] {
"create",
"--class-path", cp,
"--module-version", "1.0",
"--main-class", "jdk.test.Test",
+ jmods.resolve(modName + ".jmod").toString()
+ });
+ } else {
+ rc = JMOD_TOOL.run(System.out, System.out, new String[] {
+ "create",
+ "--class-path", cp,
+ "--module-version", "1.0",
jmods.resolve(modName + ".jmod").toString(),
- });
+ });
+ }
+
if (rc != 0) {
throw new AssertionError("Jmod failed: rc = " + rc);
}