Skip to content

Commit 0c0300e

Browse files
8308349: missing working directory option for launcher when invoked from shortcuts
1 parent 9413403 commit 0c0300e

17 files changed

+424
-138
lines changed

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
*/
2525
package jdk.jpackage.internal;
2626

27-
import jdk.jpackage.internal.model.LinuxPackage;
28-
import jdk.jpackage.internal.model.LinuxLauncher;
29-
import jdk.jpackage.internal.model.Package;
30-
import jdk.jpackage.internal.model.Launcher;
27+
import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource;
28+
import static jdk.jpackage.internal.model.LauncherShortcut.toRequest;
29+
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
30+
3131
import java.awt.image.BufferedImage;
3232
import java.io.File;
3333
import java.io.IOException;
@@ -45,12 +45,14 @@
4545
import javax.imageio.ImageIO;
4646
import javax.xml.stream.XMLStreamException;
4747
import javax.xml.stream.XMLStreamWriter;
48-
import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource;
4948
import jdk.jpackage.internal.model.FileAssociation;
49+
import jdk.jpackage.internal.model.LauncherShortcut;
50+
import jdk.jpackage.internal.model.LinuxLauncher;
51+
import jdk.jpackage.internal.model.LinuxPackage;
52+
import jdk.jpackage.internal.model.Package;
5053
import jdk.jpackage.internal.util.CompositeProxy;
5154
import jdk.jpackage.internal.util.PathUtils;
5255
import jdk.jpackage.internal.util.XmlUtils;
53-
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
5456

5557
/**
5658
* Helper to create files for desktop integration.
@@ -77,7 +79,7 @@ private DesktopIntegration(BuildEnv env, LinuxPackage pkg, LinuxLauncher launche
7779
// Need desktop and icon files if one of conditions is met:
7880
// - there are file associations configured
7981
// - user explicitly requested to create a shortcut
80-
boolean withDesktopFile = !associations.isEmpty() || launcher.shortcut().orElse(false);
82+
boolean withDesktopFile = !associations.isEmpty() || toRequest(launcher.shortcut()).orElse(false);
8183

8284
var curIconResource = createLauncherIconResource(pkg.app(), launcher,
8385
env::createResource);
@@ -132,7 +134,7 @@ private DesktopIntegration(BuildEnv env, LinuxPackage pkg, LinuxLauncher launche
132134
nestedIntegrations = pkg.app().additionalLaunchers().stream().map(v -> {
133135
return (LinuxLauncher)v;
134136
}).filter(l -> {
135-
return l.shortcut().orElse(true);
137+
return toRequest(l.shortcut()).orElse(true);
136138
}).map(toFunction(l -> {
137139
return new DesktopIntegration(env, pkg, l);
138140
})).toList();
@@ -225,15 +227,37 @@ private List<String> requiredPackagesSelf() {
225227
}
226228

227229
private Map<String, String> createDataForDesktopFile() {
230+
231+
var installedLayout = pkg.asInstalledPackageApplicationLayout().orElseThrow();
232+
228233
Map<String, String> data = new HashMap<>();
229234
data.put("APPLICATION_NAME", launcher.name());
230235
data.put("APPLICATION_DESCRIPTION", launcher.description());
231236
data.put("APPLICATION_ICON", iconFile.map(
232237
f -> f.installPath().toString()).orElse(null));
233238
data.put("DEPLOY_BUNDLE_CATEGORY", pkg.menuGroupName());
234239
data.put("APPLICATION_LAUNCHER", Enquoter.forPropertyValues().applyTo(
235-
pkg.asInstalledPackageApplicationLayout().orElseThrow().launchersDirectory().resolve(
236-
launcher.executableNameWithSuffix()).toString()));
240+
installedLayout.launchersDirectory().resolve(launcher.executableNameWithSuffix()).toString()));
241+
data.put("STARTUP_DIRECTORY", launcher.shortcut()
242+
.flatMap(LauncherShortcut::startupDirectory)
243+
.map(startupDirectory -> {
244+
switch (startupDirectory) {
245+
case DEFAULT -> {
246+
return (Path)null;
247+
}
248+
case APP_DIR -> {
249+
return installedLayout.appDirectory();
250+
}
251+
case INSTALL_DIR -> {
252+
return installedLayout.launchersDirectory();
253+
}
254+
default -> {
255+
throw new AssertionError();
256+
}
257+
}
258+
}).map(str -> {
259+
return "Path=" + str;
260+
}).orElse(null));
237261

238262
return data;
239263
}
@@ -481,7 +505,7 @@ private static int getIconSize(FileAssociation fa) {
481505

482506
private final BuildEnv env;
483507
private final LinuxPackage pkg;
484-
private final Launcher launcher;
508+
private final LinuxLauncher launcher;
485509

486510
private final List<LinuxFileAssociation> associations;
487511

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,14 @@
2929
import static jdk.jpackage.internal.FromParams.createApplicationBundlerParam;
3030
import static jdk.jpackage.internal.FromParams.createPackageBuilder;
3131
import static jdk.jpackage.internal.FromParams.createPackageBundlerParam;
32+
import static jdk.jpackage.internal.FromParams.findLauncherShortcut;
3233
import static jdk.jpackage.internal.LinuxPackagingPipeline.APPLICATION_LAYOUT;
33-
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
3434
import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB;
3535
import static jdk.jpackage.internal.model.StandardPackageType.LINUX_RPM;
3636
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
3737

3838
import java.io.IOException;
3939
import java.util.Map;
40-
import java.util.Optional;
41-
import java.util.stream.Stream;
4240
import jdk.jpackage.internal.model.ConfigException;
4341
import jdk.jpackage.internal.model.LinuxApplication;
4442
import jdk.jpackage.internal.model.LinuxLauncher;
@@ -51,11 +49,10 @@ final class LinuxFromParams {
5149
private static LinuxApplication createLinuxApplication(
5250
Map<String, ? super Object> params) throws ConfigException, IOException {
5351
final var launcherFromParams = new LauncherFromParams();
52+
5453
final var app = createApplicationBuilder(params, toFunction(launcherParams -> {
5554
final var launcher = launcherFromParams.create(launcherParams);
56-
final var shortcut = Stream.of(SHORTCUT_HINT, LINUX_SHORTCUT_HINT).map(param -> {
57-
return param.findIn(launcherParams);
58-
}).filter(Optional::isPresent).map(Optional::get).findFirst();
55+
final var shortcut = findLauncherShortcut(LINUX_SHORTCUT_HINT, params, launcherParams);
5956
return LinuxLauncher.create(launcher, new LinuxLauncherMixin.Stub(shortcut));
6057
}), APPLICATION_LAYOUT).create();
6158
return LinuxApplication.create(app);
@@ -112,12 +109,8 @@ private static LinuxPackage createLinuxDebPackage(
112109
static final BundlerParamInfo<LinuxPackage> DEB_PACKAGE = createPackageBundlerParam(
113110
LinuxFromParams::createLinuxDebPackage);
114111

115-
private static final BundlerParamInfo<Boolean> LINUX_SHORTCUT_HINT = new BundlerParamInfo<>(
116-
Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId(),
117-
Boolean.class,
118-
params -> false,
119-
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? false : Boolean.valueOf(s)
120-
);
112+
private static final BundlerParamInfo<String> LINUX_SHORTCUT_HINT = createStringBundlerParam(
113+
Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId());
121114

122115
private static final BundlerParamInfo<String> LINUX_CATEGORY = createStringBundlerParam(
123116
Arguments.CLIOptions.LINUX_CATEGORY.getId());

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncher.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package jdk.jpackage.internal.model;
2626

27+
import java.util.HashMap;
2728
import java.util.Map;
2829
import jdk.jpackage.internal.util.CompositeProxy;
2930

@@ -36,9 +37,11 @@ public interface LinuxLauncher extends Launcher, LinuxLauncherMixin {
3637

3738
@Override
3839
default Map<String, String> extraAppImageFileData() {
39-
return shortcut().map(v -> {
40-
return Map.of("shortcut", Boolean.toString(v));
41-
}).orElseGet(Map::of);
40+
Map<String, String> map = new HashMap<>();
41+
shortcut().ifPresent(shortcut -> {
42+
shortcut.store(SHORTCUT_ID, map::put);
43+
});
44+
return map;
4245
}
4346

4447
/**
@@ -52,4 +55,6 @@ default Map<String, String> extraAppImageFileData() {
5255
public static LinuxLauncher create(Launcher launcher, LinuxLauncherMixin mixin) {
5356
return CompositeProxy.create(LinuxLauncher.class, launcher, mixin);
5457
}
58+
59+
public static final String SHORTCUT_ID = "linux-shortcut";
5560
}

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxLauncherMixin.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,20 @@
3232
public interface LinuxLauncherMixin {
3333

3434
/**
35-
* Gets the start menu shortcut setting of this application launcher.
35+
* Gets the start menu shortcut of this application launcher.
3636
* <p>
37-
* Returns <code>true</code> if this application launcher was requested to have
38-
* the start menu shortcut.
39-
* <p>
40-
* Returns <code>false</code> if this application launcher was requested not to
41-
* have the start menu shortcut.
42-
* <p>
43-
* Returns an empty {@link Optional} instance if there was no request about the
44-
* start menu shortcut for this application launcher.
37+
* Returns a non-empty {@link Optional} instance if a request about the start
38+
* menu shortcut for this application launcher was made and an empty
39+
* {@link Optional} instance if there was no request about the start menu
40+
* shortcut for this application launcher.
4541
*
46-
* @return the start menu shortcut setting of this application launcher
42+
* @return the start menu shortcut of this application launcher
4743
*/
48-
Optional<Boolean> shortcut();
44+
Optional<LauncherShortcut> shortcut();
4945

5046
/**
5147
* Default implementation of {@link LinuxLauncherMixin} interface.
5248
*/
53-
record Stub(Optional<Boolean> shortcut) implements LinuxLauncherMixin {
49+
record Stub(Optional<LauncherShortcut> shortcut) implements LinuxLauncherMixin {
5450
}
5551
}

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Name=APPLICATION_NAME
33
Comment=APPLICATION_DESCRIPTION
44
Exec=APPLICATION_LAUNCHER
5+
STARTUP_DIRECTORY
56
Icon=APPLICATION_ICON
67
Terminal=false
78
Type=Application

src/jdk.jpackage/share/classes/jdk/jpackage/internal/AddLauncherArguments.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@
3636
import jdk.jpackage.internal.Arguments.CLIOptions;
3737
import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_DATA;
3838
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
39-
import static jdk.jpackage.internal.StandardBundlerParam.MENU_HINT;
40-
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
4139

4240
/*
4341
* AddLauncherArguments
@@ -135,16 +133,16 @@ private void initLauncherMap() {
135133
Arguments.putUnlessNull(bundleParams,
136134
CLIOptions.WIN_CONSOLE_HINT.getId(),
137135
getOptionValue(CLIOptions.WIN_CONSOLE_HINT));
138-
Arguments.putUnlessNull(bundleParams, SHORTCUT_HINT.getID(),
136+
Arguments.putUnlessNull(bundleParams, CLIOptions.WIN_SHORTCUT_HINT.getId(),
139137
getOptionValue(CLIOptions.WIN_SHORTCUT_HINT));
140-
Arguments.putUnlessNull(bundleParams, MENU_HINT.getID(),
138+
Arguments.putUnlessNull(bundleParams, CLIOptions.WIN_MENU_HINT.getId(),
141139
getOptionValue(CLIOptions.WIN_MENU_HINT));
142140
}
143141

144142
if (OperatingSystem.isLinux()) {
145143
Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_CATEGORY.getId(),
146144
getOptionValue(CLIOptions.LINUX_CATEGORY));
147-
Arguments.putUnlessNull(bundleParams, SHORTCUT_HINT.getID(),
145+
Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_SHORTCUT_HINT.getId(),
148146
getOptionValue(CLIOptions.LINUX_SHORTCUT_HINT));
149147
}
150148

src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.util.HashMap;
3838
import java.util.List;
3939
import java.util.Map;
40+
import java.util.Objects;
4041
import java.util.Optional;
4142
import java.util.Properties;
4243
import java.util.ResourceBundle;
@@ -348,16 +349,13 @@ public enum CLIOptions {
348349

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

351-
WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, () -> {
352-
setOptionValue("win-menu", true);
353-
}),
352+
WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN,
353+
createArgumentWithOptionalValueAction("win-menu")),
354354

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

357-
WIN_SHORTCUT_HINT ("win-shortcut",
358-
OptionCategories.PLATFORM_WIN, () -> {
359-
setOptionValue("win-shortcut", true);
360-
}),
357+
WIN_SHORTCUT_HINT ("win-shortcut", OptionCategories.PLATFORM_WIN,
358+
createArgumentWithOptionalValueAction("win-shortcut")),
361359

362360
WIN_SHORTCUT_PROMPT ("win-shortcut-prompt",
363361
OptionCategories.PLATFORM_WIN, () -> {
@@ -396,10 +394,8 @@ public enum CLIOptions {
396394
LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps",
397395
OptionCategories.PLATFORM_LINUX),
398396

399-
LINUX_SHORTCUT_HINT ("linux-shortcut",
400-
OptionCategories.PLATFORM_LINUX, () -> {
401-
setOptionValue("linux-shortcut", true);
402-
}),
397+
LINUX_SHORTCUT_HINT ("linux-shortcut", OptionCategories.PLATFORM_LINUX,
398+
createArgumentWithOptionalValueAction("linux-shortcut")),
403399

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

@@ -478,9 +474,27 @@ private static void nextArg() {
478474
context().pos++;
479475
}
480476

477+
private static void prevArg() {
478+
Objects.checkIndex(context().pos, context().argList.size());
479+
context().pos--;
480+
}
481+
481482
private static boolean hasNextArg() {
482483
return context().pos < context().argList.size();
483484
}
485+
486+
private static Runnable createArgumentWithOptionalValueAction(String option) {
487+
Objects.requireNonNull(option);
488+
return () -> {
489+
var value = popArg();
490+
if (value.startsWith("-")) {
491+
prevArg();
492+
setOptionValue(option, true);
493+
} else {
494+
setOptionValue(option, value);
495+
}
496+
};
497+
}
484498
}
485499

486500
enum OptionCategories {

src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
*/
2525
package jdk.jpackage.internal;
2626

27+
import static jdk.jpackage.internal.Arguments.CLIOptions.LINUX_SHORTCUT_HINT;
28+
import static jdk.jpackage.internal.Arguments.CLIOptions.WIN_MENU_HINT;
29+
import static jdk.jpackage.internal.Arguments.CLIOptions.WIN_SHORTCUT_HINT;
2730
import static jdk.jpackage.internal.StandardBundlerParam.ABOUT_URL;
2831
import static jdk.jpackage.internal.StandardBundlerParam.ADD_LAUNCHERS;
2932
import static jdk.jpackage.internal.StandardBundlerParam.ADD_MODULES;
@@ -49,6 +52,7 @@
4952
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
5053
import static jdk.jpackage.internal.StandardBundlerParam.hasPredefinedAppImage;
5154
import static jdk.jpackage.internal.StandardBundlerParam.isRuntimeInstaller;
55+
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
5256

5357
import java.io.IOException;
5458
import java.nio.file.Path;
@@ -63,6 +67,8 @@
6367
import jdk.jpackage.internal.model.ConfigException;
6468
import jdk.jpackage.internal.model.ExternalApplication.LauncherInfo;
6569
import jdk.jpackage.internal.model.Launcher;
70+
import jdk.jpackage.internal.model.LauncherShortcut;
71+
import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory;
6672
import jdk.jpackage.internal.model.PackageType;
6773
import jdk.jpackage.internal.model.RuntimeLayout;
6874
import jdk.jpackage.internal.util.function.ThrowingFunction;
@@ -165,6 +171,34 @@ static Optional<jdk.jpackage.internal.model.Package> getCurrentPackage(Map<Strin
165171
jdk.jpackage.internal.model.Package.class.getName()));
166172
}
167173

174+
static Optional<LauncherShortcut> findLauncherShortcut(
175+
BundlerParamInfo<String> shortcutParam,
176+
Map<String, ? super Object> mainParams,
177+
Map<String, ? super Object> launcherParams) {
178+
179+
Optional<String> launcherValue;
180+
if (launcherParams == mainParams) {
181+
// The main launcher
182+
launcherValue = Optional.empty();
183+
} else {
184+
launcherValue = shortcutParam.findIn(launcherParams);
185+
}
186+
187+
return launcherValue.map(ParsedLauncherShortcutStartupDirectory::parseForAddLauncher).or(() -> {
188+
return Optional.ofNullable(mainParams.get(shortcutParam.getID())).map(toFunction(value -> {
189+
if (value instanceof Boolean) {
190+
return new ParsedLauncherShortcutStartupDirectory(LauncherShortcutStartupDirectory.DEFAULT);
191+
} else {
192+
try {
193+
return ParsedLauncherShortcutStartupDirectory.parseForMainLauncher((String)value);
194+
} catch (IllegalArgumentException ex) {
195+
throw I18N.buildConfigException("error.invalid-option-value", value, "--" + shortcutParam.getID()).create();
196+
}
197+
}
198+
}));
199+
}).map(ParsedLauncherShortcutStartupDirectory::value).map(LauncherShortcut::new);
200+
}
201+
168202
private static ApplicationLaunchers createLaunchers(
169203
Map<String, ? super Object> params,
170204
Function<Map<String, ? super Object>, Launcher> launcherMapper) {
@@ -195,8 +229,9 @@ private static ApplicationLaunchers createLaunchers(
195229
// mainParams), APP_NAME.fetchFrom(launcherParams)));
196230
launcherParams.put(DESCRIPTION.getID(), DESCRIPTION.fetchFrom(mainParams));
197231
}
198-
return AddLauncherArguments.merge(mainParams, launcherParams, ICON.getID(), ADD_LAUNCHERS
199-
.getID(), FILE_ASSOCIATIONS.getID());
232+
return AddLauncherArguments.merge(mainParams, launcherParams, ICON.getID(),
233+
ADD_LAUNCHERS.getID(), FILE_ASSOCIATIONS.getID(), WIN_MENU_HINT.getId(),
234+
WIN_SHORTCUT_HINT.getId(), LINUX_SHORTCUT_HINT.getId());
200235
}
201236

202237
static final BundlerParamInfo<Application> APPLICATION = createApplicationBundlerParam(null);

0 commit comments

Comments
 (0)