Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -360,25 +360,38 @@ private static void writeRuntimeInfoPlist(

final var app = env.app();

// We should use full runtime info plist for standalone runtime and for

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the comment explains why jpackage should use the same info plist template for a runtime if it has the "bin" subdirectory. But what is the "full" runtime?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"full" runtime info plist -> refers to info plist from standalone runtime. I will re-write it to:

 // We should use info plist from standalone runtime for embedded runtime // if embedded runtime contains "bin" folder, so embedded runtime // can act as standalone runtime. Standalone runtime always uses same // info plist which has "JavaVM" dictionary. 
Copy link
Member

@alexeysemenyukoracle alexeysemenyukoracle Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so embedded runtime can act as standalone runtime

This can imply many things. I suggest more specific wording:

If the embedded runtime contains executable(s) in the "bin" subdirectory, we should use the standalone runtime info plist template. Otherwise, the user may be unable to run the "java" or other executables in the "bin" subdirectory of the embedded runtime.

// embedded runtime if it contains "bin" folder, so embedded runtime
// can act as standalone runtime.
final var useRuntimeInfoPlist =
Files.isDirectory(env.resolvedLayout().runtimeDirectory().resolve("bin")) ||
app.isRuntime();

Comment on lines +366 to +369

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The writeRuntimeInfoPlist() action is executed for the MacBuildApplicationTaskID.RUNTIME_INFO_PLIST and MacBuildApplicationTaskID.COPY_RUNTIME_INFO_PLIST tasks. MacBuildApplicationTaskID.RUNTIME_INFO_PLIST doesn't have dependencies. This means it can be executed before the runtime files are available in the output app image. Currently, this is not a problem because jpackage executes tasks synchronously, but eventually it will run them asynchronously, and the Files.isDirectory(...) test will fail.

The plist template should be selected at the configuration phase, not the bundling phase. However, this will require knowing/guessing if jlink will create native commands in the "bin" directory from its command line. It is straightforward if it boils down to testing whether it has the "--strip-native-commands" option. This is what we do when we test if jlink options are correct for the Apple App Store, right? Test for "--strip-native-commands" in DeployParams.java

I'd suggest adding boolean jdk.jpackage.internal.model.RuntimeBuilder.withNativeCommands() method. Then the code will be:

final var useRuntimeInfoPlist = app.isRuntime() || app.runtimeBuilder().orElseThrow().withNativeCommands(); 

Alternatively, you may add the BuildApplicationTaskID.RUNTIME task as a dependency for the MacBuildApplicationTaskID.RUNTIME_INFO_PLIST task. However, this is a less preferable solution.

Map<String, String> data = new HashMap<>();
data.put("CF_BUNDLE_IDENTIFIER", app.bundleIdentifier());
data.put("CF_BUNDLE_NAME", app.bundleName());
data.put("CF_BUNDLE_VERSION", app.version());
data.put("CF_BUNDLE_SHORT_VERSION_STRING", app.shortVersion().toString());
if (app.isRuntime()) {
if (useRuntimeInfoPlist) {
data.put("CF_BUNDLE_VENDOR", app.vendor());
}

final String template;
final String publicName;
final String category;

if (app.isRuntime()) {
if (useRuntimeInfoPlist) {
template = "Runtime-Info.plist.template";
} else {
template = "ApplicationRuntime-Info.plist.template";
}

// Public name and category should be based on standalone runtime vs
// embedded runtime.
if (app.isRuntime()) {
publicName = "Info.plist";
category = "resource.runtime-info-plist";
} else {
template = "ApplicationRuntime-Info.plist.template";
publicName = "Runtime-Info.plist";
category = "resource.app-runtime-info-plist";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ public JPackageCommand setInputToEmptyDirectory() {
}

public JPackageCommand setFakeRuntime() {
return setFakeRuntime(false);
}

public JPackageCommand setFakeRuntime(boolean includeBin) {
verifyMutable();

ThrowingConsumer<Path> createBulkFile = path -> {
Expand All @@ -308,10 +312,11 @@ public JPackageCommand setFakeRuntime() {

Files.createDirectories(fakeRuntimeDir);

if (TKit.isLinux()) {
if (TKit.isLinux() || includeBin) {
// Need to make the code in rpm spec happy as it assumes there is
// always something in application image.
fakeRuntimeDir.resolve("bin").toFile().mkdir();
createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk")));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change makes the above fakeRuntimeDir.resolve("bin").toFile().mkdir(); redundant. Overall, the whole

if (TKit.isLinux()) { // Need to make the code in rpm spec happy as it assumes there is // always something in application image. fakeRuntimeDir.resolve("bin").toFile().mkdir(); } 

construction is redundant and can be replaced with:

if (includeBin) { createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk"))); } 
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need to add file to "bin" for Linux. I will change it to:

 // Need to make the code in rpm spec happy as it assumes there is // always something in application image. includeBin |= TKit.isLinux(); if (includeBin) { createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk"))); } 

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need to add file to "bin" for Linux. I will change it to:

I simplified the body of the setFakeRuntime() function as follows:

 addPrerequisiteAction(cmd -> { Path fakeRuntimeDir = TKit.createTempDirectory("fake_runtime"); TKit.trace(String.format("Init fake runtime in [%s] directory", fakeRuntimeDir)); if (TKit.isOSX()) { // Make MacAppImageBuilder happy createBulkFile.accept(fakeRuntimeDir.resolve(Path.of( "lib/jli/libjli.dylib"))); } // Make sure fake runtime takes some disk space. // Package bundles with 0KB size are unexpected and considered // an error by PackageTest. createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("lib", "bulk"))); cmd.setArgumentValue("--runtime-image", fakeRuntimeDir); }); 

And ran Windows and Linux tests that use it. All passed.

WinOSConditionTest:

[18:44:19.893] TRACE: assertStringListEquals(): Check jpackage didn't modify ${RUNTIME_IMAGE}=[C:\jpackage-tests\WinOSConditionTest\test\fake_runtime-0] [18:44:19.893] TRACE: assertStringListEquals(1, #) [18:44:19.894] TRACE: assertStringListEquals(2, lib#) [18:44:19.894] TRACE: assertStringListEquals(3, lib\bulk#2025-10-30T22:44:17.6191874Z) [18:44:19.894] TRACE: assertTrue(): Check [WinOSConditionTest\test\output\WinOSConditionTest-1.0.msi] path exists 

AppAboutUrlTest.testDefaults (DEB):

[18:49:22.115] TRACE: assertStringListEquals(): Check jpackage didn't modify ${RUNTIME_IMAGE}=[/jpackage-tests/AppAboutUrlTest/testDefaults/fake_runtime] [18:49:22.120] TRACE: assertStringListEquals(1, #) [18:49:22.120] TRACE: assertStringListEquals(2, lib#) [18:49:22.120] TRACE: assertStringListEquals(3, lib/bulk#2025-10-30T22:49:20.128409727Z) [18:49:22.120] TRACE: assertTrue(): Check [AppAboutUrlTest/testDefaults/output/defaultsappabouturltest_1.0_amd64.deb] path exists 

AppAboutUrlTest.testDefaults (RPM):

[18:56:37.897] TRACE: assertStringListEquals(): Check jpackage didn't modify ${RUNTIME_IMAGE}=[/jpackage-tests/AppAboutUrlTest/testDefaults/fake_runtime] [18:56:37.898] TRACE: assertStringListEquals(1, #) [18:56:37.898] TRACE: assertStringListEquals(2, lib#) [18:56:37.898] TRACE: assertStringListEquals(3, lib/bulk#2025-10-30T22:56:37.204554597Z) [18:56:37.898] TRACE: assertTrue(): Check [AppAboutUrlTest/testDefaults/output/defaultsappabouturltest-1.0-1.x86_64.rpm] path exists 
Copy link
Member

@alexeysemenyukoracle alexeysemenyukoracle Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff:

diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index fcd940e725e..73af859b6ac 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -307,24 +307,16 @@ public JPackageCommand setFakeRuntime() { TKit.trace(String.format("Init fake runtime in [%s] directory", fakeRuntimeDir)); - Files.createDirectories(fakeRuntimeDir); - - if (TKit.isLinux()) { - // Need to make the code in rpm spec happy as it assumes there is - // always something in application image. - fakeRuntimeDir.resolve("bin").toFile().mkdir(); - } - if (TKit.isOSX()) { // Make MacAppImageBuilder happy createBulkFile.accept(fakeRuntimeDir.resolve(Path.of( "lib/jli/libjli.dylib"))); } - // Mak sure fake runtime takes some disk space. + // Make sure fake runtime takes some disk space. // Package bundles with 0KB size are unexpected and considered // an error by PackageTest. - createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk"))); + createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("lib", "bulk"))); cmd.setArgumentValue("--runtime-image", fakeRuntimeDir); }); 
}

if (TKit.isOSX()) {
Expand All @@ -323,7 +328,7 @@ public JPackageCommand setFakeRuntime() {
// Mak sure fake runtime takes some disk space.
// Package bundles with 0KB size are unexpected and considered
// an error by PackageTest.
createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("bin", "bulk")));
createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("lib", "bulk")));

cmd.setArgumentValue("--runtime-image", fakeRuntimeDir);
});
Expand Down
13 changes: 10 additions & 3 deletions test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public void testAppImage(TestConfig cfg) throws Throwable {
public void testNativePackage(TestConfig cfg) {
List<ThrowingConsumer<JPackageCommand>> verifier = new ArrayList<>();
new PackageTest().configureHelloApp().addInitializer(cmd -> {
cfg.init(cmd.setFakeRuntime());
cfg.init(cmd.setFakeRuntime(cfg.customPLists.contains(CustomPListType.EMBEDDED_RUNTIME_WITH_BIN)));
}).addRunOnceInitializer(() -> {
verifier.add(cfg.createPListFilesVerifier(JPackageCommand.helloAppImage().executePrerequisiteActions()));
}).addInstallVerifier(cmd -> {
Expand Down Expand Up @@ -124,6 +124,7 @@ public static Collection<Object[]> customPLists() {
Set.of(CustomPListType.APP),
Set.of(CustomPListType.APP_WITH_FA),
Set.of(CustomPListType.EMBEDDED_RUNTIME),
Set.of(CustomPListType.EMBEDDED_RUNTIME_WITH_BIN),
Set.of(CustomPListType.APP, CustomPListType.EMBEDDED_RUNTIME)
).map(TestConfig::new).map(cfg -> {
return new Object[] { cfg };
Expand Down Expand Up @@ -185,7 +186,8 @@ ThrowingConsumer<JPackageCommand> createPListFilesVerifier(JPackageCommand cmd)
if (defaultPListFiles.isEmpty()) {
return defaultVerifier;
} else {
var vanillaCmd = new JPackageCommand().setFakeRuntime()
boolean includeBin = customPLists.contains(CustomPListType.EMBEDDED_RUNTIME_WITH_BIN);
var vanillaCmd = new JPackageCommand().setFakeRuntime(includeBin)
.addArguments(cmd.getAllArguments())
.setPackageType(PackageType.IMAGE)
.removeArgumentWithValue("--resource-dir")
Expand Down Expand Up @@ -243,6 +245,11 @@ private enum CustomPListType {
CustomPListFactory.PLIST_OUTPUT::writeEmbeddedRuntimePlist,
"Runtime-Info.plist"),

EMBEDDED_RUNTIME_WITH_BIN(
CustomPListFactory.PLIST_INPUT::writeEmbeddedRuntimePlist,
CustomPListFactory.PLIST_OUTPUT::writeEmbeddedRuntimePlist,
"Runtime-Info.plist"),

RUNTIME(
CustomPListFactory.PLIST_INPUT::writeRuntimePlist,
CustomPListFactory.PLIST_OUTPUT::writeRuntimePlist,
Expand Down Expand Up @@ -285,7 +292,7 @@ void verifyPListFile(JPackageCommand cmd) throws IOException {
}

PListRole role() {
if (this == EMBEDDED_RUNTIME) {
if (this == EMBEDDED_RUNTIME || this == EMBEDDED_RUNTIME_WITH_BIN) {
return PListRole.EMBEDDED_RUNTIME;
} else {
return PListRole.MAIN;
Expand Down