# HG changeset patch # User herrick # Date 1571841474 14400 # Node ID 0fe62353385bc2df4848464fe9d5ae56892abd23 # Parent 88e2753a23346049e521e54dd65d9cbfa9a5ccf2 8232281: jpackage is not always reporting an error when no main class specified Reviewed-by: asemenyuk, asemenuk diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java Wed Oct 23 10:37:54 2019 -0400 @@ -69,7 +69,8 @@ .setCategory(I18N.getString("resource.menu-icon")) .setExternal(customIconFile); desktopFileResource = createResource("template.desktop", params) - .setCategory(I18N.getString("resource.menu-shortcut-descriptor")); + .setCategory(I18N.getString("resource.menu-shortcut-descriptor")) + .setPublicName(APP_NAME.fetchFrom(params) + ".desktop"); // XDG recommends to use vendor prefix in desktop file names as xdg // commands copy files to system directories. @@ -89,8 +90,8 @@ // - custom icon specified // desktopFile = new DesktopFile(desktopFileName); - iconFile = new DesktopFile(String.format("%s.png", - APP_NAME.fetchFrom(params))); + iconFile = new DesktopFile(APP_NAME.fetchFrom(params) + + IOUtils.getSuffix(Path.of(DEFAULT_ICON))); } else { desktopFile = null; iconFile = null; diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java Wed Oct 23 10:37:54 2019 -0400 @@ -26,14 +26,8 @@ package jdk.jpackage.internal; import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.ResourceBundle; +import java.util.*; import static jdk.jpackage.internal.StandardBundlerParam.*; @@ -85,10 +79,7 @@ public boolean validate(Map params) throws ConfigException { try { - if (params == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - + Objects.requireNonNull(params); return doValidate(params); } catch (RuntimeException re) { if (re.getCause() instanceof ConfigException) { diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppImageBuilder.java Wed Oct 23 10:37:54 2019 -0400 @@ -169,7 +169,8 @@ throws IOException { Path iconTarget = appLayout.destktopIntegrationDirectory().resolve( - APP_NAME.fetchFrom(params) + ".png"); + APP_NAME.fetchFrom(params) + IOUtils.getSuffix(Path.of( + DEFAULT_ICON))); createResource(DEFAULT_ICON, params) .setCategory("icon") diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java Wed Oct 23 10:37:54 2019 -0400 @@ -365,9 +365,12 @@ debianFiles.add(new DebianFile( configDir.resolve("postrm"), "resource.deb-postrm-script").setExecutable()); - debianFiles.add(new DebianFile( - getConfig_CopyrightFile(params).toPath(), - "resource.copyright-file")); + + if (!StandardBundlerParam.isRuntimeInstaller(params)) { + debianFiles.add(new DebianFile( + getConfig_CopyrightFile(params).toPath(), + "resource.copyright-file")); + } for (DebianFile debianFile : debianFiles) { debianFile.create(data, params); diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBundler.java Wed Oct 23 10:37:54 2019 -0400 @@ -29,6 +29,7 @@ import java.nio.file.Path; import java.text.MessageFormat; import java.util.*; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -99,18 +100,25 @@ PlatformPackage thePackage = createMetaPackage(params); + Function initAppImageLayout = imageRoot -> { + ApplicationLayout layout = appImageLayout(params); + layout.pathGroup().setPath(new Object(), + AppImageFile.getPathInAppImage(Path.of(""))); + return layout.resolveAt(imageRoot.toPath()); + }; + try { File appImage = StandardBundlerParam.getPredefinedAppImage(params); // we either have an application image or need to build one if (appImage != null) { - appImageLayout(params).resolveAt(appImage.toPath()).copy( + initAppImageLayout.apply(appImage).copy( thePackage.sourceApplicationLayout()); } else { appImage = APP_BUNDLER.fetchFrom(params).doBundle(params, thePackage.sourceRoot().toFile(), true); - ApplicationLayout srcAppLayout = appImageLayout(params).resolveAt( - appImage.toPath()); + ApplicationLayout srcAppLayout = initAppImageLayout.apply( + appImage); if (appImage.equals(PREDEFINED_RUNTIME_IMAGE.fetchFrom(params))) { // Application image points to run-time image. // Copy it. diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties Wed Oct 23 10:37:54 2019 -0400 @@ -40,9 +40,6 @@ resource.menu-icon=menu icon resource.rpm-spec-file=RPM spec file -error.parameters-null=Parameters map is null -error.parameters-null.advice=Pass in a non-null parameters map - error.tool-not-found.advice=Please install required packages error.tool-old-version.advice=Please install required packages diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_ja.properties Wed Oct 23 10:37:54 2019 -0400 @@ -40,9 +40,6 @@ resource.menu-icon=menu icon resource.rpm-spec-file=RPM spec file -error.parameters-null=Parameters map is null -error.parameters-null.advice=Pass in a non-null parameters map - error.tool-not-found.advice=Please install required packages error.tool-old-version.advice=Please install required packages diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources_zh_CN.properties Wed Oct 23 10:37:54 2019 -0400 @@ -40,9 +40,6 @@ resource.menu-icon=menu icon resource.rpm-spec-file=RPM spec file -error.parameters-null=Parameters map is null -error.parameters-null.advice=Pass in a non-null parameters map - error.tool-not-found.advice=Please install required packages error.tool-old-version.advice=Please install required packages diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppStoreBundler.java Wed Oct 23 10:37:54 2019 -0400 @@ -28,11 +28,7 @@ import java.io.File; import java.io.IOException; import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; +import java.util.*; import static jdk.jpackage.internal.StandardBundlerParam.*; import static jdk.jpackage.internal.MacAppBundler.*; @@ -235,11 +231,7 @@ public boolean validate(Map params) throws ConfigException { try { - if (params == null) { - throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - } + Objects.requireNonNull(params); // hdiutil is always available so there's no need to test for // availability. diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java Wed Oct 23 10:37:54 2019 -0400 @@ -429,9 +429,7 @@ public boolean validate(Map params) throws ConfigException { try { - if (params == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); + Objects.requireNonNull(params); //run basic validation to ensure requirements are met //we are not interested in return code, only possible exception diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java Wed Oct 23 10:37:54 2019 -0400 @@ -31,12 +31,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; +import java.util.*; import static jdk.jpackage.internal.StandardBundlerParam.*; import static jdk.jpackage.internal.MacBaseInstallerBundler.SIGNING_KEYCHAIN; @@ -502,9 +497,7 @@ public boolean validate(Map params) throws ConfigException { try { - if (params == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); + Objects.requireNonNull(params); // run basic validation to ensure requirements are met // we are not interested in return code, only possible exception diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties Wed Oct 23 10:37:54 2019 -0400 @@ -33,8 +33,6 @@ error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. -error.parameters-null=Parameters map is null -error.parameters-null.advice=Pass in a non-null parameters map. error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true error.no-app-signing-key=No Mac App Store App Signing Key diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties Wed Oct 23 10:37:54 2019 -0400 @@ -33,8 +33,6 @@ error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. -error.parameters-null=Parameters map is null -error.parameters-null.advice=Pass in a non-null parameters map. error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true error.no-app-signing-key=No Mac App Store App Signing Key diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties Wed Oct 23 10:37:54 2019 -0400 @@ -33,8 +33,6 @@ error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. -error.parameters-null=Parameters map is null -error.parameters-null.advice=Pass in a non-null parameters map. error.must-sign-app-store=Mac App Store apps must be signed, and signing has been disabled by bundler configuration error.must-sign-app-store.advice=Either unset 'signBundle' or set 'signBundle' to true error.no-app-signing-key=No Mac App Store App Signing Key diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java Wed Oct 23 10:37:54 2019 -0400 @@ -51,7 +51,7 @@ private final String launcherName; private final List addLauncherNames; - public final static String FILENAME = ".jpackage.xml"; + private final static String FILENAME = ".jpackage.xml"; private final static Map PLATFORM_LABELS = Map.of( Platform.LINUX, "linux", Platform.WINDOWS, "windows", Platform.MAC, @@ -91,13 +91,21 @@ } /** + * Returns path to application image info file. + * @param appImageDir - path to application image + */ + public static Path getPathInAppImage(Path appImageDir) { + return appImageDir.resolve(FILENAME); + } + + /** * Saves file with application image info in application image. * @param appImageDir - path to application image * @throws IOException */ - static void save(Path appImage, Map params) + static void save(Path appImageDir, Map params) throws IOException { - IOUtils.createXml(appImage.resolve(FILENAME), xml -> { + IOUtils.createXml(getPathInAppImage(appImageDir), xml -> { xml.writeStartElement("jpackage-state"); xml.writeAttribute("version", getVersion()); xml.writeAttribute("platform", getPlatform()); @@ -126,7 +134,7 @@ */ static AppImageFile load(Path appImageDir) throws IOException { try { - Path path = appImageDir.resolve(FILENAME); + Path path = getPathInAppImage(appImageDir); DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance(); dbf.setFeature( @@ -177,7 +185,7 @@ /** * Returns list of launcher names configured for the application. - * The first item in the returned list is man launcher name. + * The first item in the returned list is main launcher name. * Following items in the list are names of additional launchers. */ static List getLauncherNames(Path appImageDir, diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java Wed Oct 23 10:37:54 2019 -0400 @@ -240,6 +240,11 @@ return parent != null ? parent.resolve(filename) : Path.of(filename); } + public static String getSuffix(Path path) { + String filename = replaceSuffix(path.getFileName(), null).toString(); + return path.getFileName().toString().substring(filename.length()); + } + @FunctionalInterface public static interface XmlConsumer { void accept(XMLStreamWriter xml) throws IOException, XMLStreamException; @@ -248,7 +253,7 @@ public static void createXml(Path dstFile, XmlConsumer xmlConsumer) throws IOException { XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance(); - try (Writer w = new BufferedWriter(new FileWriter(dstFile.toFile()))) { + try (Writer w = Files.newBufferedWriter(dstFile)) { // Wrap with pretty print proxy XMLStreamWriter xml = (XMLStreamWriter) Proxy.newProxyInstance( XMLStreamWriter.class.getClassLoader(), new Class[]{ diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/share/classes/jdk/jpackage/internal/OverridableResource.java --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OverridableResource.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/OverridableResource.java Wed Oct 23 10:37:54 2019 -0400 @@ -81,10 +81,6 @@ return this; } - String getCategory() { - return category; - } - OverridableResource setResourceDir(Path v) { resourceDir = v; return this; diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/share/classes/jdk/jpackage/internal/ScriptRunner.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ScriptRunner.java Wed Oct 23 10:37:54 2019 -0400 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2019, 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.util.Map; +import java.util.Objects; +import java.util.Optional; +import static jdk.jpackage.internal.OverridableResource.createResource; +import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME; +import static jdk.jpackage.internal.StandardBundlerParam.CONFIG_ROOT; + +/** + * Runs custom script from resource directory. + */ +class ScriptRunner { + ScriptRunner() { + environment = new ProcessBuilder().environment(); + } + + ScriptRunner setResourceCategoryId(String v) { + resourceCategoryId = v; + return this; + } + + ScriptRunner setDirectory(Path v) { + directory = v; + return this; + } + + ScriptRunner setScriptNameSuffix(String v) { + scriptNameSuffix = v; + return this; + } + + ScriptRunner addEnvironment(Map v) { + environment.putAll(v); + return this; + } + + ScriptRunner setEnvironmentVariable(String envVarName, String envVarValue) { + Objects.requireNonNull(envVarName); + if (envVarValue == null) { + environment.remove(envVarName); + } else { + environment.put(envVarName, envVarValue); + } + return this; + } + + public void run(Map params) throws IOException { + String scriptName = String.format("%s-%s%s", APP_NAME.fetchFrom(params), + scriptNameSuffix, scriptSuffix()); + Path scriptPath = CONFIG_ROOT.fetchFrom(params).toPath().resolve( + scriptName); + createResource(null, params) + .setCategory(I18N.getString(resourceCategoryId)) + .saveToFile(scriptPath); + if (!Files.exists(scriptPath)) { + return; + } + + ProcessBuilder pb = new ProcessBuilder(shell(), + scriptPath.toAbsolutePath().toString()); + Map workEnvironment = pb.environment(); + workEnvironment.clear(); + workEnvironment.putAll(environment); + + if (directory != null) { + pb.directory(directory.toFile()); + } + + Executor.of(pb).executeExpectSuccess(); + } + + private static String shell() { + if (Platform.isWindows()) { + return "cscript"; + } + return Optional.ofNullable(System.getenv("SHELL")).orElseGet(() -> "sh"); + } + + private static String scriptSuffix() { + if (Platform.isWindows()) { + return ".wsf"; + } + return ".sh"; + } + + private String scriptNameSuffix; + private String resourceCategoryId; + private Path directory; + private Map environment; +} diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinAppBundler.java Wed Oct 23 10:37:54 2019 -0400 @@ -28,10 +28,7 @@ import java.io.File; import java.nio.file.Path; import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.ResourceBundle; +import java.util.*; import static jdk.jpackage.internal.WindowsBundlerParam.*; import static jdk.jpackage.internal.WinMsiBundler.WIN_APP_IMAGE; @@ -60,10 +57,7 @@ public boolean validate(Map params) throws ConfigException { try { - if (params == null) throw new ConfigException( - I18N.getString("error.parameters-null"), - I18N.getString("error.parameters-null.advice")); - + Objects.requireNonNull(params); return doValidate(params); } catch (RuntimeException re) { if (re.getCause() instanceof ConfigException) { @@ -80,21 +74,6 @@ throws ConfigException { imageBundleValidation(p); - - if (StandardBundlerParam.getPredefinedAppImage(p) != null) { - return true; - } - - // Make sure that jpackage.exe exists. - File tool = new File( - System.getProperty("java.home") + "\\bin\\jpackage.exe"); - - if (!tool.exists()) { - throw new ConfigException( - I18N.getString("error.no-windows-resources"), - I18N.getString("error.no-windows-resources.advice")); - } - return true; } diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java Wed Oct 23 10:37:54 2019 -0400 @@ -110,6 +110,13 @@ File msi = msiBundler.bundle(params, exeImageDir); try { + new ScriptRunner() + .setDirectory(msi.toPath().getParent()) + .setResourceCategoryId("resource.post-msi-script") + .setScriptNameSuffix("post-msi") + .setEnvironmentVariable("JpMsiFile", msi.getAbsolutePath().toString()) + .run(params); + return buildEXE(msi, outdir); } catch (IOException ex) { Log.verbose(ex); diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java Wed Oct 23 10:37:54 2019 -0400 @@ -335,11 +335,6 @@ try { Files.createDirectories(imageDir); - Path postImageScript = imageDir.resolve(APP_NAME.fetchFrom(params) + "-post-image.wsf"); - createResource(null, params) - .setCategory(I18N.getString("resource.post-install-script")) - .saveToFile(postImageScript); - prepareProto(params); wixSourcesBuilder @@ -349,12 +344,13 @@ Map wixVars = prepareMainProjectFile(params); - if (Files.exists(postImageScript)) { - Log.verbose(MessageFormat.format(I18N.getString( - "message.running-wsh-script"), - postImageScript.toAbsolutePath())); - Executor.of("wscript", postImageScript.toString()).executeExpectSuccess(); - } + new ScriptRunner() + .setDirectory(imageDir) + .setResourceCategoryId("resource.post-app-image-script") + .setScriptNameSuffix("post-image") + .setEnvironmentVariable("JpAppImageDir", imageDir.toAbsolutePath().toString()) + .run(params); + return buildMSI(params, wixVars, outdir); } catch (IOException ex) { Log.verbose(ex); diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixSourcesBuilder.java --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixSourcesBuilder.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixSourcesBuilder.java Wed Oct 23 10:37:54 2019 -0400 @@ -28,6 +28,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.text.MessageFormat; import java.util.*; import java.util.function.*; import java.util.stream.Collectors; @@ -75,7 +76,8 @@ // 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(), Path.of(AppImageFile.FILENAME)); + layout.pathGroup().setPath(new Object(), + AppImageFile.getPathInAppImage(Path.of(""))); // Want absolute paths to source files in generated WiX sources. // This is to handle scenario if sources would be processed from @@ -136,7 +138,8 @@ void logWixFeatures() { if (wixVersion.compareTo("3.6") >= 0) { - Log.verbose(I18N.getString("message.use-wix36-features")); + Log.verbose(MessageFormat.format(I18N.getString( + "message.use-wix36-features"), wixVersion)); } } @@ -148,6 +151,10 @@ fa.iconPath = null; } + if (fa.iconPath != null) { + fa.iconPath = fa.iconPath.toAbsolutePath(); + } + // Filter out empty extensions. fa.extensions = fa.extensions.stream().filter(Predicate.not( String::isEmpty)).collect(Collectors.toList()); @@ -277,6 +284,14 @@ return cfg.isFile; } + static void startElement(XMLStreamWriter xml, String componentId, + String componentGuid) throws XMLStreamException, IOException { + xml.writeStartElement("Component"); + xml.writeAttribute("Win64", "yes"); + xml.writeAttribute("Id", componentId); + xml.writeAttribute("Guid", componentGuid); + } + private static final class Config { Config withRegistryKeyPath() { withRegistryKeyPath = true; @@ -329,9 +344,8 @@ xml.writeAttribute("Id", Id.Folder.of(directoryRefPath)); final String componentId = "c" + role.idOf(path); - xml.writeStartElement("Component"); - xml.writeAttribute("Id", componentId); - xml.writeAttribute("Guid", String.format("{%s}", role.guidOf(path))); + Component.startElement(xml, componentId, String.format("{%s}", + role.guidOf(path))); boolean isRegistryKeyPath = !systemWide || role.isRegistryKeyPath(); if (isRegistryKeyPath) { @@ -716,9 +730,7 @@ xml.writeStartElement("DirectoryRef"); xml.writeAttribute("Id", INSTALLDIR.toString()); - xml.writeStartElement("Component"); - xml.writeAttribute("Id", componentId); - xml.writeAttribute("Guid", "*"); + Component.startElement(xml, componentId, "*"); addRegistryKeyPath(xml, INSTALLDIR, () -> propertyId, () -> { // The following code converts a path to value to be saved in registry. diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java Wed Oct 23 10:37:54 2019 -0400 @@ -26,10 +26,7 @@ package jdk.jpackage.internal; import java.io.IOException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.PathMatcher; +import java.nio.file.*; import java.text.MessageFormat; import java.util.*; import java.util.function.Supplier; @@ -119,16 +116,39 @@ private final static String MINIMAL_VERSION = "3.0"; - private final static Path PROGRAM_FILES = Path.of("C:\\Program Files"); - private final static Path PROGRAM_FILES_X86 = Path.of("C:\\Program Files (x86)"); + static Path getSystemDir(String envVar, String knownDir) { + return Optional + .ofNullable(getEnvVariableAsPath(envVar)) + .orElseGet(() -> Optional + .ofNullable(getEnvVariableAsPath("SystemDrive")) + .orElseGet(() -> Path.of("C:")).resolve(knownDir)); + } + + private static Path getEnvVariableAsPath(String envVar) { + String path = System.getenv(envVar); + if (path != null) { + try { + return Path.of(path); + } catch (InvalidPathException ex) { + Log.error(MessageFormat.format(I18N.getString( + "error.invalid-envvar"), envVar)); + return null; + } + } + return null; + } private static List findWixInstallDirs() { PathMatcher wixInstallDirMatcher = FileSystems.getDefault().getPathMatcher( "glob:WiX Toolset v*"); + Path programFiles = getSystemDir("ProgramFiles", "Program Files"); + Path programFilesX86 = getSystemDir("ProgramFiles(X86)", + "Program Files (x86)"); + // Returns list of WiX install directories ordered by WiX version number. // Newer versions go first. - return Stream.of(PROGRAM_FILES, PROGRAM_FILES_X86).map(path -> { + return Stream.of(programFiles, programFilesX86).map(path -> { List result; try (var paths = Files.walk(path, 1)) { result = paths.collect(Collectors.toList()); diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties Wed Oct 23 10:37:54 2019 -0400 @@ -32,16 +32,10 @@ resource.executable-properties-template=Template for creating executable properties file resource.setup-icon=setup dialog icon -resource.post-install-script=script to run after application image is populated +resource.post-app-image-script=script to run after application image is populated +resource.post-msi-script=script to run after msi file for exe installer is created resource.wxl-file-name=MsiInstallerStrings_en.wxl -error.parameters-null=Parameters map is null -error.parameters-null.advice=Pass in a non-null parameters map -error.no-windows-resources=This copy of the JDK does not support Windows -error.no-windows-resources.advice=Please use the Oracle JDK for Windows -error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0} -error.copyright-is-too-long=The copyright string is too long for InnoSetup -error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters. 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. error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}] @@ -50,28 +44,21 @@ error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535] error.version-string-minor-out-of-range=Minor version must be in the range [0, 255] error.version-string-part-not-number=Failed to convert version component to int -error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory error.version-swap=Failed to update version information for {0} -error.version-compare=Error: Failed to compare version {0} with {1. +error.invalid-envvar=Invalid value of {0} environment variable message.result-dir=Result application bundle: {0}. message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. -message.multiple-launchers=Multiple launchers found in predefined app-image. {0} will be used as primary launcher. message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". -message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required. message.outputting-to-location=Generating EXE for installer to: {0}. message.output-location=Installer (.exe) saved to: {0} message.tool-version=Detected [{0}] version [{1}]. -message.running-wsh-script=Running WSH script on application image [{0}]. message.creating-association-with-null-extension=Creating association with null extension. message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . -message.truncating.id=Truncating Application ID to 126 chars for Inno Setup. -message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action. +message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. message.generated-product-guid=Generated product GUID: {0}. message.preparing-msi-config=Preparing MSI config: {0}. message.generating-msi=Generating MSI: {0}. -message.light-file-string=WiX light tool set to {0}. -message.candle-file-string=WiX candle tool set to {0}. message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}". diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_ja.properties Wed Oct 23 10:37:54 2019 -0400 @@ -32,16 +32,10 @@ resource.executable-properties-template=Template for creating executable properties file resource.setup-icon=setup dialog icon -resource.post-install-script=script to run after application image is populated +resource.post-app-image-script=script to run after application image is populated +resource.post-msi-script=script to run after msi file for exe installer is created resource.wxl-file-name=MsiInstallerStrings_en.wxl -error.parameters-null=Parameters map is null -error.parameters-null.advice=Pass in a non-null parameters map -error.no-windows-resources=This copy of the JDK does not support Windows -error.no-windows-resources.advice=Please use the Oracle JDK for Windows -error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0} -error.copyright-is-too-long=The copyright string is too long for InnoSetup -error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters. 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. error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}] @@ -50,28 +44,21 @@ error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535] error.version-string-minor-out-of-range=Minor version must be in the range [0, 255] error.version-string-part-not-number=Failed to convert version component to int -error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory error.version-swap=Failed to update version information for {0} -error.version-compare=Error: Failed to compare version {0} with {1. +error.invalid-envvar=Invalid value of {0} environment variable message.result-dir=Result application bundle: {0}. message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. -message.multiple-launchers=Multiple launchers found in predefined app-image. {0} will be used as primary launcher. message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". -message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required. message.outputting-to-location=Generating EXE for installer to: {0}. message.output-location=Installer (.exe) saved to: {0} message.tool-version=Detected [{0}] version [{1}]. -message.running-wsh-script=Running WSH script on application image [{0}]. message.creating-association-with-null-extension=Creating association with null extension. message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . -message.truncating.id=Truncating Application ID to 126 chars for Inno Setup. -message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action. +message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. message.generated-product-guid=Generated product GUID: {0}. message.preparing-msi-config=Preparing MSI config: {0}. message.generating-msi=Generating MSI: {0}. -message.light-file-string=WiX light tool set to {0}. -message.candle-file-string=WiX candle tool set to {0}. message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}". diff -r 88e2753a2334 -r 0fe62353385b src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties Wed Oct 23 10:10:34 2019 -0400 +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources_zh_CN.properties Wed Oct 23 10:37:54 2019 -0400 @@ -32,16 +32,10 @@ resource.executable-properties-template=Template for creating executable properties file resource.setup-icon=setup dialog icon -resource.post-install-script=script to run after application image is populated +resource.post-app-image-script=script to run after application image is populated +resource.post-msi-script=script to run after msi file for exe installer is created resource.wxl-file-name=MsiInstallerStrings_en.wxl -error.parameters-null=Parameters map is null -error.parameters-null.advice=Pass in a non-null parameters map -error.no-windows-resources=This copy of the JDK does not support Windows -error.no-windows-resources.advice=Please use the Oracle JDK for Windows -error.cannot-find-launcher=Cannot find cfg file in predefined app image directory {0} -error.copyright-is-too-long=The copyright string is too long for InnoSetup -error.copyright-is-too-long.advice=Provide a copyright string shorter than 100 characters. 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. error.version-string-wrong-format=Version string is not compatible with MSI rules [{0}] @@ -50,28 +44,21 @@ error.version-string-build-out-of-range=Build part of version must be in the range [0, 65535] error.version-string-minor-out-of-range=Minor version must be in the range [0, 255] error.version-string-part-not-number=Failed to convert version component to int -error.cannot-walk-directory=Can not walk [{0}] - it is not a valid directory error.version-swap=Failed to update version information for {0} -error.version-compare=Error: Failed to compare version {0} with {1. +error.invalid-envvar=Invalid value of {0} environment variable message.result-dir=Result application bundle: {0}. message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. -message.multiple-launchers=Multiple launchers found in predefined app-image. {0} will be used as primary launcher. message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". -message.tool-wrong-version=Detected [{0}] version {1} but version {2} is required. message.outputting-to-location=Generating EXE for installer to: {0}. message.output-location=Installer (.exe) saved to: {0} message.tool-version=Detected [{0}] version [{1}]. -message.running-wsh-script=Running WSH script on application image [{0}]. message.creating-association-with-null-extension=Creating association with null extension. message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. message.version-string-too-many-components=Version sting may have up to 3 components - major.minor.build . -message.truncating.id=Truncating Application ID to 126 chars for Inno Setup. -message.use-wix36-features=WiX 3.6 detected. Enabling advanced cleanup action. +message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. message.generated-product-guid=Generated product GUID: {0}. message.preparing-msi-config=Preparing MSI config: {0}. message.generating-msi=Generating MSI: {0}. -message.light-file-string=WiX light tool set to {0}. -message.candle-file-string=WiX candle tool set to {0}. message.invalid.install.dir=Warning: Invalid install directory {0}. Install directory should be a relative sub-path under the default installation location such as "Program Files". Defaulting to application name "{1}". diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java Wed Oct 23 10:37:54 2019 -0400 @@ -619,7 +619,7 @@ } public void verifyIsOfType(PackageType ... types) { - final Set typesSet = Set.of(types); + final var typesSet = Stream.of(types).collect(Collectors.toSet()); if (!hasArgument("--package-type")) { if (!isImagePackageType()) { if (TKit.isLinux() && typesSet.equals(PackageType.LINUX)) { diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java Wed Oct 23 10:37:54 2019 -0400 @@ -255,7 +255,8 @@ Function, String> verifier = (lines) -> { // Lookup for xdg commands return lines.stream().filter(line -> { - Set words = Set.of(line.split("\\s+")); + Set words = Stream.of(line.split("\\s+")).collect( + Collectors.toSet()); return words.contains("xdg-desktop-menu") || words.contains( "xdg-mime") || words.contains("xdg-icon-resource"); }).findFirst().orElse(null); diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java Wed Oct 23 10:37:54 2019 -0400 @@ -351,8 +351,8 @@ } } - TKit.assertPathExists(cmd.appInstallationDirectory().resolve( - AppImageFile.FILENAME), false); + TKit.assertPathExists(AppImageFile.getPathInAppImage( + cmd.appInstallationDirectory()), false); installVerifiers.stream().forEach(v -> v.accept(cmd)); } diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java Wed Oct 23 10:37:54 2019 -0400 @@ -593,18 +593,32 @@ } public static void assertTrue(boolean actual, String msg) { + assertTrue(actual, msg, null); + } + + public static void assertFalse(boolean actual, String msg) { + assertFalse(actual, msg, null); + } + + public static void assertTrue(boolean actual, String msg, Runnable onFail) { currentTest.notifyAssert(); if (!actual) { - error(concatMessages("Unexpected FALSE", msg)); + if (onFail != null) { + onFail.run(); + } + error(concatMessages("Failed", msg)); } traceAssert(String.format("assertTrue(): %s", msg)); } - public static void assertFalse(boolean actual, String msg) { + public static void assertFalse(boolean actual, String msg, Runnable onFail) { currentTest.notifyAssert(); if (actual) { - error(concatMessages("Unexpected TRUE", msg)); + if (onFail != null) { + onFail.run(); + } + error(concatMessages("Failed", msg)); } traceAssert(String.format("assertFalse(): %s", msg)); diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/junit/jdk/jpackage/internal/AppImageFileTest.java --- a/test/jdk/tools/jpackage/junit/jdk/jpackage/internal/AppImageFileTest.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/junit/jdk/jpackage/internal/AppImageFileTest.java Wed Oct 23 10:37:54 2019 -0400 @@ -154,7 +154,7 @@ private AppImageFile createFromXml(String... xmlData) throws IOException { Path directory = tempFolder.getRoot().toPath(); - Path path = directory.resolve(AppImageFile.FILENAME); + Path path = AppImageFile.getPathInAppImage(directory); path.toFile().mkdirs(); Files.delete(path); diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/linux/ShortcutHintTest.java --- a/test/jdk/tools/jpackage/linux/ShortcutHintTest.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/linux/ShortcutHintTest.java Wed Oct 23 10:37:54 2019 -0400 @@ -21,13 +21,18 @@ * questions. */ +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.Map; import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; import jdk.jpackage.test.FileAssociations; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.TKit; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.*; /** * Test --linux-shortcut parameter. Output of the test should be @@ -139,4 +144,42 @@ TKit.TEST_SRC_ROOT.resolve("apps/dukeplug.png").toString())); }).run(); } + + /** + * .desktop file from resource dir. + */ + @Test + public static void testDesktopFileFromResourceDir() { + final String expectedVersionString = "Version=12345678"; + TKit.withTempDirectory("resources", tempDir -> { + createTest().addInitializer(cmd -> { + cmd.setFakeRuntime(); + + cmd.addArgument("--linux-shortcut"); + cmd.addArguments("--resource-dir", tempDir); + + // Create custom .desktop file in resource directory + TKit.createTextFile(tempDir.resolve(cmd.name() + ".desktop"), + List.of( + "[Desktop Entry]", + "Name=APPLICATION_NAME", + "Exec=APPLICATION_LAUNCHER", + "Terminal=false", + "Type=Application", + "Categories=DEPLOY_BUNDLE_CATEGORY", + expectedVersionString + )); + }) + .addInstallVerifier(cmd -> { + Path desktopFile = cmd.appLayout().destktopIntegrationDirectory().resolve( + String.format("%s-%s.desktop", + LinuxHelper.getPackageName(cmd), cmd.name())); + TKit.assertFileExists(desktopFile); + TKit.assertTextStream(expectedVersionString) + .label(String.format("[%s] file", desktopFile)) + .predicate(String::equals) + .apply(Files.readAllLines(desktopFile).stream()); + }).run(); + }); + } } diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/share/FileAssociationsTest.java --- a/test/jdk/tools/jpackage/share/FileAssociationsTest.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/share/FileAssociationsTest.java Wed Oct 23 10:37:54 2019 -0400 @@ -71,6 +71,8 @@ Path icon = TKit.TEST_SRC_ROOT.resolve(Path.of("resources", "icon" + TKit.ICON_SUFFIX)); + icon = TKit.createRelativePathCopy(icon); + applyFileAssociations(packageTest, new FileAssociations("jptest2").setFilename("fa2").setIcon(icon)); packageTest.run(); diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/share/RuntimePackageTest.java --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java Wed Oct 23 10:37:54 2019 -0400 @@ -21,11 +21,15 @@ * questions. */ +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashSet; import java.util.Optional; -import jdk.jpackage.test.TKit; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.JPackageCommand; +import java.util.Set; +import java.util.stream.Collectors; +import jdk.jpackage.test.*; +import jdk.jpackage.test.Annotations.Test; /** * Test --runtime-image parameter. @@ -49,23 +53,55 @@ * @comment Temporary disable for Linux and OSX until functionality implemented * @requires (os.family != "mac") * @modules jdk.jpackage/jdk.jpackage.internal - * @run main/othervm/timeout=720 -Xmx512m RuntimePackageTest + * @compile RuntimePackageTest.java + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=RuntimePackageTest */ public class RuntimePackageTest { - public static void main(String[] args) { - TKit.run(args, () -> { - new PackageTest() - .addInitializer(cmd -> { - cmd.addArguments("--runtime-image", Optional.ofNullable( - JPackageCommand.DEFAULT_RUNTIME_IMAGE).orElse(Path.of( - System.getProperty("java.home")))); - // Remove --input parameter from jpackage command line as we don't - // create input directory in the test and jpackage fails - // if --input references non existant directory. - cmd.removeArgumentWithValue("--input"); - }) - .run(); + @Test + public static void test() { + new PackageTest() + .addInitializer(cmd -> { + cmd.addArguments("--runtime-image", Optional.ofNullable( + JPackageCommand.DEFAULT_RUNTIME_IMAGE).orElse(Path.of( + System.getProperty("java.home")))); + // Remove --input parameter from jpackage command line as we don't + // create input directory in the test and jpackage fails + // if --input references non existant directory. + cmd.removeArgumentWithValue("--input"); + }) + .addInstallVerifier(cmd -> { + Set srcRuntime = listFiles(Path.of(cmd.getArgumentValue("--runtime-image"))); + Set dstRuntime = listFiles(cmd.appRuntimeDirectory()); + + Set intersection = new HashSet<>(srcRuntime); + intersection.retainAll(dstRuntime); + + srcRuntime.removeAll(intersection); + dstRuntime.removeAll(intersection); + + assertFileListEmpty(srcRuntime, "Missing"); + assertFileListEmpty(dstRuntime, "Unexpected"); + }) + .run(); + } + + private static Set listFiles(Path root) throws IOException { + try (var files = Files.walk(root)) { + return files.map(root::relativize).collect(Collectors.toSet()); + } + } + + private static void assertFileListEmpty(Set paths, String msg) { + TKit.assertTrue(paths.isEmpty(), String.format( + "Check there are no %s files in installed image", + msg.toLowerCase()), () -> { + String msg2 = String.format("%s %d files", msg, paths.size()); + TKit.trace(msg2 + ":"); + paths.stream().map(Path::toString).sorted().forEachOrdered( + TKit::trace); + TKit.trace("Done"); }); } } diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java Wed Oct 23 10:37:54 2019 -0400 @@ -44,7 +44,7 @@ * @build jdk.jpackage.test.* * @modules jdk.jpackage/jdk.jpackage.internal * @compile BasicTest.java - * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=jdk.jpackage.tests.BasicTest */ @@ -116,6 +116,7 @@ } @Test + @SuppressWarnings("unchecked") public void testVerbose() { JPackageCommand cmd = JPackageCommand.helloAppImage() .setFakeRuntime().executePrerequisiteActions(); @@ -139,12 +140,19 @@ TKit.deleteDirectoryContentsRecursive(cmd.outputDir()); List nonVerboseOutput = cmd.createExecutor().executeAndGetOutput(); + List[] verboseOutput = (List[])new List[1]; - TKit.deleteDirectoryContentsRecursive(cmd.outputDir()); - List verboseOutput = cmd.createExecutor().addArgument( - "--verbose").executeAndGetOutput(); + // Directory clean up is not 100% reliable on Windows because of + // antivirus software that can lock .exe files. Setup + // diffreent output directory instead of cleaning the default one for + // verbose jpackage run. + TKit.withTempDirectory("verbose-output", tempDir -> { + cmd.setArgumentValue("--dest", tempDir); + verboseOutput[0] = cmd.createExecutor().addArgument( + "--verbose").executeAndGetOutput(); + }); - TKit.assertTrue(nonVerboseOutput.size() < verboseOutput.size(), + TKit.assertTrue(nonVerboseOutput.size() < verboseOutput[0].size(), "Check verbose output is longer than regular"); expectedVerboseOutputStrings.forEach(str -> { @@ -155,7 +163,7 @@ expectedVerboseOutputStrings.forEach(str -> { TKit.assertTextStream(str).label("verbose output") - .apply(verboseOutput.stream()); + .apply(verboseOutput[0].stream()); }); } diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java Wed Oct 23 10:10:34 2019 -0400 +++ b/test/jdk/tools/jpackage/share/jdk/jpackage/tests/MainClassTest.java Wed Oct 23 10:37:54 2019 -0400 @@ -52,7 +52,6 @@ * @compile MainClassTest.java * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main * --jpt-run=jdk.jpackage.tests.MainClassTest - * --jpt-space-subst=_ */ public final class MainClassTest { diff -r 88e2753a2334 -r 0fe62353385b test/jdk/tools/jpackage/windows/WinScriptTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/windows/WinScriptTest.java Wed Oct 23 10:37:54 2019 -0400 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2018, 2019, 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.Path; +import java.util.List; +import java.util.ArrayList; +import jdk.jpackage.internal.IOUtils; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.JPackageCommand; + +/* + * @test usage of scripts from resource dir + * @summary jpackage with + * @library ../helpers + * @build jdk.jpackage.test.* + * @requires (os.family == "windows") + * @modules jdk.jpackage/jdk.jpackage.internal + * @compile WinScriptTest.java + * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinScriptTest + */ + +public class WinScriptTest { + + public WinScriptTest(PackageType type) { + this.packageType = type; + + test = new PackageTest() + .forTypes(type) + .configureHelloApp() + .addInitializer(cmd -> { + cmd.setFakeRuntime().saveConsoleOutput(true); + }); + } + + @Parameters + public static List data() { + return List.of(new Object[][]{ + {PackageType.WIN_MSI}, + {PackageType.WIN_EXE} + }); + } + + @Test + @Parameter("0") + @Parameter("10") + public void test(int wsfExitCode) { + final ScriptData appImageScriptData; + if (wsfExitCode != 0 && packageType == PackageType.WIN_EXE) { + appImageScriptData = new ScriptData(PackageType.WIN_MSI, 0); + } else { + appImageScriptData = new ScriptData(PackageType.WIN_MSI, wsfExitCode); + } + + final ScriptData msiScriptData = new ScriptData(PackageType.WIN_EXE, wsfExitCode); + + test.setExpectedExitCode(wsfExitCode == 0 ? 0 : 1); + TKit.withTempDirectory("resources", tempDir -> { + test.addInitializer(cmd -> { + cmd.addArguments("--resource-dir", tempDir); + + appImageScriptData.createScript(cmd); + msiScriptData.createScript(cmd); + }); + + if (packageType == PackageType.WIN_MSI) { + test.addBundleVerifier((cmd, result) -> { + appImageScriptData.assertJPackageOutput(result.getOutput()); + }); + } + + if (packageType == PackageType.WIN_EXE) { + test.addBundleVerifier((cmd, result) -> { + appImageScriptData.assertJPackageOutput(result.getOutput()); + msiScriptData.assertJPackageOutput(result.getOutput()); + }); + } + + test.run(); + }); + } + + private static class ScriptData { + ScriptData(PackageType scriptType, int wsfExitCode) { + if (scriptType == PackageType.WIN_MSI) { + echoText = "post app image wsf"; + envVarName = "JpAppImageDir"; + scriptSuffixName = "post-image"; + } else { + echoText = "post msi wsf"; + envVarName = "JpMsiFile"; + scriptSuffixName = "post-msi"; + } + this.wsfExitCode = wsfExitCode; + } + + void assertJPackageOutput(List output) { + TKit.assertTextStream(String.format("jp: %s", echoText)) + .predicate(String::equals) + .apply(output.stream()); + + String cwdPattern = String.format("jp: CWD(%s)=", envVarName); + TKit.assertTextStream(cwdPattern) + .predicate(String::startsWith) + .apply(output.stream()); + String cwd = output.stream().filter(line -> line.startsWith( + cwdPattern)).findFirst().get().substring(cwdPattern.length()); + + String envVarPattern = String.format("jp: %s=", envVarName); + TKit.assertTextStream(envVarPattern) + .predicate(String::startsWith) + .apply(output.stream()); + String envVar = output.stream().filter(line -> line.startsWith( + envVarPattern)).findFirst().get().substring(envVarPattern.length()); + + TKit.assertTrue(envVar.startsWith(cwd), String.format( + "Check value of %s environment variable [%s] starts with the current directory [%s] set for %s script", + envVarName, envVar, cwd, echoText)); + } + + void createScript(JPackageCommand cmd) throws IOException { + IOUtils.createXml(Path.of(cmd.getArgumentValue("--resource-dir"), + String.format("%s-%s.wsf", cmd.name(), scriptSuffixName)), xml -> { + xml.writeStartElement("job"); + xml.writeAttribute("id", "main"); + xml.writeStartElement("script"); + xml.writeAttribute("language", "JScript"); + xml.writeCData(String.join("\n", List.of( + "var shell = new ActiveXObject('WScript.Shell')", + "WScript.Echo('jp: " + envVarName + "=' + shell.ExpandEnvironmentStrings('%" + envVarName + "%'))", + "WScript.Echo('jp: CWD(" + envVarName + ")=' + shell.CurrentDirectory)", + String.format("WScript.Echo('jp: %s')", echoText), + String.format("WScript.Quit(%d)", wsfExitCode) + ))); + xml.writeEndElement(); + xml.writeEndElement(); + }); + } + + private final int wsfExitCode; + private final String scriptSuffixName; + private final String echoText; + private final String envVarName; + } + + private final PackageType packageType; + private PackageTest test; +}