--- old/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java 2019-11-18 21:44:48.483468100 -0500 +++ /dev/null 2019-11-18 21:44:50.000000000 -0500 @@ -1,483 +0,0 @@ -/* - * Copyright (c) 2012, 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.*; -import java.nio.file.Files; -import java.text.MessageFormat; -import java.util.*; - -import static jdk.jpackage.internal.StandardBundlerParam.*; - -public class MacDmgBundler extends MacBaseInstallerBundler { - - private static final ResourceBundle I18N = ResourceBundle.getBundle( - "jdk.jpackage.internal.resources.MacResources"); - - 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 INSTALLER_SUFFIX = - new StandardBundlerParam<> ( - "mac.dmg.installerName.suffix", - String.class, - params -> "", - (s, p) -> s); - - public File bundle(Map params, - File outdir) throws PackagerException { - Log.verbose(MessageFormat.format(I18N.getString("message.building-dmg"), - APP_NAME.fetchFrom(params))); - if (!outdir.isDirectory() && !outdir.mkdirs()) { - throw new PackagerException( - "error.cannot-create-output-dir", - outdir.getAbsolutePath()); - } - if (!outdir.canWrite()) { - throw new PackagerException( - "error.cannot-write-to-output-dir", - outdir.getAbsolutePath()); - } - - File appImageDir = APP_IMAGE_TEMP_ROOT.fetchFrom(params); - try { - appImageDir.mkdirs(); - - if (prepareAppBundle(params, true) != 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 buildDMG(params, outdir); - } - return null; - } catch (IOException ex) { - Log.verbose(ex); - throw new PackagerException(ex); - } - } - - private static final String hdiutil = "/usr/bin/hdiutil"; - - private void prepareDMGSetupScript(String volumeName, - Map 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 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(dmgSetup.getName(), - I18N.getString("resource.dmg-setup-script"), - DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(p), - RESOURCE_DIR.fetchFrom(p))); - w.close(); - } - - private File getConfig_VolumeScript(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-dmg-setup.scpt"); - } - - private File getConfig_VolumeBackground( - Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-background.png"); - } - - private File getConfig_VolumeIcon(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-volume.icns"); - } - - private File getConfig_LicenseFile(Map params) { - return new File(CONFIG_ROOT.fetchFrom(params), - APP_NAME.fetchFrom(params) + "-license.plist"); - } - - private void prepareLicense(Map params) { - try { - String licFileStr = LICENSE_FILE.fetchFrom(params); - if (licFileStr == null) { - return; - } - - File licFile = new File(licFileStr); - byte[] licenseContentOriginal = Files.readAllBytes(licFile.toPath()); - String licenseInBase64 = - Base64.getEncoder().encodeToString(licenseContentOriginal); - - Map data = new HashMap<>(); - data.put("APPLICATION_LICENSE_TEXT", licenseInBase64); - - Writer w = new BufferedWriter( - new FileWriter(getConfig_LicenseFile(params))); - w.write(preprocessTextResource( - getConfig_LicenseFile(params).getName(), - I18N.getString("resource.license-setup"), - DEFAULT_LICENSE_PLIST, data, VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params))); - w.close(); - - } catch (IOException ex) { - Log.verbose(ex); - } - } - - private boolean prepareConfigFiles(Map params) - throws IOException { - File bgTarget = getConfig_VolumeBackground(params); - fetchResource(bgTarget.getName(), - I18N.getString("resource.dmg-background"), - DEFAULT_BACKGROUND_IMAGE, - bgTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - - File iconTarget = getConfig_VolumeIcon(params); - if (MacAppBundler.ICON_ICNS.fetchFrom(params) == null || - !MacAppBundler.ICON_ICNS.fetchFrom(params).exists()) { - fetchResource(iconTarget.getName(), - I18N.getString("resource.volume-icon"), - TEMPLATE_BUNDLE_ICON, - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } else { - fetchResource(iconTarget.getName(), - I18N.getString("resource.volume-icon"), - MacAppBundler.ICON_ICNS.fetchFrom(params), - iconTarget, - VERBOSE.fetchFrom(params), - RESOURCE_DIR.fetchFrom(params)); - } - - - fetchResource(getConfig_Script(params).getName(), - I18N.getString("resource.post-install-script"), - (String) null, - getConfig_Script(params), - VERBOSE.fetchFrom(params), - RESOURCE_DIR.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 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 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_TEMP_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.error(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.verbose(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("dmg.bundler.name"); - } - - @Override - public String getDescription() { - return I18N.getString("dmg.bundler.description"); - } - - @Override - public String getID() { - return "dmg"; - } - - @Override - public Collection> getBundleParameters() { - Collection> results = new LinkedHashSet<>(); - results.addAll(MacAppBundler.getAppBundleParameters()); - results.addAll(getDMGBundleParameters()); - return results; - } - - public Collection> getDMGBundleParameters() { - Collection> results = new LinkedHashSet<>(); - - results.addAll(MacAppBundler.getAppBundleParameters()); - results.addAll(Arrays.asList( - INSTALLER_SUFFIX, - LICENSE_FILE - )); - - return results; - } - - - @Override - public boolean validate(Map 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); - - return true; - } catch (RuntimeException re) { - if (re.getCause() instanceof ConfigException) { - throw (ConfigException) re.getCause(); - } else { - throw new ConfigException(re); - } - } - } - - @Override - public File execute(Map params, - File outputParentDir) throws PackagerException { - return bundle(params, outputParentDir); - } - - @Override - public boolean supported(boolean runtimeInstaller) { - return Platform.getPlatform() == Platform.MAC; - } -}