mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8364564: Shortcut configuration is not recorded in .jpackage.xml file
Reviewed-by: almatvee
This commit is contained in:
parent
c1c0155604
commit
8ad1fcc48a
@ -24,10 +24,10 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import jdk.jpackage.internal.model.LinuxPackage;
|
||||
import jdk.jpackage.internal.model.LinuxLauncher;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.model.Launcher;
|
||||
import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource;
|
||||
import static jdk.jpackage.internal.model.LauncherShortcut.toRequest;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -45,12 +45,13 @@ import java.util.stream.Stream;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource;
|
||||
import jdk.jpackage.internal.model.FileAssociation;
|
||||
import jdk.jpackage.internal.model.LinuxLauncher;
|
||||
import jdk.jpackage.internal.model.LinuxPackage;
|
||||
import jdk.jpackage.internal.model.Package;
|
||||
import jdk.jpackage.internal.util.CompositeProxy;
|
||||
import jdk.jpackage.internal.util.PathUtils;
|
||||
import jdk.jpackage.internal.util.XmlUtils;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
|
||||
/**
|
||||
* Helper to create files for desktop integration.
|
||||
@ -77,7 +78,7 @@ final class DesktopIntegration extends ShellCustomAction {
|
||||
// Need desktop and icon files if one of conditions is met:
|
||||
// - there are file associations configured
|
||||
// - user explicitly requested to create a shortcut
|
||||
boolean withDesktopFile = !associations.isEmpty() || launcher.shortcut().orElse(false);
|
||||
boolean withDesktopFile = !associations.isEmpty() || toRequest(launcher.shortcut()).orElse(false);
|
||||
|
||||
var curIconResource = createLauncherIconResource(pkg.app(), launcher,
|
||||
env::createResource);
|
||||
@ -132,7 +133,7 @@ final class DesktopIntegration extends ShellCustomAction {
|
||||
nestedIntegrations = pkg.app().additionalLaunchers().stream().map(v -> {
|
||||
return (LinuxLauncher)v;
|
||||
}).filter(l -> {
|
||||
return l.shortcut().orElse(true);
|
||||
return toRequest(l.shortcut()).orElse(true);
|
||||
}).map(toFunction(l -> {
|
||||
return new DesktopIntegration(env, pkg, l);
|
||||
})).toList();
|
||||
@ -225,6 +226,9 @@ final class DesktopIntegration extends ShellCustomAction {
|
||||
}
|
||||
|
||||
private Map<String, String> createDataForDesktopFile() {
|
||||
|
||||
var installedLayout = pkg.asInstalledPackageApplicationLayout().orElseThrow();
|
||||
|
||||
Map<String, String> data = new HashMap<>();
|
||||
data.put("APPLICATION_NAME", launcher.name());
|
||||
data.put("APPLICATION_DESCRIPTION", launcher.description());
|
||||
@ -232,8 +236,7 @@ final class DesktopIntegration extends ShellCustomAction {
|
||||
f -> f.installPath().toString()).orElse(null));
|
||||
data.put("DEPLOY_BUNDLE_CATEGORY", pkg.menuGroupName());
|
||||
data.put("APPLICATION_LAUNCHER", Enquoter.forPropertyValues().applyTo(
|
||||
pkg.asInstalledPackageApplicationLayout().orElseThrow().launchersDirectory().resolve(
|
||||
launcher.executableNameWithSuffix()).toString()));
|
||||
installedLayout.launchersDirectory().resolve(launcher.executableNameWithSuffix()).toString()));
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -481,7 +484,7 @@ final class DesktopIntegration extends ShellCustomAction {
|
||||
|
||||
private final BuildEnv env;
|
||||
private final LinuxPackage pkg;
|
||||
private final Launcher launcher;
|
||||
private final LinuxLauncher launcher;
|
||||
|
||||
private final List<LinuxFileAssociation> associations;
|
||||
|
||||
|
||||
@ -29,16 +29,14 @@ import static jdk.jpackage.internal.FromParams.createApplicationBuilder;
|
||||
import static jdk.jpackage.internal.FromParams.createApplicationBundlerParam;
|
||||
import static jdk.jpackage.internal.FromParams.createPackageBuilder;
|
||||
import static jdk.jpackage.internal.FromParams.createPackageBundlerParam;
|
||||
import static jdk.jpackage.internal.FromParams.findLauncherShortcut;
|
||||
import static jdk.jpackage.internal.LinuxPackagingPipeline.APPLICATION_LAYOUT;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
|
||||
import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB;
|
||||
import static jdk.jpackage.internal.model.StandardPackageType.LINUX_RPM;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.LinuxApplication;
|
||||
import jdk.jpackage.internal.model.LinuxLauncher;
|
||||
@ -51,11 +49,10 @@ final class LinuxFromParams {
|
||||
private static LinuxApplication createLinuxApplication(
|
||||
Map<String, ? super Object> params) throws ConfigException, IOException {
|
||||
final var launcherFromParams = new LauncherFromParams();
|
||||
|
||||
final var app = createApplicationBuilder(params, toFunction(launcherParams -> {
|
||||
final var launcher = launcherFromParams.create(launcherParams);
|
||||
final var shortcut = Stream.of(SHORTCUT_HINT, LINUX_SHORTCUT_HINT).map(param -> {
|
||||
return param.findIn(launcherParams);
|
||||
}).filter(Optional::isPresent).map(Optional::get).findFirst();
|
||||
final var shortcut = findLauncherShortcut(LINUX_SHORTCUT_HINT, params, launcherParams);
|
||||
return LinuxLauncher.create(launcher, new LinuxLauncherMixin.Stub(shortcut));
|
||||
}), APPLICATION_LAYOUT).create();
|
||||
return LinuxApplication.create(app);
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
*/
|
||||
package jdk.jpackage.internal.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import jdk.jpackage.internal.util.CompositeProxy;
|
||||
|
||||
@ -36,9 +37,11 @@ public interface LinuxLauncher extends Launcher, LinuxLauncherMixin {
|
||||
|
||||
@Override
|
||||
default Map<String, String> extraAppImageFileData() {
|
||||
return shortcut().map(v -> {
|
||||
return Map.of("shortcut", Boolean.toString(v));
|
||||
}).orElseGet(Map::of);
|
||||
Map<String, String> map = new HashMap<>();
|
||||
shortcut().ifPresent(shortcut -> {
|
||||
shortcut.store(SHORTCUT_ID, map::put);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,4 +55,6 @@ public interface LinuxLauncher extends Launcher, LinuxLauncherMixin {
|
||||
public static LinuxLauncher create(Launcher launcher, LinuxLauncherMixin mixin) {
|
||||
return CompositeProxy.create(LinuxLauncher.class, launcher, mixin);
|
||||
}
|
||||
|
||||
public static final String SHORTCUT_ID = "linux-shortcut";
|
||||
}
|
||||
|
||||
@ -32,24 +32,20 @@ import java.util.Optional;
|
||||
public interface LinuxLauncherMixin {
|
||||
|
||||
/**
|
||||
* Gets the start menu shortcut setting of this application launcher.
|
||||
* Gets the start menu shortcut of this application launcher.
|
||||
* <p>
|
||||
* Returns <code>true</code> if this application launcher was requested to have
|
||||
* the start menu shortcut.
|
||||
* <p>
|
||||
* Returns <code>false</code> if this application launcher was requested not to
|
||||
* have the start menu shortcut.
|
||||
* <p>
|
||||
* Returns an empty {@link Optional} instance if there was no request about the
|
||||
* start menu shortcut for this application launcher.
|
||||
* Returns a non-empty {@link Optional} instance if a request about the start
|
||||
* menu shortcut for this application launcher was made and an empty
|
||||
* {@link Optional} instance if there was no request about the start menu
|
||||
* shortcut for this application launcher.
|
||||
*
|
||||
* @return the start menu shortcut setting of this application launcher
|
||||
* @return the start menu shortcut of this application launcher
|
||||
*/
|
||||
Optional<Boolean> shortcut();
|
||||
Optional<LauncherShortcut> shortcut();
|
||||
|
||||
/**
|
||||
* Default implementation of {@link LinuxLauncherMixin} interface.
|
||||
*/
|
||||
record Stub(Optional<Boolean> shortcut) implements LinuxLauncherMixin {
|
||||
record Stub(Optional<LauncherShortcut> shortcut) implements LinuxLauncherMixin {
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,8 +36,6 @@ import jdk.internal.util.OperatingSystem;
|
||||
import jdk.jpackage.internal.Arguments.CLIOptions;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_DATA;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.MENU_HINT;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
|
||||
|
||||
/*
|
||||
* AddLauncherArguments
|
||||
@ -135,16 +133,16 @@ class AddLauncherArguments {
|
||||
Arguments.putUnlessNull(bundleParams,
|
||||
CLIOptions.WIN_CONSOLE_HINT.getId(),
|
||||
getOptionValue(CLIOptions.WIN_CONSOLE_HINT));
|
||||
Arguments.putUnlessNull(bundleParams, SHORTCUT_HINT.getID(),
|
||||
Arguments.putUnlessNull(bundleParams, CLIOptions.WIN_SHORTCUT_HINT.getId(),
|
||||
getOptionValue(CLIOptions.WIN_SHORTCUT_HINT));
|
||||
Arguments.putUnlessNull(bundleParams, MENU_HINT.getID(),
|
||||
Arguments.putUnlessNull(bundleParams, CLIOptions.WIN_MENU_HINT.getId(),
|
||||
getOptionValue(CLIOptions.WIN_MENU_HINT));
|
||||
}
|
||||
|
||||
if (OperatingSystem.isLinux()) {
|
||||
Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_CATEGORY.getId(),
|
||||
getOptionValue(CLIOptions.LINUX_CATEGORY));
|
||||
Arguments.putUnlessNull(bundleParams, SHORTCUT_HINT.getID(),
|
||||
Arguments.putUnlessNull(bundleParams, CLIOptions.LINUX_SHORTCUT_HINT.getId(),
|
||||
getOptionValue(CLIOptions.LINUX_SHORTCUT_HINT));
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +24,9 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import static jdk.jpackage.internal.Arguments.CLIOptions.LINUX_SHORTCUT_HINT;
|
||||
import static jdk.jpackage.internal.Arguments.CLIOptions.WIN_MENU_HINT;
|
||||
import static jdk.jpackage.internal.Arguments.CLIOptions.WIN_SHORTCUT_HINT;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.ABOUT_URL;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.ADD_LAUNCHERS;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.ADD_MODULES;
|
||||
@ -63,6 +66,8 @@ import jdk.jpackage.internal.model.ApplicationLayout;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
import jdk.jpackage.internal.model.ExternalApplication.LauncherInfo;
|
||||
import jdk.jpackage.internal.model.Launcher;
|
||||
import jdk.jpackage.internal.model.LauncherShortcut;
|
||||
import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory;
|
||||
import jdk.jpackage.internal.model.PackageType;
|
||||
import jdk.jpackage.internal.model.RuntimeLayout;
|
||||
import jdk.jpackage.internal.util.function.ThrowingFunction;
|
||||
@ -165,6 +170,32 @@ final class FromParams {
|
||||
jdk.jpackage.internal.model.Package.class.getName()));
|
||||
}
|
||||
|
||||
static Optional<LauncherShortcut> findLauncherShortcut(
|
||||
BundlerParamInfo<Boolean> shortcutParam,
|
||||
Map<String, ? super Object> mainParams,
|
||||
Map<String, ? super Object> launcherParams) {
|
||||
|
||||
Optional<Boolean> 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);
|
||||
}
|
||||
|
||||
private static ApplicationLaunchers createLaunchers(
|
||||
Map<String, ? super Object> params,
|
||||
Function<Map<String, ? super Object>, Launcher> launcherMapper) {
|
||||
@ -195,8 +226,9 @@ final class FromParams {
|
||||
// mainParams), APP_NAME.fetchFrom(launcherParams)));
|
||||
launcherParams.put(DESCRIPTION.getID(), DESCRIPTION.fetchFrom(mainParams));
|
||||
}
|
||||
return AddLauncherArguments.merge(mainParams, launcherParams, ICON.getID(), ADD_LAUNCHERS
|
||||
.getID(), FILE_ASSOCIATIONS.getID());
|
||||
return AddLauncherArguments.merge(mainParams, launcherParams, ICON.getID(),
|
||||
ADD_LAUNCHERS.getID(), FILE_ASSOCIATIONS.getID(), WIN_MENU_HINT.getId(),
|
||||
WIN_SHORTCUT_HINT.getId(), LINUX_SHORTCUT_HINT.getId());
|
||||
}
|
||||
|
||||
static final BundlerParamInfo<Application> APPLICATION = createApplicationBundlerParam(null);
|
||||
|
||||
@ -307,24 +307,6 @@ final class StandardBundlerParam {
|
||||
true : Boolean.valueOf(s)
|
||||
);
|
||||
|
||||
static final BundlerParamInfo<Boolean> SHORTCUT_HINT =
|
||||
new BundlerParamInfo<>(
|
||||
"shortcut-hint", // not directly related to a CLI option
|
||||
Boolean.class,
|
||||
params -> true, // defaults to true
|
||||
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
|
||||
true : Boolean.valueOf(s)
|
||||
);
|
||||
|
||||
static final BundlerParamInfo<Boolean> MENU_HINT =
|
||||
new BundlerParamInfo<>(
|
||||
"menu-hint", // not directly related to a CLI option
|
||||
Boolean.class,
|
||||
params -> true, // defaults to true
|
||||
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
|
||||
true : Boolean.valueOf(s)
|
||||
);
|
||||
|
||||
static final BundlerParamInfo<Path> RESOURCE_DIR =
|
||||
new BundlerParamInfo<>(
|
||||
Arguments.CLIOptions.RESOURCE_DIR.getId(),
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* A shortcut to launch an application launcher.
|
||||
*/
|
||||
public record LauncherShortcut(Optional<LauncherShortcutStartupDirectory> startupDirectory) {
|
||||
|
||||
public LauncherShortcut {
|
||||
Objects.requireNonNull(startupDirectory);
|
||||
}
|
||||
|
||||
public LauncherShortcut(LauncherShortcutStartupDirectory startupDirectory) {
|
||||
this(Optional.of(startupDirectory));
|
||||
}
|
||||
|
||||
public LauncherShortcut() {
|
||||
this(Optional.empty());
|
||||
}
|
||||
|
||||
void store(String propertyName, BiConsumer<String, String> sink) {
|
||||
Objects.requireNonNull(propertyName);
|
||||
Objects.requireNonNull(sink);
|
||||
if (startupDirectory.isEmpty()) {
|
||||
sink.accept(propertyName, Boolean.FALSE.toString());
|
||||
} else {
|
||||
startupDirectory.ifPresent(v -> {
|
||||
sink.accept(propertyName, v.asStringValue());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given shortcut into a shortcut request.
|
||||
* <p>
|
||||
* Returns <code>true</code> if shortcut was explicitly requested.
|
||||
* <p>
|
||||
* Returns <code>false</code> if no shortcut was explicitly requested.
|
||||
* <p>
|
||||
* Returns an empty {@link Optional} instance if there was no shortcut request.
|
||||
*
|
||||
* @return shortcut request
|
||||
*/
|
||||
public static Optional<Boolean> toRequest(Optional<LauncherShortcut> shortcut) {
|
||||
return shortcut.map(v -> v.startupDirectory().isPresent());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The directory in which to run an application launcher when it is started from
|
||||
* a shortcut.
|
||||
*/
|
||||
public enum LauncherShortcutStartupDirectory {
|
||||
|
||||
/**
|
||||
* Platform-specific default value.
|
||||
* <p>
|
||||
* On Windows, it indicates that the startup directory should be the package's
|
||||
* installation directory.
|
||||
* <p>
|
||||
* On Linux, it indicates that a shortcut doesn't have the startup directory
|
||||
* configured explicitly.
|
||||
*/
|
||||
DEFAULT("true");
|
||||
|
||||
LauncherShortcutStartupDirectory(String stringValue) {
|
||||
this.stringValue = Objects.requireNonNull(stringValue);
|
||||
}
|
||||
|
||||
public String asStringValue() {
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
private final String stringValue;
|
||||
}
|
||||
@ -24,24 +24,19 @@
|
||||
*/
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static jdk.jpackage.internal.BundlerParamInfo.createBooleanBundlerParam;
|
||||
import static jdk.jpackage.internal.BundlerParamInfo.createStringBundlerParam;
|
||||
import static jdk.jpackage.internal.FromParams.createApplicationBuilder;
|
||||
import static jdk.jpackage.internal.FromParams.createApplicationBundlerParam;
|
||||
import static jdk.jpackage.internal.FromParams.createPackageBuilder;
|
||||
import static jdk.jpackage.internal.FromParams.createPackageBundlerParam;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.MENU_HINT;
|
||||
import static jdk.jpackage.internal.FromParams.findLauncherShortcut;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
|
||||
import static jdk.jpackage.internal.WinPackagingPipeline.APPLICATION_LAYOUT;
|
||||
import static jdk.jpackage.internal.model.StandardPackageType.WIN_MSI;
|
||||
import static jdk.jpackage.internal.model.WinLauncherMixin.WinShortcut.WIN_SHORTCUT_DESKTOP;
|
||||
import static jdk.jpackage.internal.model.WinLauncherMixin.WinShortcut.WIN_SHORTCUT_START_MENU;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import jdk.jpackage.internal.model.ConfigException;
|
||||
@ -63,18 +58,11 @@ final class WinFromParams {
|
||||
|
||||
final boolean isConsole = CONSOLE_HINT.findIn(launcherParams).orElse(false);
|
||||
|
||||
final var shortcuts = Map.of(WIN_SHORTCUT_DESKTOP, List.of(SHORTCUT_HINT,
|
||||
WIN_SHORTCUT_HINT), WIN_SHORTCUT_START_MENU, List.of(MENU_HINT,
|
||||
WIN_MENU_HINT)).entrySet().stream().filter(e -> {
|
||||
final var startMenuShortcut = findLauncherShortcut(WIN_MENU_HINT, params, launcherParams);
|
||||
|
||||
final var shortcutParams = e.getValue();
|
||||
final var desktopShortcut = findLauncherShortcut(WIN_SHORTCUT_HINT, params, launcherParams);
|
||||
|
||||
return shortcutParams.get(0).findIn(launcherParams).orElseGet(() -> {
|
||||
return shortcutParams.get(1).findIn(launcherParams).orElse(false);
|
||||
});
|
||||
}).map(Map.Entry::getKey).collect(toSet());
|
||||
|
||||
return WinLauncher.create(launcher, new WinLauncherMixin.Stub(isConsole, shortcuts));
|
||||
return WinLauncher.create(launcher, new WinLauncherMixin.Stub(isConsole, startMenuShortcut, desktopShortcut));
|
||||
|
||||
}), APPLICATION_LAYOUT).create();
|
||||
|
||||
|
||||
@ -25,12 +25,9 @@
|
||||
|
||||
package jdk.jpackage.internal;
|
||||
|
||||
import jdk.jpackage.internal.model.WinLauncher;
|
||||
import jdk.jpackage.internal.model.WinMsiPackage;
|
||||
import jdk.jpackage.internal.model.Launcher;
|
||||
import jdk.jpackage.internal.model.DottedVersion;
|
||||
import jdk.jpackage.internal.model.ApplicationLayout;
|
||||
import jdk.jpackage.internal.util.PathGroup;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
import static jdk.jpackage.internal.util.CollectionUtils.toCollection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
@ -50,7 +47,6 @@ import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
@ -60,15 +56,19 @@ import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import static jdk.jpackage.internal.util.CollectionUtils.toCollection;
|
||||
import jdk.jpackage.internal.model.WinLauncherMixin.WinShortcut;
|
||||
import jdk.jpackage.internal.WixToolset.WixToolsetType;
|
||||
import jdk.jpackage.internal.model.AppImageLayout;
|
||||
import jdk.jpackage.internal.model.ApplicationLayout;
|
||||
import jdk.jpackage.internal.model.DottedVersion;
|
||||
import jdk.jpackage.internal.model.FileAssociation;
|
||||
import jdk.jpackage.internal.model.Launcher;
|
||||
import jdk.jpackage.internal.model.LauncherShortcut;
|
||||
import jdk.jpackage.internal.model.WinLauncher;
|
||||
import jdk.jpackage.internal.model.WinMsiPackage;
|
||||
import jdk.jpackage.internal.util.PathGroup;
|
||||
import jdk.jpackage.internal.util.PathUtils;
|
||||
import jdk.jpackage.internal.util.XmlUtils;
|
||||
import jdk.jpackage.internal.util.XmlConsumer;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer;
|
||||
import jdk.jpackage.internal.util.XmlUtils;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
/**
|
||||
@ -352,7 +352,7 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder {
|
||||
|
||||
private final Config cfg;
|
||||
private final Id id;
|
||||
};
|
||||
}
|
||||
|
||||
private static void addComponentGroup(XMLStreamWriter xml, String id,
|
||||
List<String> componentIds) throws XMLStreamException, IOException {
|
||||
@ -469,7 +469,18 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder {
|
||||
launcher.executableNameWithSuffix());
|
||||
|
||||
if (folder.isRequestedFor(launcher)) {
|
||||
String componentId = addShortcutComponent(xml, launcherPath, folder);
|
||||
var workDirectory = folder.shortcut(launcher).startupDirectory().map(v -> {
|
||||
switch (v) {
|
||||
case DEFAULT -> {
|
||||
return INSTALLDIR;
|
||||
}
|
||||
default -> {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}).orElseThrow();
|
||||
|
||||
String componentId = addShortcutComponent(xml, launcherPath, folder, workDirectory);
|
||||
|
||||
if (componentId != null) {
|
||||
Path folderPath = folder.getPath(this);
|
||||
@ -499,23 +510,26 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder {
|
||||
}
|
||||
|
||||
private String addShortcutComponent(XMLStreamWriter xml, Path launcherPath,
|
||||
ShortcutsFolder folder) throws XMLStreamException, IOException {
|
||||
ShortcutsFolder folder, Path shortcutWorkDir) throws XMLStreamException, IOException {
|
||||
Objects.requireNonNull(folder);
|
||||
|
||||
if (!INSTALLDIR.equals(launcherPath.getName(0))) {
|
||||
throw throwInvalidPathException(launcherPath);
|
||||
}
|
||||
|
||||
if (!INSTALLDIR.equals(shortcutWorkDir.getName(0))) {
|
||||
throw throwInvalidPathException(shortcutWorkDir);
|
||||
}
|
||||
|
||||
String launcherBasename = PathUtils.replaceSuffix(
|
||||
IOUtils.getFileName(launcherPath), "").toString();
|
||||
|
||||
Path shortcutPath = folder.getPath(this).resolve(launcherBasename);
|
||||
return addComponent(xml, shortcutPath, Component.Shortcut, unused -> {
|
||||
xml.writeAttribute("Name", launcherBasename);
|
||||
xml.writeAttribute("WorkingDirectory", INSTALLDIR.toString());
|
||||
xml.writeAttribute("WorkingDirectory", Id.Folder.of(shortcutWorkDir));
|
||||
xml.writeAttribute("Advertise", "no");
|
||||
xml.writeAttribute("Target", String.format("[#%s]",
|
||||
Component.File.idOf(launcherPath)));
|
||||
xml.writeAttribute("Target", String.format("[#%s]", Id.File.of(launcherPath)));
|
||||
});
|
||||
}
|
||||
|
||||
@ -906,15 +920,15 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder {
|
||||
}
|
||||
|
||||
enum ShortcutsFolder {
|
||||
ProgramMenu(PROGRAM_MENU_PATH, WinShortcut.WIN_SHORTCUT_START_MENU,
|
||||
ProgramMenu(PROGRAM_MENU_PATH, WinLauncher::startMenuShortcut,
|
||||
"JP_INSTALL_STARTMENU_SHORTCUT", "JpStartMenuShortcutPrompt"),
|
||||
Desktop(DESKTOP_PATH, WinShortcut.WIN_SHORTCUT_DESKTOP,
|
||||
Desktop(DESKTOP_PATH, WinLauncher::desktopShortcut,
|
||||
"JP_INSTALL_DESKTOP_SHORTCUT", "JpDesktopShortcutPrompt");
|
||||
|
||||
private ShortcutsFolder(Path root, WinShortcut shortcutId,
|
||||
private ShortcutsFolder(Path root, Function<WinLauncher, Optional<LauncherShortcut>> shortcut,
|
||||
String property, String wixVariableName) {
|
||||
this.root = root;
|
||||
this.shortcutId = shortcutId;
|
||||
this.shortcut = shortcut;
|
||||
this.wixVariableName = wixVariableName;
|
||||
this.property = property;
|
||||
}
|
||||
@ -927,7 +941,11 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder {
|
||||
}
|
||||
|
||||
boolean isRequestedFor(WinLauncher launcher) {
|
||||
return launcher.shortcuts().contains(shortcutId);
|
||||
return LauncherShortcut.toRequest(shortcut.apply(launcher)).orElse(false);
|
||||
}
|
||||
|
||||
LauncherShortcut shortcut(WinLauncher launcher) {
|
||||
return shortcut.apply(launcher).orElseThrow();
|
||||
}
|
||||
|
||||
String getWixVariableName() {
|
||||
@ -947,7 +965,7 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder {
|
||||
private final Path root;
|
||||
private final String property;
|
||||
private final String wixVariableName;
|
||||
private final WinShortcut shortcutId;
|
||||
private final Function<WinLauncher, Optional<LauncherShortcut>> shortcut;
|
||||
}
|
||||
|
||||
private boolean systemWide;
|
||||
|
||||
@ -24,9 +24,8 @@
|
||||
*/
|
||||
package jdk.jpackage.internal.model;
|
||||
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import jdk.jpackage.internal.resources.ResourceLocator;
|
||||
@ -47,10 +46,20 @@ public interface WinLauncher extends Launcher, WinLauncherMixin {
|
||||
|
||||
@Override
|
||||
default Map<String, String> extraAppImageFileData() {
|
||||
return shortcuts().stream().collect(toMap(WinShortcut::name, v -> Boolean.toString(true)));
|
||||
Map<String, String> map = new HashMap<>();
|
||||
desktopShortcut().ifPresent(shortcut -> {
|
||||
shortcut.store(SHORTCUT_DESKTOP_ID, map::put);
|
||||
});
|
||||
startMenuShortcut().ifPresent(shortcut -> {
|
||||
shortcut.store(SHORTCUT_START_MENU_ID, map::put);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
public static WinLauncher create(Launcher launcher, WinLauncherMixin mixin) {
|
||||
return CompositeProxy.create(WinLauncher.class, launcher, mixin);
|
||||
}
|
||||
|
||||
public static final String SHORTCUT_START_MENU_ID = "win-menu";
|
||||
public static final String SHORTCUT_DESKTOP_ID = "win-shortcut";
|
||||
}
|
||||
|
||||
@ -24,30 +24,37 @@
|
||||
*/
|
||||
package jdk.jpackage.internal.model;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface WinLauncherMixin {
|
||||
|
||||
boolean isConsole();
|
||||
|
||||
enum WinShortcut {
|
||||
WIN_SHORTCUT_DESKTOP("shortcut"),
|
||||
WIN_SHORTCUT_START_MENU("menu"),
|
||||
;
|
||||
/**
|
||||
* Gets the start menu shortcut of this application launcher.
|
||||
* <p>
|
||||
* Returns a non-empty {@link Optional} instance if a request about the start
|
||||
* menu shortcut for this application launcher was made and an empty
|
||||
* {@link Optional} instance if there was no request about the start menu
|
||||
* shortcut for this application launcher.
|
||||
*
|
||||
* @return the start menu shortcut of this application launcher
|
||||
*/
|
||||
Optional<LauncherShortcut> startMenuShortcut();
|
||||
|
||||
WinShortcut(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
/**
|
||||
* Gets the desktop shortcut of this application launcher.
|
||||
* <p>
|
||||
* Returns a non-empty {@link Optional} instance if a request about the desktop
|
||||
* shortcut for this application launcher was made and an empty {@link Optional}
|
||||
* instance if there was no request about the desktop shortcut for this
|
||||
* application launcher.
|
||||
*
|
||||
* @return the start menu shortcut of this application launcher
|
||||
*/
|
||||
Optional<LauncherShortcut> desktopShortcut();
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
private final String name;
|
||||
}
|
||||
|
||||
Set<WinShortcut> shortcuts();
|
||||
|
||||
record Stub(boolean isConsole, Set<WinShortcut> shortcuts) implements WinLauncherMixin {
|
||||
record Stub(boolean isConsole, Optional<LauncherShortcut> startMenuShortcut,
|
||||
Optional<LauncherShortcut> desktopShortcut) implements WinLauncherMixin {
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ public enum LauncherShortcut {
|
||||
}
|
||||
|
||||
public String appImageFilePropertyName() {
|
||||
return propertyName.substring(propertyName.indexOf('-') + 1);
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public String optionName() {
|
||||
|
||||
@ -77,6 +77,11 @@ public final class LauncherVerifier {
|
||||
VERIFY_UNINSTALLED((verifier, cmd) -> {
|
||||
verifier.verifyInstalled(cmd, false);
|
||||
}),
|
||||
VERIFY_APP_IMAGE_FILE((verifier, cmd) -> {
|
||||
if (cmd.isImagePackageType()) {
|
||||
verifier.verifyInAppImageFile(cmd);
|
||||
}
|
||||
}),
|
||||
EXECUTE_LAUNCHER(LauncherVerifier::executeLauncher),
|
||||
;
|
||||
|
||||
@ -91,7 +96,7 @@ public final class LauncherVerifier {
|
||||
private final BiConsumer<LauncherVerifier, JPackageCommand> action;
|
||||
|
||||
static final List<Action> VERIFY_APP_IMAGE = List.of(
|
||||
VERIFY_ICON, VERIFY_DESCRIPTION, VERIFY_INSTALLED
|
||||
VERIFY_ICON, VERIFY_DESCRIPTION, VERIFY_INSTALLED, VERIFY_APP_IMAGE_FILE
|
||||
);
|
||||
|
||||
static final List<Action> VERIFY_DEFAULTS = Stream.concat(
|
||||
@ -279,6 +284,45 @@ public final class LauncherVerifier {
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyInAppImageFile(JPackageCommand cmd) {
|
||||
cmd.verifyIsOfType(PackageType.IMAGE);
|
||||
if (!isMainLauncher()) {
|
||||
Stream<LauncherShortcut> shortcuts;
|
||||
if (TKit.isWindows()) {
|
||||
shortcuts = Stream.of(LauncherShortcut.WIN_DESKTOP_SHORTCUT, LauncherShortcut.WIN_START_MENU_SHORTCUT);
|
||||
} else if (TKit.isLinux()) {
|
||||
shortcuts = Stream.of(LauncherShortcut.LINUX_SHORTCUT);
|
||||
} else {
|
||||
shortcuts = Stream.of();
|
||||
}
|
||||
|
||||
var aif = AppImageFile.load(cmd.outputBundle());
|
||||
var aifFileName = AppImageFile.getPathInAppImage(Path.of("")).getFileName();
|
||||
|
||||
var aifProps = Objects.requireNonNull(aif.addLaunchers().get(name));
|
||||
|
||||
shortcuts.forEach(shortcut -> {
|
||||
var recordedShortcut = aifProps.get(shortcut.appImageFilePropertyName());
|
||||
properties.flatMap(props -> {
|
||||
return props.findProperty(shortcut.propertyName());
|
||||
}).ifPresentOrElse(expectedShortcut -> {
|
||||
TKit.assertNotNull(recordedShortcut, String.format(
|
||||
"Check shortcut [%s] of launcher [%s] is recorded in %s file",
|
||||
shortcut, name, aifFileName));
|
||||
TKit.assertEquals(
|
||||
StartupDirectory.parse(expectedShortcut),
|
||||
StartupDirectory.parse(recordedShortcut),
|
||||
String.format("Check the value of shortcut [%s] of launcher [%s] recorded in %s file",
|
||||
shortcut, name, aifFileName));
|
||||
}, () -> {
|
||||
TKit.assertNull(recordedShortcut, String.format(
|
||||
"Check shortcut [%s] of launcher [%s] is NOT recorded in %s file",
|
||||
shortcut, name, aifFileName));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void executeLauncher(JPackageCommand cmd) throws IOException {
|
||||
Path launcherPath = cmd.appLauncherPath(name);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user