8236127: Use value of --icon CLI option to set icon for exe installers

Reviewed-by: almatvee, herrick
This commit is contained in:
Alexey Semenyuk 2021-04-08 15:44:11 +00:00
parent 81d35e439d
commit 5bd6c74547
8 changed files with 225 additions and 34 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -70,6 +70,7 @@ class ValidOptions {
options.put(CLIOptions.VENDOR.getId(), USE.ALL);
options.put(CLIOptions.COPYRIGHT.getId(), USE.ALL);
options.put(CLIOptions.PACKAGE_TYPE.getId(), USE.ALL);
options.put(CLIOptions.ICON.getId(), USE.ALL);
options.put(CLIOptions.INPUT.getId(), USE.LAUNCHER);
options.put(CLIOptions.MODULE.getId(), USE.LAUNCHER);
@ -77,7 +78,6 @@ class ValidOptions {
options.put(CLIOptions.ADD_MODULES.getId(), USE.LAUNCHER);
options.put(CLIOptions.MAIN_JAR.getId(), USE.LAUNCHER);
options.put(CLIOptions.APPCLASS.getId(), USE.LAUNCHER);
options.put(CLIOptions.ICON.getId(), USE.LAUNCHER);
options.put(CLIOptions.ARGUMENTS.getId(), USE.LAUNCHER);
options.put(CLIOptions.JAVA_OPTIONS.getId(), USE.LAUNCHER);
options.put(CLIOptions.ADD_LAUNCHER.getId(), USE.LAUNCHER);

View File

@ -70,6 +70,9 @@ Generic Options:\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 <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\
@ -120,9 +123,6 @@ Generic Options:\n\
\ --strip-native-commands.\n\
\n\
\Options for creating the application image:\n\
\ --icon <icon file path>\n\
\ Path of the icon of the application package\n\
\ (absolute path or relative to the current directory)\n\
\ --input -i <input path>\n\
\ Path of the input directory that contains the files to be packaged\n\
\ (absolute path or relative to the current directory)\n\

View File

@ -70,6 +70,9 @@ Generic Options:\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 <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\
@ -120,9 +123,6 @@ Generic Options:\n\
\ --strip-native-commands.\n\
\n\
\Options for creating the application image:\n\
\ --icon <icon file path>\n\
\ Path of the icon of the application package\n\
\ (absolute path or relative to the current directory)\n\
\ --input -i <input path>\n\
\ Path of the input directory that contains the files to be packaged\n\
\ (absolute path or relative to the current directory)\n\

View File

@ -70,6 +70,9 @@ Generic Options:\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 <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\
@ -120,9 +123,6 @@ Generic Options:\n\
\ --strip-native-commands.\n\
\n\
\Options for creating the application image:\n\
\ --icon <icon file path>\n\
\ Path of the icon of the application package\n\
\ (absolute path or relative to the current directory)\n\
\ --input -i <input path>\n\
\ Path of the input directory that contains the files to be packaged\n\
\ (absolute path or relative to the current directory)\n\

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 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
@ -38,6 +38,7 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.function.Supplier;
import static jdk.jpackage.internal.OverridableResource.createResource;
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
import static jdk.jpackage.internal.StandardBundlerParam.COPYRIGHT;
@ -45,6 +46,7 @@ import static jdk.jpackage.internal.StandardBundlerParam.DESCRIPTION;
import static jdk.jpackage.internal.StandardBundlerParam.TEMP_ROOT;
import static jdk.jpackage.internal.StandardBundlerParam.VENDOR;
import static jdk.jpackage.internal.StandardBundlerParam.VERSION;
import static jdk.jpackage.internal.WindowsAppImageBuilder.ICON_ICO;
final class ExecutableRebrander {
@ -63,34 +65,38 @@ final class ExecutableRebrander {
void rebrandInstaller(Map<String, ? super Object> params, Path target)
throws IOException {
if (!target.isAbsolute()) {
rebrandInstaller(params, target.toAbsolutePath());
return;
}
rebrandExecutable(params, target, (resourceLock) -> {
rebrandProperties(resourceLock, createResource(
INSTALLER_PROPERTIES_TEMPLATE, params).setPublicName(
INSTALLER_PROPERTIES_RESOURE_DIR_ID),
createSubstituteData(params), target);
});
Path icon = ICON_ICO.fetchFrom(params);
rebrandExecutable(params, icon, () -> {
return createResource(INSTALLER_PROPERTIES_TEMPLATE, params).setPublicName(
INSTALLER_PROPERTIES_RESOURE_DIR_ID);
}, target);
}
void rebrandLauncher(Map<String, ? super Object> params, Path icon,
Path target) throws IOException {
rebrandExecutable(params, icon, () -> {
return createResource(
LAUNCHER_PROPERTIES_TEMPLATE, params).setPublicName(
APP_NAME.fetchFrom(params) + ".properties");
}, target);
}
private void rebrandExecutable(Map<String, ? super Object> params, Path icon,
Supplier<OverridableResource> resourceSupplier, Path target) throws
IOException {
if (!target.isAbsolute() || (icon != null && !icon.isAbsolute())) {
Path absIcon = null;
if (icon != null) {
absIcon = icon.toAbsolutePath();
}
rebrandLauncher(params, absIcon, target.toAbsolutePath());
rebrandExecutable(params, absIcon, resourceSupplier,
target.toAbsolutePath());
return;
}
rebrandExecutable(params, target, (resourceLock) -> {
rebrandProperties(resourceLock, createResource(
LAUNCHER_PROPERTIES_TEMPLATE, params).setPublicName(
APP_NAME.fetchFrom(params) + ".properties"),
createSubstituteData(params), target);
rebrandProperties(resourceLock, resourceSupplier.get(),
createSubstituteData(
params), target);
if (icon != null) {
iconSwap(resourceLock, icon.toString());
}

View File

@ -282,8 +282,8 @@ public class LinuxHelper {
actualCriticalRuntimePaths);
} else {
// AppImagePackageTest.testEmpty() will have no dependencies,
// but will have more then 0 and less than 1K content size.
checkPrerequisites = packageSize > 1;
// but will have more then 0 and less than 5K content size when --icon is used.
checkPrerequisites = packageSize > 5;
}
List<String> prerequisites = LinuxHelper.getPrerequisitePackages(cmd);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -25,10 +25,10 @@ import java.nio.file.Path;
import java.nio.file.Files;
import java.io.IOException;
import java.util.List;
import jdk.jpackage.test.Annotations.Parameter;
import jdk.jpackage.test.TKit;
import jdk.jpackage.test.JPackageCommand;
import jdk.jpackage.test.PackageTest;
import jdk.jpackage.test.PackageType;
import jdk.jpackage.test.RunnablePackageTest.Action;
import jdk.jpackage.test.Annotations.Test;
@ -68,7 +68,9 @@ public class AppImagePackageTest {
}
@Test
public static void testEmpty() throws IOException {
@Parameter("true")
@Parameter("false")
public static void testEmpty(boolean withIcon) throws IOException {
final String name = "EmptyAppImagePackageTest";
final String imageName = name + (TKit.isOSX() ? ".app" : "");
Path appImageDir = TKit.createTempDirectory(null).resolve(imageName);
@ -81,6 +83,9 @@ public class AppImagePackageTest {
new PackageTest()
.addInitializer(cmd -> {
cmd.addArguments("--app-image", appImageDir);
if (withIcon) {
cmd.addArguments("--icon", iconPath("icon"));
}
cmd.removeArgumentWithValue("--input");
// on mac, with --app-image and without --mac-package-identifier,
@ -88,7 +93,12 @@ public class AppImagePackageTest {
if (TKit.isOSX()) {
cmd.addArguments("--mac-package-identifier", name);
}
}).run(new Action[] { Action.CREATE, Action.UNPACK });
}).run(Action.CREATE, Action.UNPACK);
// default: {CREATE, UNPACK, VERIFY}, but we can't verify foreign image
}
private static Path iconPath(String name) {
return TKit.TEST_SRC_ROOT.resolve(Path.of("resources", name
+ TKit.ICON_SUFFIX));
}
}

View File

@ -0,0 +1,175 @@
/*
* 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.
*
* 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.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Path;
import java.util.function.Consumer;
import javax.swing.Icon;
import javax.swing.filechooser.FileSystemView;
import jdk.jpackage.test.PackageTest;
import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.test.JPackageCommand;
import jdk.jpackage.test.PackageType;
import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE;
import jdk.jpackage.test.TKit;
/**
* Test that --icon also changes icon of exe installer.
*/
/*
* @test
* @summary jpackage with --icon parameter for exe installer
* @library ../helpers
* @key jpackagePlatformPackage
* @build jdk.jpackage.test.*
* @build WinInstallerIconTest
* @requires (os.family == "windows")
* @modules jdk.jpackage/jdk.jpackage.internal
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
* --jpt-run=WinInstallerIconTest
*/
public class WinInstallerIconTest {
@Test
public void test() throws IOException {
Path customIcon = iconPath("icon");
BufferedImage[] defaultInstallerIconImg = new BufferedImage[1];
// Create installer with the default icon
createInstaller(null, "WithDefaultIcon", installerIconImg -> {
defaultInstallerIconImg[0] = installerIconImg;
}, null, null);
BufferedImage[] customInstallerIconImg = new BufferedImage[1];
// Create installer with custom icon.
// This installer icon should differ from the icon
// of the installer created with the default icon.
createInstaller(customIcon, "2", installerIconImg -> {
customInstallerIconImg[0] = installerIconImg;
}, null, defaultInstallerIconImg[0]);
// Create installer with custom icon again.
// This installer icon should differ from the icon
// of the installer created with the default icon and should have
// the same icon as the icon of installer created with custom icon.
createInstaller(customIcon, null, null,
customInstallerIconImg[0], defaultInstallerIconImg[0]);
}
private void createInstaller(Path icon, String nameSuffix,
Consumer<BufferedImage> installerIconImgConsumer,
BufferedImage expectedInstallerIconImg,
BufferedImage unexpectedInstallerIconImg) throws IOException {
PackageTest test = new PackageTest()
.forTypes(PackageType.WIN_EXE)
.addInitializer(JPackageCommand::setFakeRuntime)
.configureHelloApp();
if (icon != null) {
test.addInitializer(cmd -> cmd.addArguments("--icon", icon));
}
if (nameSuffix != null) {
test.addInitializer(cmd -> {
String name = cmd.name() + nameSuffix;
cmd.setArgumentValue("--name", name);
});
}
Path installerExePath[] = new Path[1];
test.addBundleVerifier(cmd -> {
installerExePath[0] = cmd.outputBundle();
Icon actualIcon = FileSystemView.getFileSystemView().getSystemIcon(
installerExePath[0].toFile());
BufferedImage actualInstallerIconImg = loadIcon(actualIcon);
if (installerIconImgConsumer != null) {
installerIconImgConsumer.accept(actualInstallerIconImg);
}
if (expectedInstallerIconImg != null) {
TKit.assertTrue(imageEquals(expectedInstallerIconImg,
actualInstallerIconImg), String.format(
"Check icon of %s installer is matching expected value",
installerExePath[0]));
}
if (unexpectedInstallerIconImg != null) {
TKit.assertFalse(imageEquals(unexpectedInstallerIconImg,
actualInstallerIconImg), String.format(
"Check icon of %s installer is NOT matching unexpected value",
installerExePath[0]));
}
});
test.run(CREATE);
if (installerExePath[0] != null && nameSuffix != null) {
TKit.deleteIfExists(installerExePath[0]);
}
}
private BufferedImage loadIcon(Icon icon) {
TKit.assertNotEquals(0, icon.getIconWidth(),
"Check icon has not empty width");
TKit.assertNotEquals(0, icon.getIconHeight(),
"Check icon has not empty height");
BufferedImage img = new BufferedImage(
icon.getIconWidth(),
icon.getIconHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics g = img.createGraphics();
icon.paintIcon(null, g, 0, 0);
g.dispose();
return img;
}
private static boolean imageEquals(BufferedImage imgA, BufferedImage imgB) {
if (imgA.getWidth() == imgB.getWidth() && imgA.getHeight()
== imgB.getHeight()) {
for (int x = 0; x < imgA.getWidth(); x++) {
for (int y = 0; y < imgA.getHeight(); y++) {
if (imgA.getRGB(x, y) != imgB.getRGB(x, y)) {
return false;
}
}
}
} else {
return false;
}
return true;
}
private static Path iconPath(String name) {
return TKit.TEST_SRC_ROOT.resolve(Path.of("resources", name
+ TKit.ICON_SUFFIX));
}
}