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