8236128: Allow jpackage create installers for services

Reviewed-by: almatvee
This commit is contained in:
Alexey Semenyuk 2022-04-27 16:32:29 +00:00
parent ef27081fe7
commit b675c597e3
64 changed files with 2998 additions and 271 deletions

View File

@ -25,6 +25,6 @@
COPY += .gif .png .txt .spec .script .prerm .preinst \
.postrm .postinst .list .sh .desktop .copyright .control .plist .template \
.icns .scpt .wxs .wxl .wxi .ico .bmp .tiff
.icns .scpt .wxs .wxl .wxi .ico .bmp .tiff .service
CLEAN += .properties

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2022, 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
@ -25,11 +25,8 @@
package jdk.jpackage.internal;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@ -39,8 +36,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.imageio.ImageIO;
@ -56,16 +53,19 @@ import static jdk.jpackage.internal.StandardBundlerParam.DESCRIPTION;
import static jdk.jpackage.internal.StandardBundlerParam.FILE_ASSOCIATIONS;
import static jdk.jpackage.internal.StandardBundlerParam.ICON;
import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE;
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;;
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
/**
* Helper to create files for desktop integration.
*/
final class DesktopIntegration {
final class DesktopIntegration extends ShellCustomAction {
static final String DESKTOP_COMMANDS_INSTALL = "DESKTOP_COMMANDS_INSTALL";
static final String DESKTOP_COMMANDS_UNINSTALL = "DESKTOP_COMMANDS_UNINSTALL";
static final String UTILITY_SCRIPTS = "UTILITY_SCRIPTS";
private static final String COMMANDS_INSTALL = "DESKTOP_COMMANDS_INSTALL";
private static final String COMMANDS_UNINSTALL = "DESKTOP_COMMANDS_UNINSTALL";
private static final String SCRIPTS = "DESKTOP_SCRIPTS";
private static final List<String> REPLACEMENT_STRING_IDS = List.of(
COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS);
private DesktopIntegration(PlatformPackage thePackage,
Map<String, ? super Object> params,
@ -171,21 +171,28 @@ final class DesktopIntegration {
}
}
static DesktopIntegration create(PlatformPackage thePackage,
static ShellCustomAction create(PlatformPackage thePackage,
Map<String, ? super Object> params) throws IOException {
if (StandardBundlerParam.isRuntimeInstaller(params)) {
return null;
return ShellCustomAction.nop(REPLACEMENT_STRING_IDS);
}
return new DesktopIntegration(thePackage, params, null);
}
@Override
List<String> requiredPackages() {
return Stream.of(List.of(this), nestedIntegrations).flatMap(
List::stream).map(DesktopIntegration::requiredPackagesSelf).flatMap(
List::stream).distinct().toList();
}
Map<String, String> create() throws IOException {
@Override
protected List<String> replacementStringIds() {
return REPLACEMENT_STRING_IDS;
}
@Override
protected Map<String, String> createImpl() throws IOException {
associations.forEach(assoc -> assoc.data.verify());
if (iconFile != null) {
@ -230,9 +237,9 @@ final class DesktopIntegration {
// of the additional launchers and append them to the corresponding
// commands of the main launcher.
List<String> installShellCmds = new ArrayList<>(Arrays.asList(
data.get(DESKTOP_COMMANDS_INSTALL)));
data.get(COMMANDS_INSTALL)));
List<String> uninstallShellCmds = new ArrayList<>(Arrays.asList(
data.get(DESKTOP_COMMANDS_UNINSTALL)));
data.get(COMMANDS_UNINSTALL)));
for (var integration: nestedIntegrations) {
if (!integration.associations.isEmpty()) {
needCleanupScripts = true;
@ -240,26 +247,16 @@ final class DesktopIntegration {
Map<String, String> launcherData = integration.create();
installShellCmds.add(launcherData.get(DESKTOP_COMMANDS_INSTALL));
uninstallShellCmds.add(launcherData.get(
DESKTOP_COMMANDS_UNINSTALL));
installShellCmds.add(launcherData.get(COMMANDS_INSTALL));
uninstallShellCmds.add(launcherData.get(COMMANDS_UNINSTALL));
}
data.put(DESKTOP_COMMANDS_INSTALL, stringifyShellCommands(
installShellCmds));
data.put(DESKTOP_COMMANDS_UNINSTALL, stringifyShellCommands(
uninstallShellCmds));
data.put(COMMANDS_INSTALL, stringifyShellCommands(installShellCmds));
data.put(COMMANDS_UNINSTALL, stringifyShellCommands(uninstallShellCmds));
if (needCleanupScripts) {
// Pull in utils.sh scrips library.
try (InputStream is = OverridableResource.readDefault("utils.sh");
InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {
data.put(UTILITY_SCRIPTS, reader.lines().collect(
Collectors.joining(System.lineSeparator())));
}
} else {
data.put(UTILITY_SCRIPTS, "");
// Pull in desktop_utils.sh scrips library.
data.put(SCRIPTS, stringifyTextFile("desktop_utils.sh"));
}
return data;
@ -277,17 +274,12 @@ final class DesktopIntegration {
Map<String, String> data = new HashMap<>();
data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params));
data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
data.put("APPLICATION_ICON",
iconFile != null ? iconFile.installPath().toString() : null);
data.put("APPLICATION_ICON", Optional.ofNullable(iconFile).map(
f -> f.installPath().toString()).orElse(null));
data.put("DEPLOY_BUNDLE_CATEGORY", MENU_GROUP.fetchFrom(params));
String appLauncher = thePackage.installedApplicationLayout().launchersDirectory().resolve(
LinuxAppImageBuilder.getLauncherName(params)).toString();
if (Pattern.compile("\\s").matcher(appLauncher).find()) {
// Path contains whitespace(s). Enclose in double quotes.
appLauncher = "\"" + appLauncher + "\"";
}
data.put("APPLICATION_LAUNCHER", appLauncher);
data.put("APPLICATION_LAUNCHER", Enquoter.forPropertyValues().applyTo(
thePackage.installedApplicationLayout().launchersDirectory().resolve(
LinuxAppImageBuilder.getLauncherName(params)).toString()));
return data;
}
@ -356,13 +348,13 @@ final class DesktopIntegration {
cmds.add(registerDesktopFileCmd);
cmds.add(registerFileAssociationsCmd);
cmds.addAll(registerIconCmds);
data.put(DESKTOP_COMMANDS_INSTALL, stringifyShellCommands(cmds));
data.put(COMMANDS_INSTALL, stringifyShellCommands(cmds));
cmds.clear();
cmds.add(unregisterDesktopFileCmd);
cmds.add(unregisterFileAssociationsCmd);
cmds.addAll(unregisterIconCmds);
data.put(DESKTOP_COMMANDS_UNINSTALL, stringifyShellCommands(cmds));
data.put(COMMANDS_UNINSTALL, stringifyShellCommands(cmds));
}
private String registerDesktopFileCmd;
@ -385,24 +377,25 @@ final class DesktopIntegration {
private class DesktopFile {
DesktopFile(String fileName) {
installPath = thePackage
var installPath = thePackage
.installedApplicationLayout()
.destktopIntegrationDirectory().resolve(fileName);
srcPath = thePackage
var srcPath = thePackage
.sourceApplicationLayout()
.destktopIntegrationDirectory().resolve(fileName);
impl = new InstallableFile(srcPath, installPath);
}
private final Path installPath;
private final Path srcPath;
Path installPath() {
return installPath;
return impl.installPath();
}
Path srcPath() {
return srcPath;
return impl.srcPath();
}
private final InstallableFile impl;
}
private void appendFileAssociation(XMLStreamWriter xml,
@ -526,15 +519,6 @@ final class DesktopIntegration {
return commonIconSize;
}
private static String stringifyShellCommands(String... commands) {
return stringifyShellCommands(Arrays.asList(commands));
}
private static String stringifyShellCommands(List<String> commands) {
return String.join(System.lineSeparator(), commands.stream().filter(
s -> s != null && !s.isEmpty()).toList());
}
private static class LinuxFileAssociation {
LinuxFileAssociation(FileAssociation fa) {
this.data = fa;

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2022, 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;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import static jdk.jpackage.internal.OverridableResource.createResource;
/**
* Helper to install launchers as services using "systemd".
*/
public final class LinuxLaunchersAsServices extends UnixLaunchersAsServices {
private LinuxLaunchersAsServices(PlatformPackage thePackage,
Map<String, Object> params) throws IOException {
super(thePackage, REQUIRED_PACKAGES, params, li -> {
return new Launcher(thePackage, li.getName(), params);
});
}
static ShellCustomAction create(PlatformPackage thePackage,
Map<String, Object> params) throws IOException {
if (StandardBundlerParam.isRuntimeInstaller(params)) {
return ShellCustomAction.nop(REPLACEMENT_STRING_IDS);
}
return new LinuxLaunchersAsServices(thePackage, params);
}
public static Path getServiceUnitFileName(String packageName,
String launcherName) {
String baseName = launcherName.replaceAll("[\\s]", "_");
return Path.of(packageName + "-" + baseName + ".service");
}
private static class Launcher extends UnixLauncherAsService {
Launcher(PlatformPackage thePackage, String name,
Map<String, Object> mainParams) {
super(name, mainParams, createResource("unit-template.service",
mainParams).setCategory(I18N.getString(
"resource.systemd-unit-file")));
unitFilename = getServiceUnitFileName(thePackage.name(), getName());
getResource()
.setPublicName(unitFilename)
.addSubstitutionDataEntry("APPLICATION_LAUNCHER",
Enquoter.forPropertyValues().applyTo(
thePackage.installedApplicationLayout().launchersDirectory().resolve(
getName()).toString()));
}
@Override
Path descriptorFilePath(Path root) {
return root.resolve("lib/systemd/system").resolve(unitFilename);
}
private final Path unitFilename;
}
private final static List<String> REQUIRED_PACKAGES = List.of("systemd",
"coreutils" /* /usr/bin/wc */, "grep");
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2022, 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
@ -38,12 +38,9 @@ import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static jdk.jpackage.internal.DesktopIntegration.*;
import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE;
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
import static jdk.jpackage.internal.StandardBundlerParam.RELEASE;
import static jdk.jpackage.internal.StandardBundlerParam.VENDOR;
import static jdk.jpackage.internal.StandardBundlerParam.DESCRIPTION;
import static jdk.jpackage.internal.StandardBundlerParam.INSTALL_DIR;
@ -53,6 +50,9 @@ abstract class LinuxPackageBundler extends AbstractBundler {
LinuxPackageBundler(BundlerParamInfo<String> packageName) {
this.packageName = packageName;
appImageBundler = new LinuxAppBundler().setDependentTask(true);
customActions = List.of(new CustomActionInstance(
DesktopIntegration::create), new CustomActionInstance(
LinuxLaunchersAsServices::create));
}
@Override
@ -148,14 +148,14 @@ abstract class LinuxPackageBundler extends AbstractBundler {
}
}
desktopIntegration = DesktopIntegration.create(thePackage, params);
for (var ca : customActions) {
ca.init(thePackage, params);
}
Map<String, String> data = createDefaultReplacementData(params);
if (desktopIntegration != null) {
data.putAll(desktopIntegration.create());
} else {
Stream.of(DESKTOP_COMMANDS_INSTALL, DESKTOP_COMMANDS_UNINSTALL,
UTILITY_SCRIPTS).forEach(v -> data.put(v, ""));
for (var ca : customActions) {
data.putAll(ca.instance.create());
}
data.putAll(createReplacementData(params));
@ -182,12 +182,10 @@ abstract class LinuxPackageBundler extends AbstractBundler {
PlatformPackage thePackage = createMetaPackage(params);
final List<String> xdgUtilsPackage;
if (desktopIntegration != null) {
xdgUtilsPackage = desktopIntegration.requiredPackages();
} else {
xdgUtilsPackage = Collections.emptyList();
}
final List<String> caPackages = customActions.stream()
.map(ca -> ca.instance)
.map(ShellCustomAction::requiredPackages)
.flatMap(List::stream).toList();
final List<String> neededLibPackages;
if (withFindNeededPackages && Files.exists(thePackage.sourceRoot())) {
@ -204,7 +202,7 @@ abstract class LinuxPackageBundler extends AbstractBundler {
// Merge all package lists together.
// Filter out empty names, sort and remove duplicates.
List<String> result = Stream.of(xdgUtilsPackage, neededLibPackages).flatMap(
List<String> result = Stream.of(caPackages, neededLibPackages).flatMap(
List::stream).filter(Predicate.not(String::isEmpty)).sorted().distinct().toList();
Log.verbose(String.format("Required packages: %s", result));
@ -345,7 +343,23 @@ abstract class LinuxPackageBundler extends AbstractBundler {
private final BundlerParamInfo<String> packageName;
private final Bundler appImageBundler;
private boolean withFindNeededPackages;
private DesktopIntegration desktopIntegration;
private final List<CustomActionInstance> customActions;
private static final class CustomActionInstance {
CustomActionInstance(ShellCustomActionFactory factory) {
this.factory = factory;
}
void init(PlatformPackage thePackage, Map<String, ? super Object> params)
throws IOException {
instance = factory.create(thePackage, params);
Objects.requireNonNull(instance);
}
private final ShellCustomActionFactory factory;
ShellCustomAction instance;
}
private static final BundlerParamInfo<String> LINUX_PACKAGE_DEPENDENCIES =
new StandardBundlerParam<>(

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2022, 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
@ -39,6 +39,7 @@ resource.copyright-file=Copyright file
resource.menu-shortcut-descriptor=Menu shortcut descriptor
resource.menu-icon=menu icon
resource.rpm-spec-file=RPM spec file
resource.systemd-unit-file=systemd unit file
error.tool-not-found.advice=Please install required packages
error.tool-old-version.advice=Please install required packages

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2022, 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
@ -39,6 +39,7 @@ resource.copyright-file=\u30B3\u30D4\u30FC\u30E9\u30A4\u30C8\u30FB\u30D5\u30A1\u
resource.menu-shortcut-descriptor=\u30E1\u30CB\u30E5\u30FC\u30FB\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30FB\u30C7\u30A3\u30B9\u30AF\u30EA\u30D7\u30BF
resource.menu-icon=\u30E1\u30CB\u30E5\u30FC\u30FB\u30A2\u30A4\u30B3\u30F3
resource.rpm-spec-file=RPM\u4ED5\u69D8\u30D5\u30A1\u30A4\u30EB
resource.systemd-unit-file=systemd unit file
error.tool-not-found.advice=\u5FC5\u8981\u306A\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u3066\u304F\u3060\u3055\u3044
error.tool-old-version.advice=\u5FC5\u8981\u306A\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u3066\u304F\u3060\u3055\u3044

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2022, 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
@ -39,6 +39,7 @@ resource.copyright-file=\u7248\u6743\u6587\u4EF6
resource.menu-shortcut-descriptor=\u83DC\u5355\u5FEB\u6377\u65B9\u5F0F\u63CF\u8FF0\u7B26
resource.menu-icon=\u83DC\u5355\u56FE\u6807
resource.rpm-spec-file=RPM \u89C4\u8303\u6587\u4EF6
resource.systemd-unit-file=systemd unit file
error.tool-not-found.advice=\u8BF7\u5B89\u88C5\u6240\u9700\u7684\u7A0B\u5E8F\u5305
error.tool-old-version.advice=\u8BF7\u5B89\u88C5\u6240\u9700\u7684\u7A0B\u5E8F\u5305

View File

@ -0,0 +1,38 @@
#
# Register $@ unit files with systemd service.
#
register_services ()
{
for unit in "$@"; do
systemctl enable --now "$unit"
done
}
#
# Unregister $@ unit files with systemd service.
#
unregister_services ()
{
for unit in "$@"; do
if file_belongs_to_single_package "$unit"; then
local unit_name=`basename "$unit"`
if systemctl list-units --full -all | grep -q "$unit_name"; then
systemctl disable --now "$unit_name"
fi
fi
done
}
file_belongs_to_single_package ()
{
if [ ! -e "$1" ]; then
false
elif [ "$package_type" = rpm ]; then
test `rpm -q --whatprovides "$1" | wc -l` = 1
elif [ "$package_type" = deb ]; then
test `dpkg -S "$1" | wc -l` = 1
else
exit 1
fi
}

View File

@ -17,9 +17,13 @@ set -e
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
package_type=deb
LAUNCHER_AS_SERVICE_SCRIPTS
case "$1" in
configure)
DESKTOP_COMMANDS_INSTALL
LAUNCHER_AS_SERVICE_COMMANDS_INSTALL
;;
abort-upgrade|abort-remove|abort-deconfigure)

View File

@ -13,9 +13,14 @@ set -e
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
package_type=deb
LAUNCHER_AS_SERVICE_SCRIPTS
case "$1" in
install|upgrade)
if [ -n "$2" ]; then
true; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL
fi
;;
abort-upgrade)

View File

@ -17,11 +17,14 @@ set -e
# the debian-policy package
UTILITY_SCRIPTS
package_type=deb
DESKTOP_SCRIPTS
LAUNCHER_AS_SERVICE_SCRIPTS
case "$1" in
remove|upgrade|deconfigure)
DESKTOP_COMMANDS_UNINSTALL
LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL
;;
failed-upgrade)
@ -34,4 +37,3 @@ DESKTOP_COMMANDS_UNINSTALL
esac
exit 0

View File

@ -49,12 +49,16 @@ APPLICATION_DESCRIPTION
rm -rf %{buildroot}
install -d -m 755 %{buildroot}APPLICATION_DIRECTORY
cp -r %{_sourcedir}APPLICATION_DIRECTORY/* %{buildroot}APPLICATION_DIRECTORY
if [ "$(echo %{_sourcedir}/lib/systemd/system/*.service)" != '%{_sourcedir}/lib/systemd/system/*.service' ]; then
install -d -m 755 %{buildroot}/lib/systemd/system
cp %{_sourcedir}/lib/systemd/system/*.service %{buildroot}/lib/systemd/system
fi
%if "xAPPLICATION_LICENSE_FILE" != "x"
%define license_install_file %{_defaultlicensedir}/%{name}-%{version}/%{basename:APPLICATION_LICENSE_FILE}
install -d -m 755 "%{buildroot}%{dirname:%{license_install_file}}"
install -m 644 "APPLICATION_LICENSE_FILE" "%{buildroot}%{license_install_file}"
%endif
(cd %{buildroot} && find . -type d) | sed -e 's/^\.//' -e '/^$/d' | sort > %{app_filelist}
(cd %{buildroot} && find . -path ./lib/systemd -prune -o -type d -print) | sed -e 's/^\.//' -e '/^$/d' | sort > %{app_filelist}
{ rpm -ql filesystem || echo %{default_filesystem}; } | sort > %{filesystem_filelist}
comm -23 %{app_filelist} %{filesystem_filelist} > %{package_filelist}
sed -i -e 's/.*/%dir "&"/' %{package_filelist}
@ -69,10 +73,23 @@ sed -i -e 's/.*/%dir "&"/' %{package_filelist}
%endif
%post
package_type=rpm
LAUNCHER_AS_SERVICE_SCRIPTS
DESKTOP_COMMANDS_INSTALL
LAUNCHER_AS_SERVICE_COMMANDS_INSTALL
%pre
package_type=rpm
LAUNCHER_AS_SERVICE_SCRIPTS
if [ "$1" = 2 ]; then
true; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL
fi
%preun
UTILITY_SCRIPTS
package_type=rpm
DESKTOP_SCRIPTS
LAUNCHER_AS_SERVICE_SCRIPTS
DESKTOP_COMMANDS_UNINSTALL
LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL
%clean

View File

@ -0,0 +1,9 @@
[Unit]
Description=SERVICE_DESCRIPTION
[Service]
ExecStart=APPLICATION_LAUNCHER
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2022, 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
@ -33,7 +33,6 @@ import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEYCHAIN;
import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEY_USER;
import static jdk.jpackage.internal.MacAppImageBuilder.APP_STORE;
import static jdk.jpackage.internal.StandardBundlerParam.MAIN_CLASS;
import static jdk.jpackage.internal.StandardBundlerParam.VERBOSE;
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
import static jdk.jpackage.internal.StandardBundlerParam.SIGN_BUNDLE;
@ -45,13 +44,6 @@ public class MacAppBundler extends AppImageBundler {
private static final String TEMPLATE_BUNDLE_ICON = "JavaApp.icns";
public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME =
new StandardBundlerParam<>(
Arguments.CLIOptions.MAC_BUNDLE_NAME.getId(),
String.class,
params -> null,
(s, p) -> s);
public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON =
new StandardBundlerParam<>(
".mac.default.icns",

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2022, 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
@ -95,7 +95,7 @@ public abstract class MacBaseInstallerBundler extends AbstractBundler {
},
(s, p) -> s);
protected static String getInstallDir(
static String getInstallDir(
Map<String, ? super Object> params, boolean defaultOnly) {
String returnValue = INSTALL_DIR.fetchFrom(params);
if (defaultOnly && returnValue != null) {

View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2022, 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;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import static jdk.jpackage.internal.MacAppImageBuilder.MAC_CF_BUNDLE_IDENTIFIER;
import static jdk.jpackage.internal.OverridableResource.createResource;
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
/**
* Helper to install launchers as services using "launchd".
*/
public final class MacLaunchersAsServices extends UnixLaunchersAsServices {
private MacLaunchersAsServices(PlatformPackage thePackage,
Map<String, Object> params) throws IOException {
super(thePackage, List.of(), params, li -> {
return new Launcher(thePackage, li.getName(), params);
});
}
static ShellCustomAction create(Map<String, Object> params,
Path outputDir) throws IOException {
if (StandardBundlerParam.isRuntimeInstaller(params)) {
return null;
}
return Optional.of(new MacLaunchersAsServices(new PlatformPackage() {
@Override
public String name() {
return MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params);
}
@Override
public Path sourceRoot() {
return outputDir;
}
@Override
public ApplicationLayout sourceApplicationLayout() {
throw new UnsupportedOperationException();
}
@Override
public ApplicationLayout installedApplicationLayout() {
return ApplicationLayout.macAppImage().resolveAt(Path.of(
MacBaseInstallerBundler.getInstallDir(params, false),
APP_NAME.fetchFrom(params) + ".app"));
}
}, params)).filter(Predicate.not(MacLaunchersAsServices::isEmpty)).orElse(
null);
}
public static Path getServicePListFileName(String packageName,
String launcherName) {
String baseName = launcherName.replaceAll("[\\s]", "_");
return Path.of(packageName + "-" + baseName + ".plist");
}
private static class Launcher extends UnixLauncherAsService {
Launcher(PlatformPackage thePackage, String name,
Map<String, Object> mainParams) {
super(name, mainParams, createResource("launchd.plist.template",
mainParams).setCategory(I18N.getString(
"resource.launchd-plist-file")));
plistFilename = getServicePListFileName(thePackage.name(), getName());
// It is recommended to set value of "label" property in launchd
// .plist file equal to the name of this .plist file without the suffix.
String label = IOUtils.replaceSuffix(plistFilename.getFileName(), "").toString();
getResource()
.setPublicName(plistFilename)
.addSubstitutionDataEntry("LABEL", label)
.addSubstitutionDataEntry("APPLICATION_LAUNCHER",
thePackage.installedApplicationLayout().launchersDirectory().resolve(
getName()).toString());
}
@Override
Path descriptorFilePath(Path root) {
return root.resolve("Library/LaunchDaemons").resolve(plistFilename);
}
private final Path plistFilename;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2022, 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
@ -34,15 +34,17 @@ import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import static jdk.jpackage.internal.StandardBundlerParam.CONFIG_ROOT;
import static jdk.jpackage.internal.StandardBundlerParam.TEMP_ROOT;
import static jdk.jpackage.internal.StandardBundlerParam.VERBOSE;
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
import static jdk.jpackage.internal.StandardBundlerParam.LICENSE_FILE;
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
@ -52,6 +54,7 @@ import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEY_USER;
import static jdk.jpackage.internal.MacAppImageBuilder.APP_STORE;
import static jdk.jpackage.internal.MacAppImageBuilder.MAC_CF_BUNDLE_IDENTIFIER;
import static jdk.jpackage.internal.OverridableResource.createResource;
import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR;
public class MacPkgBundler extends MacBaseInstallerBundler {
@ -61,11 +64,6 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
private static final String DEFAULT_BACKGROUND_IMAGE = "background_pkg.png";
private static final String DEFAULT_PDF = "product-def.plist";
private static final String TEMPLATE_PREINSTALL_SCRIPT =
"preinstall.template";
private static final String TEMPLATE_POSTINSTALL_SCRIPT =
"postinstall.template";
private static final BundlerParamInfo<Path> PACKAGES_ROOT =
new StandardBundlerParam<>(
"mac.pkg.packagesRoot",
@ -171,6 +169,16 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
APP_NAME.fetchFrom(params) + "-app.pkg");
}
private Path getPackages_ServicesPackage(Map<String, ? super Object> params) {
return PACKAGES_ROOT.fetchFrom(params).resolve(
APP_NAME.fetchFrom(params) + "-services.pkg");
}
private Path getPackages_SupportPackage(Map<String, ? super Object> params) {
return PACKAGES_ROOT.fetchFrom(params).resolve(
APP_NAME.fetchFrom(params) + "-support.pkg");
}
private Path getConfig_DistributionXMLFile(
Map<String, ? super Object> params) {
return CONFIG_ROOT.fetchFrom(params).resolve("distribution.dist");
@ -190,19 +198,18 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
APP_NAME.fetchFrom(params) + "-background-darkAqua.png");
}
private Path getScripts_PreinstallFile(Map<String, ? super Object> params) {
return SCRIPTS_DIR.fetchFrom(params).resolve("preinstall");
}
private Path getScripts_PostinstallFile(
Map<String, ? super Object> params) {
return SCRIPTS_DIR.fetchFrom(params).resolve("postinstall");
}
private String getAppIdentifier(Map<String, ? super Object> params) {
return MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params);
}
private String getServicesIdentifier(Map<String, ? super Object> params) {
return MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) + ".services";
}
private String getSupportIdentifier(Map<String, ? super Object> params) {
return MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) + ".support";
}
private void preparePackageScripts(Map<String, ? super Object> params)
throws IOException {
Log.verbose(I18N.getString("message.preparing-scripts"));
@ -215,22 +222,35 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
data.put("INSTALL_LOCATION", getInstallDir(params, false));
data.put("APP_LOCATION", appLocation.toString());
createResource(TEMPLATE_PREINSTALL_SCRIPT, params)
.setCategory(I18N.getString("resource.pkg-preinstall-script"))
MacPkgInstallerScripts.createAppScripts()
.setResourceDir(RESOURCE_DIR.fetchFrom(params))
.setSubstitutionData(data)
.saveToFile(getScripts_PreinstallFile(params));
getScripts_PreinstallFile(params).toFile().setExecutable(true, false);
createResource(TEMPLATE_POSTINSTALL_SCRIPT, params)
.setCategory(I18N.getString("resource.pkg-postinstall-script"))
.setSubstitutionData(data)
.saveToFile(getScripts_PostinstallFile(params));
getScripts_PostinstallFile(params).toFile().setExecutable(true, false);
.saveInFolder(SCRIPTS_DIR.fetchFrom(params));
}
private static String URLEncoding(String pkgName) throws URISyntaxException {
URI uri = new URI(null, null, pkgName, null);
return uri.toASCIIString();
private void addPackageToInstallerGuiScript(XMLStreamWriter xml,
String pkgId, String pkgName, String pkgVersion) throws IOException,
XMLStreamException {
xml.writeStartElement("pkg-ref");
xml.writeAttribute("id", pkgId);
xml.writeEndElement(); // </pkg-ref>
xml.writeStartElement("choice");
xml.writeAttribute("id", pkgId);
xml.writeAttribute("visible", "false");
xml.writeStartElement("pkg-ref");
xml.writeAttribute("id", pkgId);
xml.writeEndElement(); // </pkg-ref>
xml.writeEndElement(); // </choice>
xml.writeStartElement("pkg-ref");
xml.writeAttribute("id", pkgId);
xml.writeAttribute("version", pkgVersion);
xml.writeAttribute("onConclusion", "none");
try {
xml.writeCharacters(new URI(null, null, pkgName, null).toASCIIString());
} catch (URISyntaxException ex) {
throw new RuntimeException(ex);
}
xml.writeEndElement(); // </pkg-ref>
}
private void prepareDistributionXMLFile(Map<String, ? super Object> params)
@ -277,11 +297,23 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
* Note that the content of the distribution file
* below is generated by productbuild --synthesize
*/
String appId = getAppIdentifier(params);
xml.writeStartElement("pkg-ref");
xml.writeAttribute("id", appId);
xml.writeEndElement(); // </pkg-ref>
Map<String, Path> pkgs = new LinkedHashMap<>();
pkgs.put(getAppIdentifier(params), getPackages_AppPackage(params));
if (withServicesPkg(params)) {
pkgs.put(getServicesIdentifier(params),
getPackages_ServicesPackage(params));
pkgs.put(getSupportIdentifier(params),
getPackages_SupportPackage(params));
}
for (var pkg : pkgs.entrySet()) {
addPackageToInstallerGuiScript(xml, pkg.getKey(),
pkg.getValue().getFileName().toString(),
VERSION.fetchFrom(params));
}
xml.writeStartElement("options");
xml.writeAttribute("customize", "never");
xml.writeAttribute("require-scripts", "false");
@ -291,32 +323,16 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
xml.writeStartElement("choices-outline");
xml.writeStartElement("line");
xml.writeAttribute("choice", "default");
xml.writeStartElement("line");
xml.writeAttribute("choice", appId);
xml.writeEndElement(); // </line>
for (var pkgId : pkgs.keySet()) {
xml.writeStartElement("line");
xml.writeAttribute("choice", pkgId);
xml.writeEndElement(); // </line>
}
xml.writeEndElement(); // </line>
xml.writeEndElement(); // </choices-outline>
xml.writeStartElement("choice");
xml.writeAttribute("id", "default");
xml.writeEndElement(); // </choice>
xml.writeStartElement("choice");
xml.writeAttribute("id", appId);
xml.writeAttribute("visible", "false");
xml.writeStartElement("pkg-ref");
xml.writeAttribute("id", appId);
xml.writeEndElement(); // </pkg-ref>
xml.writeEndElement(); // </choice>
xml.writeStartElement("pkg-ref");
xml.writeAttribute("id", appId);
xml.writeAttribute("version", VERSION.fetchFrom(params));
xml.writeAttribute("onConclusion", "none");
try {
xml.writeCharacters(URLEncoding(
getPackages_AppPackage(params).getFileName().toString()));
} catch (URISyntaxException ex) {
throw new IOException(ex);
}
xml.writeEndElement(); // </pkg-ref>
xml.writeEndElement(); // </installer-gui-script>
});
@ -434,6 +450,81 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
return newRoot.toString();
}
private boolean withServicesPkg(Map<String, Object> params) {
try {
return !APP_STORE.fetchFrom(params)
&& MacLaunchersAsServices.create(params, null) != null;
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private void createServicesPkg(Map<String, Object> params) throws
IOException {
Path root = TEMP_ROOT.fetchFrom(params).resolve("services");
Path srcRoot = root.resolve("src");
var services = MacLaunchersAsServices.create(params, srcRoot);
Path scriptsDir = root.resolve("scripts");
var data = services.create();
data.put("SERVICES_PACKAGE_ID", getServicesIdentifier(params));
MacPkgInstallerScripts.createServicesScripts()
.setResourceDir(RESOURCE_DIR.fetchFrom(params))
.setSubstitutionData(data)
.saveInFolder(scriptsDir);
var pb = new ProcessBuilder("/usr/bin/pkgbuild",
"--root",
srcRoot.toString(),
"--install-location",
"/",
"--scripts",
scriptsDir.toString(),
"--identifier",
getServicesIdentifier(params),
getPackages_ServicesPackage(params).toAbsolutePath().toString());
IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT);
createSupportPkg(params, data);
}
private void createSupportPkg(Map<String, Object> params,
Map<String, String> servicesSubstitutionData) throws IOException {
Path root = TEMP_ROOT.fetchFrom(params).resolve("support");
Path srcRoot = root.resolve("src");
var enqouter = Enquoter.forShellLiterals().setEnquotePredicate(str -> true);
Map<String, String> data = new HashMap<>(servicesSubstitutionData);
data.put("APP_INSTALLATION_FOLDER", enqouter.applyTo(Path.of(
getInstallDir(params, false), APP_NAME.fetchFrom(params)
+ ".app").toString()));
data.put("SUPPORT_INSTALLATION_FOLDER", enqouter.applyTo(Path.of(
"/Library/Application Support", APP_NAME.fetchFrom(params)).toString()));
new ShellScriptResource("uninstall.command")
.setResource(createResource("uninstall.command.template", params)
.setCategory(I18N.getString("resource.pkg-uninstall-script"))
.setPublicName("uninstaller")
.setSubstitutionData(data))
.saveInFolder(srcRoot.resolve(APP_NAME.fetchFrom(params)));
var pb = new ProcessBuilder("/usr/bin/pkgbuild",
"--root",
srcRoot.toString(),
"--install-location",
"/Library/Application Support",
"--identifier",
getSupportIdentifier(params),
getPackages_SupportPackage(params).toAbsolutePath().toString());
IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT);
}
private Path createPKG(Map<String, ? super Object> params,
Path outdir, Path appLocation) {
// generic find attempt
@ -442,6 +533,10 @@ public class MacPkgBundler extends MacBaseInstallerBundler {
String root = getRoot(params, appLocation);
if (withServicesPkg(params)) {
createServicesPkg(params);
}
// Generate default CPL file
Path cpl = CONFIG_ROOT.fetchFrom(params).resolve("cpl.plist");
ProcessBuilder pb = new ProcessBuilder("/usr/bin/pkgbuild",

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2022, 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;
import java.util.function.Supplier;
import jdk.jpackage.internal.PackageScripts.ResourceConfig;
/**
* MacOS PKG installer scripts.
*/
final class MacPkgInstallerScripts {
enum AppScripts implements Supplier<OverridableResource> {
preinstall(new ResourceConfig("preinstall.template",
"resource.pkg-preinstall-script")),
postinstall(new ResourceConfig("postinstall.template",
"resource.pkg-postinstall-script"));
AppScripts(ResourceConfig cfg) {
this.cfg = cfg;
}
@Override
public OverridableResource get() {
return cfg.createResource();
}
private final ResourceConfig cfg;
}
enum ServicesScripts implements Supplier<OverridableResource> {
preinstall(new ResourceConfig("services-preinstall.template",
"resource.pkg-services-preinstall-script")),
postinstall(new ResourceConfig("services-postinstall.template",
"resource.pkg-services-postinstall-script"));
ServicesScripts(ResourceConfig cfg) {
this.cfg = cfg;
}
@Override
public OverridableResource get() {
return cfg.createResource();
}
private final ResourceConfig cfg;
}
static PackageScripts<AppScripts> createAppScripts() {
return PackageScripts.create(AppScripts.class);
}
static PackageScripts<ServicesScripts> createServicesScripts() {
return PackageScripts.create(ServicesScripts.class);
}
}

View File

@ -54,8 +54,12 @@ resource.volume-icon=volume icon
resource.post-install-script=script to run after application image is populated
resource.pkg-preinstall-script=PKG preinstall script
resource.pkg-postinstall-script=PKG postinstall script
resource.pkg-services-preinstall-script=PKG preinstall script for services package
resource.pkg-services-postinstall-script=PKG postinstall script for services package
resource.pkg-uninstall-script=PKG uninstaller script
resource.pkg-background-image=pkg background image
resource.pkg-pdf=project definition file
resource.launchd-plist-file=launchd plist file
message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it.

View File

@ -54,8 +54,12 @@ resource.volume-icon=\u30DC\u30EA\u30E5\u30FC\u30E0\u30FB\u30A2\u30A4\u30B3\u30F
resource.post-install-script=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u3092\u79FB\u5165\u3057\u305F\u5F8C\u306B\u5B9F\u884C\u3059\u308B\u30B9\u30AF\u30EA\u30D7\u30C8
resource.pkg-preinstall-script=PKG\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u524D\u30B9\u30AF\u30EA\u30D7\u30C8
resource.pkg-postinstall-script=PKG\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u5F8C\u30B9\u30AF\u30EA\u30D7\u30C8
resource.pkg-services-preinstall-script=PKG preinstall script for services package
resource.pkg-services-postinstall-script=PKG postinstall script for services package
resource.pkg-uninstall-script=PKG uninstaller script
resource.pkg-background-image=pkg\u80CC\u666F\u30A4\u30E1\u30FC\u30B8
resource.pkg-pdf=\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u5B9A\u7FA9\u30D5\u30A1\u30A4\u30EB
resource.pkg-pdf=project definition file
resource.launchd-plist-file=launchd plist file
message.bundle-name-too-long-warning={0}\u304C16\u6587\u5B57\u3092\u8D85\u3048\u308B''{1}''\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u3059\u3002Mac\u3067\u306E\u64CD\u4F5C\u6027\u3092\u3088\u308A\u826F\u304F\u3059\u308B\u305F\u3081\u306B\u77ED\u304F\u3059\u308B\u3053\u3068\u3092\u691C\u8A0E\u3057\u3066\u304F\u3060\u3055\u3044\u3002

View File

@ -54,8 +54,12 @@ resource.volume-icon=\u5377\u56FE\u6807
resource.post-install-script=\u8981\u5728\u586B\u5145\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u4E4B\u540E\u8FD0\u884C\u7684\u811A\u672C
resource.pkg-preinstall-script=PKG \u5B89\u88C5\u524D\u811A\u672C
resource.pkg-postinstall-script=PKG \u5B89\u88C5\u540E\u811A\u672C
resource.pkg-services-preinstall-script=PKG preinstall script for services package
resource.pkg-services-postinstall-script=PKG postinstall script for services package
resource.pkg-uninstall-script=PKG uninstaller script
resource.pkg-background-image=pkg \u80CC\u666F\u56FE\u50CF
resource.pkg-pdf=\u9879\u76EE\u5B9A\u4E49\u6587\u4EF6
resource.pkg-pdf=project definition file
resource.launchd-plist-file=launchd plist file
message.bundle-name-too-long-warning={0}\u5DF2\u8BBE\u7F6E\u4E3A ''{1}'', \u5176\u957F\u5EA6\u8D85\u8FC7\u4E86 16 \u4E2A\u5B57\u7B26\u3002\u4E3A\u4E86\u83B7\u5F97\u66F4\u597D\u7684 Mac \u4F53\u9A8C, \u8BF7\u8003\u8651\u5C06\u5176\u7F29\u77ED\u3002

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>LABEL</string>
<key>ProgramArguments</key>
<array>
<string>APPLICATION_LAUNCHER</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
</dict>
</plist>

View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
LAUNCHER_AS_SERVICE_SCRIPTS
LAUNCHER_AS_SERVICE_COMMANDS_INSTALL

View File

@ -0,0 +1,5 @@
#!/usr/bin/env sh
LAUNCHER_AS_SERVICE_SCRIPTS
unregister_services `/usr/sbin/pkgutil --files SERVICES_PACKAGE_ID | /usr/bin/grep '\.plist$' | /usr/bin/awk '{print "/"$0}'`

View File

@ -0,0 +1,30 @@
#
# Register $@ .plist files with launchd service.
#
register_services ()
{
for daemonPlistFilePath in "$@"; do
daemonPlistFileName="${daemonPlistFilePath#/Library/LaunchDaemons/}";
/bin/launchctl load "$daemonPlistFilePath"
/bin/launchctl start "${daemonPlistFileName%.plist}"
done
}
#
# Unregister $@ .plist files with launchd service.
#
unregister_services ()
{
for daemonPlistFilePath in "$@"; do
daemonPlistFileName="${daemonPlistFilePath#/Library/LaunchDaemons/}";
sudo /bin/launchctl stop "${daemonPlistFileName%.plist}"
sudo /bin/launchctl unload "$daemonPlistFilePath"
test -z "$delete_plist_files" || sudo rm -f "$daemonPlistFilePath"
done
}

View File

@ -0,0 +1,9 @@
#!/usr/bin/env sh
LAUNCHER_AS_SERVICE_SCRIPTS
delete_plist_files=yes
LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL
sudo rm -rf APP_INSTALLATION_FOLDER
sudo rm -rf SUPPORT_INSTALLATION_FOLDER

View File

@ -29,6 +29,7 @@ import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Optional;
import jdk.jpackage.internal.Arguments.CLIOptions;
import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_DATA;
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
@ -61,6 +62,7 @@ import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
* icon
* arguments
* java-options
* launcher-as-service
* win-console
* win-shortcut
* win-menu
@ -118,9 +120,13 @@ class AddLauncherArguments {
Arguments.putUnlessNull(bundleParams, CLIOptions.RELEASE.getId(),
getOptionValue(CLIOptions.RELEASE));
String value = getOptionValue(CLIOptions.ICON);
Arguments.putUnlessNull(bundleParams, CLIOptions.ICON.getId(),
(value == null) ? null : Path.of(value));
Optional.ofNullable(getOptionValue(CLIOptions.ICON)).map(
Path::of).orElse(null));
Arguments.putUnlessNull(bundleParams,
CLIOptions.LAUNCHER_AS_SERVICE.getId(), getOptionValue(
CLIOptions.LAUNCHER_AS_SERVICE));
if (Platform.isWindows()) {
Arguments.putUnlessNull(bundleParams,

View File

@ -32,6 +32,7 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@ -48,11 +49,12 @@ import org.xml.sax.SAXException;
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
import static jdk.jpackage.internal.StandardBundlerParam.ADD_LAUNCHERS;
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
import static jdk.jpackage.internal.StandardBundlerParam.LAUNCHER_AS_SERVICE;
import static jdk.jpackage.internal.StandardBundlerParam.SHORTCUT_HINT;
import static jdk.jpackage.internal.StandardBundlerParam.MENU_HINT;
import static jdk.jpackage.internal.StandardBundlerParam.SIGN_BUNDLE;
public class AppImageFile {
public final class AppImageFile {
// These values will be loaded from AppImage xml file.
private final String creatorVersion;
@ -143,13 +145,13 @@ public class AppImageFile {
List<Map<String, ? super Object>> addLaunchers =
ADD_LAUNCHERS.fetchFrom(params);
for (int i = 0; i < addLaunchers.size(); i++) {
Map<String, ? super Object> sl = addLaunchers.get(i);
for (var launcherParams : addLaunchers) {
var li = new LauncherInfo(launcherParams);
xml.writeStartElement("add-launcher");
xml.writeAttribute("name", APP_NAME.fetchFrom(sl));
xml.writeAttribute("shortcut",
SHORTCUT_HINT.fetchFrom(sl).toString());
xml.writeAttribute("menu", MENU_HINT.fetchFrom(sl).toString());
xml.writeAttribute("name", li.getName());
xml.writeAttribute("shortcut", Boolean.toString(li.isShortcut()));
xml.writeAttribute("menu", Boolean.toString(li.isMenu()));
xml.writeAttribute("service", Boolean.toString(li.isService()));
xml.writeEndElement();
}
});
@ -190,14 +192,7 @@ public class AppImageFile {
XPathConstants.NODESET);
for (int i = 0; i != launcherNodes.getLength(); i++) {
Node item = launcherNodes.item(i);
String name = getAttribute(item, "name");
String shortcut = getAttribute(item, "shortcut");
String menu = getAttribute(item, "menu");
launcherInfos.add(new LauncherInfo(name,
!("false".equals(shortcut)),
!("false".equals(menu))));
launcherInfos.add(new LauncherInfo(launcherNodes.item(i)));
}
AppImageFile file = new AppImageFile(
@ -241,31 +236,32 @@ public class AppImageFile {
* Following items in the list are names of additional launchers.
*/
static List<LauncherInfo> getLaunchers(Path appImageDir,
Map<String, ? super Object> params) {
Map<String, Object> params) {
List<LauncherInfo> launchers = new ArrayList<>();
try {
AppImageFile appImageInfo = AppImageFile.load(appImageDir);
if (appImageInfo != null) {
launchers.add(new LauncherInfo(
appImageInfo.getLauncherName(), true, true));
launchers.addAll(appImageInfo.getAddLaunchers());
return launchers;
if (appImageDir != null) {
try {
AppImageFile appImageInfo = AppImageFile.load(appImageDir);
if (appImageInfo != null) {
launchers.add(new LauncherInfo(
appImageInfo.getLauncherName(), params));
launchers.addAll(appImageInfo.getAddLaunchers());
return launchers;
}
} catch (NoSuchFileException nsfe) {
// non jpackage generated app-image (no app/.jpackage.xml)
Log.info(MessageFormat.format(I18N.getString(
"warning.foreign-app-image"), appImageDir));
} catch (IOException ioe) {
Log.verbose(ioe);
Log.info(MessageFormat.format(I18N.getString(
"warning.invalid-app-image"), appImageDir));
}
} catch (NoSuchFileException nsfe) {
// non jpackage generated app-image (no app/.jpackage.xml)
Log.info(MessageFormat.format(I18N.getString(
"warning.foreign-app-image"), appImageDir));
} catch (IOException ioe) {
Log.verbose(ioe);
Log.info(MessageFormat.format(I18N.getString(
"warning.invalid-app-image"), appImageDir));
}
// this should never be the case, but maintaining behavior of
// creating default launchers without AppImageFile present
ADD_LAUNCHERS.fetchFrom(params).stream().map(APP_NAME::fetchFrom).map(
name -> new LauncherInfo(name, true, true)).forEach(launchers::add);
launchers.add(new LauncherInfo(params));
ADD_LAUNCHERS.fetchFrom(params).stream()
.map(launcherParams -> new LauncherInfo(launcherParams))
.forEach(launchers::add);
return launchers;
}
@ -289,11 +285,11 @@ public class AppImageFile {
return null;
}
private static String getVersion() {
return System.getProperty("java.version");
static String getVersion() {
return "1.0";
}
private static String getPlatform() {
static String getPlatform() {
return PLATFORM_LABELS.get(Platform.getPlatform());
}
@ -301,34 +297,63 @@ public class AppImageFile {
if (launcherName == null || launcherName.length() == 0) {
return false;
}
for (var launcher : addLauncherInfos) {
if ("".equals(launcher.getName())) {
return false;
}
}
if (!Objects.equals(getVersion(), creatorVersion)) {
return false;
}
if (!Objects.equals(getPlatform(), creatorPlatform)) {
return false;
}
return true;
}
static class LauncherInfo {
private String name;
private boolean shortcut;
private boolean menu;
private final String name;
private final boolean shortcut;
private final boolean menu;
private final boolean service;
public LauncherInfo(String name, boolean shortcut, boolean menu) {
this.name = name;
this.shortcut = shortcut;
this.menu = menu;
private LauncherInfo(Map<String, Object> params) {
this(APP_NAME.fetchFrom(params), params);
}
private LauncherInfo(String name, Map<String, Object> params) {
this.name = name;
this.shortcut = SHORTCUT_HINT.fetchFrom(params);
this.menu = MENU_HINT.fetchFrom(params);
this.service = LAUNCHER_AS_SERVICE.fetchFrom(params);
}
private LauncherInfo(Node node) {
this.name = getAttribute(node, "name");
this.shortcut = !"false".equals(getAttribute(node, "shortcut"));
this.menu = !"false".equals(getAttribute(node, "menu"));
this.service = !"false".equals(getAttribute(node, "service"));
}
public String getName() {
return name;
}
public boolean isShortcut() {
return shortcut;
}
public boolean isMenu() {
return menu;
}
public boolean isService() {
return service;
}
}
}

View File

@ -314,6 +314,10 @@ public class Arguments {
MODULE_PATH ("module-path", "p", OptionCategories.MODULAR),
LAUNCHER_AS_SERVICE ("launcher-as-service", OptionCategories.PROPERTY, () -> {
setOptionValue("launcher-as-service", true);
}),
MAC_SIGN ("mac-sign", "s", OptionCategories.PLATFORM_MAC, () -> {
setOptionValue("mac-sign", true);
}),

View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2022, 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;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
/**
* Add quotes to the given string in a configurable way.
*/
final class Enquoter {
private Enquoter() {
setQuoteChar('"');
}
static Enquoter forPropertyValues() {
return new Enquoter()
.setEnquotePredicate(QUOTE_IF_WHITESPACES)
.setEscaper(PREPEND_BACKSLASH);
}
static Enquoter forShellLiterals() {
return forShellLiterals('\'');
}
static Enquoter forShellLiterals(char quoteChar) {
return new Enquoter()
.setQuoteChar(quoteChar)
.setEnquotePredicate(x -> true)
.setEscaper(PREPEND_BACKSLASH);
}
String applyTo(String v) {
if (!needQuotes.test(v)) {
return v;
} else {
var buf = new StringBuilder();
buf.appendCodePoint(beginQuoteChr);
Optional.of(escaper).ifPresentOrElse(op -> {
v.codePoints().forEachOrdered(chr -> {
if (chr == beginQuoteChr || chr == endQuoteChr) {
escaper.accept(chr, buf);
} else {
buf.appendCodePoint(chr);
}
});
}, () -> {
buf.append(v);
});
buf.appendCodePoint(endQuoteChr);
return buf.toString();
}
}
Enquoter setQuoteChar(char chr) {
beginQuoteChr = chr;
endQuoteChr = chr;
return this;
}
Enquoter setEscaper(BiConsumer<Integer, StringBuilder> v) {
escaper = v;
return this;
}
Enquoter setEnquotePredicate(Predicate<String> v) {
needQuotes = v;
return this;
}
private int beginQuoteChr;
private int endQuoteChr;
private BiConsumer<Integer, StringBuilder> escaper;
private Predicate<String> needQuotes = str -> false;
private final static Predicate<String> QUOTE_IF_WHITESPACES = new Predicate<String>() {
@Override
public boolean test(String t) {
return pattern.matcher(t).find();
}
private final Pattern pattern = Pattern.compile("\\s");
};
private final static BiConsumer<Integer, StringBuilder> PREPEND_BACKSLASH = (chr, buf) -> {
buf.append('\\');
buf.appendCodePoint(chr);
};
}

View File

@ -40,14 +40,23 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReference;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stax.StAXResult;
/**
* IOUtils
@ -323,6 +332,44 @@ public class IOUtils {
}
}
public static void mergeXmls(XMLStreamWriter xml, Collection<Source> sources)
throws XMLStreamException, IOException {
xml = (XMLStreamWriter) Proxy.newProxyInstance(
XMLStreamWriter.class.getClassLoader(), new Class<?>[]{
XMLStreamWriter.class}, new SkipDocumentHandler(xml));
try {
TransformerFactory tf = TransformerFactory.newInstance();
Result result = new StAXResult(xml);
for (var src : sources) {
tf.newTransformer().transform(src, result);
}
} catch (TransformerException ex) {
// Should never happen
throw new RuntimeException(ex);
}
}
public static DocumentBuilderFactory initDocumentBuilderFactory() {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance();
try {
dbf.setFeature(
"http://apache.org/xml/features/nonvalidating/load-external-dtd",
false);
} catch (ParserConfigurationException ex) {
throw new IllegalStateException(ex);
}
return dbf;
}
public static DocumentBuilder initDocumentBuilder() {
try {
return initDocumentBuilderFactory().newDocumentBuilder();
} catch (ParserConfigurationException ex) {
throw new IllegalStateException(ex);
}
}
public static Path getParent(Path p) {
Path parent = p.getParent();
if (parent == null) {
@ -416,4 +463,24 @@ public class IOUtils {
private static final String INDENT = " ";
private static final String EOL = "\n";
}
private static class SkipDocumentHandler implements InvocationHandler {
SkipDocumentHandler(XMLStreamWriter target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
switch (method.getName()) {
case "writeStartDocument", "writeEndDocument" -> {
}
default -> method.invoke(target, args);
}
return null;
}
private final XMLStreamWriter target;
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2022, 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;
import java.nio.file.Path;
import java.util.Objects;
final class InstallableFile {
InstallableFile(Path srcPath, Path installPath) {
Objects.requireNonNull(srcPath);
this.srcPath = srcPath;
this.installPath = installPath;
}
Path installPath() {
return installPath;
}
Path srcPath() {
return srcPath;
}
void applyToApplicationLayouts(ApplicationLayout src,
ApplicationLayout install) {
var key = new Object();
src.pathGroup().setPath(key, srcPath);
if (installPath != null && install != null) {
install.pathGroup().setPath(key, installPath);
}
}
void excludeFromApplicationLayout(ApplicationLayout layout) {
applyToApplicationLayouts(layout, null);
}
private final Path installPath;
private final Path srcPath;
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2022, 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;
import java.util.Map;
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
import static jdk.jpackage.internal.StandardBundlerParam.DESCRIPTION;
class LauncherAsService {
LauncherAsService(String name, Map<String, Object> mainParams,
OverridableResource resource) {
if (name == null || APP_NAME.fetchFrom(mainParams).equals(name)) {
// Main launcher
name = APP_NAME.fetchFrom(mainParams);
this.description = DESCRIPTION.fetchFrom(mainParams);
} else {
// Additional launcher
this.description = String.format("%s (%s)", DESCRIPTION.fetchFrom(
mainParams), name);
}
this.name = name;
this.resource = resource;
resource.addSubstitutionDataEntry("SERVICE_DESCRIPTION", description);
}
protected OverridableResource getResource() {
return resource;
}
protected String getName() {
return name;
}
protected String getDescription() {
return description;
}
private final String name;
private final String description;
private final OverridableResource resource;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2022, 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
@ -76,6 +76,22 @@ final class OverridableResource {
setSourceOrder(Source.values());
}
Path getResourceDir() {
return resourceDir;
}
String getDefaultName() {
return defaultName;
}
Path getPublicName() {
return publicName;
}
Path getExternalPath() {
return externalPath;
}
OverridableResource setSubstitutionData(Map<String, String> v) {
if (v != null) {
// Disconnect `v`
@ -86,6 +102,13 @@ final class OverridableResource {
return this;
}
OverridableResource addSubstitutionDataEntry(String key, String value) {
var entry = Map.of(key, value);
Optional.ofNullable(substitutionData).ifPresentOrElse(v -> v.putAll(
entry), () -> setSubstitutionData(entry));
return this;
}
OverridableResource setCategory(String v) {
category = v;
return this;
@ -163,6 +186,10 @@ final class OverridableResource {
});
}
Source saveInFolder(Path folderPath) throws IOException {
return saveToFile(folderPath.resolve(getPublicName()));
}
Source saveToFile(Path dest) throws IOException {
if (dest == null) {
return sendToConsumer(null);
@ -240,12 +267,8 @@ final class OverridableResource {
final Path customResource = resourceDir.resolve(resourceName);
used = Files.exists(customResource);
if (used && dest != null) {
final Path logResourceName;
if (logPublicName != null) {
logResourceName = logPublicName.normalize();
} else {
logResourceName = resourceName.normalize();
}
final Path logResourceName = Optional.ofNullable(logPublicName).orElse(
resourceName).normalize();
Log.verbose(MessageFormat.format(I18N.getString(
"message.using-custom-resource"), getPrintableCategory(),

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2022 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
@ -31,13 +31,15 @@ import java.nio.file.Path;
* Platform package of an application.
*/
interface PlatformPackage {
/**
* Platform-specific package name.
*/
String name();
/**
* Root directory where sources for packaging tool should be stored
* Root directory where sources for packaging tool should be stored. On Unix
* systems contents of this directory will be installed in "/" directory.
*/
Path sourceRoot();

View File

@ -345,6 +345,16 @@ class StandardBundlerParam<T> extends BundlerParamInfo<T> {
(s, p) -> s
);
static final StandardBundlerParam<Boolean> LAUNCHER_AS_SERVICE =
new StandardBundlerParam<>(
Arguments.CLIOptions.LAUNCHER_AS_SERVICE.getId(),
Boolean.class,
params -> false,
// valueOf(null) is false, and we actually do want null
(s, p) -> (s == null || "null".equalsIgnoreCase(s)) ?
true : Boolean.valueOf(s)
);
@SuppressWarnings("unchecked")
static final StandardBundlerParam<List<Map<String, ? super Object>>> ADD_LAUNCHERS =

View File

@ -87,6 +87,7 @@ class ValidOptions {
options.put(CLIOptions.LICENSE_FILE.getId(), USE.INSTALL);
options.put(CLIOptions.INSTALL_DIR.getId(), USE.INSTALL);
options.put(CLIOptions.PREDEFINED_APP_IMAGE.getId(), USE.INSTALL);
options.put(CLIOptions.LAUNCHER_AS_SERVICE.getId(), USE.INSTALL);
options.put(CLIOptions.ABOUT_URL.getId(), USE.INSTALL);

View File

@ -140,6 +140,7 @@ Generic Options:\n\
\ (absolute path or relative to the current directory)\n\
\ The keys "module", "main-jar", "main-class", "description",\n\
\ "arguments", "java-options", "app-version", "icon",\n\
\ "launcher-as-service",\n\
\ "win-console", "win-shortcut", "win-menu",\n\
\ "linux-app-category", and "linux-shortcut" can be used.\n\
\ These options are added to, or used to overwrite, the original\n\
@ -197,6 +198,9 @@ Generic Options:\n\
\ Path of the predefined runtime image to install\n\
\ (absolute path or relative to the current directory)\n\
\ Option is required when creating a runtime package.\n\
\ --launcher-as-service\n\
\ Request to create an installer that will register the main\n\
\ application launcher as a background service-type application.\n\
\n\
\Platform dependent options for creating the application package:\n\
{3}

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2022, 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
@ -24,11 +24,186 @@
#
#
MSG_Help=\u4F7F\u7528\u65B9\u6CD5: jpackage <options>\n\n\u4F7F\u7528\u4F8B:\n--------------\n \u30DB\u30B9\u30C8\u30FB\u30B7\u30B9\u30C6\u30E0\u306B\u9069\u3057\u305F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u751F\u6210\u3057\u307E\u3059\u3002\n \u30E2\u30B8\u30E5\u30E9\u30FB\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u5834\u5408:\n jpackage -n name -p modulePath -m moduleName/className\n \u975E\u30E2\u30B8\u30E5\u30E9\u30FB\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u5834\u5408:\n jpackage -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n \u4E8B\u524D\u4F5C\u6210\u3055\u308C\u305F\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u304B\u3089:\n jpackage -n name --app-image appImageDir\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u306E\u751F\u6210:\n \u30E2\u30B8\u30E5\u30E9\u30FB\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u5834\u5408:\n jpackage --type app-image -n name -p modulePath \\\n -m moduleName/className\n \u975E\u30E2\u30B8\u30E5\u30E9\u30FB\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u5834\u5408:\n jpackage --type app-image -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n jlink\u306B\u72EC\u81EA\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u6307\u5B9A\u3059\u308B\u306B\u306F\u3001jlink\u3092\u5225\u500B\u306B\u5B9F\u884C\u3057\u307E\u3059\u3002\n jlink --output appRuntimeImage -p modulePath \\\n --add-modules moduleName \\\n --no-header-files [<additional jlink options>...]\n jpackage --type app-image -n name \\\n -m moduleName/className --runtime-image appRuntimeImage\n Java\u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u751F\u6210\u3057\u307E\u3059\u3002\n jpackage -n name --runtime-image <runtime-image>\n\n\u4E00\u822C\u7684\u306A\u30AA\u30D7\u30B7\u30E7\u30F3:\n @<filename> \n \u30D5\u30A1\u30A4\u30EB\u304B\u3089\u306E\u8AAD\u53D6\u308A\u30AA\u30D7\u30B7\u30E7\u30F3\u304A\u3088\u3073\u30E2\u30FC\u30C9 \n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u8907\u6570\u56DE\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\n --type -t <type> \n \u4F5C\u6210\u3059\u308B\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u30BF\u30A4\u30D7\n \u6709\u52B9\u306A\u5024: {1} \n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408\u3001\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u4F9D\u5B58\u306E\n \u30C7\u30D5\u30A9\u30EB\u30C8\u30FB\u30BF\u30A4\u30D7\u304C\u4F5C\u6210\u3055\u308C\u307E\u3059\n --app-version <version>\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u304A\u3088\u3073\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\n --copyright <copyright string>\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30B3\u30D4\u30FC\u30E9\u30A4\u30C8\n --description <description string>\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u8AAC\u660E\n --help -h \n \u4F7F\u7528\u65B9\u6CD5\u30C6\u30AD\u30B9\u30C8\u3068\u73FE\u5728\u306E\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u306E\u6709\u52B9\u306A\u30AA\u30D7\u30B7\u30E7\u30F3\u306E\u30EA\u30B9\u30C8\u3068\u8AAC\u660E\u3092\n \u51FA\u529B\u30B9\u30C8\u30EA\u30FC\u30E0\u306B\u51FA\u529B\u3057\u3066\u3001\u7D42\u4E86\u3057\u307E\u3059\n --icon <file path>\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u30A2\u30A4\u30B3\u30F3\u306E\u30D1\u30B9\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n --name \
-n <name>\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u304A\u3088\u3073\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u540D\u524D\n --dest -d <destination path>\n \u751F\u6210\u3055\u308C\u305F\u51FA\u529B\u30D5\u30A1\u30A4\u30EB\u304C\u914D\u7F6E\u3055\u308C\u308B\u30D1\u30B9\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n \u30C7\u30D5\u30A9\u30EB\u30C8\u306F\u73FE\u5728\u306E\u4F5C\u696D\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u3059\u3002\n --temp <directory path>\n \u4E00\u6642\u30D5\u30A1\u30A4\u30EB\u306E\u4F5C\u6210\u306B\u4F7F\u7528\u3055\u308C\u308B\u65B0\u898F\u307E\u305F\u306F\u7A7A\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u30D1\u30B9\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n \u6307\u5B9A\u3057\u305F\u5834\u5408\u3001\u30BF\u30B9\u30AF\u5B8C\u4E86\u6642\u306B\u4E00\u6642\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u524A\u9664\u3055\u308C\u306A\u3044\u305F\u3081\n \u624B\u52D5\u3067\u524A\u9664\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\n \u6307\u5B9A\u3057\u306A\u304B\u3063\u305F\u5834\u5408\u3001\u4E00\u6642\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u4F5C\u6210\u3055\u308C\n \u30BF\u30B9\u30AF\u5B8C\u4E86\u6642\u306B\u524A\u9664\u3055\u308C\u307E\u3059\u3002\n --vendor <vendor string>\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30D9\u30F3\u30C0\u30FC\n --verbose\n \u8A73\u7D30\u306A\u51FA\u529B\u3092\u6709\u52B9\u306B\u3057\u307E\u3059\n --version\n \u88FD\u54C1\u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u51FA\u529B\u30B9\u30C8\u30EA\u30FC\u30E0\u306B\u51FA\u529B\u3057\u3066\u7D42\u4E86\u3057\u307E\u3059\n\n\u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30A4\u30E1\u30FC\u30B8\u3092\u4F5C\u6210\u3059\u308B\u305F\u3081\u306E\u30AA\u30D7\u30B7\u30E7\u30F3:\n --add-modules <module name>[,<module name>...]\n \u8FFD\u52A0\u3059\u308B\u30E2\u30B8\u30E5\u30FC\u30EB\u306E\u30AB\u30F3\u30DE(",")\u533A\u5207\u308A\u30EA\u30B9\u30C8\n \u3053\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u30FB\u30EA\u30B9\u30C8\u3068\u30E1\u30A4\u30F3\u30FB\u30E2\u30B8\u30E5\u30FC\u30EB(\u6307\u5B9A\u3057\u305F\u5834\u5408)\n \u304C--add-module\u5F15\u6570\u3068\u3057\u3066jlink\u306B\u6E21\u3055\u308C\u307E\u3059\u3002\n \u6307\u5B9A\u3057\u306A\u304B\u3063\u305F\u5834\u5408\u3001\u30E1\u30A4\u30F3\u30FB\u30E2\u30B8\u30E5\u30FC\u30EB\u306E\u307F(--module\u304C\n \u6307\u5B9A\u3055\u308C\u305F\u5834\u5408)\u3001\u307E\u305F\u306F\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u30FB\u30BB\u30C3\u30C8(--main-jar\u304C \n \u6307\u5B9A\u3055\u308C\u305F\u5834\u5408)\u304C\u4F7F\u7528\u3055\u308C\u307E\u3059\u3002\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u8907\u6570\u56DE\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\n --module-path -p <module path>...\n \u30D1\u30B9\u306E{0}\u533A\u5207\u308A\u30EA\u30B9\u30C8\n \u5404\u30D1\u30B9\u306F\u3001\u30E2\u30B8\u30E5\u30FC\u30EB\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u307E\u305F\u306F\n \u30E2\u30B8\u30E5\u30E9jar\u3078\u306E\u30D1\u30B9\u3067\u3059\u3002\n (\u5404\u30D1\u30B9\u306F\u3001\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9\u3067\u3059\u3002)\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u8907\u6570\u56DE\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\n --jlink-options <jlink options> \n jlink\u306B\u6E21\u3059\u30AA\u30D7\u30B7\u30E7\u30F3\u306E\u30B9\u30DA\u30FC\u30B9\u533A\u5207\u308A\u306E\u30EA\u30B9\u30C8 \n \u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u3001"--strip-native-commands \n --strip-debug --no-man-pages \
--no-header-files"\u3002 \n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u8907\u6570\u56DE\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\n --runtime-image <directory path>\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u306B\u30B3\u30D4\u30FC\u3055\u308C\u308B\u3001\u4E8B\u524D\u5B9A\u7FA9\u6E08\u307F\u306E\u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30A4\u30E1\u30FC\u30B8\n \u306E\u30D1\u30B9\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n --runtime-image\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408\u3001jpackage\u306Fjlink\u3092\u5B9F\u884C\u3057\u3001\n \u6B21\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u4F7F\u7528\u3057\u3066\u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30A4\u30E1\u30FC\u30B8\u3092\u4F5C\u6210\u3057\u307E\u3059:\n --strip-debug\u3001--no-header-files\u3001--no-man-pages\u304A\u3088\u3073\n --strip-native-commands\u3002\n\n\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u3092\u4F5C\u6210\u3059\u308B\u305F\u3081\u306E\u30AA\u30D7\u30B7\u30E7\u30F3:\n --input -i <directory path>\n \u30D1\u30C3\u30B1\u30FC\u30B8\u5316\u3059\u308B\u30D5\u30A1\u30A4\u30EB\u3092\u542B\u3080\u5165\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3078\u306E\u30D1\u30B9\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n \u5165\u529B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u3059\u3079\u3066\u306E\u30D5\u30A1\u30A4\u30EB\u306F\u3001\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u306B\n \u30D1\u30C3\u30B1\u30FC\u30B8\u5316\u3055\u308C\u307E\u3059\u3002\n --app-content <additional content>[,<additional content>...]\n \u30D5\u30A1\u30A4\u30EB\u307E\u305F\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA(\u3042\u308B\u3044\u306F\u4E21\u65B9)\u306E\u30D1\u30B9\u306E\u30AB\u30F3\u30DE\u533A\u5207\u308A\u306E\u30EA\u30B9\u30C8\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30DA\u30A4\u30ED\u30FC\u30C9\u306B\u8FFD\u52A0\u3057\u307E\u3059\u3002\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u8907\u6570\u56DE\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\n\n\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30E9\u30F3\u30C1\u30E3\u3092\u4F5C\u6210\u3059\u308B\u305F\u3081\u306E\u30AA\u30D7\u30B7\u30E7\u30F3:\n --add-launcher <launcher name>=<file path>\n \u30E9\u30F3\u30C1\u30E3\u306E\u540D\u524D\u3001\u304A\u3088\u3073\u30AD\u30FC\u3001\u5024\u306E\u30DA\u30A2\u306E\u30EA\u30B9\u30C8\n \u3092\u542B\u3080\u30D7\u30ED\u30D1\u30C6\u30A3\u30FB\u30D5\u30A1\u30A4\u30EB\u3078\u306E\u30D1\u30B9\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n \u30AD\u30FC"module"\u3001"main-jar"\u3001"main-class"\u3001\n "arguments"\u3001"java-options"\u3001"app-version"\u3001"icon"\u3001\n "win-console"\u3001"win-shortcut"\u3001"win-menu"\u3001\n "linux-app-category"\u304A\u3088\u3073"linux-shortcut"\u3092\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\n \u3053\u308C\u3089\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u5143\u306E\u30B3\u30DE\u30F3\u30C9\u30E9\u30A4\u30F3\u30FB\u30AA\u30D7\u30B7\u30E7\u30F3\u306B\u8FFD\u52A0\u3059\u308B\u304B\u3001\u3053\u308C\u3089\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\n \u4F7F\u7528\u3057\u3066\u5143\u306E\u30B3\u30DE\u30F3\u30C9\u30E9\u30A4\u30F3\u30FB\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u4E0A\u66F8\u304D\u3057\u3066\u3001\u8FFD\u52A0\u306E\u4EE3\u66FF\u30E9\u30F3\u30C1\u30E3\u3092\u4F5C\u6210\u3057\u307E\u3059\u3002\n \
\u30E1\u30A4\u30F3\u30FB\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30E9\u30F3\u30C1\u30E3\u306F\u30B3\u30DE\u30F3\u30C9\u30E9\u30A4\u30F3\u30FB\u30AA\u30D7\u30B7\u30E7\u30F3\u304B\u3089\u4F5C\u6210\u3055\u308C\u307E\u3059\u3002\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u4F7F\u7528\u3057\u3066\u8FFD\u52A0\u306E\u4EE3\u66FF\u30E9\u30F3\u30C1\u30E3\u3092\u4F5C\u6210\u3067\u304D\u3001\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u8907\u6570\u56DE\u4F7F\u7528\u3057\u3066\n \u8907\u6570\u306E\u8FFD\u52A0\u306E\u30E9\u30F3\u30C1\u30E3\u3092\u4F5C\u6210\u3067\u304D\u307E\u3059\u3002 \n --arguments <main class arguments>\n \u30E9\u30F3\u30C1\u30E3\u306B\u30B3\u30DE\u30F3\u30C9\u30FB\u30E9\u30A4\u30F3\u5F15\u6570\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408\u306B\u30E1\u30A4\u30F3\u30FB\u30AF\u30E9\u30B9\u306B\u6E21\u3059\n \u30B3\u30DE\u30F3\u30C9\u30FB\u30E9\u30A4\u30F3\u5F15\u6570\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u8907\u6570\u56DE\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\n --java-options <java options>\n Java\u30E9\u30F3\u30BF\u30A4\u30E0\u306B\u6E21\u3059\u30AA\u30D7\u30B7\u30E7\u30F3\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u8907\u6570\u56DE\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\n --main-class <class name>\n \u5B9F\u884C\u3059\u308B\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30E1\u30A4\u30F3\u30FB\u30AF\u30E9\u30B9\u306E\u4FEE\u98FE\u540D\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u4F7F\u7528\u3067\u304D\u308B\u306E\u306F\u3001--main-jar\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u308B\u5834\u5408\u3060\u3051\u3067\u3059\u3002\n --main-jar <main jar file>\n \u30E1\u30A4\u30F3\u30FB\u30AF\u30E9\u30B9\u3092\u542B\u3080\u3001\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30E1\u30A4\u30F3JAR\n (\u5165\u529B\u30D1\u30B9\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9\u3068\u3057\u3066\u6307\u5B9A)\n --module\u307E\u305F\u306F--main-jar\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u6307\u5B9A\u3067\u304D\u307E\u3059\u304C\u3001\u4E21\u65B9\u306F\n \u6307\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002\n --module -m <module name>[/<main class>]\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30E1\u30A4\u30F3\u30FB\u30E2\u30B8\u30E5\u30FC\u30EB(\u304A\u3088\u3073\u30AA\u30D7\u30B7\u30E7\u30F3\u3067\u30E1\u30A4\u30F3\u30FB\u30AF\u30E9\u30B9)\n \u3053\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u306F\u3001\u30E2\u30B8\u30E5\u30FC\u30EB\u30FB\u30D1\u30B9\u306B\u7F6E\u304B\u308C\u3066\u3044\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u308B\u5834\u5408\u3001\u30E1\u30A4\u30F3\u30FB\u30E2\u30B8\u30E5\u30FC\u30EB\u306F\n Java\u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30A4\u30E1\u30FC\u30B8\u5185\u3067\u30EA\u30F3\u30AF\u3055\u308C\u307E\u3059\u3002--module\u307E\u305F\u306F--main-jar\n \u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u6307\u5B9A\u3067\u304D\u307E\u3059\u304C\u3001\u4E21\u65B9\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093\u3002\n{2}\n\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u4F5C\u6210\u3059\u308B\u305F\u3081\u306E\u30AA\u30D7\u30B7\u30E7\u30F3:\n --about-url <url>\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30DB\u30FC\u30E0\u30DA\u30FC\u30B8\u306EURL\n --app-image <directory path>\n \u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u53EF\u80FD\u306A\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u4F5C\u6210\u306B\u4F7F\u7528\u3059\u308B\u3001\u4E8B\u524D\u5B9A\u7FA9\u6E08\u307F\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u306E\u5834\u6240\n \
(\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n --file-associations <file path>\n \u30AD\u30FC\u3001\u5024\u306E\u30DA\u30A2\u306E\u30EA\u30B9\u30C8\u3092\u542B\u3080\u30D7\u30ED\u30D1\u30C6\u30A3\u30FB\u30D5\u30A1\u30A4\u30EB\u3078\u306E\u30D1\u30B9\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n \u30AD\u30FC"extension"\u3001"mime-type"\u3001"icon"\u3001"description"\n \u3092\u4F7F\u7528\u3057\u3066\u95A2\u9023\u4ED8\u3051\u3092\u8A18\u8FF0\u3067\u304D\u307E\u3059\u3002\n \u3053\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u8907\u6570\u56DE\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002\n --install-dir <directory path>\n {4} --license-file <file path>\n \u30E9\u30A4\u30BB\u30F3\u30B9\u30FB\u30D5\u30A1\u30A4\u30EB\u3078\u306E\u30D1\u30B9\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n --resource-dir <directory path>\n \u30AA\u30FC\u30D0\u30FC\u30E9\u30A4\u30C9jpackage\u30EA\u30BD\u30FC\u30B9\u3078\u306E\u30D1\u30B9\n \u30A2\u30A4\u30B3\u30F3\u3001\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u30FB\u30D5\u30A1\u30A4\u30EB\u304A\u3088\u3073jpackage\u306E\u305D\u306E\u4ED6\u306E\u30EA\u30BD\u30FC\u30B9\u306F\u3001\n \u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u7F6E\u63DB\u30EA\u30BD\u30FC\u30B9\u3092\u8FFD\u52A0\u3059\u308B\u3053\u3068\u3067\u30AA\u30FC\u30D0\u30FC\u30E9\u30A4\u30C9\u3067\u304D\u307E\u3059\u3002\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n --runtime-image <directory path>\n \u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3059\u308B\u4E8B\u524D\u5B9A\u7FA9\u6E08\u307F\u306E\u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30A4\u30E1\u30FC\u30B8\u306E\u30D1\u30B9\n (\u7D76\u5BFE\u30D1\u30B9\u307E\u305F\u306F\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304B\u3089\u306E\u76F8\u5BFE\u30D1\u30B9)\n \u30E9\u30F3\u30BF\u30A4\u30E0\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u4F5C\u6210\u6642\u306B\u306F\u3001\u30AA\u30D7\u30B7\u30E7\u30F3\u304C\u5FC5\u8981\u3067\u3059\u3002\n\n\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u4F5C\u6210\u3059\u308B\u305F\u3081\u306E\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u4F9D\u5B58\u30AA\u30D7\u30B7\u30E7\u30F3:\n{3}
MSG_Help=Usage: jpackage <options>\n\
\n\
Sample usages:\n\
--------------\n\
\ Generate an application package suitable for the host system:\n\
\ For a modular application:\n\
\ jpackage -n name -p modulePath -m moduleName/className\n\
\ For a non-modular application:\n\
\ jpackage -i inputDir -n name \\\n\
\ --main-class className --main-jar myJar.jar\n\
\ From a pre-built application image:\n\
\ jpackage -n name --app-image appImageDir\n\
\ Generate an application image:\n\
\ For a modular application:\n\
\ jpackage --type app-image -n name -p modulePath \\\n\
\ -m moduleName/className\n\
\ For a non-modular application:\n\
\ jpackage --type app-image -i inputDir -n name \\\n\
\ --main-class className --main-jar myJar.jar\n\
\ To provide your own options to jlink, run jlink separately:\n\
\ jlink --output appRuntimeImage -p modulePath \\\n\
\ --add-modules moduleName \\\n\
\ --no-header-files [<additional jlink options>...]\n\
\ jpackage --type app-image -n name \\\n\
\ -m moduleName/className --runtime-image appRuntimeImage\n\
\ Generate a Java runtime package:\n\
\ jpackage -n name --runtime-image <runtime-image>\n\
\n\
Generic Options:\n\
\ @<filename> \n\
\ Read options and/or mode from a file \n\
\ This option can be used multiple times.\n\
\ --type -t <type> \n\
\ The type of package to create\n\
\ Valid values are: {1} \n\
\ If this option is not specified a platform dependent\n\
\ default type will be created.\n\
\ --app-version <version>\n\
\ Version of the application and/or package\n\
\ --copyright <copyright string>\n\
\ Copyright for the application\n\
\ --description <description string>\n\
\ Description of the application\n\
\ --help -h \n\
\ Print the usage text with a list and description of each valid\n\
\ option for the current platform to the output stream, and exit\n\
\ --icon <file path>\n\
\ Path of the icon of the application package\n\
\ (absolute path or relative to the current directory)\n\
\ --name -n <name>\n\
\ Name of the application and/or package\n\
\ --dest -d <destination path>\n\
\ Path where generated output file is placed\n\
\ (absolute path or relative to the current directory)\n\
\ Defaults to the current working directory.\n\
\ --temp <directory path>\n\
\ Path of a new or empty directory used to create temporary files\n\
\ (absolute path or relative to the current directory)\n\
\ If specified, the temp dir will not be removed upon the task\n\
\ completion and must be removed manually.\n\
\ If not specified, a temporary directory will be created and\n\
\ removed upon the task completion.\n\
\ --vendor <vendor string>\n\
\ Vendor of the application\n\
\ --verbose\n\
\ Enables verbose output\n\
\ --version\n\
\ Print the product version to the output stream and exit.\n\
\n\
\Options for creating the runtime image:\n\
\ --add-modules <module name>[,<module name>...]\n\
\ A comma (",") separated list of modules to add\n\
\ This module list, along with the main module (if specified)\n\
\ will be passed to jlink as the --add-module argument.\n\
\ If not specified, either just the main module (if --module is\n\
\ specified), or the default set of modules (if --main-jar is \n\
\ specified) are used.\n\
\ This option can be used multiple times.\n\
\ --module-path -p <module path>...\n\
\ A {0} separated list of paths\n\
\ Each path is either a directory of modules or the path to a\n\
\ modular jar.\n\
\ (Each path is absolute or relative to the current directory.)\n\
\ This option can be used multiple times.\n\
\ --jlink-options <jlink options> \n\
\ A space separated list of options to pass to jlink \n\
\ If not specified, defaults to "--strip-native-commands \n\
\ --strip-debug --no-man-pages --no-header-files". \n\
\ This option can be used multiple times.\n\
\ --runtime-image <directory path>\n\
\ Path of the predefined runtime image that will be copied into\n\
\ the application image\n\
\ (absolute path or relative to the current directory)\n\
\ If --runtime-image is not specified, jpackage will run jlink to\n\
\ create the runtime image using options:\n\
\ --strip-debug, --no-header-files, --no-man-pages, and\n\
\ --strip-native-commands.\n\
\n\
\Options for creating the application image:\n\
\ --input -i <directory path>\n\
\ Path of the input directory that contains the files to be packaged\n\
\ (absolute path or relative to the current directory)\n\
\ All files in the input directory will be packaged into the\n\
\ application image.\n\
\ --app-content <additional content>[,<additional content>...]\n\
\ A comma separated list of paths to files and/or directories\n\
\ to add to the application payload.\n\
\ This option can be used more than once.\n\
\n\
\Options for creating the application launcher(s):\n\
\ --add-launcher <launcher name>=<file path>\n\
\ Name of launcher, and a path to a Properties file that contains\n\
\ a list of key, value pairs\n\
\ (absolute path or relative to the current directory)\n\
\ The keys "module", "main-jar", "main-class",\n\
\ "arguments", "java-options", "app-version", "icon",\n\
\ "launcher-as-service",\n\
\ "win-console", "win-shortcut", "win-menu",\n\
\ "linux-app-category", and "linux-shortcut" can be used.\n\
\ These options are added to, or used to overwrite, the original\n\
\ command line options to build an additional alternative launcher.\n\
\ The main application launcher will be built from the command line\n\
\ options. Additional alternative launchers can be built using\n\
\ this option, and this option can be used multiple times to\n\
\ build multiple additional launchers. \n\
\ --arguments <main class arguments>\n\
\ Command line arguments to pass to the main class if no command\n\
\ line arguments are given to the launcher\n\
\ This option can be used multiple times.\n\
\ --java-options <java options>\n\
\ Options to pass to the Java runtime\n\
\ This option can be used multiple times.\n\
\ --main-class <class name>\n\
\ Qualified name of the application main class to execute\n\
\ This option can only be used if --main-jar is specified.\n\
\ --main-jar <main jar file>\n\
\ The main JAR of the application; containing the main class\n\
\ (specified as a path relative to the input path)\n\
\ Either --module or --main-jar option can be specified but not\n\
\ both.\n\
\ --module -m <module name>[/<main class>]\n\
\ The main module (and optionally main class) of the application\n\
\ This module must be located on the module path.\n\
\ When this option is specified, the main module will be linked\n\
\ in the Java runtime image. Either --module or --main-jar\n\
\ option can be specified but not both.\n\
{2}\n\
\Options for creating the application package:\n\
\ --about-url <url>\n\
\ URL of the application''s home page\n\
\ --app-image <directory path>\n\
\ Location of the predefined application image that is used\n\
\ to build an installable package\n\
\ (absolute path or relative to the current directory)\n\
\ --file-associations <file path>\n\
\ Path to a Properties file that contains list of key, value pairs\n\
\ (absolute path or relative to the current directory)\n\
\ The keys "extension", "mime-type", "icon", and "description"\n\
\ can be used to describe the association.\n\
\ This option can be used multiple times.\n\
\ --install-dir <directory path>\n\
\ {4}\
\ --license-file <file path>\n\
\ Path to the license file\n\
\ (absolute path or relative to the current directory)\n\
\ --resource-dir <directory path>\n\
\ Path to override jpackage resources\n\
\ Icons, template files, and other resources of jpackage can be\n\
\ over-ridden by adding replacement resources to this directory.\n\
\ (absolute path or relative to the current directory)\n\
\ --runtime-image <directory path>\n\
\ Path of the predefined runtime image to install\n\
\ (absolute path or relative to the current directory)\n\
\ Option is required when creating a runtime package.\n\
\ --launcher-as-service\n\
\ Request to create an installer that will register the main\n\
\ application launcher as a background service-type application.\n\
\n\
\Platform dependent options for creating the application package:\n\
{3}
MSG_Help_win_launcher=\n\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30E9\u30F3\u30C1\u30E3\u3092\u4F5C\u6210\u3059\u308B\u305F\u3081\u306E\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u4F9D\u5B58\u30AA\u30D7\u30B7\u30E7\u30F3:\n --win-console\n \u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30B3\u30F3\u30BD\u30FC\u30EB\u30FB\u30E9\u30F3\u30C1\u30E3\u3092\u4F5C\u6210\u3057\u307E\u3059\u3002\u30B3\u30F3\u30BD\u30FC\u30EB\u30FB\n \u30A4\u30F3\u30BF\u30E9\u30AF\u30B7\u30E7\u30F3\u304C\u5FC5\u8981\u306A\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306B\u6307\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\n
MSG_Help_win_install=\ --win-dir-chooser\n \u30E6\u30FC\u30B6\u30FC\u304C\u88FD\u54C1\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3059\u308B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u9078\u629E\u3059\u308B\u305F\u3081\u306E\n \u30C0\u30A4\u30A2\u30ED\u30B0\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002\n --win-help-url <url>\n \u30E6\u30FC\u30B6\u30FC\u304C\u8A73\u7D30\u60C5\u5831\u307E\u305F\u306F\u6280\u8853\u7684\u306A\u30B5\u30DD\u30FC\u30C8\u3092\u53D6\u5F97\u3067\u304D\u308BURL\n --win-menu\n \u3053\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30B9\u30BF\u30FC\u30C8\u30FB\u30E1\u30CB\u30E5\u30FC\u30FB\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u8FFD\u52A0\u3059\u308B\u3088\u3046\u306B\u30EA\u30AF\u30A8\u30B9\u30C8\u3057\u307E\u3059\n --win-menu-group <menu group name>\n \u3053\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u914D\u7F6E\u3059\u308B\u30B9\u30BF\u30FC\u30C8\u30FB\u30E1\u30CB\u30E5\u30FC\u30FB\u30B0\u30EB\u30FC\u30D7\n --win-per-user-install\n \u30E6\u30FC\u30B6\u30FC\u3054\u3068\u306B\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3092\u5B9F\u884C\u3059\u308B\u3088\u3046\u306B\u30EA\u30AF\u30A8\u30B9\u30C8\u3057\u307E\u3059\n --win-shortcut\n \u3053\u306E\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7\u30FB\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u8FFD\u52A0\u3059\u308B\u3088\u3046\u306B\u30EA\u30AF\u30A8\u30B9\u30C8\u3057\u307E\u3059\n --win-shortcut-prompt\n \u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u3067\u4F5C\u6210\u3059\u308B\u304B\u3069\u3046\u304B\u3092\u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3067\u304D\u308B\u3088\u3046\u306B\u3059\u308B\n \u30C0\u30A4\u30A2\u30ED\u30B0\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002\n --win-update-url <url>\n \u4F7F\u7528\u53EF\u80FD\u306A\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u306E\u66F4\u65B0\u60C5\u5831\u306EURL\n --win-upgrade-uuid <id string>\n \u3053\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9\u306B\u95A2\u9023\u4ED8\u3051\u3089\u308C\u305FUUID\n

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2022, 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
@ -24,10 +24,186 @@
#
#
MSG_Help=\u7528\u6CD5\uFF1Ajpackage <options>\n\n\u793A\u4F8B\u7528\u6CD5:\n--------------\n \u751F\u6210\u9002\u5408\u4E3B\u673A\u7CFB\u7EDF\u7684\u5E94\u7528\u7A0B\u5E8F\u5305\uFF1A\n \u5BF9\u4E8E\u6A21\u5757\u5316\u5E94\u7528\u7A0B\u5E8F\uFF1A\n jpackage -n name -p modulePath -m moduleName/className\n \u5BF9\u4E8E\u975E\u6A21\u5757\u5316\u5E94\u7528\u7A0B\u5E8F\uFF1A\n jpackage -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n \u4ECE\u9884\u6784\u5EFA\u7684\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\uFF1A\n jpackage -n name --app-image appImageDir\n \u751F\u6210\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\uFF1A\n \u5BF9\u4E8E\u6A21\u5757\u5316\u5E94\u7528\u7A0B\u5E8F\uFF1A\n jpackage --type app-image -n name -p modulePath \\\n -m moduleName/className\n \u5BF9\u4E8E\u975E\u6A21\u5757\u5316\u5E94\u7528\u7A0B\u5E8F\uFF1A\n jpackage --type app-image -i inputDir -n name \\\n --main-class className --main-jar myJar.jar\n \u8981\u4E3A jlink \u63D0\u4F9B\u60A8\u81EA\u5DF1\u7684\u9009\u9879\uFF0C\u8BF7\u5355\u72EC\u8FD0\u884C jlink\uFF1A\n jlink --output appRuntimeImage -p modulePath \\\n --add-modules moduleName \\\n --no-header-files [<additional jlink options>...]\n jpackage --type app-image -n name \\\n -m moduleName/className --runtime-image appRuntimeImage\n \u751F\u6210 Java \u8FD0\u884C\u65F6\u7A0B\u5E8F\u5305\uFF1A\n jpackage -n name --runtime-image <runtime-image>\n\n\u4E00\u822C\u9009\u9879\uFF1A\n @<filename> \n \u4ECE\u6587\u4EF6\u8BFB\u53D6\u9009\u9879\u548C/\u6216\u6A21\u5F0F \n \u53EF\u4EE5\u591A\u6B21\u4F7F\u7528\u6B64\u9009\u9879\u3002\n --type -t <type> \n \u8981\u521B\u5EFA\u7684\u7A0B\u5E8F\u5305\u7684\u7C7B\u578B\n \u6709\u6548\u503C\u4E3A\uFF1A{1} \n \u5982\u679C\u672A\u6307\u5B9A\u6B64\u9009\u9879\uFF0C\u5219\u5C06\u521B\u5EFA\u4E0E\u5E73\u53F0\u76F8\u5173\u7684\n \u9ED8\u8BA4\u7C7B\u578B\u3002\n --app-version <version>\n \u5E94\u7528\u7A0B\u5E8F\u548C/\u6216\u7A0B\u5E8F\u5305\u7684\u7248\u672C\n --copyright <copyright string>\n \u5E94\u7528\u7A0B\u5E8F\u7684\u7248\u6743\n --description <description string>\n \u5E94\u7528\u7A0B\u5E8F\u7684\u8BF4\u660E\n --help -h \n \u5C06\u7528\u6CD5\u6587\u672C\u8F93\u51FA\u5230\u8F93\u51FA\u6D41\u5E76\u9000\u51FA\uFF0C\u7528\u6CD5\u6587\u672C\u4E2D\u5305\u542B\n \u9002\u7528\u4E8E\u5F53\u524D\u5E73\u53F0\u7684\u6BCF\u4E2A\u6709\u6548\u9009\u9879\u7684\u5217\u8868\u548C\u8BF4\u660E\n --icon <file path>\n \u5E94\u7528\u7A0B\u5E8F\u5305\u56FE\u6807\u7684\u8DEF\u5F84\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n --name -n <name>\n \u5E94\u7528\u7A0B\u5E8F\u548C/\u6216\u7A0B\u5E8F\u5305\u7684\u540D\u79F0\n --dest -d <destination path>\n \u7528\u6765\u653E\u7F6E\u6240\u751F\u6210\u7684\u8F93\u51FA\u6587\u4EF6\u7684\u8DEF\u5F84\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n \u9ED8\u8BA4\u4E3A\u5F53\u524D\u7684\u5DE5\u4F5C\u76EE\u5F55\u3002\n --temp <directory path>\n \u7528\u6765\u521B\u5EFA\u4E34\u65F6\u6587\u4EF6\u7684\u65B0\u76EE\u5F55\u6216\u7A7A\u767D\u76EE\u5F55\u7684\u8DEF\u5F84\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n \u5982\u679C\u6307\u5B9A\uFF0C\u5219\u5728\u4EFB\u52A1\u5B8C\u6210\u65F6\u5C06\u4E0D\u5220\u9664\u4E34\u65F6\u76EE\u5F55\uFF0C\n \u5FC5\u987B\u624B\u52A8\u5220\u9664\u4E34\u65F6\u76EE\u5F55\u3002\n \u5982\u679C\u672A\u6307\u5B9A\uFF0C\u5219\u5C06\u521B\u5EFA\u4E00\u4E2A\u4E34\u65F6\u76EE\u5F55\uFF0C\n \
\u5E76\u5728\u4EFB\u52A1\u5B8C\u6210\u65F6\u5220\u9664\u8BE5\u4E34\u65F6\u76EE\u5F55\u3002\n --vendor <vendor string>\n \u5E94\u7528\u7A0B\u5E8F\u7684\u4F9B\u5E94\u5546\n --verbose\n \u542F\u7528\u8BE6\u7EC6\u7684\u8F93\u51FA\n --version\n \u5C06\u4EA7\u54C1\u7248\u672C\u8F93\u51FA\u5230\u8F93\u51FA\u6D41\u5E76\u9000\u51FA\u3002\n\n\u7528\u6765\u521B\u5EFA\u8FD0\u884C\u65F6\u6620\u50CF\u7684\u9009\u9879\uFF1A\n --add-modules <\u6A21\u5757\u540D\u79F0>[,<\u6A21\u5757\u540D\u79F0>...]\n \u8981\u6DFB\u52A0\u7684\u6A21\u5757\u7684\u9017\u53F7 (",") \u5206\u9694\u5217\u8868\n \u6B64\u6A21\u5757\u5217\u8868\u8FDE\u540C\u4E3B\u6A21\u5757\uFF08\u5982\u679C\u6307\u5B9A\uFF09\n \u5C06\u4F5C\u4E3A --add-module \u53C2\u6570\u4F20\u9012\u5230 jlink\u3002\n \u5982\u679C\u672A\u6307\u5B9A\uFF0C\u5219\u4EC5\u4F7F\u7528\u4E3B\u6A21\u5757\uFF08\u5982\u679C\u6307\u5B9A\u4E86 --module\uFF09\uFF0C\n \u6216\u8005\u4F7F\u7528\u9ED8\u8BA4\u7684\u6A21\u5757\u96C6\uFF08\u5982\u679C\u6307\u5B9A\u4E86 \n --main-jar\uFF09\u3002\n \u53EF\u4EE5\u591A\u6B21\u4F7F\u7528\u6B64\u9009\u9879\u3002\n --module-path -p <module path>...\n \u8DEF\u5F84\u7684 {0} \u5206\u9694\u5217\u8868\n \u6BCF\u4E2A\u8DEF\u5F84\u8981\u4E48\u662F\u6A21\u5757\u7684\u76EE\u5F55\uFF0C\u8981\u4E48\u662F\n \u6A21\u5757 jar \u7684\u8DEF\u5F84\u3002\n \uFF08\u6BCF\u4E2A\u8DEF\u5F84\u53EF\u4EE5\u662F\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4E5F\u53EF\u4EE5\u662F\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\u3002\uFF09\n \u53EF\u4EE5\u591A\u6B21\u4F7F\u7528\u6B64\u9009\u9879\u3002\n --jlink-options <jlink \u9009\u9879> \n \u8981\u4F20\u9012\u7ED9 jlink \u7684\u9009\u9879\u5217\u8868\uFF08\u7528\u7A7A\u683C\u5206\u9694\uFF09 \n \u5982\u679C\u672A\u6307\u5B9A\uFF0C\u5219\u9ED8\u8BA4\u4E3A "--strip-native-commands \n --strip-debug --no-man-pages --no-header-files"\u3002 \n \u53EF\u4EE5\u591A\u6B21\u4F7F\u7528\u6B64\u9009\u9879\u3002\n --runtime-image <directory path>\n \u5C06\u590D\u5236\u5230\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u7684\u9884\u5B9A\u4E49\n \u8FD0\u884C\u65F6\u6620\u50CF\u7684\u8DEF\u5F84\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n \u5982\u679C\u672A\u6307\u5B9A --runtime-image\uFF0Cjpackage \u5C06\u8FD0\u884C jlink \u4EE5\n \u4F7F\u7528\u5982\u4E0B\u9009\u9879\u521B\u5EFA\u8FD0\u884C\u65F6\u6620\u50CF\uFF1A\n --strip-debug\u3001--no-header-files\u3001--no-man-pages \u548C \n --strip-native-commands\u3002\n\n\u7528\u6765\u521B\u5EFA\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u7684\u9009\u9879\uFF1A\n --input -i <directory path>\n \u5305\u542B\u8981\u6253\u5305\u7684\u6587\u4EF6\u7684\u8F93\u5165\u76EE\u5F55\u7684\u8DEF\u5F84\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n \u8F93\u5165\u76EE\u5F55\u4E2D\u7684\u6240\u6709\u6587\u4EF6\u5C06\u6253\u5305\u5230\n \u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u4E2D\u3002\n --app-content <additional content>[,<additional content>...]\n \u8981\u6DFB\u52A0\u5230\u5E94\u7528\u7A0B\u5E8F\u6709\u6548\u8D1F\u8F7D\u4E2D\u7684\u6587\u4EF6\u548C/\u6216\n \u76EE\u5F55\u7684\u9017\u53F7\u5206\u9694\u8DEF\u5F84\u5217\u8868\u3002\n \u6B64\u9009\u9879\u53EF\u4EE5\u591A\u6B21\u4F7F\u7528\u3002\n\n\u7528\u6765\u521B\u5EFA\u5E94\u7528\u7A0B\u5E8F\u542F\u52A8\u7A0B\u5E8F\u7684\u9009\u9879\uFF1A\n --add-launcher <launcher name>=<file path>\n \u542F\u52A8\u7A0B\u5E8F\u7684\u540D\u79F0\u548C\u5305\u542B\u5173\u952E\u5B57-\u503C\u5BF9\u5217\u8868\u7684\n \u5C5E\u6027\u6587\u4EF6\u7684\u8DEF\u5F84\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n \
\u53EF\u4EE5\u4F7F\u7528\u5173\u952E\u5B57 "module"\u3001"main-jar"\u3001"main-class"\u3001\n "arguments"\u3001"java-options"\u3001"app-version"\u3001"icon"\u3001\n "win-console"\u3001"win-shortcut"\u3001"win-menu"\u3001\n "linux-app-category" \u548C "linux-shortcut"\u3002\n \u8FD9\u4E9B\u9009\u9879\u5C06\u6DFB\u52A0\u5230\u539F\u59CB\u547D\u4EE4\u884C\u9009\u9879\u4E2D\u6216\u8005\u7528\u6765\u8986\u76D6\n \u539F\u59CB\u547D\u4EE4\u884C\u9009\u9879\uFF0C\u4EE5\u6784\u5EFA\u989D\u5916\u7684\u66FF\u4EE3\u542F\u52A8\u7A0B\u5E8F\u3002\n \u5C06\u4ECE\u547D\u4EE4\u884C\u9009\u9879\u6784\u5EFA\u4E3B\u5E94\u7528\u7A0B\u5E8F\u542F\u52A8\u7A0B\u5E8F\u3002\n \u53EF\u4EE5\u4F7F\u7528\u6B64\u9009\u9879\u6784\u5EFA\u989D\u5916\u7684\u66FF\u4EE3\u542F\u52A8\u7A0B\u5E8F\uFF0C\n \u53EF\u4EE5\u591A\u6B21\u4F7F\u7528\u6B64\u9009\u9879\u6765\u6784\u5EFA\n \u591A\u4E2A\u989D\u5916\u7684\u542F\u52A8\u7A0B\u5E8F\u3002 \n --arguments <main class arguments>\n \u5728\u6CA1\u6709\u4E3A\u542F\u52A8\u7A0B\u5E8F\u63D0\u4F9B\u547D\u4EE4\u884C\u53C2\u6570\u65F6\uFF0C\n \u8981\u4F20\u9012\u5230\u4E3B\u7C7B\u7684\u547D\u4EE4\u884C\u53C2\u6570\n \u53EF\u4EE5\u591A\u6B21\u4F7F\u7528\u6B64\u9009\u9879\u3002\n --java-options <java options>\n \u8981\u4F20\u9012\u5230 Java \u8FD0\u884C\u65F6\u7684\u9009\u9879\n \u53EF\u4EE5\u591A\u6B21\u4F7F\u7528\u6B64\u9009\u9879\u3002\n --main-class <class name>\n \u8981\u6267\u884C\u7684\u5E94\u7528\u7A0B\u5E8F\u4E3B\u7C7B\u7684\u9650\u5B9A\u540D\u79F0\n \u53EA\u6709\u5728\u6307\u5B9A\u4E86 --main-jar \u65F6\u624D\u80FD\u4F7F\u7528\u6B64\u9009\u9879\u3002\n --main-jar <main jar file>\n \u5E94\u7528\u7A0B\u5E8F\u7684\u4E3B JAR\uFF1B\u5305\u542B\u4E3B\u7C7B\n \uFF08\u6307\u5B9A\u4E3A\u76F8\u5BF9\u4E8E\u8F93\u5165\u8DEF\u5F84\u7684\u8DEF\u5F84\uFF09\n \u53EF\u4EE5\u6307\u5B9A --module \u6216 --main-jar \u9009\u9879\uFF0C\u4F46\u662F\u4E0D\u80FD\u540C\u65F6\u6307\u5B9A\n \u4E24\u8005\u3002\n --module -m <module name>[/<main class>]\n \u5E94\u7528\u7A0B\u5E8F\u7684\u4E3B\u6A21\u5757\uFF08\u4EE5\u53CA\u53EF\u9009\u7684\u4E3B\u7C7B\uFF09\n \u6B64\u6A21\u5757\u5FC5\u987B\u4F4D\u4E8E\u6A21\u5757\u8DEF\u5F84\u4E2D\u3002\n \u5982\u679C\u6307\u5B9A\u4E86\u6B64\u9009\u9879\uFF0C\u5219\u5C06\u5728 Java \u8FD0\u884C\u65F6\u6620\u50CF\u4E2D\n \u94FE\u63A5\u4E3B\u6A21\u5757\u3002\u53EF\u4EE5\u6307\u5B9A --module \u6216 --main-jar \u9009\u9879\uFF0C\n \u4F46\u662F\u4E0D\u80FD\u540C\u65F6\u6307\u5B9A\u8FD9\u4E24\u4E2A\u9009\u9879\u3002\n{2}\n\u7528\u6765\u521B\u5EFA\u5E94\u7528\u7A0B\u5E8F\u5305\u7684\u9009\u9879\uFF1A\n --about-url <url>\n \u5E94\u7528\u7A0B\u5E8F\u4E3B\u9875\u7684 URL\n --app-image <directory path>\n \u7528\u6765\u6784\u5EFA\u53EF\u5B89\u88C5\u7A0B\u5E8F\u5305\u7684\n \u9884\u5B9A\u4E49\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u7684\u4F4D\u7F6E\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n --file-associations <file path>\n \u5305\u542B\u5173\u952E\u5B57-\u503C\u5BF9\u5217\u8868\u7684\u5C5E\u6027\u6587\u4EF6\u7684\u8DEF\u5F84\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n \u53EF\u4EE5\u4F7F\u7528\u5173\u952E\u5B57 "extension"\u3001"mime-type"\u3001"icon" \u548C "description" \n \u6765\u63CF\u8FF0\u6B64\u5173\u8054\u3002\n \u53EF\u4EE5\u591A\u6B21\u4F7F\u7528\u6B64\u9009\u9879\u3002\n --install-dir <directory path>\n {4} --license-file <file path>\n \u8BB8\u53EF\u8BC1\u6587\u4EF6\u7684\u8DEF\u5F84\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n --resource-dir <directory path>\n \u8986\u76D6 jpackage \
\u8D44\u6E90\u7684\u8DEF\u5F84\n \u53EF\u4EE5\u901A\u8FC7\u5411\u8BE5\u76EE\u5F55\u4E2D\u6DFB\u52A0\u66FF\u4EE3\u8D44\u6E90\u6765\u8986\u76D6 jpackage \u7684\n \u56FE\u6807\u3001\u6A21\u677F\u6587\u4EF6\u548C\u5176\u4ED6\u8D44\u6E90\u3002\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n --runtime-image <directory path>\n \u8981\u5B89\u88C5\u7684\u9884\u5B9A\u4E49\u8FD0\u884C\u65F6\u6620\u50CF\u7684\u8DEF\u5F84\n \uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u4E8E\u5F53\u524D\u76EE\u5F55\u7684\u8DEF\u5F84\uFF09\n \u5728\u521B\u5EFA\u8FD0\u884C\u65F6\u7A0B\u5E8F\u5305\u65F6\u9700\u8981\u4F7F\u7528\u9009\u9879\u3002\n\n\u7528\u6765\u521B\u5EFA\u5E94\u7528\u7A0B\u5E8F\u5305\u7684\u4E0E\u5E73\u53F0\u76F8\u5173\u7684\u9009\u9879\uFF1A\n{3}
MSG_Help=Usage: jpackage <options>\n\
\n\
Sample usages:\n\
--------------\n\
\ Generate an application package suitable for the host system:\n\
\ For a modular application:\n\
\ jpackage -n name -p modulePath -m moduleName/className\n\
\ For a non-modular application:\n\
\ jpackage -i inputDir -n name \\\n\
\ --main-class className --main-jar myJar.jar\n\
\ From a pre-built application image:\n\
\ jpackage -n name --app-image appImageDir\n\
\ Generate an application image:\n\
\ For a modular application:\n\
\ jpackage --type app-image -n name -p modulePath \\\n\
\ -m moduleName/className\n\
\ For a non-modular application:\n\
\ jpackage --type app-image -i inputDir -n name \\\n\
\ --main-class className --main-jar myJar.jar\n\
\ To provide your own options to jlink, run jlink separately:\n\
\ jlink --output appRuntimeImage -p modulePath \\\n\
\ --add-modules moduleName \\\n\
\ --no-header-files [<additional jlink options>...]\n\
\ jpackage --type app-image -n name \\\n\
\ -m moduleName/className --runtime-image appRuntimeImage\n\
\ Generate a Java runtime package:\n\
\ jpackage -n name --runtime-image <runtime-image>\n\
\n\
Generic Options:\n\
\ @<filename> \n\
\ Read options and/or mode from a file \n\
\ This option can be used multiple times.\n\
\ --type -t <type> \n\
\ The type of package to create\n\
\ Valid values are: {1} \n\
\ If this option is not specified a platform dependent\n\
\ default type will be created.\n\
\ --app-version <version>\n\
\ Version of the application and/or package\n\
\ --copyright <copyright string>\n\
\ Copyright for the application\n\
\ --description <description string>\n\
\ Description of the application\n\
\ --help -h \n\
\ Print the usage text with a list and description of each valid\n\
\ option for the current platform to the output stream, and exit\n\
\ --icon <file path>\n\
\ Path of the icon of the application package\n\
\ (absolute path or relative to the current directory)\n\
\ --name -n <name>\n\
\ Name of the application and/or package\n\
\ --dest -d <destination path>\n\
\ Path where generated output file is placed\n\
\ (absolute path or relative to the current directory)\n\
\ Defaults to the current working directory.\n\
\ --temp <directory path>\n\
\ Path of a new or empty directory used to create temporary files\n\
\ (absolute path or relative to the current directory)\n\
\ If specified, the temp dir will not be removed upon the task\n\
\ completion and must be removed manually.\n\
\ If not specified, a temporary directory will be created and\n\
\ removed upon the task completion.\n\
\ --vendor <vendor string>\n\
\ Vendor of the application\n\
\ --verbose\n\
\ Enables verbose output\n\
\ --version\n\
\ Print the product version to the output stream and exit.\n\
\n\
\Options for creating the runtime image:\n\
\ --add-modules <module name>[,<module name>...]\n\
\ A comma (",") separated list of modules to add\n\
\ This module list, along with the main module (if specified)\n\
\ will be passed to jlink as the --add-module argument.\n\
\ If not specified, either just the main module (if --module is\n\
\ specified), or the default set of modules (if --main-jar is \n\
\ specified) are used.\n\
\ This option can be used multiple times.\n\
\ --module-path -p <module path>...\n\
\ A {0} separated list of paths\n\
\ Each path is either a directory of modules or the path to a\n\
\ modular jar.\n\
\ (Each path is absolute or relative to the current directory.)\n\
\ This option can be used multiple times.\n\
\ --jlink-options <jlink options> \n\
\ A space separated list of options to pass to jlink \n\
\ If not specified, defaults to "--strip-native-commands \n\
\ --strip-debug --no-man-pages --no-header-files". \n\
\ This option can be used multiple times.\n\
\ --runtime-image <directory path>\n\
\ Path of the predefined runtime image that will be copied into\n\
\ the application image\n\
\ (absolute path or relative to the current directory)\n\
\ If --runtime-image is not specified, jpackage will run jlink to\n\
\ create the runtime image using options:\n\
\ --strip-debug, --no-header-files, --no-man-pages, and\n\
\ --strip-native-commands.\n\
\n\
\Options for creating the application image:\n\
\ --input -i <directory path>\n\
\ Path of the input directory that contains the files to be packaged\n\
\ (absolute path or relative to the current directory)\n\
\ All files in the input directory will be packaged into the\n\
\ application image.\n\
\ --app-content <additional content>[,<additional content>...]\n\
\ A comma separated list of paths to files and/or directories\n\
\ to add to the application payload.\n\
\ This option can be used more than once.\n\
\n\
\Options for creating the application launcher(s):\n\
\ --add-launcher <launcher name>=<file path>\n\
\ Name of launcher, and a path to a Properties file that contains\n\
\ a list of key, value pairs\n\
\ (absolute path or relative to the current directory)\n\
\ The keys "module", "main-jar", "main-class",\n\
\ "arguments", "java-options", "app-version", "icon",\n\
\ "launcher-as-service",\n\
\ "win-console", "win-shortcut", "win-menu",\n\
\ "linux-app-category", and "linux-shortcut" can be used.\n\
\ These options are added to, or used to overwrite, the original\n\
\ command line options to build an additional alternative launcher.\n\
\ The main application launcher will be built from the command line\n\
\ options. Additional alternative launchers can be built using\n\
\ this option, and this option can be used multiple times to\n\
\ build multiple additional launchers. \n\
\ --arguments <main class arguments>\n\
\ Command line arguments to pass to the main class if no command\n\
\ line arguments are given to the launcher\n\
\ This option can be used multiple times.\n\
\ --java-options <java options>\n\
\ Options to pass to the Java runtime\n\
\ This option can be used multiple times.\n\
\ --main-class <class name>\n\
\ Qualified name of the application main class to execute\n\
\ This option can only be used if --main-jar is specified.\n\
\ --main-jar <main jar file>\n\
\ The main JAR of the application; containing the main class\n\
\ (specified as a path relative to the input path)\n\
\ Either --module or --main-jar option can be specified but not\n\
\ both.\n\
\ --module -m <module name>[/<main class>]\n\
\ The main module (and optionally main class) of the application\n\
\ This module must be located on the module path.\n\
\ When this option is specified, the main module will be linked\n\
\ in the Java runtime image. Either --module or --main-jar\n\
\ option can be specified but not both.\n\
{2}\n\
\Options for creating the application package:\n\
\ --about-url <url>\n\
\ URL of the application''s home page\n\
\ --app-image <directory path>\n\
\ Location of the predefined application image that is used\n\
\ to build an installable package\n\
\ (absolute path or relative to the current directory)\n\
\ --file-associations <file path>\n\
\ Path to a Properties file that contains list of key, value pairs\n\
\ (absolute path or relative to the current directory)\n\
\ The keys "extension", "mime-type", "icon", and "description"\n\
\ can be used to describe the association.\n\
\ This option can be used multiple times.\n\
\ --install-dir <directory path>\n\
\ {4}\
\ --license-file <file path>\n\
\ Path to the license file\n\
\ (absolute path or relative to the current directory)\n\
\ --resource-dir <directory path>\n\
\ Path to override jpackage resources\n\
\ Icons, template files, and other resources of jpackage can be\n\
\ over-ridden by adding replacement resources to this directory.\n\
\ (absolute path or relative to the current directory)\n\
\ --runtime-image <directory path>\n\
\ Path of the predefined runtime image to install\n\
\ (absolute path or relative to the current directory)\n\
\ Option is required when creating a runtime package.\n\
\ --launcher-as-service\n\
\ Request to create an installer that will register the main\n\
\ application launcher as a background service-type application.\n\
\n\
\Platform dependent options for creating the application package:\n\
{3}
MSG_Help_win_launcher=\n\u7528\u6765\u521B\u5EFA\u5E94\u7528\u7A0B\u5E8F\u542F\u52A8\u7A0B\u5E8F\u7684\u4E0E\u5E73\u53F0\u76F8\u5173\u7684\u9009\u9879\uFF1A\n --win-console\n \u4E3A\u5E94\u7528\u7A0B\u5E8F\u521B\u5EFA\u63A7\u5236\u53F0\u542F\u52A8\u7A0B\u5E8F\uFF0C\u5E94\u5F53\u4E3A\n \u9700\u8981\u63A7\u5236\u53F0\u4EA4\u4E92\u7684\u5E94\u7528\u7A0B\u5E8F\u6307\u5B9A\n
MSG_Help_win_install=\ --win-dir-chooser\n \u6DFB\u52A0\u4E00\u4E2A\u5BF9\u8BDD\u6846\u4EE5\u5141\u8BB8\u7528\u6237\u9009\u62E9\n \u4EA7\u54C1\u7684\u5B89\u88C5\u76EE\u5F55\u3002\n --win-help-url <url>\n \u7528\u6237\u53EF\u4EE5\u4ECE\u4E2D\u83B7\u53D6\u66F4\u591A\u4FE1\u606F\u6216\u6280\u672F\u652F\u6301\u7684 URL\n --win-menu\n \u8BF7\u6C42\u4E3A\u6B64\u5E94\u7528\u7A0B\u5E8F\u6DFB\u52A0\u5F00\u59CB\u83DC\u5355\u5FEB\u6377\u65B9\u5F0F\n --win-menu-group <menu group name>\n \u6B64\u5E94\u7528\u7A0B\u5E8F\u6240\u5728\u7684\u5F00\u59CB\u83DC\u5355\u7EC4\n --win-per-user-install\n \u8BF7\u6C42\u57FA\u4E8E\u6BCF\u4E2A\u7528\u6237\u6267\u884C\u5B89\u88C5\n --win-shortcut\n \u8BF7\u6C42\u4E3A\u6B64\u5E94\u7528\u7A0B\u5E8F\u6DFB\u52A0\u684C\u9762\u5FEB\u6377\u65B9\u5F0F\n --win-shortcut-prompt\n \u6DFB\u52A0\u4E00\u4E2A\u5BF9\u8BDD\u6846\u4EE5\u5141\u8BB8\u7528\u6237\u9009\u62E9\u662F\u5426\u5C06\u7531\u5B89\u88C5\u7A0B\u5E8F\n \u521B\u5EFA\u5FEB\u6377\u65B9\u5F0F\u3002\n --win-update-url <url>\n \u53EF\u7528\u5E94\u7528\u7A0B\u5E8F\u66F4\u65B0\u4FE1\u606F\u7684 URL\n --win-upgrade-uuid <id string>\n \u4E0E\u6B64\u7A0B\u5E8F\u5305\u7684\u5347\u7EA7\u76F8\u5173\u8054\u7684 UUID\n

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2022, 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;
import java.io.IOException;
import java.nio.file.Path;
import java.util.EnumSet;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
/**
* Shell scripts of a package.
*/
final class PackageScripts<T extends Enum<T> & Supplier<OverridableResource>> {
static <T extends Enum<T> & Supplier<OverridableResource>> PackageScripts<T> create(
Class<T> scriptIdsType) {
return new PackageScripts<>(scriptIdsType);
}
PackageScripts(Class<T> scriptIdsType) {
scripts = EnumSet.allOf(scriptIdsType).stream().collect(
Collectors.toMap(UnaryOperator.identity(), scriptId -> {
return new ShellScriptResource(scriptId.name()).setResource(
scriptId.get());
}));
}
PackageScripts<T> setSubstitutionData(T id, Map<String, String> data) {
scripts.get(id).getResource().setSubstitutionData(data);
return this;
}
PackageScripts<T> setSubstitutionData(Map<String, String> data) {
scripts.values().forEach(
script -> script.getResource().setSubstitutionData(data));
return this;
}
PackageScripts<T> setResourceDir(Path v) throws IOException {
for (var script : scripts.values()) {
script.getResource().setResourceDir(v);
}
return this;
}
void saveInFolder(Path folder) throws IOException {
for (var script : scripts.values()) {
script.saveInFolder(folder);
}
}
static class ResourceConfig {
ResourceConfig(String defaultName, String categoryId) {
this.defaultName = defaultName;
this.category = I18N.getString(categoryId);
}
OverridableResource createResource() {
var resource = new OverridableResource(defaultName).setCategory(category);
return getDefaultPublicName().map(resource::setPublicName).orElse(
resource);
}
private Optional<String> getDefaultPublicName() {
final String wellKnownSuffix = ".template";
if (defaultName.endsWith(wellKnownSuffix)) {
return Optional.of(defaultName.substring(0, defaultName.length()
- wellKnownSuffix.length()));
}
return Optional.ofNullable(null);
}
private final String defaultName;
private final String category;
}
private final Map<T, ShellScriptResource> scripts;
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2022, 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;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Interface to add custom actions composed of shell commands to installers.
*/
abstract class ShellCustomAction {
List<String> requiredPackages() {
return Collections.emptyList();
}
final Map<String, String> create() throws IOException {
Map<String, String> result = new HashMap<>();
replacementStringIds().forEach(key -> {
result.put(key, "");
});
result.putAll(createImpl());
return result;
}
protected List<String> replacementStringIds() {
return Collections.emptyList();
}
protected abstract Map<String, String> createImpl() throws IOException;
static ShellCustomAction nop(List<String> replacementStringIds) {
return new ShellCustomAction() {
@Override
protected List<String> replacementStringIds() {
return replacementStringIds;
}
@Override
protected Map<String, String> createImpl() throws IOException {
return Map.of();
}
};
}
protected static String stringifyShellCommands(String... commands) {
return stringifyShellCommands(Arrays.asList(commands));
}
protected static String stringifyShellCommands(List<String> commands) {
return String.join("\n", commands.stream().filter(
s -> s != null && !s.isEmpty()).toList());
}
protected static String stringifyTextFile(String resourceName) throws IOException {
try ( InputStream is = OverridableResource.readDefault(resourceName);
InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)) {
return reader.lines().collect(Collectors.joining("\n"));
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2022, 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;
import java.io.IOException;
import java.util.Map;
interface ShellCustomActionFactory {
ShellCustomAction create(PlatformPackage thePackage,
Map<String, ? super Object> params) throws IOException;
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2022, 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;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Shell script resource.
*/
final class ShellScriptResource {
ShellScriptResource(String publicFileName) {
this.publicFileName = Path.of(publicFileName);
}
void saveInFolder(Path folder) throws IOException {
Path dstFile = folder.resolve(publicFileName);
resource.saveToFile(dstFile);
Files.setPosixFilePermissions(dstFile, Stream.of(execPerms, Set.of(
PosixFilePermission.OWNER_READ,
PosixFilePermission.OWNER_WRITE,
PosixFilePermission.GROUP_READ,
PosixFilePermission.OTHERS_READ
)).flatMap(x -> x.stream()).collect(Collectors.toSet()));
}
ShellScriptResource setResource(OverridableResource v) {
resource = v;
return this;
}
ShellScriptResource onlyOwnerCanExecute(boolean v) {
execPerms = v ? OWNER_CAN_EXECUTE : ALL_CAN_EXECUTE;
return this;
}
OverridableResource getResource() {
return resource;
}
final Path publicFileName;
private Set<PosixFilePermission> execPerms = ALL_CAN_EXECUTE;
private OverridableResource resource;
private final static Set<PosixFilePermission> ALL_CAN_EXECUTE = Set.of(
PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_EXECUTE,
PosixFilePermission.OTHERS_EXECUTE);
private final static Set<PosixFilePermission> OWNER_CAN_EXECUTE = Set.of(
PosixFilePermission.OWNER_EXECUTE);
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2022, 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;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.jpackage.internal.AppImageFile.LauncherInfo;
import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE;
/**
* Helper to install launchers as services for Unix installers.
*/
class UnixLaunchersAsServices extends ShellCustomAction {
UnixLaunchersAsServices(PlatformPackage thePackage,
List<String> requiredPackages, Map<String, Object> params,
Function<LauncherInfo, UnixLauncherAsService> factory) throws
IOException {
this.thePackage = thePackage;
this.requiredPackages = requiredPackages;
// Read launchers information
launchers = AppImageFile.getLaunchers(PREDEFINED_APP_IMAGE.fetchFrom(
params), params).stream().filter(LauncherInfo::isService).map(
factory::apply).toList();
}
@Override
final List<String> requiredPackages() {
if (launchers.isEmpty()) {
return Collections.emptyList();
} else {
return requiredPackages;
}
}
@Override
final protected List<String> replacementStringIds() {
return List.of(COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS);
}
@Override
final protected Map<String, String> createImpl() throws IOException {
Map<String, String> data = new HashMap<>();
if (launchers.isEmpty()) {
return data;
}
var installedDescriptorFiles = launchers.stream().map(
launcher -> enqouter.applyTo(launcher.descriptorFilePath(
Path.of("/")).toString())).toList();
Function<String, String> strigifier = cmd -> {
return stringifyShellCommands(Stream.of(List.of(
cmd), installedDescriptorFiles).flatMap(x -> x.stream()).collect(
Collectors.joining(" ")));
};
try {
data.put(SCRIPTS, stringifyTextFile("services_utils.sh"));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
data.put(COMMANDS_INSTALL, strigifier.apply("register_services"));
data.put(COMMANDS_UNINSTALL, strigifier.apply("unregister_services"));
for (var launcher : launchers) {
launcher.getResource().saveToFile(launcher.descriptorFilePath(
thePackage.sourceRoot()));
}
return data;
}
boolean isEmpty() {
return launchers.isEmpty();
}
static abstract class UnixLauncherAsService extends LauncherAsService {
UnixLauncherAsService(String name, Map<String, Object> mainParams,
OverridableResource resource) {
super(name, mainParams, resource);
}
abstract Path descriptorFilePath(Path root);
}
private final PlatformPackage thePackage;
private final List<String> requiredPackages;
private final List<UnixLauncherAsService> launchers;
private final Enquoter enqouter = Enquoter.forShellLiterals();
private static final String COMMANDS_INSTALL = "LAUNCHER_AS_SERVICE_COMMANDS_INSTALL";
private static final String COMMANDS_UNINSTALL = "LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL";
private static final String SCRIPTS = "LAUNCHER_AS_SERVICE_SCRIPTS";
protected static final List<String> REPLACEMENT_STRING_IDS = List.of(
COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2022, 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
@ -54,6 +54,7 @@ import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import jdk.jpackage.internal.AppImageFile.LauncherInfo;
import static jdk.jpackage.internal.OverridableResource.createResource;
import static jdk.jpackage.internal.StandardBundlerParam.ABOUT_URL;
@ -157,6 +158,14 @@ public class WinMsiBundler extends AbstractBundler {
null,
(s, p) -> null);
static final StandardBundlerParam<InstallableFile> SERVICE_INSTALLER
= new StandardBundlerParam<>(
"win.msi.serviceInstaller",
InstallableFile.class,
null,
null
);
public static final StandardBundlerParam<Boolean> MSI_SYSTEM_WIDE =
new StandardBundlerParam<>(
Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
@ -323,6 +332,15 @@ public class WinMsiBundler extends AbstractBundler {
FileAssociation.verify(FileAssociation.fetchFrom(params));
var serviceInstallerResource = initServiceInstallerResource(params);
if (serviceInstallerResource != null) {
if (!Files.exists(serviceInstallerResource.getExternalPath())) {
throw new ConfigException(I18N.getString(
"error.missing-service-installer"), I18N.getString(
"error.missing-service-installer.advice"));
}
}
return true;
} catch (RuntimeException re) {
if (re.getCause() instanceof ConfigException) {
@ -385,6 +403,13 @@ public class WinMsiBundler extends AbstractBundler {
destFile.toFile().setWritable(true);
ensureByMutationFileIsRTF(destFile);
}
var serviceInstallerResource = initServiceInstallerResource(params);
if (serviceInstallerResource != null) {
var serviceInstallerPath = serviceInstallerResource.getExternalPath();
params.put(SERVICE_INSTALLER.getID(), new InstallableFile(
serviceInstallerPath, serviceInstallerPath.getFileName()));
}
}
@Override
@ -676,9 +701,35 @@ public class WinMsiBundler extends AbstractBundler {
}
private static OverridableResource initServiceInstallerResource(
Map<String, ? super Object> params) {
if (StandardBundlerParam.isRuntimeInstaller(params)) {
// Runtime installer doesn't install launchers,
// service installer not needed
return null;
}
if (!AppImageFile.getLaunchers(
StandardBundlerParam.getPredefinedAppImage(params), params).stream().anyMatch(
LauncherInfo::isService)) {
// Not a single launcher is requested to be installed as a service,
// service installer not needed
return null;
}
var result = createResource(null, params)
.setPublicName("service-installer.exe")
.setSourceOrder(OverridableResource.Source.External);
if (result.getResourceDir() == null) {
return null;
}
return result.setExternal(result.getResourceDir().resolve(
result.getPublicName()));
}
private Path installerIcon;
private Map<WixTool, WixTool.ToolInfo> wixToolset;
private AppImageBundler appImageBundler;
private List<WixFragmentBuilder> wixFragments;
private final List<WixFragmentBuilder> wixFragments;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2022, 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
@ -47,15 +47,24 @@ import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import jdk.jpackage.internal.AppImageFile.LauncherInfo;
import jdk.jpackage.internal.IOUtils.XmlConsumer;
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
import static jdk.jpackage.internal.StandardBundlerParam.INSTALL_DIR;
import static jdk.jpackage.internal.StandardBundlerParam.VENDOR;
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
import static jdk.jpackage.internal.WinMsiBundler.MSI_SYSTEM_WIDE;
import static jdk.jpackage.internal.WinMsiBundler.SERVICE_INSTALLER;
import static jdk.jpackage.internal.WinMsiBundler.WIN_APP_IMAGE;
import org.w3c.dom.NodeList;
/**
* Creates WiX fragment with components for contents of app image.
@ -89,10 +98,8 @@ class WixAppImageFragmentBuilder extends WixFragmentBuilder {
do {
ApplicationLayout layout = appImageSupplier.get();
// Don't want AppImageFile.FILENAME in installed application.
// Register it with app image at a role without a match in installed
// app layout to exclude it from layout transformation.
layout.pathGroup().setPath(new Object(),
AppImageFile.getPathInAppImage(Path.of("")));
new InstallableFile(AppImageFile.getPathInAppImage(Path.of("")),
null).excludeFromApplicationLayout(layout);
// Want absolute paths to source files in generated WiX sources.
// This is to handle scenario if sources would be processed from
@ -112,6 +119,27 @@ class WixAppImageFragmentBuilder extends WixFragmentBuilder {
launchers = AppImageFile.getLaunchers(appImageRoot, params);
}
launchersAsServices = launchers.stream()
.filter(LauncherInfo::isService)
.map(launcher -> {
var launcherPath = addExeSuffixToPath(
installedAppImage.launchersDirectory().resolve(
launcher.getName()));
var id = Id.File.of(launcherPath);
return new WixLauncherAsService(launcher.getName(), params)
.setLauncherInstallPath(toWixPath(launcherPath))
.setLauncherInstallPathId(id);
}).toList();
if (!launchersAsServices.isEmpty()) {
serviceInstaller = SERVICE_INSTALLER.fetchFrom(params);
// Service installer tool will be installed in launchers directory
serviceInstaller = new InstallableFile(
serviceInstaller.srcPath().toAbsolutePath().normalize(),
installedAppImage.launchersDirectory().resolve(
serviceInstaller.installPath()));
}
programMenuFolderName = MENU_GROUP.fetchFrom(params);
initFileAssociations(params);
@ -388,6 +416,13 @@ class WixAppImageFragmentBuilder extends WixFragmentBuilder {
xmlConsumer.accept(xml);
xml.writeEndElement();
if (role == Component.File && serviceInstaller != null && path.equals(
serviceInstaller.installPath())) {
for (var launcherAsService : launchersAsServices) {
launcherAsService.writeServiceInstall(xml);
}
}
xml.writeEndElement(); // <Component>
xml.writeEndElement(); // <DirectoryRef>
@ -643,6 +678,11 @@ class WixAppImageFragmentBuilder extends WixFragmentBuilder {
}
});
if (serviceInstaller != null) {
files.add(Map.entry(serviceInstaller.srcPath(),
serviceInstaller.installPath()));
}
List<String> componentIds = new ArrayList<>();
for (var file : files) {
Path src = file.getKey();
@ -661,9 +701,52 @@ class WixAppImageFragmentBuilder extends WixFragmentBuilder {
componentIds.add(addDirectoryCleaner(xml, INSTALLDIR));
componentIds.addAll(addServiceConfigs(xml));
addComponentGroup(xml, "Files", componentIds);
}
private List<String> addServiceConfigs(XMLStreamWriter xml) throws
XMLStreamException, IOException {
if (launchersAsServices.isEmpty()) {
return List.of();
}
try {
var buffer = new DOMResult(IOUtils.initDocumentBuilder().newDocument());
var bufferWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(
buffer);
bufferWriter.writeStartDocument();
bufferWriter.writeStartElement("Include");
for (var launcherAsService : launchersAsServices) {
launcherAsService.writeServiceConfig(xml);
launcherAsService.writeServiceConfig(bufferWriter);
}
bufferWriter.writeEndElement();
bufferWriter.writeEndDocument();
bufferWriter.flush();
bufferWriter.close();
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodes = (NodeList) xPath.evaluate("/Include/descendant::Component/@Id",
buffer.getNode(), XPathConstants.NODESET);
List<String> componentIds = new ArrayList<>();
for (int i = 0; i != nodes.getLength(); i++) {
var node = nodes.item(i);
componentIds.add(node.getNodeValue());
}
return componentIds;
} catch (XMLStreamException ex) {
throw new IOException(ex);
} catch (XPathExpressionException ex) {
// Should never happen
throw new RuntimeException(ex);
}
}
private void addIcons(XMLStreamWriter xml) throws
XMLStreamException, IOException {
@ -747,18 +830,7 @@ class WixAppImageFragmentBuilder extends WixFragmentBuilder {
Component.startElement(xml, componentId, "*");
addRegistryKeyPath(xml, INSTALLDIR, () -> propertyId, () -> {
// The following code converts a path to value to be saved in registry.
// E.g.:
// INSTALLDIR -> [INSTALLDIR]
// TERGETDIR/ProgramFiles64Folder/foo/bar -> [ProgramFiles64Folder]foo/bar
final Path rootDir = KNOWN_DIRS.stream()
.sorted(Comparator.comparing(Path::getNameCount).reversed())
.filter(path::startsWith)
.findFirst().get();
StringBuilder sb = new StringBuilder();
sb.append(String.format("[%s]", rootDir.getFileName().toString()));
sb.append(rootDir.relativize(path).toString());
return sb.toString();
return toWixPath(path);
});
xml.writeStartElement(
@ -773,6 +845,20 @@ class WixAppImageFragmentBuilder extends WixFragmentBuilder {
return componentId;
}
// Does the following conversions:
// INSTALLDIR -> [INSTALLDIR]
// TARGETDIR/ProgramFiles64Folder/foo/bar -> [ProgramFiles64Folder]foo/bar
private static String toWixPath(Path path) {
final Path rootDir = KNOWN_DIRS.stream()
.sorted(Comparator.comparing(Path::getNameCount).reversed())
.filter(path::startsWith)
.findFirst().get();
StringBuilder sb = new StringBuilder();
sb.append(String.format("[%s]", rootDir.getFileName().toString()));
sb.append(rootDir.relativize(path).toString());
return sb.toString();
}
private static IllegalArgumentException throwInvalidPathException(Path v) {
throw new IllegalArgumentException(String.format("Invalid path [%s]", v));
}
@ -831,7 +917,11 @@ class WixAppImageFragmentBuilder extends WixFragmentBuilder {
private Set<ShortcutsFolder> shortcutFolders;
private List<AppImageFile.LauncherInfo> launchers;
private List<LauncherInfo> launchers;
private List<WixLauncherAsService> launchersAsServices;
private InstallableFile serviceInstaller;
private ApplicationLayout appImage;
private ApplicationLayout installedAppImage;

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2021, 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;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
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.OverridableResource.createResource;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
class WixLauncherAsService extends LauncherAsService {
WixLauncherAsService(String name, Map<String, ? super Object> mainParams) {
super(name, mainParams,
createResource("service-install.wxi", mainParams).setCategory(
I18N.getString("resource.launcher-as-service-wix-file")));
serviceConfigResource = createResource("service-config.wxi", mainParams).setCategory(
I18N.getString("resource.launcher-as-service-wix-file"));
addSubstitutionDataEntry("SERVICE_NAME", getName());
setPublicName(getResource());
setPublicName(serviceConfigResource);
}
WixLauncherAsService setLauncherInstallPath(String v) {
return addSubstitutionDataEntry("APPLICATION_LAUNCHER", v);
}
WixLauncherAsService setLauncherInstallPathId(String v) {
return addSubstitutionDataEntry("APPLICATION_LAUNCHER_ID", v);
}
void writeServiceConfig(XMLStreamWriter xml) throws XMLStreamException,
IOException {
writeResource(serviceConfigResource, xml);
}
void writeServiceInstall(XMLStreamWriter xml) throws XMLStreamException,
IOException {
writeResource(getResource(), xml);
}
private WixLauncherAsService addSubstitutionDataEntry(String name,
String value) {
getResource().addSubstitutionDataEntry(name, value);
serviceConfigResource.addSubstitutionDataEntry(name, value);
return this;
}
private OverridableResource setPublicName(OverridableResource r) {
return r.setPublicName(getName() + "-" + r.getDefaultName());
}
private void writeResource(OverridableResource resource, XMLStreamWriter xml)
throws XMLStreamException, IOException {
var buffer = new ByteArrayOutputStream();
resource.saveToStream(buffer);
try {
Document doc = IOUtils.initDocumentBuilder().parse(
new ByteArrayInputStream(buffer.toByteArray()));
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodes = (NodeList) xPath.evaluate("/Include/*", doc,
XPathConstants.NODESET);
List<Source> sources = new ArrayList<>();
for (int i = 0; i != nodes.getLength(); i++) {
Node n = nodes.item(i);
sources.add(new DOMSource(n));
}
IOUtils.mergeXmls(xml, sources);
} catch (SAXException ex) {
throw new IOException(ex);
} catch (XPathExpressionException ex) {
// Should never happen
throw new RuntimeException(ex);
}
}
private final OverridableResource serviceConfigResource;
}

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2022, 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
@ -39,6 +39,7 @@ resource.main-wix-file=Main WiX project file
resource.overrides-wix-file=Overrides WiX project file
resource.shortcutpromptdlg-wix-file=Shortcut prompt dialog WiX project file
resource.installdirnotemptydlg-wix-file=Not empty install directory dialog WiX project file
resource.launcher-as-service-wix-file=Service installer WiX project file
error.no-wix-tools=Can not find WiX tools (light.exe, candle.exe)
error.no-wix-tools.advice=Download WiX 3.0 or later from https://wixtoolset.org and add it to the PATH.

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2022, 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
@ -39,6 +39,7 @@ resource.main-wix-file=\u30E1\u30A4\u30F3WiX\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8
resource.overrides-wix-file=WiX\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30FB\u30D5\u30A1\u30A4\u30EB\u306E\u30AA\u30FC\u30D0\u30FC\u30E9\u30A4\u30C9
resource.shortcutpromptdlg-wix-file=\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30FB\u30D7\u30ED\u30F3\u30D7\u30C8\u30FB\u30C0\u30A4\u30A2\u30ED\u30B0WiX\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30FB\u30D5\u30A1\u30A4\u30EB
resource.installdirnotemptydlg-wix-file=\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FB\u30C0\u30A4\u30A2\u30ED\u30B0\u306EWiX\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30FB\u30D5\u30A1\u30A4\u30EB\u304C\u7A7A\u3067\u306F\u3042\u308A\u307E\u305B\u3093
resource.launcher-as-service-wix-file=Service installer WiX project file
error.no-wix-tools=WiX\u30C4\u30FC\u30EB(light.exe\u3001candle.exe)\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093
error.no-wix-tools.advice=WiX 3.0\u4EE5\u964D\u3092https://wixtoolset.org\u304B\u3089\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3057\u3001PATH\u306B\u8FFD\u52A0\u3057\u307E\u3059\u3002

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2017, 2022, 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
@ -39,6 +39,7 @@ resource.main-wix-file=\u4E3B WiX \u9879\u76EE\u6587\u4EF6
resource.overrides-wix-file=\u8986\u76D6 WiX \u9879\u76EE\u6587\u4EF6
resource.shortcutpromptdlg-wix-file=\u5FEB\u6377\u65B9\u5F0F\u63D0\u793A\u5BF9\u8BDD\u6846 WiX \u9879\u76EE\u6587\u4EF6
resource.installdirnotemptydlg-wix-file=\u5B89\u88C5\u76EE\u5F55\u5BF9\u8BDD\u6846 WiX \u9879\u76EE\u6587\u4EF6\u975E\u7A7A
resource.launcher-as-service-wix-file=Service installer WiX project file
error.no-wix-tools=\u627E\u4E0D\u5230 WiX \u5DE5\u5177 (light.exe, candle.exe)
error.no-wix-tools.advice=\u4ECE https://wixtoolset.org \u4E0B\u8F7D WiX 3.0 \u6216\u66F4\u9AD8\u7248\u672C\uFF0C\u7136\u540E\u5C06\u5176\u6DFB\u52A0\u5230 PATH\u3002

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<!--
Configuration correponds to NSSM
-->
<DirectoryRef Id="INSTALLDIR">
<Component Id="nssm_Application_APPLICATION_LAUNCHER_ID" Guid="*">
<RegistryValue Root="HKLM"
Key="System\CurrentControlSet\Services\SERVICE_NAME\Parameters"
Type="string"
KeyPath="yes"
Name="Application"
Value="APPLICATION_LAUNCHER"
/>
</Component>
<Component Id="nssm_AppDirectory_APPLICATION_LAUNCHER_ID" Guid="*">
<RegistryValue Root="HKLM"
Key="System\CurrentControlSet\Services\SERVICE_NAME\Parameters"
Type="string"
KeyPath="yes"
Name="AppDirectory"
Value="[INSTALLDIR]"
/>
</Component>
</DirectoryRef>
</Include>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<ServiceInstall Type="ownProcess"
Start="auto"
Id="si_APPLICATION_LAUNCHER_ID"
Name="SERVICE_NAME"
Description="SERVICE_DESCRIPTION"
ErrorControl="normal"
Account="LocalSystem"
Vital="yes"
/>
<ServiceControl Start="install"
Stop="both"
Remove="uninstall"
Wait="yes"
Id="sc_APPLICATION_LAUNCHER_ID"
Name="SERVICE_NAME"
/>
</Include>

View File

@ -76,6 +76,10 @@ public class AdditionalLauncher {
return this;
}
final public AdditionalLauncher setLauncherAsService() {
return addRawProperties(LAUNCHER_AS_SERVICE);
}
final public AdditionalLauncher addRawProperties(
Map.Entry<String, String>... v) {
return addRawProperties(List.of(v));
@ -338,7 +342,13 @@ public class AdditionalLauncher {
"--java-options"))).stream().map(
str -> resolveVariables(cmd, str)).toList());
appVerifier.executeAndVerifyOutput();
if (!rawProperties.contains(LAUNCHER_AS_SERVICE)) {
appVerifier.executeAndVerifyOutput();
} else if (!cmd.isPackageUnpacked(String.format(
"Not verifying contents of test output file for [%s] launcher",
launcherPath))) {
appVerifier.verifyOutput();
}
}
public static final class PropertyFile {
@ -394,4 +404,6 @@ public class AdditionalLauncher {
private Boolean withShortcut;
private final static Path NO_ICON = Path.of("");
private final static Map.Entry<String, String> LAUNCHER_AS_SERVICE = Map.entry(
"launcher-as-service", "true");
}

View File

@ -51,6 +51,14 @@ public final class CfgFile {
return value;
}
public String getValueUnchecked(String section, String key) {
Objects.requireNonNull(section);
Objects.requireNonNull(key);
return Optional.ofNullable(data.get(section)).map(v -> v.get(key)).orElse(
null);
}
private CfgFile(Map<String, Map<String, String>> data, String id) {
this.data = data;
this.id = id;

View File

@ -0,0 +1,342 @@
/*
* Copyright (c) 2022, 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.
*
* 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.test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.jpackage.internal.IOUtils;
import static jdk.jpackage.test.Functional.ThrowingConsumer.toConsumer;
import static jdk.jpackage.test.PackageType.LINUX;
import static jdk.jpackage.test.PackageType.MAC_PKG;
import static jdk.jpackage.test.PackageType.WINDOWS;
public final class LauncherAsServiceVerifier {
public final static class Builder {
public Builder setExpectedValue(String v) {
expectedValue = v;
return this;
}
public Builder setLauncherName(String v) {
launcherName = v;
return this;
}
public Builder setAppOutputFileName(String v) {
appOutputFileName = v;
return this;
}
public LauncherAsServiceVerifier create() {
Objects.requireNonNull(expectedValue);
return new LauncherAsServiceVerifier(launcherName, appOutputFileName,
expectedValue);
}
public Builder applyTo(PackageTest pkg) {
create().applyTo(pkg);
return this;
}
private String launcherName;
private String expectedValue;
private String appOutputFileName = "launcher-as-service.txt";
}
public static Builder build() {
return new Builder();
}
private LauncherAsServiceVerifier(String launcherName,
String appOutputFileName,
String expectedArgValue) {
this.expectedValue = expectedArgValue;
this.launcherName = launcherName;
this.appOutputFileName = Path.of(appOutputFileName);
}
public void applyTo(PackageTest pkg) {
if (launcherName == null) {
pkg.forTypes(WINDOWS, () -> {
pkg.addInitializer(cmd -> {
// Remove parameter added to jpackage command line in HelloApp.addTo()
cmd.removeArgument("--win-console");
});
});
applyToMainLauncher(pkg);
} else {
applyToAdditionalLauncher(pkg);
}
}
static void verify(JPackageCommand cmd) {
cmd.verifyIsOfType(SUPPORTED_PACKAGES);
var launcherNames = getLaunchersAsServices(cmd);
launcherNames.forEach(toConsumer(launcherName -> {
verify(cmd, launcherName);
}));
if (WINDOWS.contains(cmd.packageType()) && !cmd.isRuntime()) {
Path serviceInstallerPath = cmd.appLayout().launchersDirectory().resolve(
"service-installer.exe");
if (launcherNames.isEmpty()) {
TKit.assertPathExists(serviceInstallerPath, false);
} else {
TKit.assertFileExists(serviceInstallerPath);
}
}
List<Path> servicesSpecificFiles = new ArrayList<>();
List<Path> servicesSpecificFolders = new ArrayList<>();
if (MAC_PKG.equals(cmd.packageType())) {
servicesSpecificFiles.add(MacHelper.getUninstallCommand(cmd));
if (cmd.isPackageUnpacked()) {
servicesSpecificFolders.add(MacHelper.getServicePlistFilePath(
cmd, null).getParent());
}
} else if (LINUX.contains(cmd.packageType())) {
if (cmd.isPackageUnpacked()) {
servicesSpecificFolders.add(LinuxHelper.getServiceUnitFilePath(
cmd, null).getParent());
}
}
if (launcherNames.isEmpty() || cmd.isRuntime()) {
servicesSpecificFiles.forEach(path -> TKit.assertPathExists(path,
false));
servicesSpecificFolders.forEach(path -> TKit.assertPathExists(path,
false));
} else {
servicesSpecificFiles.forEach(TKit::assertFileExists);
servicesSpecificFolders.forEach(TKit::assertDirectoryExists);
}
}
static void verifyUninstalled(JPackageCommand cmd) {
cmd.verifyIsOfType(SUPPORTED_PACKAGES);
var launcherNames = getLaunchersAsServices(cmd);
for (var launcherName : launcherNames) {
if (TKit.isLinux()) {
TKit.assertPathExists(LinuxHelper.getServiceUnitFilePath(cmd,
launcherName), false);
} else if (TKit.isOSX()) {
TKit.assertPathExists(MacHelper.getServicePlistFilePath(cmd,
launcherName), false);
}
}
if (TKit.isOSX()) {
TKit.assertPathExists(MacHelper.getUninstallCommand(cmd).getParent(),
false);
}
}
static List<String> getLaunchersAsServices(JPackageCommand cmd) {
List<String> launcherNames = new ArrayList<>();
if (cmd.hasArgument("--launcher-as-service")) {
launcherNames.add(null);
}
AdditionalLauncher.forEachAdditionalLauncher(cmd,
Functional.ThrowingBiConsumer.toBiConsumer(
(launcherName, propFilePath) -> {
if (Files.readAllLines(propFilePath).stream().anyMatch(
line -> {
if (line.startsWith(
"launcher-as-service=")) {
return Boolean.parseBoolean(
line.substring(
"launcher-as-service=".length()));
} else {
return false;
}
})) {
launcherNames.add(launcherName);
}
}));
return launcherNames;
}
private boolean canVerifyInstall(JPackageCommand cmd) throws IOException {
String msg = String.format(
"Not verifying contents of test output file [%s] for %s launcher",
appOutputFilePathInitialize(),
Optional.ofNullable(launcherName).orElse("the main"));
if (cmd.isPackageUnpacked(msg) || cmd.isFakeRuntime(msg)) {
return false;
}
var cfgFile = CfgFile.readFromFile(cmd.appLauncherCfgPath(launcherName));
if (!expectedValue.equals(cfgFile.getValueUnchecked("ArgOptions",
"arguments"))) {
TKit.trace(String.format(
"%s because different version of the package is installed",
msg));
return false;
}
return true;
}
private void applyToMainLauncher(PackageTest pkg) {
pkg.addInitializer(cmd -> {
cmd.addArgument("--launcher-as-service");
cmd.addArguments("--arguments",
JPackageCommand.escapeAndJoin(expectedValue));
cmd.addArguments("--java-options", "-Djpackage.test.appOutput="
+ appOutputFilePathInitialize().toString());
cmd.addArguments("--java-options", "-Djpackage.test.noexit=true");
});
pkg.addInstallVerifier(cmd -> {
if (canVerifyInstall(cmd)) {
delayInstallVerify();
HelloApp.assertApp(cmd.appLauncherPath())
.addParam("jpackage.test.appOutput",
appOutputFilePathVerify(cmd).toString())
.addDefaultArguments(expectedValue)
.verifyOutput();
TKit.deleteIfExists(appOutputFileName);
}
});
pkg.addInstallVerifier(cmd -> {
verify(cmd, launcherName);
});
}
private void applyToAdditionalLauncher(PackageTest pkg) {
new AdditionalLauncher(launcherName) {
@Override
protected void verify(JPackageCommand cmd) throws IOException {
if (canVerifyInstall(cmd)) {
delayInstallVerify();
super.verify(cmd);
TKit.deleteIfExists(appOutputFileName);
}
LauncherAsServiceVerifier.verify(cmd, launcherName);
}
}.setLauncherAsService()
.addJavaOptions("-Djpackage.test.appOutput="
+ appOutputFilePathInitialize().toString())
.addJavaOptions("-Djpackage.test.noexit=true")
.addDefaultArguments(expectedValue)
.applyTo(pkg);
}
private static void verify(JPackageCommand cmd, String launcherName) throws
IOException {
if (LINUX.contains(cmd.packageType())) {
verifyLinuxUnitFile(cmd, launcherName);
} else if (MAC_PKG.equals(cmd.packageType())) {
verifyMacDaemonPlistFile(cmd, launcherName);
}
}
private static void verifyLinuxUnitFile(JPackageCommand cmd,
String launcherName) throws IOException {
var serviceUnitFile = LinuxHelper.getServiceUnitFilePath(cmd, launcherName);
TKit.traceFileContents(serviceUnitFile, "unit file");
var installedLauncherPath = cmd.pathToPackageFile(cmd.appLauncherPath(
launcherName));
var execStartValue = (Pattern.compile("\\s").matcher(
installedLauncherPath.toString()).find() ? String.format(
"\"%s\"", installedLauncherPath) : installedLauncherPath);
TKit.assertTextStream("ExecStart=" + execStartValue)
.label("unit file")
.predicate(String::equals)
.apply(Files.readAllLines(serviceUnitFile).stream());
}
private static void verifyMacDaemonPlistFile(JPackageCommand cmd,
String launcherName) throws IOException {
var servicePlistFile = MacHelper.getServicePlistFilePath(cmd, launcherName);
TKit.traceFileContents(servicePlistFile, "property file");
var installedLauncherPath = cmd.pathToPackageFile(cmd.appLauncherPath(
launcherName));
var servicePlist = MacHelper.readPList(servicePlistFile);
var args = servicePlist.queryArrayValue("ProgramArguments");
TKit.assertEquals(1, args.size(),
"Check number of array elements in 'ProgramArguments' property in the property file");
TKit.assertEquals(installedLauncherPath.toString(), args.get(0),
"Check path to launcher in 'ProgramArguments' property in the property file");
var expectedLabel = IOUtils.replaceSuffix(servicePlistFile.getFileName(), "").toString();
TKit.assertEquals(expectedLabel, servicePlist.queryValue("Label"),
"Check value of 'Label' property in the property file");
}
private static void delayInstallVerify() {
// Sleep a bit to let system launch the service
Functional.ThrowingRunnable.toRunnable(() -> Thread.sleep(5 * 1000)).run();
}
private Path appOutputFilePathInitialize() {
final Path dir;
if (TKit.isWindows()) {
dir = Path.of("$ROOTDIR");
} else {
dir = Path.of(System.getProperty("java.io.tmpdir"));
}
return dir.resolve(appOutputFileName);
}
private Path appOutputFilePathVerify(JPackageCommand cmd) {
if (TKit.isWindows()) {
return cmd.appInstallationDirectory().resolve(appOutputFileName);
} else {
return appOutputFilePathInitialize();
}
}
private final String expectedValue;
private final String launcherName;
private final Path appOutputFileName;
final static Set<PackageType> SUPPORTED_PACKAGES = Stream.of(LINUX, WINDOWS,
Set.of(MAC_PKG)).flatMap(x -> x.stream()).collect(Collectors.toSet());
}

View File

@ -23,6 +23,8 @@
package jdk.jpackage.test;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@ -80,6 +82,14 @@ public final class LinuxHelper {
desktopFileName);
}
static Path getServiceUnitFilePath(JPackageCommand cmd, String launcherName) {
cmd.verifyIsOfType(PackageType.LINUX);
return cmd.pathToUnpackedPackageFile(
Path.of("/lib/systemd/system").resolve(getServiceUnitFileName(
getPackageName(cmd),
Optional.ofNullable(launcherName).orElseGet(cmd::name))));
}
static String getBundleName(JPackageCommand cmd) {
cmd.verifyIsOfType(PackageType.LINUX);
@ -668,6 +678,31 @@ public final class LinuxHelper {
return arch;
}
private static String getServiceUnitFileName(String packageName,
String launcherName) {
try {
return getServiceUnitFileName.invoke(null, packageName, launcherName).toString();
} catch (InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
private static Method initGetServiceUnitFileName() {
try {
return Class.forName(
"jdk.jpackage.internal.LinuxLaunchersAsServices").getMethod(
"getServiceUnitFileName", String.class, String.class);
} catch (ClassNotFoundException ex) {
if (TKit.isLinux()) {
throw new RuntimeException(ex);
} else {
return null;
}
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
static final Set<Path> CRITICAL_RUNTIME_FILES = Set.of(Path.of(
"lib/server/libjvm.so"));
@ -677,4 +712,6 @@ public final class LinuxHelper {
// Values grabbed from https://linux.die.net/man/1/xdg-icon-resource
private final static Set<Integer> XDG_CMD_VALID_ICON_SIZES = Set.of(16, 22, 32, 48, 64, 128);
private final static Method getServiceUnitFileName = initGetServiceUnitFileName();
}

View File

@ -24,11 +24,14 @@ package jdk.jpackage.test;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.ArrayList;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -225,10 +228,14 @@ public final class MacHelper {
pkg.uninstallHandler = cmd -> {
cmd.verifyIsOfType(PackageType.MAC_PKG);
Executor.of("sudo", "rm", "-rf")
.addArgument(cmd.appInstallationDirectory())
.execute();
if (Files.exists(getUninstallCommand(cmd))) {
Executor.of("sudo", "/bin/sh",
getUninstallCommand(cmd).toString()).execute();
} else {
Executor.of("sudo", "rm", "-rf")
.addArgument(cmd.appInstallationDirectory())
.execute();
}
};
return pkg;
@ -247,10 +254,34 @@ public final class MacHelper {
cmd.name() + (cmd.isRuntime() ? "" : ".app"));
}
static Path getUninstallCommand(JPackageCommand cmd) {
cmd.verifyIsOfType(PackageType.MAC_PKG);
return cmd.pathToUnpackedPackageFile(Path.of(
"/Library/Application Support", getPackageName(cmd),
"uninstall.command"));
}
static Path getServicePlistFilePath(JPackageCommand cmd, String launcherName) {
cmd.verifyIsOfType(PackageType.MAC_PKG);
return cmd.pathToUnpackedPackageFile(
Path.of("/Library/LaunchDaemons").resolve(
getServicePListFileName(getPackageId(cmd),
Optional.ofNullable(launcherName).orElseGet(
cmd::name))));
}
private static String getPackageName(JPackageCommand cmd) {
return cmd.getArgumentValue("--mac-package-name", cmd::installerName);
}
private static String getPackageId(JPackageCommand cmd) {
return cmd.getArgumentValue("--mac-package-identifier", () -> {
return cmd.getArgumentValue("--main-class", cmd::name, className -> {
return JavaAppDesc.parse(className).packageName();
});
});
}
public static final class PListWrapper {
public String queryValue(String keyName) {
XPath xPath = XPathFactory.newInstance().newXPath();
@ -314,6 +345,34 @@ public final class MacHelper {
return dbf.newDocumentBuilder();
}
private static String getServicePListFileName(String packageName,
String launcherName) {
try {
return getServicePListFileName.invoke(null, packageName,
launcherName).toString();
} catch (InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
private static Method initGetServicePListFileName() {
try {
return Class.forName(
"jdk.jpackage.internal.MacLaunchersAsServices").getMethod(
"getServicePListFileName", String.class, String.class);
} catch (ClassNotFoundException ex) {
if (TKit.isOSX()) {
throw new RuntimeException(ex);
} else {
return null;
}
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
static final Set<Path> CRITICAL_RUNTIME_FILES = Set.of(Path.of(
"Contents/Home/lib/server/libjvm.dylib"));
private final static Method getServicePListFileName = initGetServicePListFileName();
}

View File

@ -324,6 +324,13 @@ public final class PackageTest extends RunnablePackageTest {
return this;
}
public PackageTest addHelloAppInitializer(String javaAppDesc) {
addInitializer(
cmd -> new HelloApp(JavaAppDesc.parse(javaAppDesc)).addTo(cmd),
"HelloApp");
return this;
}
public final static class Group extends RunnablePackageTest {
public Group(PackageTest... tests) {
handlers = Stream.of(tests)
@ -367,12 +374,6 @@ public final class PackageTest extends RunnablePackageTest {
throw new UnsupportedOperationException();
}
private void addHelloAppInitializer(String javaAppDesc) {
addInitializer(
cmd -> new HelloApp(JavaAppDesc.parse(javaAppDesc)).addTo(cmd),
"HelloApp");
}
private List<Consumer<Action>> createPackageTypeHandlers() {
return NATIVE.stream()
.map(type -> {
@ -600,6 +601,11 @@ public final class PackageTest extends RunnablePackageTest {
}
}
if (LauncherAsServiceVerifier.SUPPORTED_PACKAGES.contains(
cmd.packageType())) {
LauncherAsServiceVerifier.verify(cmd);
}
cmd.assertAppLayout();
installVerifiers.forEach(v -> v.accept(cmd));
@ -608,15 +614,24 @@ public final class PackageTest extends RunnablePackageTest {
private void verifyRootCountInUnpackedPackage(JPackageCommand cmd,
Path unpackedDir) {
final boolean withServices = !cmd.isRuntime()
&& !LauncherAsServiceVerifier.getLaunchersAsServices(cmd).isEmpty();
final long expectedRootCount;
if (WINDOWS.contains(cmd.packageType())) {
// On Windows it is always two entries:
// installation home directory and MSI file
expectedRootCount = 2;
} else if (withServices && MAC_PKG.equals(cmd.packageType())) {
expectedRootCount = 2;
} else if (LINUX.contains(cmd.packageType())) {
Set<Path> roots = new HashSet<>();
roots.add(Path.of("/").resolve(Path.of(cmd.getArgumentValue(
"--install-dir", () -> "/opt")).getName(0)));
if (withServices) {
// /lib/systemd
roots.add(Path.of("/lib"));
}
if (cmd.hasArgument("--license-file")) {
switch (cmd.packageType()) {
case LINUX_RPM -> {
@ -673,6 +688,11 @@ public final class PackageTest extends RunnablePackageTest {
TKit.assertPathExists(appInstallDir, false);
}
if (LauncherAsServiceVerifier.SUPPORTED_PACKAGES.contains(
cmd.packageType())) {
LauncherAsServiceVerifier.verifyUninstalled(cmd);
}
uninstallVerifiers.forEach(v -> v.accept(cmd));
}

View File

@ -237,6 +237,13 @@ final public class TKit {
trace("Done");
}
public static void traceFileContents(Path path, String label) throws IOException {
assertFileExists(path);
trace(String.format("Dump [%s] %s...", path, label));
Files.readAllLines(path).forEach(TKit::trace);
trace("Done");
}
public static void createPropertiesFile(Path propsFilename,
Map.Entry<String, String>... props) {
createPropertiesFile(propsFilename, List.of(props));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2022, 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
@ -43,7 +43,7 @@ public class AppImageFileTest {
@Test
public void testIdentity() throws IOException {
Map<String, ? super Object> params = new LinkedHashMap<>();
Map<String, Object> params = new LinkedHashMap<>();
params.put(Arguments.CLIOptions.NAME.getId(), "Foo");
params.put(Arguments.CLIOptions.VERSION.getId(), "2.3");
params.put(Arguments.CLIOptions.DESCRIPTION.getId(), "Duck is the King");
@ -58,7 +58,7 @@ public class AppImageFileTest {
// never create app image at both load/save phases.
// People would edit this file just because they can.
// We should be ready to handle curious minds.
Map<String, ? super Object> params = new LinkedHashMap<>();
Map<String, Object> params = new LinkedHashMap<>();
params.put("invalidParamName", "randomStringValue");
params.put(Arguments.CLIOptions.APPCLASS.getId(), "TestClass");
params.put(Arguments.CLIOptions.MAIN_JAR.getId(), "test.jar");
@ -74,12 +74,13 @@ public class AppImageFileTest {
public void testInavlidXml() throws IOException {
assertInvalid(createFromXml("<foo/>"));
assertInvalid(createFromXml("<jpackage-state/>"));
assertInvalid(createFromXml(JPACKAGE_STATE_OPEN, "</jpackage-state>"));
assertInvalid(createFromXml(
"<jpackage-state>",
JPACKAGE_STATE_OPEN,
"<main-launcher></main-launcher>",
"</jpackage-state>"));
assertInvalid(createFromXml(
"<jpackage-state>",
JPACKAGE_STATE_OPEN,
"<launcher>A</launcher>",
"<launcher>B</launcher>",
"</jpackage-state>"));
@ -88,18 +89,18 @@ public class AppImageFileTest {
@Test
public void testValidXml() throws IOException {
Assert.assertEquals("Foo", (createFromXml(
"<jpackage-state>",
JPACKAGE_STATE_OPEN,
"<main-launcher>Foo</main-launcher>",
"</jpackage-state>")).getLauncherName());
Assert.assertEquals("Boo", (createFromXml(
"<jpackage-state>",
JPACKAGE_STATE_OPEN,
"<main-launcher>Boo</main-launcher>",
"<main-launcher>Bar</main-launcher>",
"</jpackage-state>")).getLauncherName());
var file = createFromXml(
"<jpackage-state>",
JPACKAGE_STATE_OPEN,
"<main-launcher>Foo</main-launcher>",
"<launcher></launcher>",
"</jpackage-state>");
@ -110,7 +111,7 @@ public class AppImageFileTest {
@Test
public void testMainLauncherName() throws IOException {
Map<String, ? super Object> params = new LinkedHashMap<>();
Map<String, Object> params = new LinkedHashMap<>();
params.put("name", "Foo");
params.put("description", "Duck App Description");
AppImageFile aif = create(params);
@ -120,14 +121,14 @@ public class AppImageFileTest {
@Test
public void testAddLaunchers() throws IOException {
Map<String, ? super Object> params = new LinkedHashMap<>();
List<Map<String, ? super Object>> launchersAsMap = new ArrayList<>();
Map<String, Object> params = new LinkedHashMap<>();
List<Map<String, Object>> launchersAsMap = new ArrayList<>();
Map<String, ? super Object> addLauncher2Params = new LinkedHashMap();
Map<String, Object> addLauncher2Params = new LinkedHashMap<>();
addLauncher2Params.put("name", "Launcher2Name");
launchersAsMap.add(addLauncher2Params);
Map<String, ? super Object> addLauncher3Params = new LinkedHashMap();
Map<String, Object> addLauncher3Params = new LinkedHashMap<>();
addLauncher3Params.put("name", "Launcher3Name");
launchersAsMap.add(addLauncher3Params);
@ -138,7 +139,7 @@ public class AppImageFileTest {
List<AppImageFile.LauncherInfo> addLaunchers = aif.getAddLaunchers();
Assert.assertEquals(2, addLaunchers.size());
List<String> names = new ArrayList<String>();
List<String> names = new ArrayList<>();
names.add(addLaunchers.get(0).getName());
names.add(addLaunchers.get(1).getName());
@ -163,7 +164,7 @@ public class AppImageFileTest {
path.toFile().mkdirs();
Files.delete(path);
ArrayList<String> data = new ArrayList();
List<String> data = new ArrayList<>();
data.add("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>");
data.addAll(List.of(xmlData));
@ -174,4 +175,8 @@ public class AppImageFileTest {
return image;
}
private final static String JPACKAGE_STATE_OPEN = String.format(
"<jpackage-state platform=\"%s\" version=\"%s\">",
AppImageFile.getPlatform(), AppImageFile.getVersion());
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2022, 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.
*
* 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;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class EnquoterTest {
@Test
public void testForShellLiterals() {
var enquoter = Enquoter.forShellLiterals();
assertEquals(null, "''", enquoter.applyTo(""));
assertEquals(null, "'foo'", enquoter.applyTo("foo"));
assertEquals(null, "' foo '", enquoter.applyTo(" foo "));
assertEquals(null, "'foo bar'", enquoter.applyTo("foo bar"));
assertEquals(null, "'foo\\' bar'", enquoter.applyTo("foo' bar"));
}
@Test
public void testForPropertyValues() {
var enquoter = Enquoter.forPropertyValues();
assertEquals(null, "", enquoter.applyTo(""));
assertEquals(null, "foo", enquoter.applyTo("foo"));
assertEquals(null, "\" foo \"", enquoter.applyTo(" foo "));
assertEquals(null, "\"foo bar\"", enquoter.applyTo("foo bar"));
assertEquals(null, "\"foo' bar\"", enquoter.applyTo("foo' bar"));
}
}

View File

@ -0,0 +1,174 @@
/*
* Copyright (c) 2022, 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.
*
* 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.
*/
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.stream.Stream;
import jdk.jpackage.test.PackageTest;
import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.test.JPackageCommand;
import jdk.jpackage.test.JavaTool;
import jdk.jpackage.test.LauncherAsServiceVerifier;
import static jdk.jpackage.test.PackageType.MAC_DMG;
import static jdk.jpackage.test.PackageType.WINDOWS;
import jdk.jpackage.test.RunnablePackageTest;
import jdk.jpackage.test.TKit;
/**
* Launcher as service packaging test. Output of the test should be
* servicetest*.* and updateservicetest*.* package bundles.
*/
/*
* @test
* @summary Launcher as service packaging test
* @library ../helpers
* @key jpackagePlatformPackage
* @build jdk.jpackage.test.*
* @modules jdk.jpackage/jdk.jpackage.internal
* @compile ServiceTest.java
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=ServiceTest
*/
public class ServiceTest {
public ServiceTest() {
if (TKit.isWindows()) {
final String propName = "jpackage.test.ServiceTest.service-installer";
String serviceInstallerExec = System.getProperty(propName);
if (serviceInstallerExec == null) {
if (Stream.of(RunnablePackageTest.Action.CREATE,
RunnablePackageTest.Action.INSTALL).allMatch(
RunnablePackageTest::hasAction)) {
TKit.throwSkippedException(String.format(
"%s system property not set", propName));
} else {
// Use cmd.exe as a stub as the output packages will not be
// created and installed in the test run
serviceInstallerExec = Optional.ofNullable(System.getenv(
"COMSPEC")).orElseGet(() -> {
return JavaTool.JAVA.getPath().toString();
});
TKit.trace(
String.format("Using [%s] as a service installer",
serviceInstallerExec));
}
}
winServiceInstaller = Path.of(serviceInstallerExec);
} else {
winServiceInstaller = null;
}
}
@Test
public void test() throws Throwable {
var testInitializer = createTestInitializer();
var pkg = createPackageTest().addHelloAppInitializer("com.foo.ServiceTest");
LauncherAsServiceVerifier.build().setExpectedValue("A1").applyTo(pkg);
testInitializer.applyTo(pkg);
pkg.run();
}
@Test
public void testUpdate() throws Throwable {
var testInitializer = createTestInitializer().setUpgradeCode(
"4050AD4D-D6CC-452A-9CB0-58E5FA8C410F");
// Package name will be used as package ID on macOS. Keep it the same for
// both packages to allow update installation.
final String packageName = "com.bar";
var pkg = createPackageTest()
.addHelloAppInitializer(String.join(".", packageName, "Hello"))
.disablePackageUninstaller();
testInitializer.applyTo(pkg);
LauncherAsServiceVerifier.build().setExpectedValue("Default").applyTo(pkg);
var pkg2 = createPackageTest()
.addHelloAppInitializer(String.join(".", packageName, "Bye"))
.addInitializer(cmd -> {
cmd.addArguments("--app-version", "2.0");
});
testInitializer.applyTo(pkg2);
var builder = LauncherAsServiceVerifier.build()
.setLauncherName("foo")
.setAppOutputFileName("foo-launcher-as-service.txt");
builder.setExpectedValue("Foo").applyTo(pkg);
builder.setExpectedValue("Foo2").applyTo(pkg2);
builder.setExpectedValue("Bar")
.setLauncherName("bar")
.setAppOutputFileName("bar-launcher-as-service.txt")
.applyTo(pkg2);
new PackageTest.Group(pkg, pkg2).run();
}
private final class TestInitializer {
TestInitializer setUpgradeCode(String v) {
upgradeCode = v;
return this;
}
void applyTo(PackageTest test) throws IOException {
if (winServiceInstaller != null) {
var resourceDir = TKit.createTempDirectory("resource-dir");
Files.copy(winServiceInstaller, resourceDir.resolve(
"service-installer.exe"));
test.forTypes(WINDOWS, () -> test.addInitializer(cmd -> {
cmd.addArguments("--resource-dir", resourceDir);
}));
}
if (upgradeCode != null) {
test.forTypes(WINDOWS, () -> test.addInitializer(cmd -> {
cmd.addArguments("--win-upgrade-uuid", upgradeCode);
}));
}
}
private String upgradeCode;
}
private TestInitializer createTestInitializer() {
return new TestInitializer();
}
private static PackageTest createPackageTest() {
return new PackageTest()
.excludeTypes(MAC_DMG) // DMG not supported
.addInitializer(JPackageCommand::setInputToEmptyDirectory);
}
private final Path winServiceInstaller;
}