Skip to content

Commit 8ad1fcc

Browse files
author
Alexey Semenyuk
committed
8364564: Shortcut configuration is not recorded in .jpackage.xml file
Reviewed-by: almatvee
1 parent c1c0155 commit 8ad1fcc

File tree

15 files changed

+329
-121
lines changed

15 files changed

+329
-121
lines changed

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

Lines changed: 14 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,13 @@
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.LinuxLauncher;
50+
import jdk.jpackage.internal.model.LinuxPackage;
51+
import jdk.jpackage.internal.model.Package;
5052
import jdk.jpackage.internal.util.CompositeProxy;
5153
import jdk.jpackage.internal.util.PathUtils;
5254
import jdk.jpackage.internal.util.XmlUtils;
53-
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
5455

5556
/**
5657
* Helper to create files for desktop integration.
@@ -77,7 +78,7 @@ private DesktopIntegration(BuildEnv env, LinuxPackage pkg, LinuxLauncher launche
7778
// Need desktop and icon files if one of conditions is met:
7879
// - there are file associations configured
7980
// - user explicitly requested to create a shortcut
80-
boolean withDesktopFile = !associations.isEmpty() || launcher.shortcut().orElse(false);
81+
boolean withDesktopFile = !associations.isEmpty() || toRequest(launcher.shortcut()).orElse(false);
8182

8283
var curIconResource = createLauncherIconResource(pkg.app(), launcher,
8384
env::createResource);
@@ -132,7 +133,7 @@ private DesktopIntegration(BuildEnv env, LinuxPackage pkg, LinuxLauncher launche
132133
nestedIntegrations = pkg.app().additionalLaunchers().stream().map(v -> {
133134
return (LinuxLauncher)v;
134135
}).filter(l -> {
135-
return l.shortcut().orElse(true);
136+
return toRequest(l.shortcut()).orElse(true);
136137
}).map(toFunction(l -> {
137138
return new DesktopIntegration(env, pkg, l);
138139
})).toList();
@@ -225,15 +226,17 @@ private List<String> requiredPackagesSelf() {
225226
}
226227

227228
private Map<String, String> createDataForDesktopFile() {
229+
230+
var installedLayout = pkg.asInstalledPackageApplicationLayout().orElseThrow();
231+
228232
Map<String, String> data = new HashMap<>();
229233
data.put("APPLICATION_NAME", launcher.name());
230234
data.put("APPLICATION_DESCRIPTION", launcher.description());
231235
data.put("APPLICATION_ICON", iconFile.map(
232236
f -> f.installPath().toString()).orElse(null));
233237
data.put("DEPLOY_BUNDLE_CATEGORY", pkg.menuGroupName());
234238
data.put("APPLICATION_LAUNCHER", Enquoter.forPropertyValues().applyTo(
235-
pkg.asInstalledPackageApplicationLayout().orElseThrow().launchersDirectory().resolve(
236-
launcher.executableNameWithSuffix()).toString()));
239+
installedLayout.launchersDirectory().resolve(launcher.executableNameWithSuffix()).toString()));
237240

238241
return data;
239242
}
@@ -481,7 +484,7 @@ private static int getIconSize(FileAssociation fa) {
481484

482485
private final BuildEnv env;
483486
private final LinuxPackage pkg;
484-
private final Launcher launcher;
487+
private final LinuxLauncher launcher;
485488

486489
private final List<LinuxFileAssociation> associations;
487490

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

Lines changed: 3 additions & 6 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);

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/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/FromParams.java

Lines changed: 34 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;
@@ -63,6 +66,8 @@
6366
import jdk.jpackage.internal.model.ConfigException;
6467
import jdk.jpackage.internal.model.ExternalApplication.LauncherInfo;
6568
import jdk.jpackage.internal.model.Launcher;
69+
import jdk.jpackage.internal.model.LauncherShortcut;
70+
import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory;
6671
import jdk.jpackage.internal.model.PackageType;
6772
import jdk.jpackage.internal.model.RuntimeLayout;
6873
import jdk.jpackage.internal.util.function.ThrowingFunction;
@@ -165,6 +170,32 @@ static Optional<jdk.jpackage.internal.model.Package> getCurrentPackage(Map<Strin
165170
jdk.jpackage.internal.model.Package.class.getName()));
166171
}
167172

173+
static Optional<LauncherShortcut> findLauncherShortcut(
174+
BundlerParamInfo<Boolean> shortcutParam,
175+
Map<String, ? super Object> mainParams,
176+
Map<String, ? super Object> launcherParams) {
177+
178+
Optional<Boolean> launcherValue;
179+
if (launcherParams == mainParams) {
180+
// The main launcher
181+
launcherValue = Optional.empty();
182+
} else {
183+
launcherValue = shortcutParam.findIn(launcherParams);
184+
}
185+
186+
return launcherValue.map(withShortcut -> {
187+
if (withShortcut) {
188+
return Optional.of(LauncherShortcutStartupDirectory.DEFAULT);
189+
} else {
190+
return Optional.<LauncherShortcutStartupDirectory>empty();
191+
}
192+
}).or(() -> {
193+
return shortcutParam.findIn(mainParams).map(_ -> {
194+
return Optional.of(LauncherShortcutStartupDirectory.DEFAULT);
195+
});
196+
}).map(LauncherShortcut::new);
197+
}
198+
168199
private static ApplicationLaunchers createLaunchers(
169200
Map<String, ? super Object> params,
170201
Function<Map<String, ? super Object>, Launcher> launcherMapper) {
@@ -195,8 +226,9 @@ private static ApplicationLaunchers createLaunchers(
195226
// mainParams), APP_NAME.fetchFrom(launcherParams)));
196227
launcherParams.put(DESCRIPTION.getID(), DESCRIPTION.fetchFrom(mainParams));
197228
}
198-
return AddLauncherArguments.merge(mainParams, launcherParams, ICON.getID(), ADD_LAUNCHERS
199-
.getID(), FILE_ASSOCIATIONS.getID());
229+
return AddLauncherArguments.merge(mainParams, launcherParams, ICON.getID(),
230+
ADD_LAUNCHERS.getID(), FILE_ASSOCIATIONS.getID(), WIN_MENU_HINT.getId(),
231+
WIN_SHORTCUT_HINT.getId(), LINUX_SHORTCUT_HINT.getId());
200232
}
201233

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

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

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -307,24 +307,6 @@ final class StandardBundlerParam {
307307
true : Boolean.valueOf(s)
308308
);
309309

310-
static final BundlerParamInfo<Boolean> SHORTCUT_HINT =
311-
new BundlerParamInfo<>(
312-
"shortcut-hint", // not directly related to a CLI option
313-
Boolean.class,
314-
params -> true, // defaults to true
315-
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
316-
true : Boolean.valueOf(s)
317-
);
318-
319-
static final BundlerParamInfo<Boolean> MENU_HINT =
320-
new BundlerParamInfo<>(
321-
"menu-hint", // not directly related to a CLI option
322-
Boolean.class,
323-
params -> true, // defaults to true
324-
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
325-
true : Boolean.valueOf(s)
326-
);
327-
328310
static final BundlerParamInfo<Path> RESOURCE_DIR =
329311
new BundlerParamInfo<>(
330312
Arguments.CLIOptions.RESOURCE_DIR.getId(),
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.jpackage.internal.model;
26+
27+
import java.util.Objects;
28+
import java.util.Optional;
29+
import java.util.function.BiConsumer;
30+
31+
/**
32+
* A shortcut to launch an application launcher.
33+
*/
34+
public record LauncherShortcut(Optional<LauncherShortcutStartupDirectory> startupDirectory) {
35+
36+
public LauncherShortcut {
37+
Objects.requireNonNull(startupDirectory);
38+
}
39+
40+
public LauncherShortcut(LauncherShortcutStartupDirectory startupDirectory) {
41+
this(Optional.of(startupDirectory));
42+
}
43+
44+
public LauncherShortcut() {
45+
this(Optional.empty());
46+
}
47+
48+
void store(String propertyName, BiConsumer<String, String> sink) {
49+
Objects.requireNonNull(propertyName);
50+
Objects.requireNonNull(sink);
51+
if (startupDirectory.isEmpty()) {
52+
sink.accept(propertyName, Boolean.FALSE.toString());
53+
} else {
54+
startupDirectory.ifPresent(v -> {
55+
sink.accept(propertyName, v.asStringValue());
56+
});
57+
}
58+
}
59+
60+
/**
61+
* Converts the given shortcut into a shortcut request.
62+
* <p>
63+
* Returns <code>true</code> if shortcut was explicitly requested.
64+
* <p>
65+
* Returns <code>false</code> if no shortcut was explicitly requested.
66+
* <p>
67+
* Returns an empty {@link Optional} instance if there was no shortcut request.
68+
*
69+
* @return shortcut request
70+
*/
71+
public static Optional<Boolean> toRequest(Optional<LauncherShortcut> shortcut) {
72+
return shortcut.map(v -> v.startupDirectory().isPresent());
73+
}
74+
}

0 commit comments

Comments
 (0)