diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java index 0e027ebbf417f..6a5b200f8f304 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -63,9 +63,10 @@ final class DesktopIntegration extends ShellCustomAction { 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 String COMMON_SCRIPTS = "COMMON_SCRIPTS"; private static final List REPLACEMENT_STRING_IDS = List.of( - COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS); + COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS, COMMON_SCRIPTS); private DesktopIntegration(PlatformPackage thePackage, Map params, @@ -229,8 +230,6 @@ protected Map createImpl() throws IOException { shellCommands.applyTo(data); } - boolean needCleanupScripts = !associations.isEmpty(); - // Take care of additional launchers if there are any. // Process every additional launcher as the main application launcher. // Collect shell commands to install/uninstall integration with desktop @@ -241,10 +240,6 @@ protected Map createImpl() throws IOException { List uninstallShellCmds = new ArrayList<>(Arrays.asList( data.get(COMMANDS_UNINSTALL))); for (var integration: nestedIntegrations) { - if (!integration.associations.isEmpty()) { - needCleanupScripts = true; - } - Map launcherData = integration.create(); installShellCmds.add(launcherData.get(COMMANDS_INSTALL)); @@ -254,10 +249,8 @@ protected Map createImpl() throws IOException { data.put(COMMANDS_INSTALL, stringifyShellCommands(installShellCmds)); data.put(COMMANDS_UNINSTALL, stringifyShellCommands(uninstallShellCmds)); - if (needCleanupScripts) { - // Pull in desktop_utils.sh scrips library. - data.put(SCRIPTS, stringifyTextFile("desktop_utils.sh")); - } + data.put(COMMON_SCRIPTS, stringifyTextFile("common_utils.sh")); + data.put(SCRIPTS, stringifyTextFile("desktop_utils.sh")); return data; } @@ -295,7 +288,9 @@ private class ShellCommands { registerDesktopFileCmd = String.join(" ", "xdg-desktop-menu", "install", desktopFile.installPath().toString()); - unregisterDesktopFileCmd = String.join(" ", "xdg-desktop-menu", + unregisterDesktopFileCmd = String.join(" ", + "do_if_file_belongs_to_single_package", desktopFile. + installPath().toString(), "xdg-desktop-menu", "uninstall", desktopFile.installPath().toString()); } @@ -303,8 +298,10 @@ void setFileAssociations() { registerFileAssociationsCmd = String.join(" ", "xdg-mime", "install", mimeInfoFile.installPath().toString()); - unregisterFileAssociationsCmd = String.join(" ", "xdg-mime", - "uninstall", mimeInfoFile.installPath().toString()); + unregisterFileAssociationsCmd = String.join(" ", + "do_if_file_belongs_to_single_package", mimeInfoFile. + installPath().toString(), "xdg-mime", "uninstall", + mimeInfoFile.installPath().toString()); // // Add manual cleanup of system files to get rid of @@ -320,26 +317,26 @@ void setFileAssociations() { // of non-existing desktop file. // String cleanUpCommand = String.join(" ", - "uninstall_default_mime_handler", - desktopFile.installPath().getFileName().toString(), - String.join(" ", getMimeTypeNamesFromFileAssociations())); + "do_if_file_belongs_to_single_package", desktopFile. + installPath().toString(), + "desktop_uninstall_default_mime_handler", desktopFile. + installPath().getFileName().toString(), String.join( + " ", getMimeTypeNamesFromFileAssociations())); unregisterFileAssociationsCmd = stringifyShellCommands( unregisterFileAssociationsCmd, cleanUpCommand); } - void addIcon(String mimeType, Path iconFile) { - addIcon(mimeType, iconFile, getSquareSizeOfImage(iconFile.toFile())); - } - void addIcon(String mimeType, Path iconFile, int imgSize) { imgSize = normalizeIconSize(imgSize); final String dashMime = mimeType.replace('/', '-'); registerIconCmds.add(String.join(" ", "xdg-icon-resource", "install", "--context", "mimetypes", "--size", Integer.toString(imgSize), iconFile.toString(), dashMime)); - unregisterIconCmds.add(String.join(" ", "xdg-icon-resource", - "uninstall", dashMime, "--size", Integer.toString(imgSize))); + unregisterIconCmds.add(String.join(" ", + "do_if_file_belongs_to_single_package", iconFile.toString(), + "xdg-icon-resource", "uninstall", dashMime, "--size", + Integer.toString(imgSize))); } void applyTo(Map data) { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java index 10a655538507e..9c5b272b4312e 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -26,6 +26,8 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import static jdk.jpackage.internal.OverridableResource.createResource; @@ -42,10 +44,24 @@ private LinuxLaunchersAsServices(PlatformPackage thePackage, }); } + @Override + protected List replacementStringIds() { + return LINUX_REPLACEMENT_STRING_IDS; + } + + @Override + protected Map createImpl() throws IOException { + var data = super.createImpl(); + if (!data.isEmpty()) { + data.put(COMMON_SCRIPTS, stringifyTextFile("common_utils.sh")); + } + return data; + } + static ShellCustomAction create(PlatformPackage thePackage, Map params) throws IOException { if (StandardBundlerParam.isRuntimeInstaller(params)) { - return ShellCustomAction.nop(REPLACEMENT_STRING_IDS); + return ShellCustomAction.nop(LINUX_REPLACEMENT_STRING_IDS); } return new LinuxLaunchersAsServices(thePackage, params); } @@ -84,4 +100,16 @@ Path descriptorFilePath(Path root) { private final static List REQUIRED_PACKAGES = List.of("systemd", "coreutils" /* /usr/bin/wc */, "grep"); + + private static final String COMMON_SCRIPTS = "COMMON_SCRIPTS"; + + private static final List LINUX_REPLACEMENT_STRING_IDS; + + static { + ArrayList buf = new ArrayList<>(); + buf.addAll(UnixLaunchersAsServices.REPLACEMENT_STRING_IDS); + buf.add(COMMON_SCRIPTS); + + LINUX_REPLACEMENT_STRING_IDS = Collections.unmodifiableList(buf); + } } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java index 374f88878e82d..b510ed414118b 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -155,7 +155,8 @@ public final Path execute(Map params, Map data = createDefaultReplacementData(params); for (var ca : customActions) { - data.putAll(ca.instance.create()); + ShellCustomAction.mergeReplacementData(data, ca.instance. + create()); } data.putAll(createReplacementData(params)); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/common_utils.sh b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/common_utils.sh new file mode 100644 index 0000000000000..31a81bb5fb0bf --- /dev/null +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/common_utils.sh @@ -0,0 +1,23 @@ +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 +} + + +do_if_file_belongs_to_single_package () +{ + local file="$1" + shift + + if file_belongs_to_single_package "$file"; then + "$@" + fi +} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/desktop_utils.sh b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/desktop_utils.sh index 1ed96076282c4..7c243b2b5c5bc 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/desktop_utils.sh +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/desktop_utils.sh @@ -2,7 +2,7 @@ # Remove $1 desktop file from the list of default handlers for $2 mime type # in $3 file dumping output to stdout. # -_filter_out_default_mime_handler () +desktop_filter_out_default_mime_handler () { local defaults_list="$3" @@ -50,7 +50,7 @@ EOF # in $1 file. # Result is saved in $1 file. # -_uninstall_default_mime_handler () +desktop_uninstall_default_mime_handler_0 () { local defaults_list=$1 shift @@ -66,20 +66,20 @@ _uninstall_default_mime_handler () local v local update= for mime in "$@"; do - _filter_out_default_mime_handler "$desktop_file" "$mime" "$tmpfile1" > "$tmpfile2" + desktop_filter_out_default_mime_handler "$desktop_file" "$mime" "$tmpfile1" > "$tmpfile2" v="$tmpfile2" tmpfile2="$tmpfile1" tmpfile1="$v" if ! diff -q "$tmpfile1" "$tmpfile2" > /dev/null; then update=yes - trace Remove $desktop_file default handler for $mime mime type from $defaults_list file + desktop_trace Remove $desktop_file default handler for $mime mime type from $defaults_list file fi done if [ -n "$update" ]; then cat "$tmpfile1" > "$defaults_list" - trace "$defaults_list" file updated + desktop_trace "$defaults_list" file updated fi rm -f "$tmpfile1" "$tmpfile2" @@ -90,15 +90,15 @@ _uninstall_default_mime_handler () # Remove $1 desktop file from the list of default handlers for $@ mime types # in all known system defaults lists. # -uninstall_default_mime_handler () +desktop_uninstall_default_mime_handler () { for f in /usr/share/applications/defaults.list /usr/local/share/applications/defaults.list; do - _uninstall_default_mime_handler "$f" "$@" + desktop_uninstall_default_mime_handler_0 "$f" "$@" done } -trace () +desktop_trace () { echo "$@" } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/services_utils.sh b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/services_utils.sh index 39ea958d1a427..730137104cf4f 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/services_utils.sh +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/services_utils.sh @@ -23,16 +23,3 @@ unregister_services () 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 -} diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst index 16288e4f34894..489c161377297 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.preinst @@ -14,12 +14,13 @@ set -e # the debian-policy package package_type=deb +COMMON_SCRIPTS LAUNCHER_AS_SERVICE_SCRIPTS case "$1" in install|upgrade) if [ -n "$2" ]; then - true; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL + :; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL fi ;; diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm index a772e309aceae..b5ed447ad69e2 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.prerm @@ -18,6 +18,7 @@ set -e package_type=deb +COMMON_SCRIPTS DESKTOP_SCRIPTS LAUNCHER_AS_SERVICE_SCRIPTS diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec index 45e5e86126bc6..7918ce6bd884f 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.spec @@ -83,13 +83,15 @@ LAUNCHER_AS_SERVICE_COMMANDS_INSTALL %pre package_type=rpm +COMMON_SCRIPTS LAUNCHER_AS_SERVICE_SCRIPTS -if [ "$1" = 2 ]; then - true; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL +if [ "$1" -gt 1 ]; then + :; LAUNCHER_AS_SERVICE_COMMANDS_UNINSTALL fi %preun package_type=rpm +COMMON_SCRIPTS DESKTOP_SCRIPTS LAUNCHER_AS_SERVICE_SCRIPTS DESKTOP_COMMANDS_UNINSTALL diff --git a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/ShellCustomAction.java b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/ShellCustomAction.java index 8b37377058c10..64afc95516da8 100644 --- a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/ShellCustomAction.java +++ b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/ShellCustomAction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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,6 +34,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -74,6 +75,26 @@ protected Map createImpl() throws IOException { }; } + static void mergeReplacementData(Map target, + Map newValues) { + Objects.requireNonNull(target); + Objects.requireNonNull(newValues); + + for (var kvp : newValues.entrySet()) { + String newValue = kvp.getValue(); + String existingValue = target.putIfAbsent(kvp.getKey(), newValue); + if (existingValue != null) { + if (existingValue.isEmpty()) { + target.replace(kvp.getKey(), newValue); + } else if (!newValue.isEmpty() && !newValue. + equals(existingValue)) { + throw new IllegalArgumentException(String.format( + "Key [%s] value mismatch", kvp.getKey())); + } + } + } + } + protected static String stringifyShellCommands(String... commands) { return stringifyShellCommands(Arrays.asList(commands)); } diff --git a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java index 59f4dacca57b5..1e90c8371b10c 100644 --- a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java +++ b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -65,12 +65,12 @@ final List requiredPackages() { } @Override - final protected List replacementStringIds() { - return List.of(COMMANDS_INSTALL, COMMANDS_UNINSTALL, SCRIPTS); + protected List replacementStringIds() { + return REPLACEMENT_STRING_IDS; } @Override - final protected Map createImpl() throws IOException { + protected Map createImpl() throws IOException { Map data = new HashMap<>(); if (launchers.isEmpty()) { @@ -87,11 +87,7 @@ final protected Map createImpl() throws IOException { Collectors.joining(" "))); }; - try { - data.put(SCRIPTS, stringifyTextFile("services_utils.sh")); - } catch (IOException ex) { - throw new RuntimeException(ex); - } + data.put(SCRIPTS, stringifyTextFile("services_utils.sh")); data.put(COMMANDS_INSTALL, strigifier.apply("register_services")); data.put(COMMANDS_UNINSTALL, strigifier.apply("unregister_services")); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java index d2d7ac9f66ee9..6c388ac77ffb9 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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,11 @@ final public AdditionalLauncher addJavaOptions(String... v) { return this; } + final public AdditionalLauncher setVerifyUninstalled(boolean value) { + verifyUninstalled = value; + return this; + } + final public AdditionalLauncher setLauncherAsService() { return addRawProperties(LAUNCHER_AS_SERVICE); } @@ -141,6 +146,13 @@ final public void applyTo(JPackageCommand cmd) { final public void applyTo(PackageTest test) { test.addInitializer(this::initialize); test.addInstallVerifier(this::verify); + if (verifyUninstalled) { + test.addUninstallVerifier(this::verifyUninstalled); + } + } + + final public void verifyRemovedInUpgrade(PackageTest test) { + test.addInstallVerifier(this::verifyUninstalled); } static void forEachAdditionalLauncher(JPackageCommand cmd, @@ -318,10 +330,40 @@ private void verifyDescription(JPackageCommand cmd) throws IOException { } } + private void verifyInstalled(JPackageCommand cmd, boolean installed) throws IOException { + if (TKit.isLinux() && !cmd.isImagePackageType() && !cmd. + isPackageUnpacked(String.format( + "Not verifying package and system .desktop files for [%s] launcher", + cmd.appLauncherPath(name)))) { + Path packageDesktopFile = LinuxHelper.getDesktopFile(cmd, name); + Path systemDesktopFile = LinuxHelper.getSystemDesktopFilesFolder(). + resolve(packageDesktopFile.getFileName()); + if (Files.exists(packageDesktopFile) && installed) { + TKit.assertFileExists(systemDesktopFile); + TKit.assertStringListEquals(Files.readAllLines( + packageDesktopFile), + Files.readAllLines(systemDesktopFile), String.format( + "Check [%s] and [%s] files are equal", + packageDesktopFile, + systemDesktopFile)); + } else { + TKit.assertPathExists(packageDesktopFile, false); + TKit.assertPathExists(systemDesktopFile, false); + } + } + } + + protected void verifyUninstalled(JPackageCommand cmd) throws IOException { + verifyInstalled(cmd, false); + Path launcherPath = cmd.appLauncherPath(name); + TKit.assertPathExists(launcherPath, false); + } + protected void verify(JPackageCommand cmd) throws IOException { verifyIcon(cmd); verifyShortcuts(cmd); verifyDescription(cmd); + verifyInstalled(cmd, true); Path launcherPath = cmd.appLauncherPath(name); @@ -394,6 +436,7 @@ private static String resolveVariables(JPackageCommand cmd, String str) { return str; } + private boolean verifyUninstalled; private List javaOptions; private List defaultArguments; private Path icon; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java index a695a3b693d5f..b7ebc4939ba38 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherAsServiceVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, 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 @@ -23,6 +23,7 @@ package jdk.jpackage.test; import java.io.IOException; +import java.nio.file.FileSystemException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -30,6 +31,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -58,10 +60,16 @@ public Builder setAppOutputFileName(String v) { return this; } + public Builder setAdditionalLauncherCallback(Consumer v) { + additionalLauncherCallback = v; + return this; + } + public LauncherAsServiceVerifier create() { Objects.requireNonNull(expectedValue); return new LauncherAsServiceVerifier(launcherName, appOutputFileName, - expectedValue); + expectedValue, + launcherName != null ? additionalLauncherCallback : null); } public Builder applyTo(PackageTest pkg) { @@ -72,6 +80,7 @@ public Builder applyTo(PackageTest pkg) { private String launcherName; private String expectedValue; private String appOutputFileName = "launcher-as-service.txt"; + private Consumer additionalLauncherCallback; } public static Builder build() { @@ -80,10 +89,12 @@ public static Builder build() { private LauncherAsServiceVerifier(String launcherName, String appOutputFileName, - String expectedArgValue) { + String expectedArgValue, + Consumer additionalLauncherCallback) { this.expectedValue = expectedArgValue; this.launcherName = launcherName; this.appOutputFileName = Path.of(appOutputFileName); + this.additionalLauncherCallback = additionalLauncherCallback; } public void applyTo(PackageTest pkg) { @@ -233,7 +244,7 @@ private void applyToMainLauncher(PackageTest pkg) { outputFilePath.toString()) .addDefaultArguments(expectedValue) .verifyOutput(); - TKit.deleteIfExists(outputFilePath); + deleteOutputFile(outputFilePath); } }); pkg.addInstallVerifier(cmd -> { @@ -242,13 +253,13 @@ private void applyToMainLauncher(PackageTest pkg) { } private void applyToAdditionalLauncher(PackageTest pkg) { - new AdditionalLauncher(launcherName) { + AdditionalLauncher al = new AdditionalLauncher(launcherName) { @Override protected void verify(JPackageCommand cmd) throws IOException { if (canVerifyInstall(cmd)) { delayInstallVerify(); super.verify(cmd); - TKit.deleteIfExists(appOutputFilePathVerify(cmd)); + deleteOutputFile(appOutputFilePathVerify(cmd)); } LauncherAsServiceVerifier.verify(cmd, launcherName); } @@ -256,8 +267,26 @@ protected void verify(JPackageCommand cmd) throws IOException { .addJavaOptions("-Djpackage.test.appOutput=" + appOutputFilePathInitialize().toString()) .addJavaOptions("-Djpackage.test.noexit=true") - .addDefaultArguments(expectedValue) - .applyTo(pkg); + .addDefaultArguments(expectedValue); + + Optional.ofNullable(additionalLauncherCallback).ifPresent(v -> v.accept(al)); + + al.applyTo(pkg); + } + + private static void deleteOutputFile(Path file) throws IOException { + try { + TKit.deleteIfExists(file); + } catch (FileSystemException ex) { + if (TKit.isLinux() || TKit.isOSX()) { + // Probably "Operation no permitted" error. Try with "sudo" as the + // file is created by a launcher started under root account. + Executor.of("sudo", "rm", "-f").addArgument(file.toString()). + execute(); + } else { + throw ex; + } + } } private static void verify(JPackageCommand cmd, String launcherName) throws @@ -337,6 +366,7 @@ private Path appOutputFilePathVerify(JPackageCommand cmd) { private final String expectedValue; private final String launcherName; private final Path appOutputFileName; + private final Consumer additionalLauncherCallback; final static Set SUPPORTED_PACKAGES = Stream.of(LINUX, WINDOWS, Set.of(MAC_PKG)).flatMap(x -> x.stream()).collect(Collectors.toSet()); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java index 44212587f7dd9..bf5ced09bc771 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, 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 @@ -464,7 +464,7 @@ static void initFileAssociationsTestFile(Path testFile) { } } - private static Path getSystemDesktopFilesFolder() { + static Path getSystemDesktopFilesFolder() { return Stream.of("/usr/share/applications", "/usr/local/share/applications").map(Path::of).filter(dir -> { return Files.exists(dir.resolve("defaults.list")); @@ -558,12 +558,17 @@ private static void verifyIconInScriptlet(Scriptlet scriptletType, final String xdgCmdName = "xdg-icon-resource"; Stream scriptletBodyStream = scriptletBody.stream() - .filter(str -> str.startsWith(xdgCmdName)) .filter(str -> Pattern.compile( "\\b" + dashMime + "\\b").matcher(str).find()); if (scriptletType == Scriptlet.PostInstall) { + scriptletBodyStream = scriptletBodyStream.filter(str -> str. + startsWith(xdgCmdName)); scriptletBodyStream = scriptletBodyStream.filter(str -> List.of( str.split("\\s+")).contains(iconPathInPackage.toString())); + } else { + scriptletBodyStream = scriptletBodyStream.filter(str -> str. + contains(xdgCmdName)).filter(str -> str.startsWith( + "do_if_file_belongs_to_single_package")); } scriptletBodyStream.peek(xdgCmd -> { diff --git a/test/jdk/tools/jpackage/linux/ServiceAndDesktopTest.java b/test/jdk/tools/jpackage/linux/ServiceAndDesktopTest.java new file mode 100644 index 0000000000000..b047ce3e8f427 --- /dev/null +++ b/test/jdk/tools/jpackage/linux/ServiceAndDesktopTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023, 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.nio.file.Path; +import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.LauncherAsServiceVerifier; +import jdk.jpackage.test.TKit; + +/** + * Test how services and desktop integration align together in the same package. + * On Linux these features share common code in custom actions (common_utils.sh). + * Test correctness of integration of this code. + * + * The test is not intended to be executed by SQE. It is for internal use only + */ + +/* + * @test + * @summary jpackage with desktop integration and services on Linux + * @library ../helpers + * @key jpackagePlatformPackage + * @requires jpackage.test.SQETest == null + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.jpackage/jdk.jpackage.internal + * @compile ServiceAndDesktopTest.java + * @run main/othervm/timeout=720 jdk.jpackage.test.Main + * --jpt-run=ServiceAndDesktopTest + */ + +public class ServiceAndDesktopTest { + + @Test + public static void test() { + var pkg = new PackageTest() + .configureHelloApp() + .addBundleDesktopIntegrationVerifier(true) + .addInitializer(cmd -> { + // Want a .desktop file for the main launcher + cmd.addArguments("--icon", GOLDEN_ICON.toString()); + }); + LauncherAsServiceVerifier.build().setLauncherName("foo"). + setExpectedValue("Fun").setAdditionalLauncherCallback(al -> { + // Don't want .desktop file for service launcher + al.setNoIcon(); + }).applyTo(pkg); + pkg.run(); + } + + private final static Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( + "resources", "icon" + TKit.ICON_SUFFIX)); +} diff --git a/test/jdk/tools/jpackage/linux/UpgradeTest.java b/test/jdk/tools/jpackage/linux/UpgradeTest.java new file mode 100644 index 0000000000000..1a4234db86f62 --- /dev/null +++ b/test/jdk/tools/jpackage/linux/UpgradeTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023, 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.util.Map; +import java.nio.file.Path; +import jdk.jpackage.test.AdditionalLauncher; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; + +/* + * @test + * @summary Linux upgrade testing + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @requires (os.family == "linux") + * @modules jdk.jpackage/jdk.jpackage.internal + * @compile UpgradeTest.java + * @run main/othervm/timeout=360 jdk.jpackage.test.Main + * --jpt-run=UpgradeTest + */ +public class UpgradeTest { + + @Test + public void testDesktopFiles() { + // Create two packages with the same name but different versions. + // The first will have `launcherA`, and `launcherB` additional launchers. + // The second will have `launcherB`, and `launcherC` additional launchers. + // Launchers are configured in a way to have correpsonding .desktop files. + // These files will be installed in system directories. + // After the upgrade `launcherA`-related files must be deleted and + // `launcherB`-related files from the first package must be replaced with + // the files from the second package. + // Checks that correct files are installed in system directories + // encapsulated in AdditionalLauncher class. + + var pkg = createPackageTest().disablePackageUninstaller(); + + var alA = createAdditionalLauncher("launcherA"); + + alA.applyTo(pkg); + createAdditionalLauncher("launcherB").addRawProperties(Map.entry( + "description", "Foo")).applyTo(pkg); + + var pkg2 = createPackageTest().addInitializer(cmd -> { + cmd.addArguments("--app-version", "2.0"); + }); + + alA.verifyRemovedInUpgrade(pkg2); + createAdditionalLauncher("launcherB").addRawProperties(Map.entry( + "description", "Bar")).applyTo(pkg2); + createAdditionalLauncher("launcherC").applyTo(pkg2); + + new PackageTest.Group(pkg, pkg2).run(); + } + + private static PackageTest createPackageTest() { + return new PackageTest().configureHelloApp().addInitializer( + JPackageCommand::setInputToEmptyDirectory).addInitializer( + JPackageCommand::setFakeRuntime). + addBundleDesktopIntegrationVerifier(true); + } + + private static AdditionalLauncher createAdditionalLauncher(String name) { + // Configure additionl launcher in a way to trigger jpackage create + // corresponding .desktop file. + return new AdditionalLauncher(name).setIcon(GOLDEN_ICON); + } + + private final static Path GOLDEN_ICON = TKit.TEST_SRC_ROOT.resolve(Path.of( + "resources", "icon" + TKit.ICON_SUFFIX)); +}