Skip to content

Commit 057992f

Browse files
author
Andy Herrick
committed
8269387: jpackage --add-launcher should have option to not create shortcuts for additional launchers
Reviewed-by: asemenyuk, almatvee
1 parent 746fe5d commit 057992f

File tree

11 files changed

+328
-83
lines changed

11 files changed

+328
-83
lines changed

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

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import static jdk.jpackage.internal.StandardBundlerParam.FILE_ASSOCIATIONS;
5757
import static jdk.jpackage.internal.StandardBundlerParam.ICON;
5858
import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE;
59+
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;;
5960

6061
/**
6162
* Helper to create files for desktop integration.
@@ -82,7 +83,7 @@ private DesktopIntegration(PlatformPackage thePackage,
8283
// Need desktop and icon files if one of conditions is met:
8384
// - there are file associations configured
8485
// - user explicitely requested to create a shortcut
85-
boolean withDesktopFile = !associations.isEmpty() || SHORTCUT_HINT.fetchFrom(params);
86+
boolean withDesktopFile = !associations.isEmpty() || LINUX_SHORTCUT_HINT.fetchFrom(params);
8687

8788
var curIconResource = LinuxAppImageBuilder.createIconResource(DEFAULT_ICON,
8889
ICON_PNG, params, mainParams);
@@ -138,28 +139,34 @@ private DesktopIntegration(PlatformPackage thePackage,
138139
// Read launchers information from predefine app image
139140
if (launchers.isEmpty() &&
140141
PREDEFINED_APP_IMAGE.fetchFrom(params) != null) {
141-
List<String> launcherPaths = AppImageFile.getLauncherNames(
142+
List<AppImageFile.LauncherInfo> launcherInfos =
143+
AppImageFile.getLaunchers(
142144
PREDEFINED_APP_IMAGE.fetchFrom(params), params);
143-
if (!launcherPaths.isEmpty()) {
144-
launcherPaths.remove(0); // Remove main launcher
145+
if (!launcherInfos.isEmpty()) {
146+
launcherInfos.remove(0); // Remove main launcher
145147
}
146-
for (var launcherPath : launcherPaths) {
148+
for (var launcherInfo : launcherInfos) {
147149
Map<String, ? super Object> launcherParams = new HashMap<>();
148150
Arguments.putUnlessNull(launcherParams, CLIOptions.NAME.getId(),
149-
launcherPath);
150-
launcherParams = AddLauncherArguments.merge(params, launcherParams,
151-
ICON.getID(), ICON_PNG.getID(), ADD_LAUNCHERS.getID(),
152-
FILE_ASSOCIATIONS.getID(), PREDEFINED_APP_IMAGE.getID());
153-
nestedIntegrations.add(new DesktopIntegration(thePackage,
154-
launcherParams, params));
151+
launcherInfo.getName());
152+
launcherParams = AddLauncherArguments.merge(params,
153+
launcherParams, ICON.getID(), ICON_PNG.getID(),
154+
ADD_LAUNCHERS.getID(), FILE_ASSOCIATIONS.getID(),
155+
PREDEFINED_APP_IMAGE.getID());
156+
if (launcherInfo.isShortcut()) {
157+
nestedIntegrations.add(new DesktopIntegration(thePackage,
158+
launcherParams, params));
159+
}
155160
}
156161
} else {
157162
for (var launcherParams : launchers) {
158-
launcherParams = AddLauncherArguments.merge(params, launcherParams,
159-
ICON.getID(), ICON_PNG.getID(), ADD_LAUNCHERS.getID(),
160-
FILE_ASSOCIATIONS.getID());
161-
nestedIntegrations.add(new DesktopIntegration(thePackage,
162-
launcherParams, params));
163+
launcherParams = AddLauncherArguments.merge(params,
164+
launcherParams, ICON.getID(), ICON_PNG.getID(),
165+
ADD_LAUNCHERS.getID(), FILE_ASSOCIATIONS.getID());
166+
if (SHORTCUT_HINT.fetchFrom(launcherParams)) {
167+
nestedIntegrations.add(new DesktopIntegration(thePackage,
168+
launcherParams, params));
169+
}
163170
}
164171
}
165172
}
@@ -567,7 +574,7 @@ private static class LinuxFileAssociation {
567574
(s, p) -> s
568575
);
569576

570-
private static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
577+
private static final StandardBundlerParam<Boolean> LINUX_SHORTCUT_HINT =
571578
new StandardBundlerParam<>(
572579
Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId(),
573580
Boolean.class,

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,8 @@
3232
import jdk.jpackage.internal.Arguments.CLIOptions;
3333
import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_DATA;
3434
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
35+
import static jdk.jpackage.internal.StandardBundlerParam.MENU_HINT;
36+
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
3537

3638
/*
3739
* AddLauncherArguments
@@ -59,7 +61,10 @@
5961
* arguments
6062
* java-options
6163
* win-console
64+
* win-shortcut
65+
* win-menu
6266
* linux-app-category
67+
* linux-shortcut
6368
*
6469
*/
6570
class AddLauncherArguments {
@@ -109,17 +114,27 @@ private void initLauncherMap() {
109114
Arguments.putUnlessNull(bundleParams, CLIOptions.RELEASE.getId(),
110115
getOptionValue(CLIOptions.RELEASE));
111116

112-
Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_CATEGORY.getId(),
113-
getOptionValue(CLIOptions.LINUX_CATEGORY));
114-
115-
Arguments.putUnlessNull(bundleParams,
116-
CLIOptions.WIN_CONSOLE_HINT.getId(),
117-
getOptionValue(CLIOptions.WIN_CONSOLE_HINT));
118-
119117
String value = getOptionValue(CLIOptions.ICON);
120118
Arguments.putUnlessNull(bundleParams, CLIOptions.ICON.getId(),
121119
(value == null) ? null : Path.of(value));
122120

121+
if (Platform.isWindows()) {
122+
Arguments.putUnlessNull(bundleParams,
123+
CLIOptions.WIN_CONSOLE_HINT.getId(),
124+
getOptionValue(CLIOptions.WIN_CONSOLE_HINT));
125+
Arguments.putUnlessNull(bundleParams, SHORTCUT_HINT.getID(),
126+
getOptionValue(CLIOptions.WIN_SHORTCUT_HINT));
127+
Arguments.putUnlessNull(bundleParams, MENU_HINT.getID(),
128+
getOptionValue(CLIOptions.WIN_MENU_HINT));
129+
}
130+
131+
if (Platform.isLinux()) {
132+
Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_CATEGORY.getId(),
133+
getOptionValue(CLIOptions.LINUX_CATEGORY));
134+
Arguments.putUnlessNull(bundleParams, SHORTCUT_HINT.getID(),
135+
getOptionValue(CLIOptions.LINUX_SHORTCUT_HINT));
136+
}
137+
123138
// "arguments" and "java-options" even if value is null:
124139
if (allArgs.containsKey(CLIOptions.ARGUMENTS.getId())) {
125140
String argumentStr = getOptionValue(CLIOptions.ARGUMENTS);

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

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -40,20 +40,24 @@
4040
import javax.xml.xpath.XPathExpressionException;
4141
import javax.xml.xpath.XPathFactory;
4242
import org.w3c.dom.Document;
43+
import org.w3c.dom.Node;
4344
import org.w3c.dom.NodeList;
45+
import org.w3c.dom.NamedNodeMap;
4446
import org.xml.sax.SAXException;
4547

4648
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
4749
import static jdk.jpackage.internal.StandardBundlerParam.ADD_LAUNCHERS;
4850
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
51+
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
52+
import static jdk.jpackage.internal.StandardBundlerParam.MENU_HINT;
4953

5054
public class AppImageFile {
5155

5256
// These values will be loaded from AppImage xml file.
5357
private final String creatorVersion;
5458
private final String creatorPlatform;
5559
private final String launcherName;
56-
private final List<String> addLauncherNames;
60+
private final List<LauncherInfo> addLauncherInfos;
5761

5862
private static final String FILENAME = ".jpackage.xml";
5963

@@ -66,10 +70,10 @@ private AppImageFile() {
6670
this(null, null, null, null);
6771
}
6872

69-
private AppImageFile(String launcherName, List<String> addLauncherNames,
73+
private AppImageFile(String launcherName, List<LauncherInfo> launcherInfos,
7074
String creatorVersion, String creatorPlatform) {
7175
this.launcherName = launcherName;
72-
this.addLauncherNames = addLauncherNames;
76+
this.addLauncherInfos = launcherInfos;
7377
this.creatorVersion = creatorVersion;
7478
this.creatorPlatform = creatorPlatform;
7579
}
@@ -79,8 +83,8 @@ private AppImageFile(String launcherName, List<String> addLauncherNames,
7983
* Each item in the list is not null or empty string.
8084
* Returns empty list for application without additional launchers.
8185
*/
82-
List<String> getAddLauncherNames() {
83-
return addLauncherNames;
86+
List<LauncherInfo> getAddLaunchers() {
87+
return addLauncherInfos;
8488
}
8589

8690
/**
@@ -131,7 +135,10 @@ static void save(Path appImageDir, Map<String, Object> params)
131135
for (int i = 0; i < addLaunchers.size(); i++) {
132136
Map<String, ? super Object> sl = addLaunchers.get(i);
133137
xml.writeStartElement("add-launcher");
134-
xml.writeCharacters(APP_NAME.fetchFrom(sl));
138+
xml.writeAttribute("name", APP_NAME.fetchFrom(sl));
139+
xml.writeAttribute("shortcut",
140+
SHORTCUT_HINT.fetchFrom(sl).toString());
141+
xml.writeAttribute("menu", MENU_HINT.fetchFrom(sl).toString());
135142
xml.writeEndElement();
136143
}
137144
});
@@ -156,24 +163,31 @@ static AppImageFile load(Path appImageDir) throws IOException {
156163
return new AppImageFile();
157164
}
158165

159-
List<String> addLaunchers = new ArrayList<>();
166+
List<LauncherInfo> launcherInfos = new ArrayList<>();
160167

161168
String platform = xpathQueryNullable(xPath,
162169
"/jpackage-state/@platform", doc);
163170

164171
String version = xpathQueryNullable(xPath,
165172
"/jpackage-state/@version", doc);
166173

167-
NodeList launcherNameNodes = (NodeList) xPath.evaluate(
168-
"/jpackage-state/add-launcher/text()", doc,
174+
NodeList launcherNodes = (NodeList) xPath.evaluate(
175+
"/jpackage-state/add-launcher", doc,
169176
XPathConstants.NODESET);
170177

171-
for (int i = 0; i != launcherNameNodes.getLength(); i++) {
172-
addLaunchers.add(launcherNameNodes.item(i).getNodeValue());
178+
for (int i = 0; i != launcherNodes.getLength(); i++) {
179+
Node item = launcherNodes.item(i);
180+
String name = getAttribute(item, "name");
181+
String shortcut = getAttribute(item, "shortcut");
182+
String menu = getAttribute(item, "menu");
183+
184+
launcherInfos.add(new LauncherInfo(name,
185+
!("false".equals(shortcut)),
186+
!("false".equals(menu))));
173187
}
174188

175189
AppImageFile file = new AppImageFile(
176-
mainLauncher, addLaunchers, version, platform);
190+
mainLauncher, launcherInfos, version, platform);
177191
if (!file.isValid()) {
178192
file = new AppImageFile();
179193
}
@@ -184,6 +198,12 @@ static AppImageFile load(Path appImageDir) throws IOException {
184198
}
185199
}
186200

201+
private static String getAttribute(Node item, String attr) {
202+
NamedNodeMap attrs = item.getAttributes();
203+
Node attrNode = attrs.getNamedItem(attr);
204+
return ((attrNode == null) ? null : attrNode.getNodeValue());
205+
}
206+
187207
public static Document readXml(Path appImageDir) throws IOException {
188208
try {
189209
Path path = getPathInAppImage(appImageDir);
@@ -202,18 +222,19 @@ public static Document readXml(Path appImageDir) throws IOException {
202222
}
203223

204224
/**
205-
* Returns list of launcher names configured for the application.
206-
* The first item in the returned list is main launcher name.
225+
* Returns list of LauncherInfo objects configured for the application.
226+
* The first item in the returned list is main launcher.
207227
* Following items in the list are names of additional launchers.
208228
*/
209-
static List<String> getLauncherNames(Path appImageDir,
229+
static List<LauncherInfo> getLaunchers(Path appImageDir,
210230
Map<String, ? super Object> params) {
211-
List<String> launchers = new ArrayList<>();
231+
List<LauncherInfo> launchers = new ArrayList<>();
212232
try {
213233
AppImageFile appImageInfo = AppImageFile.load(appImageDir);
214234
if (appImageInfo != null) {
215-
launchers.add(appImageInfo.getLauncherName());
216-
launchers.addAll(appImageInfo.getAddLauncherNames());
235+
launchers.add(new LauncherInfo(
236+
appImageInfo.getLauncherName(), true, true));
237+
launchers.addAll(appImageInfo.getAddLaunchers());
217238
return launchers;
218239
}
219240
} catch (NoSuchFileException nsfe) {
@@ -226,10 +247,11 @@ static List<String> getLauncherNames(Path appImageDir,
226247
"warning.invalid-app-image"), appImageDir));
227248

228249
}
250+
// this should never be the case, but maintaining behavior of
251+
// creating default launchers without AppImageFile present
229252

230-
launchers.add(APP_NAME.fetchFrom(params));
231-
ADD_LAUNCHERS.fetchFrom(params).stream().map(APP_NAME::fetchFrom).forEach(
232-
launchers::add);
253+
ADD_LAUNCHERS.fetchFrom(params).stream().map(APP_NAME::fetchFrom).map(
254+
name -> new LauncherInfo(name, true, true)).forEach(launchers::add);
233255
return launchers;
234256
}
235257

@@ -262,15 +284,37 @@ private static String getPlatform() {
262284
}
263285

264286
private boolean isValid() {
265-
if (launcherName == null || launcherName.length() == 0 ||
266-
addLauncherNames.indexOf("") != -1) {
267-
// Some launchers have empty names. This is invalid.
287+
if (launcherName == null || launcherName.length() == 0) {
268288
return false;
269289
}
270-
271-
// Add more validation.
290+
for (var launcher : addLauncherInfos) {
291+
if ("".equals(launcher.getName())) {
292+
return false;
293+
}
294+
}
272295

273296
return true;
274297
}
275298

299+
static class LauncherInfo {
300+
private String name;
301+
private boolean shortcut;
302+
private boolean menu;
303+
304+
public LauncherInfo(String name, boolean shortcut, boolean menu) {
305+
this.name = name;
306+
this.shortcut = shortcut;
307+
this.menu = menu;
308+
}
309+
public String getName() {
310+
return name;
311+
}
312+
public boolean isShortcut() {
313+
return shortcut;
314+
}
315+
public boolean isMenu() {
316+
return menu;
317+
}
318+
}
319+
276320
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,24 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
311311
true : Boolean.valueOf(s)
312312
);
313313

314+
static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
315+
new StandardBundlerParam<>(
316+
"shortcut-hint", // not directly related to a CLI option
317+
Boolean.class,
318+
params -> true, // defaults to true
319+
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
320+
true : Boolean.valueOf(s)
321+
);
322+
323+
static final StandardBundlerParam<Boolean> MENU_HINT =
324+
new StandardBundlerParam<>(
325+
"menu-hint", // not directly related to a CLI option
326+
Boolean.class,
327+
params -> true, // defaults to true
328+
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
329+
true : Boolean.valueOf(s)
330+
);
331+
314332
static final StandardBundlerParam<Path> RESOURCE_DIR =
315333
new StandardBundlerParam<>(
316334
Arguments.CLIOptions.RESOURCE_DIR.getId(),

src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,9 @@ Generic Options:\n\
135135
\ a list of key, value pairs\n\
136136
\ (absolute path or relative to the current directory)\n\
137137
\ The keys "module", "main-jar", "main-class",\n\
138-
\ "arguments", "java-options", "app-version", "icon", and\n\
139-
\ "win-console" can be used.\n\
138+
\ "arguments", "java-options", "app-version", "icon",\n\
139+
\ "win-console", "win-shortcut", "win-menu",\n\
140+
\ "linux-app-category", and "linux-shortcut" can be used.\n\
140141
\ These options are added to, or used to overwrite, the original\n\
141142
\ command line options to build an additional alternative launcher.\n\
142143
\ The main application launcher will be built from the command line\n\

src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources_ja.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,9 @@ Generic Options:\n\
135135
\ a list of key, value pairs\n\
136136
\ (absolute path or relative to the current directory)\n\
137137
\ The keys "module", "main-jar", "main-class",\n\
138-
\ "arguments", "java-options", "app-version", "icon", and\n\
139-
\ "win-console" can be used.\n\
138+
\ "arguments", "java-options", "app-version", "icon",\n\
139+
\ "win-console", "win-shortcut", "win-menu",\n\
140+
\ "linux-app-category", and "linux-shortcut" can be used.\n\
140141
\ These options are added to, or used to overwrite, the original\n\
141142
\ command line options to build an additional alternative launcher.\n\
142143
\ The main application launcher will be built from the command line\n\

0 commit comments

Comments
 (0)