src/jdk.packager/macosx/classes/jdk/packager/internal/mac/MacDmgBundler.java
branchJDK-8200758-branch
changeset 57017 1b08af362a30
parent 57016 f63f13da91c0
child 57018 9d782e357916
--- a/src/jdk.packager/macosx/classes/jdk/packager/internal/mac/MacDmgBundler.java	Fri Nov 02 11:34:56 2018 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,576 +0,0 @@
-/*
- * Copyright (c) 2012, 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.packager.internal.mac;
-
-import jdk.packager.internal.*;
-import jdk.packager.internal.IOUtils;
-import jdk.packager.internal.resources.mac.MacResources;
-import jdk.packager.internal.Arguments;
-
-import java.io.*;
-import java.nio.file.Files;
-import java.text.MessageFormat;
-import java.util.*;
-
-import static jdk.packager.internal.StandardBundlerParam.*;
-
-public class MacDmgBundler extends MacBaseInstallerBundler {
-
-    private static final ResourceBundle I18N =
-            ResourceBundle.getBundle(
-            "jdk.packager.internal.resources.mac.MacDmgBundler");
-
-    static final String DEFAULT_BACKGROUND_IMAGE="background_dmg.png";
-    static final String DEFAULT_DMG_SETUP_SCRIPT="DMGsetup.scpt";
-    static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns";
-
-    static final String DEFAULT_LICENSE_PLIST="lic_template.plist";
-
-    public static final BundlerParamInfo<String> INSTALLER_SUFFIX =
-            new StandardBundlerParam<> (
-            I18N.getString("param.installer-suffix.name"),
-            I18N.getString("param.installer-suffix.description"),
-            "mac.dmg.installerName.suffix",
-            String.class,
-            params -> "",
-            (s, p) -> s);
-
-    public MacDmgBundler() {
-        super();
-        baseResourceLoader = MacResources.class;
-    }
-
-    public File bundle(Map<String, ? super Object> params, File outdir) {
-        Log.info(MessageFormat.format(I18N.getString("message.building-dmg"),
-                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 = APP_IMAGE_BUILD_ROOT.fetchFrom(params);
-        try {
-            appImageDir.mkdirs();
-
-            if (prepareAppBundle(params, true) != null &&
-                    prepareConfigFiles(params)) {
-                File configScript = getConfig_Script(params);
-                if (configScript.exists()) {
-                    Log.info(MessageFormat.format(
-                            I18N.getString("message.running-script"),
-                            configScript.getAbsolutePath()));
-                    IOUtils.run("bash", configScript, false);
-                }
-
-                return buildDMG(params, outdir);
-            }
-            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()) {
-                    IOUtils.deleteRecursive(appImageDir);
-                } else if (appImageDir != null) {
-                    Log.info(MessageFormat.format(I18N.getString(
-                            "message.intermediate-image-location"),
-                            appImageDir.getAbsolutePath()));
-                }
-
-                //cleanup
-                cleanupConfigFiles(params);
-            } catch (IOException ex) {
-                Log.debug(ex);
-                //noinspection ReturnInsideFinallyBlock
-                return null;
-            }
-        }
-    }
-
-    //remove
-    protected void cleanupConfigFiles(Map<String, ? super Object> params) {
-        if (getConfig_VolumeBackground(params) != null) {
-            getConfig_VolumeBackground(params).delete();
-        }
-        if (getConfig_VolumeIcon(params) != null) {
-            getConfig_VolumeIcon(params).delete();
-        }
-        if (getConfig_VolumeScript(params) != null) {
-            getConfig_VolumeScript(params).delete();
-        }
-        if (getConfig_Script(params) != null) {
-            getConfig_Script(params).delete();
-        }
-        if (getConfig_LicenseFile(params) != null) {
-            getConfig_LicenseFile(params).delete();
-        }
-        APP_BUNDLER.fetchFrom(params).cleanupConfigFiles(params);
-    }
-
-    private static final String hdiutil = "/usr/bin/hdiutil";
-
-    private void prepareDMGSetupScript(String volumeName,
-            Map<String, ? super Object> p) throws IOException {
-        File dmgSetup = getConfig_VolumeScript(p);
-        Log.verbose(MessageFormat.format(
-                I18N.getString("message.preparing-dmg-setup"),
-                dmgSetup.getAbsolutePath()));
-
-        //prepare config for exe
-        Map<String, String> data = new HashMap<>();
-        data.put("DEPLOY_ACTUAL_VOLUME_NAME", volumeName);
-        data.put("DEPLOY_APPLICATION_NAME", APP_NAME.fetchFrom(p));
-
-        data.put("DEPLOY_INSTALL_LOCATION", "(path to desktop folder)");
-        data.put("DEPLOY_INSTALL_NAME", "Desktop");
-
-        Writer w = new BufferedWriter(new FileWriter(dmgSetup));
-        w.write(preprocessTextResource(
-                MacAppBundler.MAC_BUNDLER_PREFIX + dmgSetup.getName(),
-                I18N.getString("resource.dmg-setup-script"),
-                        DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(p),
-                DROP_IN_RESOURCES_ROOT.fetchFrom(p)));
-        w.close();
-    }
-
-    private File getConfig_VolumeScript(Map<String, ? super Object> params) {
-        return new File(CONFIG_ROOT.fetchFrom(params),
-                APP_NAME.fetchFrom(params) + "-dmg-setup.scpt");
-    }
-
-    private File getConfig_VolumeBackground(
-            Map<String, ? super Object> params) {
-        return new File(CONFIG_ROOT.fetchFrom(params),
-                APP_NAME.fetchFrom(params) + "-background.png");
-    }
-
-    private File getConfig_VolumeIcon(Map<String, ? super Object> params) {
-        return new File(CONFIG_ROOT.fetchFrom(params),
-                APP_NAME.fetchFrom(params) + "-volume.icns");
-    }
-
-    private File getConfig_LicenseFile(Map<String, ? super Object> params) {
-        return new File(CONFIG_ROOT.fetchFrom(params),
-                APP_NAME.fetchFrom(params) + "-license.plist");
-    }
-
-    private void prepareLicense(Map<String, ? super Object> params) {
-        try {
-            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;
-                }
-            }
-
-            if (licFile == null) {
-                // this is NPE protection,
-                // validate should have already caught it's absence
-                Log.error("Licence file is null");
-                return;
-            }
-
-            byte[] licenseContentOriginal = Files.readAllBytes(licFile.toPath());
-            String licenseInBase64 =
-                    Base64.getEncoder().encodeToString(licenseContentOriginal);
-
-            Map<String, String> data = new HashMap<>();
-            data.put("APPLICATION_LICENSE_TEXT", licenseInBase64);
-
-            Writer w = new BufferedWriter(
-                    new FileWriter(getConfig_LicenseFile(params)));
-            w.write(preprocessTextResource(
-                    MacAppBundler.MAC_BUNDLER_PREFIX
-                    + getConfig_LicenseFile(params).getName(),
-                    I18N.getString("resource.license-setup"),
-                    DEFAULT_LICENSE_PLIST, data, VERBOSE.fetchFrom(params),
-                    DROP_IN_RESOURCES_ROOT.fetchFrom(params)));
-            w.close();
-
-        } catch (IOException ex) {
-            Log.verbose(ex);
-        }
-    }
-
-    private boolean prepareConfigFiles(Map<String, ? super Object> params)
-            throws IOException {
-        File bgTarget = getConfig_VolumeBackground(params);
-        fetchResource(MacAppBundler.MAC_BUNDLER_PREFIX + bgTarget.getName(),
-                I18N.getString("resource.dmg-background"),
-                DEFAULT_BACKGROUND_IMAGE,
-                bgTarget,
-                VERBOSE.fetchFrom(params),
-                DROP_IN_RESOURCES_ROOT.fetchFrom(params));
-
-        File iconTarget = getConfig_VolumeIcon(params);
-        if (MacAppBundler.ICON_ICNS.fetchFrom(params) == null ||
-                !MacAppBundler.ICON_ICNS.fetchFrom(params).exists()) {
-            fetchResource(
-                    MacAppBundler.MAC_BUNDLER_PREFIX + iconTarget.getName(),
-                    I18N.getString("resource.volume-icon"),
-                    TEMPLATE_BUNDLE_ICON,
-                    iconTarget,
-                    VERBOSE.fetchFrom(params),
-                    DROP_IN_RESOURCES_ROOT.fetchFrom(params));
-        } else {
-            fetchResource(
-                    MacAppBundler.MAC_BUNDLER_PREFIX + iconTarget.getName(),
-                    I18N.getString("resource.volume-icon"),
-                    MacAppBundler.ICON_ICNS.fetchFrom(params),
-                    iconTarget,
-                    VERBOSE.fetchFrom(params),
-                    DROP_IN_RESOURCES_ROOT.fetchFrom(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));
-
-        prepareLicense(params);
-
-        // In theory we need to extract name from results of attach command
-        // However, this will be a problem for customization as name will
-        // possibly change every time and developer will not be able to fix it
-        // As we are using tmp dir chance we get "different" name are low =>
-        // Use fixed name we used for bundle
-        prepareDMGSetupScript(APP_NAME.fetchFrom(params), 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");
-    }
-
-    // Location of SetFile utility may be different depending on MacOS version
-    // We look for several known places and if none of them work will
-    // try ot find it
-    private String findSetFileUtility() {
-        String typicalPaths[] = {"/Developer/Tools/SetFile",
-                "/usr/bin/SetFile", "/Developer/usr/bin/SetFile"};
-
-        for (String path: typicalPaths) {
-            File f = new File(path);
-            if (f.exists() && f.canExecute()) {
-                return path;
-            }
-        }
-
-        // generic find attempt
-        try {
-            ProcessBuilder pb = new ProcessBuilder("xcrun", "-find", "SetFile");
-            Process p = pb.start();
-            InputStreamReader isr = new InputStreamReader(p.getInputStream());
-            BufferedReader br = new BufferedReader(isr);
-            String lineRead = br.readLine();
-            if (lineRead != null) {
-                File f = new File(lineRead);
-                if (f.exists() && f.canExecute()) {
-                    return f.getAbsolutePath();
-                }
-            }
-        } catch (IOException ignored) {}
-
-        return null;
-    }
-
-    private File buildDMG(
-            Map<String, ? super Object> p, File outdir)
-            throws IOException {
-        File imagesRoot = IMAGES_ROOT.fetchFrom(p);
-        if (!imagesRoot.exists()) imagesRoot.mkdirs();
-
-        File protoDMG = new File(imagesRoot, APP_NAME.fetchFrom(p) +"-tmp.dmg");
-        File finalDMG = new File(outdir, INSTALLER_NAME.fetchFrom(p)
-                + INSTALLER_SUFFIX.fetchFrom(p)
-                + ".dmg");
-
-        File srcFolder = APP_IMAGE_BUILD_ROOT.fetchFrom(p);
-        File predefinedImage = StandardBundlerParam.getPredefinedAppImage(p);
-        if (predefinedImage != null) {
-            srcFolder = predefinedImage;
-        }
-
-        Log.verbose(MessageFormat.format(I18N.getString(
-                "message.creating-dmg-file"), finalDMG.getAbsolutePath()));
-
-        protoDMG.delete();
-        if (finalDMG.exists() && !finalDMG.delete()) {
-            throw new IOException(MessageFormat.format(I18N.getString(
-                    "message.dmg-cannot-be-overwritten"),
-                    finalDMG.getAbsolutePath()));
-        }
-
-        protoDMG.getParentFile().mkdirs();
-        finalDMG.getParentFile().mkdirs();
-
-        String hdiUtilVerbosityFlag = Log.isDebug() ? "-verbose" : "-quiet";
-
-        // create temp image
-        ProcessBuilder pb = new ProcessBuilder(
-                hdiutil,
-                "create",
-                hdiUtilVerbosityFlag,
-                "-srcfolder", srcFolder.getAbsolutePath(),
-                "-volname", APP_NAME.fetchFrom(p),
-                "-ov", protoDMG.getAbsolutePath(),
-                "-fs", "HFS+",
-                "-format", "UDRW");
-        IOUtils.exec(pb, false);
-
-        // mount temp image
-        pb = new ProcessBuilder(
-                hdiutil,
-                "attach",
-                protoDMG.getAbsolutePath(),
-                hdiUtilVerbosityFlag,
-                "-mountroot", imagesRoot.getAbsolutePath());
-        IOUtils.exec(pb, false);
-
-        File mountedRoot =
-                new File(imagesRoot.getAbsolutePath(), APP_NAME.fetchFrom(p));
-
-        // volume icon
-        File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns");
-        IOUtils.copyFile(getConfig_VolumeIcon(p),
-                volumeIconFile);
-
-        pb = new ProcessBuilder("osascript",
-                getConfig_VolumeScript(p).getAbsolutePath());
-        IOUtils.exec(pb, false);
-
-        // Indicate that we want a custom icon
-        // NB: attributes of the root directory are ignored
-        // when creating the volume
-        // Therefore we have to do this after we mount image
-        String setFileUtility = findSetFileUtility();
-        if (setFileUtility != null) {
-                //can not find utility => keep going without icon
-            try {
-                volumeIconFile.setWritable(true);
-                // The "creator" attribute on a file is a legacy attribute
-                // but it seems Finder excepts these bytes to be
-                // "icnC" for the volume icon
-                // http://endrift.com/blog/2010/06/14/dmg-files-volume-icons-cli
-                // (might not work on Mac 10.13 with old XCode)
-                pb = new ProcessBuilder(
-                        setFileUtility,
-                        "-c", "icnC",
-                        volumeIconFile.getAbsolutePath());
-                IOUtils.exec(pb, false);
-                volumeIconFile.setReadOnly();
-
-                pb = new ProcessBuilder(
-                        setFileUtility,
-                        "-a", "C",
-                        mountedRoot.getAbsolutePath());
-                IOUtils.exec(pb, false);
-            } catch (IOException ex) {
-                Log.info(ex.getMessage());
-                Log.verbose(
-                    "Cannot enable custom icon using SetFile utility");
-            }
-        } else {
-            Log.verbose(
-                "Skip enabling custom icon as SetFile utility is not found");
-        }
-
-        // Detach the temporary image
-        pb = new ProcessBuilder(
-                hdiutil,
-                "detach",
-                hdiUtilVerbosityFlag,
-                mountedRoot.getAbsolutePath());
-        IOUtils.exec(pb, false);
-
-        // Compress it to a new image
-        pb = new ProcessBuilder(
-                hdiutil,
-                "convert",
-                protoDMG.getAbsolutePath(),
-                hdiUtilVerbosityFlag,
-                "-format", "UDZO",
-                "-o", finalDMG.getAbsolutePath());
-        IOUtils.exec(pb, false);
-
-        //add license if needed
-        if (getConfig_LicenseFile(p).exists()) {
-            //hdiutil unflatten your_image_file.dmg
-            pb = new ProcessBuilder(
-                    hdiutil,
-                    "unflatten",
-                    finalDMG.getAbsolutePath()
-            );
-            IOUtils.exec(pb, false);
-
-            //add license
-            pb = new ProcessBuilder(
-                    hdiutil,
-                    "udifrez",
-                    finalDMG.getAbsolutePath(),
-                    "-xml",
-                    getConfig_LicenseFile(p).getAbsolutePath()
-            );
-            IOUtils.exec(pb, false);
-
-            //hdiutil flatten your_image_file.dmg
-            pb = new ProcessBuilder(
-                    hdiutil,
-                    "flatten",
-                    finalDMG.getAbsolutePath()
-            );
-            IOUtils.exec(pb, false);
-
-        }
-
-        //Delete the temporary image
-        protoDMG.delete();
-
-        Log.info(MessageFormat.format(I18N.getString(
-                "message.output-to-location"),
-                APP_NAME.fetchFrom(p), finalDMG.getAbsolutePath()));
-
-        return finalDMG;
-    }
-
-
-    //////////////////////////////////////////////////////////////////////////
-    // 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 "dmg";
-    }
-
-    @Override
-    public Collection<BundlerParamInfo<?>> getBundleParameters() {
-        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
-        results.addAll(MacAppBundler.getAppBundleParameters());
-        results.addAll(getDMGBundleParameters());
-        return results;
-    }
-
-    public Collection<BundlerParamInfo<?>> getDMGBundleParameters() {
-        Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
-
-        results.addAll(MacAppBundler.getAppBundleParameters());
-        results.addAll(Arrays.asList(
-                INSTALLER_SUFFIX,
-                LICENSE_FILE
-        ));
-
-        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));
-                    }
-                }
-            }
-
-            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;
-    }
-}