71 "Info-lite.plist.template"; 72 private static final String TEMPLATE_RUNTIME_INFO_PLIST = 73 "Runtime-Info.plist.template"; 74 75 private final Path root; 76 private final Path contentsDir; 77 private final Path javaDir; 78 private final Path javaModsDir; 79 private final Path resourcesDir; 80 private final Path macOSDir; 81 private final Path runtimeDir; 82 private final Path runtimeRoot; 83 private final Path mdir; 84 85 private final Map<String, ? super Object> params; 86 87 private static List<String> keyChains; 88 89 public static final BundlerParamInfo<Boolean> 90 MAC_CONFIGURE_LAUNCHER_IN_PLIST = new StandardBundlerParam<>( 91 I18N.getString("param.configure-launcher-in-plist"), 92 I18N.getString( 93 "param.configure-launcher-in-plist.description"), 94 "mac.configure-launcher-in-plist", 95 Boolean.class, 96 params -> Boolean.FALSE, 97 (s, p) -> Boolean.valueOf(s)); 98 99 public static final BundlerParamInfo<String> MAC_CATEGORY = 100 new StandardBundlerParam<>( 101 I18N.getString("param.category-name"), 102 I18N.getString("param.category-name.description"), 103 "mac.category", 104 String.class, 105 CATEGORY::fetchFrom, 106 (s, p) -> s 107 ); 108 109 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME = 110 new StandardBundlerParam<>( 111 I18N.getString("param.cfbundle-name.name"), 112 I18N.getString("param.cfbundle-name.description"), 113 "mac.CFBundleName", 114 String.class, 115 params -> null, 116 (s, p) -> s); 117 118 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_IDENTIFIER = 119 new StandardBundlerParam<>( 120 I18N.getString("param.cfbundle-identifier.name"), 121 I18N.getString("param.cfbundle-identifier.description"), 122 Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), 123 String.class, 124 IDENTIFIER::fetchFrom, 125 (s, p) -> s); 126 127 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_VERSION = 128 new StandardBundlerParam<>( 129 I18N.getString("param.cfbundle-version.name"), 130 I18N.getString("param.cfbundle-version.description"), 131 "mac.CFBundleVersion", 132 String.class, 133 p -> { 134 String s = VERSION.fetchFrom(p); 135 if (validCFBundleVersion(s)) { 136 return s; 137 } else { 138 return "100"; 139 } 140 }, 141 (s, p) -> s); 142 143 public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON = 144 new StandardBundlerParam<>( 145 I18N.getString("param.default-icon-icns"), 146 I18N.getString("param.default-icon-icns.description"), 147 ".mac.default.icns", 148 String.class, 149 params -> TEMPLATE_BUNDLE_ICON, 150 (s, p) -> s); 151 152 public static final BundlerParamInfo<File> ICON_ICNS = 153 new StandardBundlerParam<>( 154 I18N.getString("param.icon-icns.name"), 155 I18N.getString("param.icon-icns.description"), 156 "icon.icns", 157 File.class, 158 params -> { 159 File f = ICON.fetchFrom(params); 160 if (f != null && !f.getName().toLowerCase().endsWith(".icns")) { 161 Log.error(MessageFormat.format( 162 I18N.getString("message.icon-not-icns"), f)); 163 return null; 164 } 165 return f; 166 }, 167 (s, p) -> new File(s)); 168 169 public static final StandardBundlerParam<Boolean> SIGN_BUNDLE = 170 new StandardBundlerParam<>( 171 I18N.getString("param.sign-bundle.name"), 172 I18N.getString("param.sign-bundle.description"), 173 Arguments.CLIOptions.MAC_SIGN.getId(), 174 Boolean.class, 175 params -> false, 176 // valueOf(null) is false, we actually do want null in some cases 177 (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? 178 null : Boolean.valueOf(s) 179 ); 180 181 public MacAppImageBuilder(Map<String, Object> config, Path imageOutDir) 182 throws IOException { 183 super(config, imageOutDir.resolve(APP_NAME.fetchFrom(config) 184 + ".app/Contents/PlugIns/Java.runtime/Contents/Home")); 185 186 Objects.requireNonNull(imageOutDir); 187 188 this.params = config; 189 this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params) + ".app"); 190 this.contentsDir = root.resolve("Contents"); 191 this.javaDir = contentsDir.resolve("Java"); 192 this.javaModsDir = javaDir.resolve("mods"); 317 // Generate PkgInfo 318 File pkgInfoFile = new File(contentsDir.toFile(), "PkgInfo"); 319 pkgInfoFile.createNewFile(); 320 writePkgInfo(pkgInfoFile); 321 322 Path executable = macOSDir.resolve(getLauncherName(params)); 323 324 // create the main app launcher 325 try (InputStream is_launcher = 326 getResourceAsStream("jpackageapplauncher"); 327 InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { 328 // Copy executable and library to MacOS folder 329 writeEntry(is_launcher, executable); 330 writeEntry(is_lib, macOSDir.resolve(LIBRARY_NAME)); 331 } 332 executable.toFile().setExecutable(true, false); 333 // generate main app launcher config file 334 File cfg = new File(root.toFile(), getLauncherCfgName(params)); 335 writeCfgFile(params, cfg, "$APPDIR/PlugIns/Java.runtime"); 336 337 // create secondary app launcher(s) and config file(s) 338 List<Map<String, ? super Object>> entryPoints = 339 StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(params); 340 for (Map<String, ? super Object> entryPoint : entryPoints) { 341 Map<String, ? super Object> tmp = new HashMap<>(originalParams); 342 tmp.putAll(entryPoint); 343 344 // add executable for secondary launcher 345 Path secondaryExecutable = macOSDir.resolve(getLauncherName(tmp)); 346 try (InputStream is = getResourceAsStream("jpackageapplauncher");) { 347 writeEntry(is, secondaryExecutable); 348 } 349 secondaryExecutable.toFile().setExecutable(true, false); 350 351 // add config file for secondary launcher 352 cfg = new File(root.toFile(), getLauncherCfgName(tmp)); 353 writeCfgFile(tmp, cfg, "$APPDIR/PlugIns/Java.runtime"); 354 } 355 356 // Copy class path entries to Java folder 357 copyClassPathEntries(javaDir); 358 359 /*********** Take care of "config" files *******/ 360 File icon = ICON_ICNS.fetchFrom(params); 361 362 InputStream in = locateResource( 363 APP_NAME.fetchFrom(params) + ".icns", 364 "icon", 365 DEFAULT_ICNS_ICON.fetchFrom(params), 366 icon, 367 VERBOSE.fetchFrom(params), 368 RESOURCE_DIR.fetchFrom(params)); 369 Files.copy(in, 370 resourcesDir.resolve(APP_NAME.fetchFrom(params) + ".icns"), 371 StandardCopyOption.REPLACE_EXISTING); 464 String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params); 465 if (bn.length() > 16) { 466 Log.error(MessageFormat.format(I18N.getString( 467 "message.bundle-name-too-long-warning"), 468 MAC_CF_BUNDLE_NAME.getID(), bn)); 469 } 470 return MAC_CF_BUNDLE_NAME.fetchFrom(params); 471 } else if (APP_NAME.fetchFrom(params) != null) { 472 return APP_NAME.fetchFrom(params); 473 } else { 474 String nm = MAIN_CLASS.fetchFrom(params); 475 if (nm.length() > 16) { 476 nm = nm.substring(0, 16); 477 } 478 return nm; 479 } 480 } 481 482 private void writeRuntimeInfoPlist(File file) throws IOException { 483 Map<String, String> data = new HashMap<>(); 484 String identifier = RUNTIME_INSTALLER.fetchFrom(params) ? 485 MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) : 486 "com.oracle.java." + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params); 487 data.put("CF_BUNDLE_IDENTIFIER", identifier); 488 String name = RUNTIME_INSTALLER.fetchFrom(params) ? 489 getBundleName(params): "Java Runtime Image"; 490 data.put("CF_BUNDLE_NAME", name); 491 data.put("CF_BUNDLE_VERSION", VERSION.fetchFrom(params)); 492 data.put("CF_BUNDLE_SHORT_VERSION_STRING", VERSION.fetchFrom(params)); 493 494 Writer w = new BufferedWriter(new FileWriter(file)); 495 w.write(preprocessTextResource("Runtime-Info.plist", 496 I18N.getString("resource.runtime-info-plist"), 497 TEMPLATE_RUNTIME_INFO_PLIST, 498 data, 499 VERBOSE.fetchFrom(params), 500 RESOURCE_DIR.fetchFrom(params))); 501 w.close(); 502 } 503 504 private void writeInfoPlist(File file) throws IOException { 505 Log.verbose(MessageFormat.format(I18N.getString( 506 "message.preparing-info-plist"), file.getAbsolutePath())); 507 508 //prepare config for exe | 71 "Info-lite.plist.template"; 72 private static final String TEMPLATE_RUNTIME_INFO_PLIST = 73 "Runtime-Info.plist.template"; 74 75 private final Path root; 76 private final Path contentsDir; 77 private final Path javaDir; 78 private final Path javaModsDir; 79 private final Path resourcesDir; 80 private final Path macOSDir; 81 private final Path runtimeDir; 82 private final Path runtimeRoot; 83 private final Path mdir; 84 85 private final Map<String, ? super Object> params; 86 87 private static List<String> keyChains; 88 89 public static final BundlerParamInfo<Boolean> 90 MAC_CONFIGURE_LAUNCHER_IN_PLIST = new StandardBundlerParam<>( 91 "mac.configure-launcher-in-plist", 92 Boolean.class, 93 params -> Boolean.FALSE, 94 (s, p) -> Boolean.valueOf(s)); 95 96 public static final EnumeratedBundlerParam<String> MAC_CATEGORY = 97 new EnumeratedBundlerParam<>( 98 Arguments.CLIOptions.MAC_APP_STORE_CATEGORY.getId(), 99 String.class, 100 params -> "Unknown", 101 (s, p) -> s, 102 MacAppBundler.getMacCategories(), 103 false //strict - for MacStoreBundler this should be strict 104 ); 105 106 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME = 107 new StandardBundlerParam<>( 108 "mac.CFBundleName", 109 String.class, 110 params -> null, 111 (s, p) -> s); 112 113 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_IDENTIFIER = 114 new StandardBundlerParam<>( 115 Arguments.CLIOptions.MAC_BUNDLE_IDENTIFIER.getId(), 116 String.class, 117 IDENTIFIER::fetchFrom, 118 (s, p) -> s); 119 120 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_VERSION = 121 new StandardBundlerParam<>( 122 "mac.CFBundleVersion", 123 String.class, 124 p -> { 125 String s = VERSION.fetchFrom(p); 126 if (validCFBundleVersion(s)) { 127 return s; 128 } else { 129 return "100"; 130 } 131 }, 132 (s, p) -> s); 133 134 public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON = 135 new StandardBundlerParam<>( 136 ".mac.default.icns", 137 String.class, 138 params -> TEMPLATE_BUNDLE_ICON, 139 (s, p) -> s); 140 141 public static final BundlerParamInfo<File> ICON_ICNS = 142 new StandardBundlerParam<>( 143 "icon.icns", 144 File.class, 145 params -> { 146 File f = ICON.fetchFrom(params); 147 if (f != null && !f.getName().toLowerCase().endsWith(".icns")) { 148 Log.error(MessageFormat.format( 149 I18N.getString("message.icon-not-icns"), f)); 150 return null; 151 } 152 return f; 153 }, 154 (s, p) -> new File(s)); 155 156 public static final StandardBundlerParam<Boolean> SIGN_BUNDLE = 157 new StandardBundlerParam<>( 158 Arguments.CLIOptions.MAC_SIGN.getId(), 159 Boolean.class, 160 params -> false, 161 // valueOf(null) is false, we actually do want null in some cases 162 (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? 163 null : Boolean.valueOf(s) 164 ); 165 166 public MacAppImageBuilder(Map<String, Object> config, Path imageOutDir) 167 throws IOException { 168 super(config, imageOutDir.resolve(APP_NAME.fetchFrom(config) 169 + ".app/Contents/PlugIns/Java.runtime/Contents/Home")); 170 171 Objects.requireNonNull(imageOutDir); 172 173 this.params = config; 174 this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params) + ".app"); 175 this.contentsDir = root.resolve("Contents"); 176 this.javaDir = contentsDir.resolve("Java"); 177 this.javaModsDir = javaDir.resolve("mods"); 302 // Generate PkgInfo 303 File pkgInfoFile = new File(contentsDir.toFile(), "PkgInfo"); 304 pkgInfoFile.createNewFile(); 305 writePkgInfo(pkgInfoFile); 306 307 Path executable = macOSDir.resolve(getLauncherName(params)); 308 309 // create the main app launcher 310 try (InputStream is_launcher = 311 getResourceAsStream("jpackageapplauncher"); 312 InputStream is_lib = getResourceAsStream(LIBRARY_NAME)) { 313 // Copy executable and library to MacOS folder 314 writeEntry(is_launcher, executable); 315 writeEntry(is_lib, macOSDir.resolve(LIBRARY_NAME)); 316 } 317 executable.toFile().setExecutable(true, false); 318 // generate main app launcher config file 319 File cfg = new File(root.toFile(), getLauncherCfgName(params)); 320 writeCfgFile(params, cfg, "$APPDIR/PlugIns/Java.runtime"); 321 322 // create additional app launcher(s) and config file(s) 323 List<Map<String, ? super Object>> entryPoints = 324 StandardBundlerParam.ADD_LAUNCHERS.fetchFrom(params); 325 for (Map<String, ? super Object> entryPoint : entryPoints) { 326 Map<String, ? super Object> tmp = new HashMap<>(originalParams); 327 tmp.putAll(entryPoint); 328 329 // add executable for add launcher 330 Path addExecutable = macOSDir.resolve(getLauncherName(tmp)); 331 try (InputStream is = getResourceAsStream("jpackageapplauncher");) { 332 writeEntry(is, addExecutable); 333 } 334 addExecutable.toFile().setExecutable(true, false); 335 336 // add config file for add launcher 337 cfg = new File(root.toFile(), getLauncherCfgName(tmp)); 338 writeCfgFile(tmp, cfg, "$APPDIR/PlugIns/Java.runtime"); 339 } 340 341 // Copy class path entries to Java folder 342 copyClassPathEntries(javaDir); 343 344 /*********** Take care of "config" files *******/ 345 File icon = ICON_ICNS.fetchFrom(params); 346 347 InputStream in = locateResource( 348 APP_NAME.fetchFrom(params) + ".icns", 349 "icon", 350 DEFAULT_ICNS_ICON.fetchFrom(params), 351 icon, 352 VERBOSE.fetchFrom(params), 353 RESOURCE_DIR.fetchFrom(params)); 354 Files.copy(in, 355 resourcesDir.resolve(APP_NAME.fetchFrom(params) + ".icns"), 356 StandardCopyOption.REPLACE_EXISTING); 449 String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params); 450 if (bn.length() > 16) { 451 Log.error(MessageFormat.format(I18N.getString( 452 "message.bundle-name-too-long-warning"), 453 MAC_CF_BUNDLE_NAME.getID(), bn)); 454 } 455 return MAC_CF_BUNDLE_NAME.fetchFrom(params); 456 } else if (APP_NAME.fetchFrom(params) != null) { 457 return APP_NAME.fetchFrom(params); 458 } else { 459 String nm = MAIN_CLASS.fetchFrom(params); 460 if (nm.length() > 16) { 461 nm = nm.substring(0, 16); 462 } 463 return nm; 464 } 465 } 466 467 private void writeRuntimeInfoPlist(File file) throws IOException { 468 Map<String, String> data = new HashMap<>(); 469 String identifier = StandardBundlerParam.isRuntimeInstaller(params) ? 470 MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) : 471 "com.oracle.java." + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params); 472 data.put("CF_BUNDLE_IDENTIFIER", identifier); 473 String name = StandardBundlerParam.isRuntimeInstaller(params) ? 474 getBundleName(params): "Java Runtime Image"; 475 data.put("CF_BUNDLE_NAME", name); 476 data.put("CF_BUNDLE_VERSION", VERSION.fetchFrom(params)); 477 data.put("CF_BUNDLE_SHORT_VERSION_STRING", VERSION.fetchFrom(params)); 478 479 Writer w = new BufferedWriter(new FileWriter(file)); 480 w.write(preprocessTextResource("Runtime-Info.plist", 481 I18N.getString("resource.runtime-info-plist"), 482 TEMPLATE_RUNTIME_INFO_PLIST, 483 data, 484 VERBOSE.fetchFrom(params), 485 RESOURCE_DIR.fetchFrom(params))); 486 w.close(); 487 } 488 489 private void writeInfoPlist(File file) throws IOException { 490 Log.verbose(MessageFormat.format(I18N.getString( 491 "message.preparing-info-plist"), file.getAbsolutePath())); 492 493 //prepare config for exe |