< prev index next >

src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java

Print this page




  74                 File configScript = getConfig_Script(params);
  75                 if (configScript.exists()) {
  76                     Log.verbose(MessageFormat.format(
  77                             I18N.getString("message.running-script"),
  78                             configScript.getAbsolutePath()));
  79                     IOUtils.run("bash", configScript);
  80                 }
  81 
  82                 return buildDMG(params, outdir);
  83             }
  84             return null;
  85         } catch (IOException ex) {
  86             Log.verbose(ex);
  87             throw new PackagerException(ex);
  88         }
  89     }
  90 
  91     private static final String hdiutil = "/usr/bin/hdiutil";
  92 
  93     private void prepareDMGSetupScript(String volumeName,
  94             Map<String, ? super Object> p) throws IOException {
  95         File dmgSetup = getConfig_VolumeScript(p);
  96         Log.verbose(MessageFormat.format(
  97                 I18N.getString("message.preparing-dmg-setup"),
  98                 dmgSetup.getAbsolutePath()));
  99 
 100         //prepare config for exe
 101         Map<String, String> data = new HashMap<>();
 102         data.put("DEPLOY_ACTUAL_VOLUME_NAME", volumeName);
 103         data.put("DEPLOY_APPLICATION_NAME", APP_NAME.fetchFrom(p));
 104 
 105         data.put("DEPLOY_INSTALL_LOCATION", "(path to desktop folder)");
 106         data.put("DEPLOY_INSTALL_NAME", "Desktop");
 107 
 108         try (Writer w = new BufferedWriter(new FileWriter(dmgSetup))) {
 109             w.write(preprocessTextResource(dmgSetup.getName(),
 110                     I18N.getString("resource.dmg-setup-script"),
 111                     DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(p),
 112                     RESOURCE_DIR.fetchFrom(p)));
 113         }
 114     }
 115 
 116     private File getConfig_VolumeScript(Map<String, ? super Object> params) {
 117         return new File(CONFIG_ROOT.fetchFrom(params),
 118                 APP_NAME.fetchFrom(params) + "-dmg-setup.scpt");
 119     }
 120 
 121     private File getConfig_VolumeBackground(
 122             Map<String, ? super Object> params) {
 123         return new File(CONFIG_ROOT.fetchFrom(params),
 124                 APP_NAME.fetchFrom(params) + "-background.png");
 125     }
 126 
 127     private File getConfig_VolumeIcon(Map<String, ? super Object> params) {
 128         return new File(CONFIG_ROOT.fetchFrom(params),
 129                 APP_NAME.fetchFrom(params) + "-volume.icns");
 130     }
 131 
 132     private File getConfig_LicenseFile(Map<String, ? super Object> params) {


 233 
 234         // generic find attempt
 235         try {
 236             ProcessBuilder pb = new ProcessBuilder("xcrun", "-find", "SetFile");
 237             Process p = pb.start();
 238             InputStreamReader isr = new InputStreamReader(p.getInputStream());
 239             BufferedReader br = new BufferedReader(isr);
 240             String lineRead = br.readLine();
 241             if (lineRead != null) {
 242                 File f = new File(lineRead);
 243                 if (f.exists() && f.canExecute()) {
 244                     return f.getAbsolutePath();
 245                 }
 246             }
 247         } catch (IOException ignored) {}
 248 
 249         return null;
 250     }
 251 
 252     private File buildDMG(
 253             Map<String, ? super Object> p, File outdir)
 254             throws IOException {
 255         File imagesRoot = IMAGES_ROOT.fetchFrom(p);
 256         if (!imagesRoot.exists()) imagesRoot.mkdirs();
 257 
 258         File protoDMG = new File(imagesRoot, APP_NAME.fetchFrom(p) +"-tmp.dmg");
 259         File finalDMG = new File(outdir, INSTALLER_NAME.fetchFrom(p)
 260                 + INSTALLER_SUFFIX.fetchFrom(p)
 261                 + ".dmg");
 262 
 263         File srcFolder = APP_IMAGE_TEMP_ROOT.fetchFrom(p);
 264         File predefinedImage = StandardBundlerParam.getPredefinedAppImage(p);

 265         if (predefinedImage != null) {
 266             srcFolder = predefinedImage;
 267         }
 268 
 269         Log.verbose(MessageFormat.format(I18N.getString(
 270                 "message.creating-dmg-file"), finalDMG.getAbsolutePath()));
 271 
 272         protoDMG.delete();
 273         if (finalDMG.exists() && !finalDMG.delete()) {
 274             throw new IOException(MessageFormat.format(I18N.getString(
 275                     "message.dmg-cannot-be-overwritten"),
 276                     finalDMG.getAbsolutePath()));
 277         }
 278 
 279         protoDMG.getParentFile().mkdirs();
 280         finalDMG.getParentFile().mkdirs();
 281 
 282         String hdiUtilVerbosityFlag = Log.isDebug() ? "-verbose" : "-quiet";
 283 
 284         // create temp image
 285         ProcessBuilder pb = new ProcessBuilder(
 286                 hdiutil,
 287                 "create",
 288                 hdiUtilVerbosityFlag,
 289                 "-srcfolder", srcFolder.getAbsolutePath(),
 290                 "-volname", APP_NAME.fetchFrom(p),
 291                 "-ov", protoDMG.getAbsolutePath(),
 292                 "-fs", "HFS+",
 293                 "-format", "UDRW");
 294         IOUtils.exec(pb);
 295 
 296         // mount temp image
 297         pb = new ProcessBuilder(
 298                 hdiutil,
 299                 "attach",
 300                 protoDMG.getAbsolutePath(),
 301                 hdiUtilVerbosityFlag,
 302                 "-mountroot", imagesRoot.getAbsolutePath());
 303         IOUtils.exec(pb);
 304 
 305         File mountedRoot =
 306                 new File(imagesRoot.getAbsolutePath(), APP_NAME.fetchFrom(p));
 307 
 308         // volume icon
 309         File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns");
 310         IOUtils.copyFile(getConfig_VolumeIcon(p),
 311                 volumeIconFile);
 312 
 313         pb = new ProcessBuilder("osascript",
 314                 getConfig_VolumeScript(p).getAbsolutePath());
 315         IOUtils.exec(pb);
 316 
 317         // Indicate that we want a custom icon
 318         // NB: attributes of the root directory are ignored
 319         // when creating the volume
 320         // Therefore we have to do this after we mount image
 321         String setFileUtility = findSetFileUtility();
 322         if (setFileUtility != null) {
 323                 //can not find utility => keep going without icon
 324             try {
 325                 volumeIconFile.setWritable(true);
 326                 // The "creator" attribute on a file is a legacy attribute
 327                 // but it seems Finder excepts these bytes to be
 328                 // "icnC" for the volume icon
 329                 // http://endrift.com/blog/2010/06/14/dmg-files-volume-icons-cli
 330                 // (might not work on Mac 10.13 with old XCode)
 331                 pb = new ProcessBuilder(
 332                         setFileUtility,
 333                         "-c", "icnC",
 334                         volumeIconFile.getAbsolutePath());


 351 
 352         // Detach the temporary image
 353         pb = new ProcessBuilder(
 354                 hdiutil,
 355                 "detach",
 356                 hdiUtilVerbosityFlag,
 357                 mountedRoot.getAbsolutePath());
 358         IOUtils.exec(pb);
 359 
 360         // Compress it to a new image
 361         pb = new ProcessBuilder(
 362                 hdiutil,
 363                 "convert",
 364                 protoDMG.getAbsolutePath(),
 365                 hdiUtilVerbosityFlag,
 366                 "-format", "UDZO",
 367                 "-o", finalDMG.getAbsolutePath());
 368         IOUtils.exec(pb);
 369 
 370         //add license if needed
 371         if (getConfig_LicenseFile(p).exists()) {
 372             //hdiutil unflatten your_image_file.dmg
 373             pb = new ProcessBuilder(
 374                     hdiutil,
 375                     "unflatten",
 376                     finalDMG.getAbsolutePath()
 377             );
 378             IOUtils.exec(pb);
 379 
 380             //add license
 381             pb = new ProcessBuilder(
 382                     hdiutil,
 383                     "udifrez",
 384                     finalDMG.getAbsolutePath(),
 385                     "-xml",
 386                     getConfig_LicenseFile(p).getAbsolutePath()
 387             );
 388             IOUtils.exec(pb);
 389 
 390             //hdiutil flatten your_image_file.dmg
 391             pb = new ProcessBuilder(
 392                     hdiutil,
 393                     "flatten",
 394                     finalDMG.getAbsolutePath()
 395             );
 396             IOUtils.exec(pb);
 397 
 398         }
 399 
 400         //Delete the temporary image
 401         protoDMG.delete();
 402 
 403         Log.verbose(MessageFormat.format(I18N.getString(
 404                 "message.output-to-location"),
 405                 APP_NAME.fetchFrom(p), finalDMG.getAbsolutePath()));
 406 
 407         return finalDMG;
 408     }
 409 
 410 
 411     //////////////////////////////////////////////////////////////////////////
 412     // Implement Bundler
 413     //////////////////////////////////////////////////////////////////////////
 414 
 415     @Override
 416     public String getName() {
 417         return I18N.getString("dmg.bundler.name");
 418     }
 419 
 420     @Override
 421     public String getDescription() {
 422         return I18N.getString("dmg.bundler.description");
 423     }
 424 
 425     @Override




  74                 File configScript = getConfig_Script(params);
  75                 if (configScript.exists()) {
  76                     Log.verbose(MessageFormat.format(
  77                             I18N.getString("message.running-script"),
  78                             configScript.getAbsolutePath()));
  79                     IOUtils.run("bash", configScript);
  80                 }
  81 
  82                 return buildDMG(params, outdir);
  83             }
  84             return null;
  85         } catch (IOException ex) {
  86             Log.verbose(ex);
  87             throw new PackagerException(ex);
  88         }
  89     }
  90 
  91     private static final String hdiutil = "/usr/bin/hdiutil";
  92 
  93     private void prepareDMGSetupScript(String volumeName,
  94             Map<String, ? super Object> params) throws IOException {
  95         File dmgSetup = getConfig_VolumeScript(params);
  96         Log.verbose(MessageFormat.format(
  97                 I18N.getString("message.preparing-dmg-setup"),
  98                 dmgSetup.getAbsolutePath()));
  99 
 100         //prepare config for exe
 101         Map<String, String> data = new HashMap<>();
 102         data.put("DEPLOY_ACTUAL_VOLUME_NAME", volumeName);
 103         data.put("DEPLOY_APPLICATION_NAME", APP_NAME.fetchFrom(params));
 104 
 105         data.put("DEPLOY_INSTALL_LOCATION", "(path to desktop folder)");
 106         data.put("DEPLOY_INSTALL_NAME", "Desktop");
 107 
 108         try (Writer w = new BufferedWriter(new FileWriter(dmgSetup))) {
 109             w.write(preprocessTextResource(dmgSetup.getName(),
 110                     I18N.getString("resource.dmg-setup-script"),
 111                     DEFAULT_DMG_SETUP_SCRIPT, data, VERBOSE.fetchFrom(params),
 112                     RESOURCE_DIR.fetchFrom(params)));
 113         }
 114     }
 115 
 116     private File getConfig_VolumeScript(Map<String, ? super Object> params) {
 117         return new File(CONFIG_ROOT.fetchFrom(params),
 118                 APP_NAME.fetchFrom(params) + "-dmg-setup.scpt");
 119     }
 120 
 121     private File getConfig_VolumeBackground(
 122             Map<String, ? super Object> params) {
 123         return new File(CONFIG_ROOT.fetchFrom(params),
 124                 APP_NAME.fetchFrom(params) + "-background.png");
 125     }
 126 
 127     private File getConfig_VolumeIcon(Map<String, ? super Object> params) {
 128         return new File(CONFIG_ROOT.fetchFrom(params),
 129                 APP_NAME.fetchFrom(params) + "-volume.icns");
 130     }
 131 
 132     private File getConfig_LicenseFile(Map<String, ? super Object> params) {


 233 
 234         // generic find attempt
 235         try {
 236             ProcessBuilder pb = new ProcessBuilder("xcrun", "-find", "SetFile");
 237             Process p = pb.start();
 238             InputStreamReader isr = new InputStreamReader(p.getInputStream());
 239             BufferedReader br = new BufferedReader(isr);
 240             String lineRead = br.readLine();
 241             if (lineRead != null) {
 242                 File f = new File(lineRead);
 243                 if (f.exists() && f.canExecute()) {
 244                     return f.getAbsolutePath();
 245                 }
 246             }
 247         } catch (IOException ignored) {}
 248 
 249         return null;
 250     }
 251 
 252     private File buildDMG(
 253             Map<String, ? super Object> params, File outdir)
 254             throws IOException {
 255         File imagesRoot = IMAGES_ROOT.fetchFrom(params);
 256         if (!imagesRoot.exists()) imagesRoot.mkdirs();
 257 
 258         File protoDMG = new File(imagesRoot,
 259                 APP_NAME.fetchFrom(params) +"-tmp.dmg");
 260         File finalDMG = new File(outdir, INSTALLER_NAME.fetchFrom(params)
 261                 + INSTALLER_SUFFIX.fetchFrom(params) + ".dmg");
 262 
 263         File srcFolder = APP_IMAGE_TEMP_ROOT.fetchFrom(params);
 264         File predefinedImage =
 265                 StandardBundlerParam.getPredefinedAppImage(params);
 266         if (predefinedImage != null) {
 267             srcFolder = predefinedImage;
 268         }
 269 
 270         Log.verbose(MessageFormat.format(I18N.getString(
 271                 "message.creating-dmg-file"), finalDMG.getAbsolutePath()));
 272 
 273         protoDMG.delete();
 274         if (finalDMG.exists() && !finalDMG.delete()) {
 275             throw new IOException(MessageFormat.format(I18N.getString(
 276                     "message.dmg-cannot-be-overwritten"),
 277                     finalDMG.getAbsolutePath()));
 278         }
 279 
 280         protoDMG.getParentFile().mkdirs();
 281         finalDMG.getParentFile().mkdirs();
 282 
 283         String hdiUtilVerbosityFlag = Log.isDebug() ? "-verbose" : "-quiet";
 284 
 285         // create temp image
 286         ProcessBuilder pb = new ProcessBuilder(
 287                 hdiutil,
 288                 "create",
 289                 hdiUtilVerbosityFlag,
 290                 "-srcfolder", srcFolder.getAbsolutePath(),
 291                 "-volname", APP_NAME.fetchFrom(params),
 292                 "-ov", protoDMG.getAbsolutePath(),
 293                 "-fs", "HFS+",
 294                 "-format", "UDRW");
 295         IOUtils.exec(pb);
 296 
 297         // mount temp image
 298         pb = new ProcessBuilder(
 299                 hdiutil,
 300                 "attach",
 301                 protoDMG.getAbsolutePath(),
 302                 hdiUtilVerbosityFlag,
 303                 "-mountroot", imagesRoot.getAbsolutePath());
 304         IOUtils.exec(pb);
 305 
 306         File mountedRoot = new File(imagesRoot.getAbsolutePath(),
 307                 APP_NAME.fetchFrom(params));
 308 
 309         // volume icon
 310         File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns");
 311         IOUtils.copyFile(getConfig_VolumeIcon(params),
 312                 volumeIconFile);
 313 
 314         pb = new ProcessBuilder("osascript",
 315                 getConfig_VolumeScript(params).getAbsolutePath());
 316         IOUtils.exec(pb);
 317 
 318         // Indicate that we want a custom icon
 319         // NB: attributes of the root directory are ignored
 320         // when creating the volume
 321         // Therefore we have to do this after we mount image
 322         String setFileUtility = findSetFileUtility();
 323         if (setFileUtility != null) {
 324                 //can not find utility => keep going without icon
 325             try {
 326                 volumeIconFile.setWritable(true);
 327                 // The "creator" attribute on a file is a legacy attribute
 328                 // but it seems Finder excepts these bytes to be
 329                 // "icnC" for the volume icon
 330                 // http://endrift.com/blog/2010/06/14/dmg-files-volume-icons-cli
 331                 // (might not work on Mac 10.13 with old XCode)
 332                 pb = new ProcessBuilder(
 333                         setFileUtility,
 334                         "-c", "icnC",
 335                         volumeIconFile.getAbsolutePath());


 352 
 353         // Detach the temporary image
 354         pb = new ProcessBuilder(
 355                 hdiutil,
 356                 "detach",
 357                 hdiUtilVerbosityFlag,
 358                 mountedRoot.getAbsolutePath());
 359         IOUtils.exec(pb);
 360 
 361         // Compress it to a new image
 362         pb = new ProcessBuilder(
 363                 hdiutil,
 364                 "convert",
 365                 protoDMG.getAbsolutePath(),
 366                 hdiUtilVerbosityFlag,
 367                 "-format", "UDZO",
 368                 "-o", finalDMG.getAbsolutePath());
 369         IOUtils.exec(pb);
 370 
 371         //add license if needed
 372         if (getConfig_LicenseFile(params).exists()) {
 373             //hdiutil unflatten your_image_file.dmg
 374             pb = new ProcessBuilder(
 375                     hdiutil,
 376                     "unflatten",
 377                     finalDMG.getAbsolutePath()
 378             );
 379             IOUtils.exec(pb);
 380 
 381             //add license
 382             pb = new ProcessBuilder(
 383                     hdiutil,
 384                     "udifrez",
 385                     finalDMG.getAbsolutePath(),
 386                     "-xml",
 387                     getConfig_LicenseFile(params).getAbsolutePath()
 388             );
 389             IOUtils.exec(pb);
 390 
 391             //hdiutil flatten your_image_file.dmg
 392             pb = new ProcessBuilder(
 393                     hdiutil,
 394                     "flatten",
 395                     finalDMG.getAbsolutePath()
 396             );
 397             IOUtils.exec(pb);
 398 
 399         }
 400 
 401         //Delete the temporary image
 402         protoDMG.delete();
 403 
 404         Log.verbose(MessageFormat.format(I18N.getString(
 405                 "message.output-to-location"),
 406                 APP_NAME.fetchFrom(params), finalDMG.getAbsolutePath()));
 407 
 408         return finalDMG;
 409     }
 410 
 411 
 412     //////////////////////////////////////////////////////////////////////////
 413     // Implement Bundler
 414     //////////////////////////////////////////////////////////////////////////
 415 
 416     @Override
 417     public String getName() {
 418         return I18N.getString("dmg.bundler.name");
 419     }
 420 
 421     @Override
 422     public String getDescription() {
 423         return I18N.getString("dmg.bundler.description");
 424     }
 425 
 426     @Override


< prev index next >