mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-26 02:00:12 +00:00
8248254: jpackage fails if app module is in external runtime
Reviewed-by: herrick, almatvee
This commit is contained in:
parent
1a4f31409a
commit
d180fb3044
@ -26,25 +26,34 @@ package jdk.incubator.jpackage.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import static jdk.incubator.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE;
|
||||
|
||||
/**
|
||||
* Extracts data needed to run application from parameters.
|
||||
*/
|
||||
final class LauncherData {
|
||||
boolean isModular() {
|
||||
return moduleDescriptor != null;
|
||||
return moduleInfo != null;
|
||||
}
|
||||
|
||||
String qualifiedClassName() {
|
||||
@ -61,7 +70,7 @@ final class LauncherData {
|
||||
|
||||
String moduleName() {
|
||||
verifyIsModular(true);
|
||||
return moduleDescriptor.name();
|
||||
return moduleInfo.name;
|
||||
}
|
||||
|
||||
List<Path> modulePath() {
|
||||
@ -80,11 +89,7 @@ final class LauncherData {
|
||||
|
||||
String getAppVersion() {
|
||||
if (isModular()) {
|
||||
ModuleDescriptor.Version ver = moduleDescriptor.version().orElse(null);
|
||||
if (ver != null) {
|
||||
return ver.toString();
|
||||
}
|
||||
return moduleDescriptor.rawVersion().orElse(null);
|
||||
return moduleInfo.version;
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -94,7 +99,7 @@ final class LauncherData {
|
||||
}
|
||||
|
||||
private void verifyIsModular(boolean isModular) {
|
||||
if ((moduleDescriptor != null) != isModular) {
|
||||
if ((moduleInfo != null) != isModular) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
@ -103,9 +108,19 @@ final class LauncherData {
|
||||
ConfigException, IOException {
|
||||
|
||||
final String mainModule = getMainModule(params);
|
||||
final LauncherData result;
|
||||
if (mainModule == null) {
|
||||
return createNonModular(params);
|
||||
result = createNonModular(params);
|
||||
} else {
|
||||
result = createModular(mainModule, params);
|
||||
}
|
||||
result.initClasspath(params);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static LauncherData createModular(String mainModule,
|
||||
Map<String, ? super Object> params) throws ConfigException,
|
||||
IOException {
|
||||
|
||||
LauncherData launcherData = new LauncherData();
|
||||
|
||||
@ -119,18 +134,34 @@ final class LauncherData {
|
||||
}
|
||||
launcherData.modulePath = getModulePath(params);
|
||||
|
||||
launcherData.moduleDescriptor = JLinkBundlerHelper.createModuleFinder(
|
||||
launcherData.modulePath).find(moduleName).orElseThrow(
|
||||
() -> new ConfigException(MessageFormat.format(I18N.getString(
|
||||
"error.no-module-in-path"), moduleName), null)).descriptor();
|
||||
// Try to find module in the specified module path list.
|
||||
ModuleReference moduleRef = JLinkBundlerHelper.createModuleFinder(
|
||||
launcherData.modulePath).find(moduleName).orElse(null);
|
||||
|
||||
if (launcherData.qualifiedClassName == null) {
|
||||
launcherData.qualifiedClassName = launcherData.moduleDescriptor.mainClass().orElseThrow(
|
||||
() -> new ConfigException(I18N.getString("ERR_NoMainClass"),
|
||||
null));
|
||||
if (moduleRef != null) {
|
||||
launcherData.moduleInfo = ModuleInfo.fromModuleDescriptor(
|
||||
moduleRef.descriptor());
|
||||
} else if (params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID())) {
|
||||
// Failed to find module in the specified module path list and
|
||||
// there is external runtime given to jpackage.
|
||||
// Lookup module in this runtime.
|
||||
Path cookedRuntime = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params).toPath();
|
||||
launcherData.moduleInfo = ModuleInfo.fromCookedRuntime(moduleName,
|
||||
cookedRuntime);
|
||||
}
|
||||
|
||||
if (launcherData.moduleInfo == null) {
|
||||
throw new ConfigException(MessageFormat.format(I18N.getString(
|
||||
"error.no-module-in-path"), moduleName), null);
|
||||
}
|
||||
|
||||
if (launcherData.qualifiedClassName == null) {
|
||||
launcherData.qualifiedClassName = launcherData.moduleInfo.mainClass;
|
||||
if (launcherData.qualifiedClassName == null) {
|
||||
throw new ConfigException(I18N.getString("ERR_NoMainClass"), null);
|
||||
}
|
||||
}
|
||||
|
||||
launcherData.initClasspath(params);
|
||||
return launcherData;
|
||||
}
|
||||
|
||||
@ -191,7 +222,6 @@ final class LauncherData {
|
||||
launcherData.mainJarName));
|
||||
}
|
||||
|
||||
launcherData.initClasspath(params);
|
||||
return launcherData;
|
||||
}
|
||||
|
||||
@ -259,8 +289,17 @@ final class LauncherData {
|
||||
|
||||
private static List<Path> getModulePath(Map<String, ? super Object> params)
|
||||
throws ConfigException {
|
||||
return getPathListParameter(Arguments.CLIOptions.MODULE_PATH.getId(),
|
||||
params);
|
||||
List<Path> modulePath = getPathListParameter(Arguments.CLIOptions.MODULE_PATH.getId(), params);
|
||||
|
||||
if (params.containsKey(PREDEFINED_RUNTIME_IMAGE.getID())) {
|
||||
Path runtimePath = PREDEFINED_RUNTIME_IMAGE.fetchFrom(params).toPath();
|
||||
runtimePath = runtimePath.resolve("lib");
|
||||
modulePath = Stream.of(modulePath, List.of(runtimePath))
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
return modulePath;
|
||||
}
|
||||
|
||||
private static List<Path> getPathListParameter(String paramName,
|
||||
@ -277,5 +316,77 @@ final class LauncherData {
|
||||
private Path mainJarName;
|
||||
private List<Path> classPath;
|
||||
private List<Path> modulePath;
|
||||
private ModuleDescriptor moduleDescriptor;
|
||||
private ModuleInfo moduleInfo;
|
||||
|
||||
private static final class ModuleInfo {
|
||||
String name;
|
||||
String version;
|
||||
String mainClass;
|
||||
|
||||
static ModuleInfo fromModuleDescriptor(ModuleDescriptor md) {
|
||||
ModuleInfo result = new ModuleInfo();
|
||||
result.name = md.name();
|
||||
result.mainClass = md.mainClass().orElse(null);
|
||||
|
||||
ModuleDescriptor.Version ver = md.version().orElse(null);
|
||||
if (ver != null) {
|
||||
result.version = ver.toString();
|
||||
} else {
|
||||
result.version = md.rawVersion().orElse(null);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ModuleInfo fromCookedRuntime(String moduleName,
|
||||
Path cookedRuntime) {
|
||||
Objects.requireNonNull(moduleName);
|
||||
|
||||
// We can't extract info about version and main class of a module
|
||||
// linked in external runtime without running ModuleFinder in that
|
||||
// runtime. But this is too much work as the runtime might have been
|
||||
// coocked without native launchers. So just make sure the module
|
||||
// is linked in the runtime by simply analysing the data
|
||||
// of `release` file.
|
||||
|
||||
final Path releaseFile;
|
||||
if (!Platform.isMac()) {
|
||||
releaseFile = cookedRuntime.resolve("release");
|
||||
} else {
|
||||
// On Mac `cookedRuntime` can be runtime root or runtime home.
|
||||
Path runtimeHome = cookedRuntime.resolve("Contents/Home");
|
||||
if (!Files.isDirectory(runtimeHome)) {
|
||||
runtimeHome = cookedRuntime;
|
||||
}
|
||||
releaseFile = runtimeHome.resolve("release");
|
||||
}
|
||||
|
||||
try (Reader reader = Files.newBufferedReader(releaseFile)) {
|
||||
Properties props = new Properties();
|
||||
props.load(reader);
|
||||
String moduleList = props.getProperty("MODULES");
|
||||
if (moduleList == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((moduleList.startsWith("\"") && moduleList.endsWith("\""))
|
||||
|| (moduleList.startsWith("\'") && moduleList.endsWith(
|
||||
"\'"))) {
|
||||
moduleList = moduleList.substring(1, moduleList.length() - 1);
|
||||
}
|
||||
|
||||
if (!List.of(moduleList.split("\\s+")).contains(moduleName)) {
|
||||
return null;
|
||||
}
|
||||
} catch (IOException|IllegalArgumentException ex) {
|
||||
Log.verbose(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
ModuleInfo result = new ModuleInfo();
|
||||
result.name = moduleName;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -924,10 +924,11 @@ jdk/jfr/event/os/TestThreadContextSwitches.java 8247776 windows-
|
||||
|
||||
# jdk_jpackage
|
||||
|
||||
tools/jpackage/share/EmptyFolderPackageTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/IconTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/AppImagePackageTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/SimplePackageTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/jdk/jpackage/tests/BasicTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/EmptyFolderPackageTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/IconTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/AppImagePackageTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/SimplePackageTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/jdk/jpackage/tests/BasicTest.java 8248059 macosx-all
|
||||
tools/jpackage/share/jdk/jpackage/tests/ModulePathTest3.java#id0 8248418 generic-all
|
||||
|
||||
############################################################################
|
||||
|
||||
@ -300,7 +300,7 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
public static JPackageCommand helloAppImage(JavaAppDesc javaAppDesc) {
|
||||
JPackageCommand cmd = new JPackageCommand();
|
||||
cmd.setDefaultInputOutput().setDefaultAppName();
|
||||
PackageType.IMAGE.applyTo(cmd);
|
||||
cmd.setPackageType(PackageType.IMAGE);
|
||||
new HelloApp(javaAppDesc).addTo(cmd);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
package jdk.jpackage.tests;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import jdk.incubator.jpackage.internal.AppImageFile;
|
||||
import jdk.jpackage.test.HelloApp;
|
||||
import jdk.jpackage.test.JavaAppDesc;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.Annotations.Parameter;
|
||||
import jdk.jpackage.test.Annotations.Parameters;
|
||||
import jdk.jpackage.test.Executor;
|
||||
import jdk.jpackage.test.JPackageCommand;
|
||||
import jdk.jpackage.test.JavaTool;
|
||||
import jdk.jpackage.test.PackageType;
|
||||
import jdk.jpackage.test.TKit;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary jpackage for app's module linked in external runtime
|
||||
* @library ../../../../helpers
|
||||
* @build jdk.jpackage.test.*
|
||||
* @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
|
||||
* @compile ModulePathTest3.java
|
||||
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
|
||||
* --jpt-run=jdk.jpackage.tests.ModulePathTest3
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary jpackage for app's module linked in external runtime
|
||||
* @library ../../../../helpers
|
||||
* @build jdk.jpackage.test.*
|
||||
* @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal
|
||||
* @compile ModulePathTest3.java
|
||||
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
|
||||
* --jpt-run=jdk.jpackage.tests.ModulePathTest3
|
||||
* --jpt-exclude=test8248418
|
||||
*/
|
||||
|
||||
public final class ModulePathTest3 {
|
||||
|
||||
public ModulePathTest3(String jlinkOutputSubdir, String runtimeSubdir) {
|
||||
this.jlinkOutputSubdir = Path.of(jlinkOutputSubdir);
|
||||
this.runtimeSubdir = Path.of(runtimeSubdir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case for JDK-8248254.
|
||||
* App's module in runtime directory.
|
||||
*/
|
||||
@Test
|
||||
public void test8248254() throws XPathExpressionException, IOException {
|
||||
testIt("me.mymodule/me.mymodule.Main");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case for JDK-8248418.
|
||||
* App's module with version specified in runtime directory.
|
||||
*/
|
||||
@Test
|
||||
public void test8248418() throws XPathExpressionException, IOException {
|
||||
testIt("me.mymodule/me.mymodule.Main@3.7");
|
||||
}
|
||||
|
||||
private void testIt(String mainAppDesc) throws XPathExpressionException,
|
||||
IOException {
|
||||
final JavaAppDesc appDesc = JavaAppDesc.parse(mainAppDesc);
|
||||
final Path moduleOutputDir = TKit.createTempDirectory("modules");
|
||||
HelloApp.createBundle(appDesc, moduleOutputDir);
|
||||
|
||||
final Path workDir = TKit.createTempDirectory("runtime").resolve("data");
|
||||
final Path jlinkOutputDir = workDir.resolve(jlinkOutputSubdir);
|
||||
Files.createDirectories(jlinkOutputDir.getParent());
|
||||
|
||||
new Executor()
|
||||
.setToolProvider(JavaTool.JLINK)
|
||||
.dumpOutput()
|
||||
.addArguments(
|
||||
"--add-modules", appDesc.moduleName(),
|
||||
"--output", jlinkOutputDir.toString(),
|
||||
"--module-path", moduleOutputDir.resolve(appDesc.jarFileName()).toString(),
|
||||
"--strip-debug",
|
||||
"--no-header-files",
|
||||
"--no-man-pages",
|
||||
"--strip-native-commands")
|
||||
.execute();
|
||||
|
||||
JPackageCommand cmd = new JPackageCommand()
|
||||
.setDefaultAppName()
|
||||
.setPackageType(PackageType.IMAGE)
|
||||
.setDefaultInputOutput()
|
||||
.removeArgumentWithValue("--input")
|
||||
.addArguments("--module", appDesc.moduleName() + "/" + appDesc.className())
|
||||
.setArgumentValue("--runtime-image", workDir.resolve(runtimeSubdir));
|
||||
|
||||
cmd.executeAndAssertHelloAppImageCreated();
|
||||
|
||||
if (appDesc.moduleVersion() != null) {
|
||||
Document xml = AppImageFile.readXml(cmd.outputBundle());
|
||||
String actualVersion = XPathFactory.newInstance().newXPath().evaluate(
|
||||
"/jpackage-state/app-version/text()", xml,
|
||||
XPathConstants.STRING).toString();
|
||||
|
||||
TKit.assertEquals(appDesc.moduleVersion(), actualVersion,
|
||||
"Check application version");
|
||||
}
|
||||
}
|
||||
|
||||
@Parameters
|
||||
public static Collection data() {
|
||||
final List<String[]> paths = new ArrayList<>();
|
||||
paths.add(new String[] { "", "" });
|
||||
if (TKit.isOSX()) {
|
||||
// On OSX jpackage should accept both runtime root and runtime home
|
||||
// directories.
|
||||
paths.add(new String[] { "Contents/Home", "" });
|
||||
}
|
||||
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
for (var pathCfg : paths) {
|
||||
data.add(new Object[] { pathCfg[0], pathCfg[1] });
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private final Path jlinkOutputSubdir;
|
||||
private final Path runtimeSubdir;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user