src/jdk.jpackager/macosx/classes/jdk/jpackager/internal/mac/MacPkgBundler.java
branchJDK-8200758-branch
changeset 57038 b0f09e7c4680
parent 57032 a42d0a8e0916
child 57039 98d3963b0b7b
--- a/src/jdk.jpackager/macosx/classes/jdk/jpackager/internal/mac/MacPkgBundler.java	Wed Nov 21 13:53:17 2018 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,612 +0,0 @@
-/*
- * Copyright (c) 2014, 2018, 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.jpackager.internal.mac;
-
-import jdk.jpackager.internal.BundlerParamInfo;
-import jdk.jpackager.internal.StandardBundlerParam;
-import jdk.jpackager.internal.Log;
-import jdk.jpackager.internal.ConfigException;
-import jdk.jpackager.internal.IOUtils;
-import jdk.jpackager.internal.Platform;
-import jdk.jpackager.internal.RelativeFileSet;
-import jdk.jpackager.internal.UnsupportedPlatformException;
-import jdk.jpackager.internal.resources.mac.MacResources;
-import jdk.jpackager.internal.builders.mac.MacAppImageBuilder;
-import jdk.jpackager.internal.Arguments;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.io.Writer;
-import java.net.URLEncoder;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.ResourceBundle;
-
-import static jdk.jpackager.internal.StandardBundlerParam.*;
-import static
-        jdk.jpackager.internal.mac.MacBaseInstallerBundler.SIGNING_KEYCHAIN;
-import static
-        jdk.jpackager.internal.mac.MacBaseInstallerBundler.SIGNING_KEY_USER;
-
-public class MacPkgBundler extends MacBaseInstallerBundler {
-
-    private static final ResourceBundle I18N = ResourceBundle.getBundle(
-            "jdk.jpackager.internal.resources.mac.MacPkgBundler");
-
-    public final static String MAC_BUNDLER_PREFIX =
-            BUNDLER_PREFIX + "macosx" + File.separator;
-
-    private static final String DEFAULT_BACKGROUND_IMAGE = "background_pkg.png";
-
-    private static final String TEMPLATE_PREINSTALL_SCRIPT =
-            "preinstall.template";
-    private static final String TEMPLATE_POSTINSTALL_SCRIPT =
-            "postinstall.template";
-
-    private static final BundlerParamInfo<File> PACKAGES_ROOT =
-            new StandardBundlerParam<>(
-            I18N.getString("param.packages-root.name"),
-            I18N.getString("param.packages-root.description"),
-            "mac.pkg.packagesRoot",
-            File.class,
-            params -> {
-                File packagesRoot =
-                        new File(BUILD_ROOT.fetchFrom(params), "packages");
-                packagesRoot.mkdirs();
-                return packagesRoot;
-            },
-            (s, p) -> new File(s));
-
-
-    protected final BundlerParamInfo<File> SCRIPTS_DIR =
-            new StandardBundlerParam<>(
-            I18N.getString("param.scripts-dir.name"),
-            I18N.getString("param.scripts-dir.description"),
-            "mac.pkg.scriptsDir",
-            File.class,
-            params -> {
-                File scriptsDir =
-                        new File(CONFIG_ROOT.fetchFrom(params), "scripts");
-                scriptsDir.mkdirs();
-                return scriptsDir;
-            },
-            (s, p) -> new File(s));
-
-    public static final
-            BundlerParamInfo<String> DEVELOPER_ID_INSTALLER_SIGNING_KEY =
-            new StandardBundlerParam<>(
-            I18N.getString("param.signing-key-developer-id-installer.name"),
-            I18N.getString(
-            "param.signing-key-developer-id-installer.description"),
-            "mac.signing-key-developer-id-installer",
-            String.class,
-            params -> {
-                    String result = MacBaseInstallerBundler.findKey(
-                            "Developer ID Installer: "
-                            + SIGNING_KEY_USER.fetchFrom(params),
-                            SIGNING_KEYCHAIN.fetchFrom(params),
-                            VERBOSE.fetchFrom(params));
-                    if (result != null) {
-                        MacCertificate certificate = new MacCertificate(
-                                result, VERBOSE.fetchFrom(params));
-
-                        if (!certificate.isValid()) {
-                            Log.error(MessageFormat.format(
-                                    I18N.getString("error.certificate.expired"),
-                                    result));
-                        }
-                    }
-
-                    return result;
-                },
-            (s, p) -> s);
-
-    public static final BundlerParamInfo<String> MAC_INSTALL_DIR =
-            new StandardBundlerParam<>(
-            I18N.getString("param.mac-install-dir.name"),
-            I18N.getString("param.mac-install-dir.description"),
-            "mac-install-dir",
-            String.class,
-             params -> {
-                 String dir = INSTALL_DIR.fetchFrom(params);
-                 return (dir != null) ? dir : "/Applications";
-             },
-            (s, p) -> s
-    );
-
-    public static final BundlerParamInfo<String> INSTALLER_SUFFIX =
-            new StandardBundlerParam<> (
-            I18N.getString("param.installer-suffix.name"),
-            I18N.getString("param.installer-suffix.description"),
-            "mac.pkg.installerName.suffix",
-            String.class,
-            params -> "",
-            (s, p) -> s);
-
-    public MacPkgBundler() {
-        super();
-        baseResourceLoader = MacResources.class;
-    }
-
-    public File bundle(Map<String, ? super Object> params, File outdir) {
-        Log.verbose(MessageFormat.format(I18N.getString("message.building-pkg"),
-                APP_NAME.fetchFrom(params)));
-        if (!outdir.isDirectory() && !outdir.mkdirs()) {
-            throw new RuntimeException(MessageFormat.format(
-                    I18N.getString("error.cannot-create-output-dir"),
-                    outdir.getAbsolutePath()));
-        }
-        if (!outdir.canWrite()) {
-            throw new RuntimeException(MessageFormat.format(
-                    I18N.getString("error.cannot-write-to-output-dir"),
-                    outdir.getAbsolutePath()));
-        }
-
-        File appImageDir = null;
-        try {
-            appImageDir = prepareAppBundle(params, false);
-
-            if (appImageDir != null && prepareConfigFiles(params)) {
-
-                File configScript = getConfig_Script(params);
-                if (configScript.exists()) {
-                    Log.verbose(MessageFormat.format(I18N.getString(
-                            "message.running-script"),
-                            configScript.getAbsolutePath()));
-                    IOUtils.run("bash", configScript, false);
-                }
-
-                return createPKG(params, outdir, appImageDir);
-            }
-            return null;
-        } catch (IOException ex) {
-            Log.verbose(ex);
-            return null;
-        } finally {
-            try {
-                if (appImageDir != null &&
-                        PREDEFINED_APP_IMAGE.fetchFrom(params) == null &&
-                        (PREDEFINED_RUNTIME_IMAGE.fetchFrom(params) == null ||
-                        !Arguments.CREATE_JRE_INSTALLER.fetchFrom(params)) &&
-                        !Log.isDebug() &&
-                        !Log.isVerbose()) {
-                    IOUtils.deleteRecursive(appImageDir);
-                } else if (appImageDir != null) {
-                    Log.verbose(MessageFormat.format(I18N.getString(
-                            "message.intermediate-image-location"),
-                            appImageDir.getAbsolutePath()));
-                }
-
-                // cleanup
-                cleanupConfigFiles(params);
-            } catch (IOException ex) {
-                Log.debug(ex);
-                // noinspection ReturnInsideFinallyBlock
-                return null;
-            }
-        }
-    }
-
-    private File getPackages_AppPackage(Map<String, ? super Object> params) {
-        return new File(PACKAGES_ROOT.fetchFrom(params),
-                APP_FS_NAME.fetchFrom(params) + "-app.pkg");
-    }
-
-    private File getPackages_DaemonPackage(Map<String, ? super Object> params) {
-        return new File(PACKAGES_ROOT.fetchFrom(params),
-                APP_FS_NAME.fetchFrom(params) + "-daemon.pkg");
-    }
-
-    private void cleanupPackagesFiles(Map<String, ? super Object> params) {
-        if (Log.isDebug() || Log.isVerbose()) {
-            return;
-        }
-
-        if (getPackages_AppPackage(params) != null) {
-            getPackages_AppPackage(params).delete();
-        }
-        if (getPackages_DaemonPackage(params) != null) {
-            getPackages_DaemonPackage(params).delete();
-        }
-    }
-
-    private File getConfig_DistributionXMLFile(
-            Map<String, ? super Object> params) {
-        return new File(CONFIG_ROOT.fetchFrom(params), "distribution.dist");
-    }
-
-    private File getConfig_BackgroundImage(Map<String, ? super Object> params) {
-        return new File(CONFIG_ROOT.fetchFrom(params),
-                APP_NAME.fetchFrom(params) + "-background.png");
-    }
-
-    private File getScripts_PreinstallFile(Map<String, ? super Object> params) {
-        return new File(SCRIPTS_DIR.fetchFrom(params), "preinstall");
-    }
-
-    private File getScripts_PostinstallFile(
-            Map<String, ? super Object> params) {
-        return new File(SCRIPTS_DIR.fetchFrom(params), "postinstall");
-    }
-
-    private void cleanupConfigFiles(Map<String, ? super Object> params) {
-        if (Log.isDebug() || Log.isVerbose()) {
-            return;
-        }
-
-        if (getConfig_DistributionXMLFile(params) != null) {
-            getConfig_DistributionXMLFile(params).delete();
-        }
-        if (getConfig_BackgroundImage(params) != null) {
-            getConfig_BackgroundImage(params).delete();
-        }
-    }
-
-    private String getAppIdentifier(Map<String, ? super Object> params) {
-        return IDENTIFIER.fetchFrom(params);
-    }
-
-    private String getDaemonIdentifier(Map<String, ? super Object> params) {
-        return IDENTIFIER.fetchFrom(params) + ".daemon";
-    }
-
-    private void preparePackageScripts(Map<String, ? super Object> params)
-            throws IOException {
-        Log.verbose(I18N.getString("message.preparing-scripts"));
-
-        Map<String, String> data = new HashMap<>();
-
-        data.put("DEPLOY_DAEMON_IDENTIFIER", getDaemonIdentifier(params));
-        data.put("DEPLOY_LAUNCHD_PLIST_FILE",
-                IDENTIFIER.fetchFrom(params).toLowerCase() + ".launchd.plist");
-
-        Writer w = new BufferedWriter(
-                new FileWriter(getScripts_PreinstallFile(params)));
-        String content = preprocessTextResource(MAC_BUNDLER_PREFIX
-                + getScripts_PreinstallFile(params).getName(),
-                I18N.getString("resource.pkg-preinstall-script"),
-                TEMPLATE_PREINSTALL_SCRIPT,
-                data,
-                VERBOSE.fetchFrom(params),
-                DROP_IN_RESOURCES_ROOT.fetchFrom(params));
-        w.write(content);
-        w.close();
-        getScripts_PreinstallFile(params).setExecutable(true, false);
-
-        w = new BufferedWriter(
-                new FileWriter(getScripts_PostinstallFile(params)));
-        content = preprocessTextResource(MAC_BUNDLER_PREFIX
-                + getScripts_PostinstallFile(params).getName(),
-                I18N.getString("resource.pkg-postinstall-script"),
-                TEMPLATE_POSTINSTALL_SCRIPT,
-                data,
-                VERBOSE.fetchFrom(params),
-                DROP_IN_RESOURCES_ROOT.fetchFrom(params));
-        w.write(content);
-        w.close();
-        getScripts_PostinstallFile(params).setExecutable(true, false);
-    }
-
-    private void prepareDistributionXMLFile(Map<String, ? super Object> params)
-            throws IOException {
-        File f = getConfig_DistributionXMLFile(params);
-
-        Log.verbose(MessageFormat.format(I18N.getString(
-                "message.preparing-distribution-dist"), f.getAbsolutePath()));
-
-        PrintStream out = new PrintStream(f);
-
-        out.println(
-                "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>");
-        out.println("<installer-gui-script minSpecVersion=\"1\">");
-
-        out.println("<title>" + APP_NAME.fetchFrom(params) + "</title>");
-        out.println("<background" + " file=\""
-                + getConfig_BackgroundImage(params).getName()
-                + "\""
-                + " mime-type=\"image/png\""
-                + " alignment=\"bottomleft\" "
-                + " scaling=\"none\""
-                + "/>");
-
-        if (!LICENSE_FILE.fetchFrom(params).isEmpty()) {
-            File licFile = null;
-
-            List<String> licFiles = LICENSE_FILE.fetchFrom(params);
-            if (licFiles.isEmpty()) {
-                return;
-            }
-            String licFileStr = licFiles.get(0);
-
-            for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(params)) {
-                if (rfs.contains(licFileStr)) {
-                    licFile = new File(rfs.getBaseDirectory(), licFileStr);
-                    break;
-                }
-            }
-
-            // this is NPE protection, validate should have caught it's absence
-            // so we don't complain or throw an error
-            if (licFile != null) {
-                out.println("<license"
-                        + " file=\"" + licFile.getAbsolutePath() + "\""
-                        + " mime-type=\"text/rtf\""
-                        + "/>");
-            }
-        }
-
-        /*
-         * Note that the content of the distribution file
-         * below is generated by productbuild --synthesize
-         */
-
-        String appId = getAppIdentifier(params);
-        String daemonId = getDaemonIdentifier(params);
-
-        out.println("<pkg-ref id=\"" + appId + "\"/>");
-
-        out.println("<options customize=\"never\" require-scripts=\"false\"/>");
-        out.println("<choices-outline>");
-        out.println("    <line choice=\"default\">");
-        out.println("        <line choice=\"" + appId + "\"/>");
-        out.println("    </line>");
-        out.println("</choices-outline>");
-        out.println("<choice id=\"default\"/>");
-        out.println("<choice id=\"" + appId + "\" visible=\"false\">");
-        out.println("    <pkg-ref id=\"" + appId + "\"/>");
-        out.println("</choice>");
-        out.println("<pkg-ref id=\"" + appId + "\" version=\""
-                + VERSION.fetchFrom(params) + "\" onConclusion=\"none\">"
-                + URLEncoder.encode(getPackages_AppPackage(params).getName(),
-                "UTF-8") + "</pkg-ref>");
-
-        out.println("</installer-gui-script>");
-
-        out.close();
-    }
-
-    private boolean prepareConfigFiles(Map<String, ? super Object> params)
-            throws IOException {
-        File imageTarget = getConfig_BackgroundImage(params);
-        fetchResource(MacAppBundler.MAC_BUNDLER_PREFIX + imageTarget.getName(),
-                I18N.getString("resource.pkg-background-image"),
-                DEFAULT_BACKGROUND_IMAGE,
-                imageTarget,
-                VERBOSE.fetchFrom(params),
-                DROP_IN_RESOURCES_ROOT.fetchFrom(params));
-
-        prepareDistributionXMLFile(params);
-
-        fetchResource(MacAppBundler.MAC_BUNDLER_PREFIX
-                + getConfig_Script(params).getName(),
-                I18N.getString("resource.post-install-script"),
-                (String) null,
-                getConfig_Script(params),
-                VERBOSE.fetchFrom(params),
-                DROP_IN_RESOURCES_ROOT.fetchFrom(params));
-
-        return true;
-    }
-
-    // name of post-image script
-    private File getConfig_Script(Map<String, ? super Object> params) {
-        return new File(CONFIG_ROOT.fetchFrom(params),
-                APP_NAME.fetchFrom(params) + "-post-image.sh");
-    }
-
-    private File createPKG(Map<String, ? super Object> params,
-            File outdir, File appLocation) {
-        // generic find attempt
-        try {
-            File appPKG = getPackages_AppPackage(params);
-
-            // build application package
-            ProcessBuilder pb = new ProcessBuilder("pkgbuild",
-                    "--component",
-                    appLocation.toString(),
-                    "--install-location",
-                    MAC_INSTALL_DIR.fetchFrom(params),
-                    appPKG.getAbsolutePath());
-            IOUtils.exec(pb, false);
-
-            // build final package
-            File finalPKG = new File(outdir, INSTALLER_NAME.fetchFrom(params)
-                    + INSTALLER_SUFFIX.fetchFrom(params)
-                    + ".pkg");
-            outdir.mkdirs();
-
-            List<String> commandLine = new ArrayList<>();
-            commandLine.add("productbuild");
-
-            commandLine.add("--resources");
-            commandLine.add(CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
-
-            // maybe sign
-            if (Optional.ofNullable(MacAppImageBuilder.
-                    SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) {
-                if (Platform.getMajorVersion() > 10 ||
-                    (Platform.getMajorVersion() == 10 &&
-                    Platform.getMinorVersion() >= 12)) {
-                    // we need this for OS X 10.12+
-                    Log.verbose(I18N.getString("message.signing.pkg"));
-                }
-
-                String signingIdentity =
-                        DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params);
-                if (signingIdentity != null) {
-                    commandLine.add("--sign");
-                    commandLine.add(signingIdentity);
-                }
-
-                String keychainName = SIGNING_KEYCHAIN.fetchFrom(params);
-                if (keychainName != null && !keychainName.isEmpty()) {
-                    commandLine.add("--keychain");
-                    commandLine.add(keychainName);
-                }
-            }
-
-            commandLine.add("--distribution");
-            commandLine.add(
-                    getConfig_DistributionXMLFile(params).getAbsolutePath());
-            commandLine.add("--package-path");
-            commandLine.add(PACKAGES_ROOT.fetchFrom(params).getAbsolutePath());
-
-            commandLine.add(finalPKG.getAbsolutePath());
-
-            pb = new ProcessBuilder(commandLine);
-            IOUtils.exec(pb, false);
-
-            return finalPKG;
-        } catch (Exception ignored) {
-            Log.verbose(ignored);
-            return null;
-        } finally {
-            cleanupPackagesFiles(params);
-            cleanupConfigFiles(params);
-        }
-    }
-
-    //////////////////////////////////////////////////////////////////////////
-    // Implement Bundler
-    //////////////////////////////////////////////////////////////////////////
-
-    @Override
-    public String getName() {
-        return I18N.getString("bundler.name");
-    }
-
-    @Override
-    public String getDescription() {
-        return I18N.getString("bundler.description");
-    }
-
-    @Override
-    public String getID() {
-        return "pkg";
-    }
-
-    @Override
-    public Collection<BundlerParamInfo<?>> getBundleParameters() {
-        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
-        results.addAll(MacAppBundler.getAppBundleParameters());
-        results.addAll(getPKGBundleParameters());
-        return results;
-    }
-
-    public Collection<BundlerParamInfo<?>> getPKGBundleParameters() {
-        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
-
-        results.addAll(MacAppBundler.getAppBundleParameters());
-        results.addAll(Arrays.asList(
-                DEVELOPER_ID_INSTALLER_SIGNING_KEY,
-                // IDENTIFIER,
-                INSTALLER_SUFFIX,
-                LICENSE_FILE,
-                // SERVICE_HINT,
-                SIGNING_KEYCHAIN));
-
-        return results;
-    }
-
-    @Override
-    public boolean validate(Map<String, ? super Object> params)
-            throws UnsupportedPlatformException, ConfigException {
-        try {
-            if (params == null) throw new ConfigException(
-                    I18N.getString("error.parameters-null"),
-                    I18N.getString("error.parameters-null.advice"));
-
-            // run basic validation to ensure requirements are met
-            // we are not interested in return code, only possible exception
-            validateAppImageAndBundeler(params);
-
-            // validate license file, if used, exists in the proper place
-            if (params.containsKey(LICENSE_FILE.getID())) {
-                List<RelativeFileSet> appResourcesList =
-                        APP_RESOURCES_LIST.fetchFrom(params);
-                for (String license : LICENSE_FILE.fetchFrom(params)) {
-                    boolean found = false;
-                    for (RelativeFileSet appResources : appResourcesList) {
-                        found = found || appResources.contains(license);
-                    }
-                    if (!found) {
-                        throw new ConfigException(
-                                I18N.getString("error.license-missing"),
-                                MessageFormat.format(
-                                I18N.getString("error.license-missing.advice"),
-                                license));
-                    }
-                }
-            }
-
-            // reject explicitly set sign to true and no valid signature key
-            if (Optional.ofNullable(MacAppImageBuilder.
-                    SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.FALSE)) {
-                String signingIdentity =
-                        DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(params);
-                if (signingIdentity == null) {
-                    throw new ConfigException(
-                            I18N.getString("error.explicit-sign-no-cert"),
-                            I18N.getString(
-                            "error.explicit-sign-no-cert.advice"));
-                }
-            }
-
-            // hdiutil is always available so there's no need
-            // to test for availability.
-
-            return true;
-        } catch (RuntimeException re) {
-            if (re.getCause() instanceof ConfigException) {
-                throw (ConfigException) re.getCause();
-            } else {
-                throw new ConfigException(re);
-            }
-        }
-    }
-
-    @Override
-    public File execute(
-        Map<String, ? super Object> params, File outputParentDir) {
-        return bundle(params, outputParentDir);
-    }
-
-    @Override
-    public boolean supported() {
-        return Platform.getPlatform() == Platform.MAC;
-    }
-
-}