Skip to content

8308349: missing working directory option for launcher when invoked from shortcuts #26707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import jdk.jpackage.internal.model.FileAssociation;
import jdk.jpackage.internal.model.LauncherShortcut;
import jdk.jpackage.internal.model.LinuxLauncher;
import jdk.jpackage.internal.model.LinuxPackage;
import jdk.jpackage.internal.model.Package;
Expand Down Expand Up @@ -237,6 +238,23 @@ private Map<String, String> createDataForDesktopFile() {
data.put("DEPLOY_BUNDLE_CATEGORY", pkg.menuGroupName());
data.put("APPLICATION_LAUNCHER", Enquoter.forPropertyValues().applyTo(
installedLayout.launchersDirectory().resolve(launcher.executableNameWithSuffix()).toString()));
data.put("STARTUP_DIRECTORY", launcher.shortcut()
.flatMap(LauncherShortcut::startupDirectory)
.map(startupDirectory -> {
switch (startupDirectory) {
case DEFAULT -> {
return (Path)null;
}
case APP_DIR -> {
return installedLayout.appDirectory();
}
default -> {
throw new AssertionError();
}
}
}).map(str -> {
return "Path=" + str;
}).orElse(null));

return data;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,8 @@ private static LinuxPackage createLinuxDebPackage(
static final BundlerParamInfo<LinuxPackage> DEB_PACKAGE = createPackageBundlerParam(
LinuxFromParams::createLinuxDebPackage);

private static final BundlerParamInfo<Boolean> LINUX_SHORTCUT_HINT = new BundlerParamInfo<>(
Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId(),
Boolean.class,
params -> false,
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? false : Boolean.valueOf(s)
);
private static final BundlerParamInfo<String> LINUX_SHORTCUT_HINT = createStringBundlerParam(
Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId());

private static final BundlerParamInfo<String> LINUX_CATEGORY = createStringBundlerParam(
Arguments.CLIOptions.LINUX_CATEGORY.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Name=APPLICATION_NAME
Comment=APPLICATION_DESCRIPTION
Exec=APPLICATION_LAUNCHER
STARTUP_DIRECTORY
Icon=APPLICATION_ICON
Terminal=false
Type=Application
Expand Down
41 changes: 30 additions & 11 deletions src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.ResourceBundle;
Expand Down Expand Up @@ -348,16 +349,13 @@ public enum CLIOptions {

WIN_UPDATE_URL ("win-update-url", OptionCategories.PLATFORM_WIN),

WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, () -> {
setOptionValue("win-menu", true);
}),
WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN,
createArgumentWithOptionalValueAction("win-menu")),

WIN_MENU_GROUP ("win-menu-group", OptionCategories.PLATFORM_WIN),

WIN_SHORTCUT_HINT ("win-shortcut",
OptionCategories.PLATFORM_WIN, () -> {
setOptionValue("win-shortcut", true);
}),
WIN_SHORTCUT_HINT ("win-shortcut", OptionCategories.PLATFORM_WIN,
createArgumentWithOptionalValueAction("win-shortcut")),

WIN_SHORTCUT_PROMPT ("win-shortcut-prompt",
OptionCategories.PLATFORM_WIN, () -> {
Expand Down Expand Up @@ -396,10 +394,8 @@ public enum CLIOptions {
LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps",
OptionCategories.PLATFORM_LINUX),

LINUX_SHORTCUT_HINT ("linux-shortcut",
OptionCategories.PLATFORM_LINUX, () -> {
setOptionValue("linux-shortcut", true);
}),
LINUX_SHORTCUT_HINT ("linux-shortcut", OptionCategories.PLATFORM_LINUX,
createArgumentWithOptionalValueAction("linux-shortcut")),

LINUX_MENU_GROUP ("linux-menu-group", OptionCategories.PLATFORM_LINUX);

Expand Down Expand Up @@ -478,9 +474,32 @@ private static void nextArg() {
context().pos++;
}

private static void prevArg() {
Objects.checkIndex(context().pos, context().argList.size());
context().pos--;
}

private static boolean hasNextArg() {
return context().pos < context().argList.size();
}

private static Runnable createArgumentWithOptionalValueAction(String option) {
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure, but did you test case when --win-menu or --win-shortcut or --linux-shortcut is last argument without value? With new code var value = popArg(); will be "" and we will call setOptionValue(option, value). Before change it will be set to true.

Copy link
Member Author

Choose a reason for hiding this comment

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

You are right. I reworked the tests, and they start failing if the last argument is --win-menu, --win-shortcut, or --linux-shortcut.

Objects.requireNonNull(option);
return () -> {
nextArg();
if (hasNextArg()) {
var value = getArg();
if (value.startsWith("-")) {
prevArg();
setOptionValue(option, true);
} else {
setOptionValue(option, value);
}
} else {
setOptionValue(option, true);
}
};
}
}

enum OptionCategories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
import static jdk.jpackage.internal.StandardBundlerParam.hasPredefinedAppImage;
import static jdk.jpackage.internal.StandardBundlerParam.isRuntimeInstaller;
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;

import java.io.IOException;
import java.nio.file.Path;
Expand All @@ -69,6 +70,7 @@
import jdk.jpackage.internal.model.LauncherShortcut;
import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory;
import jdk.jpackage.internal.model.PackageType;
import jdk.jpackage.internal.model.ParseUtils;
import jdk.jpackage.internal.model.RuntimeLayout;
import jdk.jpackage.internal.util.function.ThrowingFunction;

Expand Down Expand Up @@ -171,29 +173,31 @@ static Optional<jdk.jpackage.internal.model.Package> getCurrentPackage(Map<Strin
}

static Optional<LauncherShortcut> findLauncherShortcut(
BundlerParamInfo<Boolean> shortcutParam,
BundlerParamInfo<String> shortcutParam,
Map<String, ? super Object> mainParams,
Map<String, ? super Object> launcherParams) {

Optional<Boolean> launcherValue;
Optional<String> launcherValue;
if (launcherParams == mainParams) {
// The main launcher
launcherValue = Optional.empty();
} else {
launcherValue = shortcutParam.findIn(launcherParams);
}

return launcherValue.map(withShortcut -> {
if (withShortcut) {
return Optional.of(LauncherShortcutStartupDirectory.DEFAULT);
} else {
return Optional.<LauncherShortcutStartupDirectory>empty();
}
}).or(() -> {
return shortcutParam.findIn(mainParams).map(_ -> {
return Optional.of(LauncherShortcutStartupDirectory.DEFAULT);
});
}).map(LauncherShortcut::new);
return launcherValue.map(ParseUtils::parseLauncherShortcutForAddLauncher).or(() -> {
return Optional.ofNullable(mainParams.get(shortcutParam.getID())).map(toFunction(value -> {
if (value instanceof Boolean) {
return new LauncherShortcut(LauncherShortcutStartupDirectory.DEFAULT);
} else {
try {
return ParseUtils.parseLauncherShortcutForMainLauncher((String)value);
} catch (IllegalArgumentException ex) {
throw I18N.buildConfigException("error.invalid-option-value", value, "--" + shortcutParam.getID()).create();
}
}
}));
});
}

private static ApplicationLaunchers createLaunchers(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,14 @@ public enum LauncherShortcutStartupDirectory {
* On Linux, it indicates that a shortcut doesn't have the startup directory
* configured explicitly.
*/
DEFAULT("true");
DEFAULT("true"),

/**
* The 'app' directory in the installed application app image. This is the
* directory that is referenced with {@link ApplicationLayout#appDirectory()}
* method.
*/
APP_DIR("app-dir");

LauncherShortcutStartupDirectory(String stringValue) {
this.stringValue = Objects.requireNonNull(stringValue);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2025, 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.jpackage.internal.model;

import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

/**
* Collection of functions to create instances of types defined in this package from strings.
*/
public final class ParseUtils {

private ParseUtils() {
}

public static LauncherShortcut parseLauncherShortcutForMainLauncher(String str) {
return parse(str, LauncherShortcutStartupDirectory.APP_DIR).map(LauncherShortcut::new).orElseThrow(IllegalArgumentException::new);
}

public static LauncherShortcut parseLauncherShortcutForAddLauncher(String str) {
return parse(str, LauncherShortcutStartupDirectory.values()).map(LauncherShortcut::new).orElseGet(() -> {
if (Boolean.valueOf(str)) {
return new LauncherShortcut(LauncherShortcutStartupDirectory.DEFAULT);
} else {
return new LauncherShortcut();
}
});
}

private static Optional<LauncherShortcutStartupDirectory> parse(String str, LauncherShortcutStartupDirectory... recognizedValues) {
Objects.requireNonNull(str);
return Stream.of(recognizedValues).filter(v -> {
return str.equals(v.asStringValue());
}).findFirst();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ error.invalid-app-image=Error: app-image dir "{0}" generated by another jpackage

error.invalid-install-dir=Invalid installation directory "{0}"

error.invalid-option-value=Invalid value "{0}" of option {1}

MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package
MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1} \n\
Advice to fix: {2}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ private static WinMsiPackage createWinMsiPackage(Map<String, ? super Object> par
static final BundlerParamInfo<WinMsiPackage> MSI_PACKAGE = createPackageBundlerParam(
WinFromParams::createWinMsiPackage);

private static final BundlerParamInfo<Boolean> WIN_MENU_HINT = createBooleanBundlerParam(
private static final BundlerParamInfo<String> WIN_MENU_HINT = createStringBundlerParam(
Arguments.CLIOptions.WIN_MENU_HINT.getId());

private static final BundlerParamInfo<Boolean> WIN_SHORTCUT_HINT = createBooleanBundlerParam(
private static final BundlerParamInfo<String> WIN_SHORTCUT_HINT = createStringBundlerParam(
Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId());

public static final BundlerParamInfo<Boolean> CONSOLE_HINT = createBooleanBundlerParam(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,9 @@ private void addShortcutComponentGroup(XMLStreamWriter xml) throws
case DEFAULT -> {
return INSTALLDIR;
}
case APP_DIR -> {
return installedAppImage.appDirectory();
}
default -> {
throw new AssertionError();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public AdditionalLauncher(String name) {
setPersistenceHandler(null);
}

public String name() {
return name;
}

public AdditionalLauncher withVerifyActions(Action... actions) {
verifyActions.addAll(List.of(actions));
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public enum LauncherShortcut {

public enum StartupDirectory {
DEFAULT("true"),
APP_DIR("app-dir"),
;

StartupDirectory(String stringValue) {
Expand Down Expand Up @@ -79,7 +80,7 @@ static Optional<StartupDirectory> parse(String str) {

private final String stringValue;

private final static Map<String, StartupDirectory> VALUE_MAP =
private static final Map<String, StartupDirectory> VALUE_MAP =
Stream.of(values()).collect(toMap(StartupDirectory::asStringValue, x -> x));
}

Expand Down Expand Up @@ -147,7 +148,14 @@ record Stub(

private Optional<StartupDirectory> findMainLauncherShortcut(JPackageCommand cmd) {
if (cmd.hasArgument(optionName())) {
return Optional.of(StartupDirectory.DEFAULT);
var value = Optional.ofNullable(cmd.getArgumentValue(optionName())).filter(optionValue -> {
return !optionValue.startsWith("-");
});
if (value.isPresent()) {
return value.flatMap(StartupDirectory::parse);
} else {
return Optional.of(StartupDirectory.DEFAULT);
}
} else {
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,9 @@ private static void verifyDesktopFile(JPackageCommand cmd, Optional<AppImageFile
case DEFAULT -> {
return (Path)null;
}
case APP_DIR -> {
return cmd.pathToPackageFile(appLayout.appDirectory());
}
default -> {
throw new AssertionError();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,17 @@ private static Collection<Shortcut> expectLauncherShortcuts(JPackageCommand cmd,
final var installDir = Path.of(installRoot.getMsiPropertyName()).resolve(getInstallationSubDirectory(cmd));

final Function<StartupDirectory, Path> workDir = startupDirectory -> {
return installDir;
switch (startupDirectory) {
case DEFAULT -> {
return installDir;
}
case APP_DIR -> {
return ApplicationLayout.windowsAppImage().resolveAt(installDir).appDirectory();
}
default -> {
throw new IllegalArgumentException();
}
}
};

if (winMenu.isPresent()) {
Expand Down
Loading