1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.javafx.tools.packager.bundlers; 26 27 import com.oracle.bundlers.*; 28 import com.oracle.bundlers.JreUtils.Rule; 29 import com.sun.javafx.tools.packager.Log; 30 import com.sun.javafx.tools.resource.mac.MacResources; 31 32 import java.io.*; 33 import java.net.MalformedURLException; 34 import java.net.URL; 35 import java.text.MessageFormat; 36 import java.util.*; 37 38 import static com.oracle.bundlers.StandardBundlerParam.*; 39 import static com.oracle.bundlers.mac.MacBaseInstallerBundler.getPredefinedImage; 40 41 public class MacAppBundler extends AbstractBundler { 42 43 private static final ResourceBundle I18N = 44 ResourceBundle.getBundle("com.oracle.bundlers.mac.MacAppBundler"); 45 46 public final static String MAC_BUNDLER_PREFIX = 47 BUNDLER_PREFIX + "macosx" + File.separator; 48 49 private static final String EXECUTABLE_NAME = "JavaAppLauncher"; 50 private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns"; 51 private static final String OS_TYPE_CODE = "APPL"; 52 private static final String TEMPLATE_INFO_PLIST = "Info.plist.template"; 53 54 private static Map<String, String> getMacCategories() { 55 Map<String, String> map = new HashMap<>(); 56 map.put("Business", "public.app-category.business"); 57 map.put("Developer Tools", "public.app-category.developer-tools"); 58 map.put("Education", "public.app-category.education"); 59 map.put("Entertainment", "public.app-category.entertainment"); 60 map.put("Finance", "public.app-category.finance"); 61 map.put("Games", "public.app-category.games"); 62 map.put("Graphics & Design", "public.app-category.graphics-design"); 63 map.put("Healthcare & Fitness", "public.app-category.healthcare-fitness"); 64 map.put("Lifestyle", "public.app-category.lifestyle"); 65 map.put("Medical", "public.app-category.medical"); 66 map.put("Music", "public.app-category.music"); 67 map.put("News", "public.app-category.news"); 68 map.put("Photography", "public.app-category.photography"); 69 map.put("Productivity", "public.app-category.productivity"); 70 map.put("Reference", "public.app-category.reference"); 71 map.put("Social Networking", "public.app-category.social-networking"); 72 map.put("Sports", "public.app-category.sports"); 73 map.put("Travel", "public.app-category.travel"); 74 map.put("Utilities", "public.app-category.utilities"); 75 map.put("Video", "public.app-category.video"); 76 map.put("Weather", "public.app-category.weather"); 77 78 map.put("Action Games", "public.app-category.action-games"); 79 map.put("Adventure Games", "public.app-category.adventure-games"); 80 map.put("Arcade Games", "public.app-category.arcade-games"); 81 map.put("Board Games", "public.app-category.board-games"); 82 map.put("Card Games", "public.app-category.card-games"); 83 map.put("Casino Games", "public.app-category.casino-games"); 84 map.put("Dice Games", "public.app-category.dice-games"); 85 map.put("Educational Games", "public.app-category.educational-games"); 86 map.put("Family Games", "public.app-category.family-games"); 87 map.put("Kids Games", "public.app-category.kids-games"); 88 map.put("Music Games", "public.app-category.music-games"); 89 map.put("Puzzle Games", "public.app-category.puzzle-games"); 90 map.put("Racing Games", "public.app-category.racing-games"); 91 map.put("Role Playing Games", "public.app-category.role-playing-games"); 92 map.put("Simulation Games", "public.app-category.simulation-games"); 93 map.put("Sports Games", "public.app-category.sports-games"); 94 map.put("Strategy Games", "public.app-category.strategy-games"); 95 map.put("Trivia Games", "public.app-category.trivia-games"); 96 map.put("Word Games", "public.app-category.word-games"); 97 98 return map; 99 } 100 101 public static final EnumeratedBundlerParam<String> MAC_CATEGORY = 102 new EnumeratedBundlerParam<>( 103 "Category", 104 "Mac App Store Categories. Note that the key is the string to display to the user and the value is the id of the category", 105 "mac.category", 106 String.class, 107 new String[] {CATEGORY.getID()}, 108 params -> "Unknown", 109 false, 110 (s, p) -> s, 111 getMacCategories(), 112 false //strict - for MacStoreBundler this should be strict 113 ); 114 115 public static final BundlerParamInfo<String> MAC_CF_BUNDLE_NAME = 116 new StandardBundlerParam<>( 117 "CFBundleName", 118 "The name of the app as it appears in the Menu Bar. This can be different from the application name. This name should be less than 16 characters long and be suitable for displaying in the menu bar and the app’s Info window.", 119 "mac.CFBundleName", 120 String.class, 121 null, 122 params -> null, 123 false, 124 (s, p) -> s); 125 126 public static final BundlerParamInfo<File> CONFIG_ROOT = new StandardBundlerParam<>( 127 I18N.getString("param.config-root.name"), 128 I18N.getString("param.config-root.description"), 129 "configRoot", 130 File.class, 131 null, 132 params -> { 133 File configRoot = new File(BUILD_ROOT.fetchFrom(params), "macosx"); 134 configRoot.mkdirs(); 135 return configRoot; 136 }, 137 false, 138 (s, p) -> new File(s)); 139 140 public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new StandardBundlerParam<>( 141 "Launcher URL", 142 "Override the packager default launcher with a custom launcher.", 143 "mac.launcher.url", 144 URL.class, 145 null, 146 params -> MacResources.class.getResource(EXECUTABLE_NAME), 147 false, 148 (s, p) -> { 149 try { 150 return new URL(s); 151 } catch (MalformedURLException e) { 152 Log.info(e.toString()); 153 return null; 154 } 155 }); 156 157 public static final BundlerParamInfo<String> DEFAULT_ICNS_ICON = new StandardBundlerParam<>( 158 "Default Icon", 159 "The Default Icon for when a user does not specify an icns file.", 160 ".mac.default.icns", 161 String.class, 162 null, 163 params -> TEMPLATE_BUNDLE_ICON, 164 false, 165 (s, p) -> s); 166 167 //Subsetting of JRE is restricted. 168 //JRE README defines what is allowed to strip: 169 // http://www.oracle.com/technetwork/java/javase/jre-7-readme-430162.html //TODO update when 8 goes GA 170 // 171 public static final BundlerParamInfo<Rule[]> MAC_JDK_RULES = new StandardBundlerParam<>( 172 "", 173 "", 174 ".mac-jdk.runtime.rules", 175 Rule[].class, 176 null, 177 params -> new Rule[]{ 178 Rule.suffixNeg("macos/libjli.dylib"), 179 Rule.suffixNeg("resources"), 180 Rule.suffixNeg("home/bin"), 181 Rule.suffixNeg("home/db"), 182 Rule.suffixNeg("home/demo"), 183 Rule.suffixNeg("home/include"), 184 Rule.suffixNeg("home/lib"), 185 Rule.suffixNeg("home/man"), 186 Rule.suffixNeg("home/release"), 187 Rule.suffixNeg("home/sample"), 188 Rule.suffixNeg("home/src.zip"), 189 //"home/rt" is not part of the official builds 190 // but we may be creating this symlink to make older NB projects 191 // happy. Make sure to not include it into final artifact 192 Rule.suffixNeg("home/rt"), 193 Rule.suffixNeg("jre/bin"), 194 Rule.suffixNeg("jre/bin/rmiregistry"), 195 Rule.suffixNeg("jre/bin/tnameserv"), 196 Rule.suffixNeg("jre/bin/keytool"), 197 Rule.suffixNeg("jre/bin/klist"), 198 Rule.suffixNeg("jre/bin/ktab"), 199 Rule.suffixNeg("jre/bin/policytool"), 200 Rule.suffixNeg("jre/bin/orbd"), 201 Rule.suffixNeg("jre/bin/servertool"), 202 Rule.suffixNeg("jre/bin/javaws"), 203 Rule.suffixNeg("jre/bin/java"), 204 //Rule.suffixNeg("jre/lib/ext"), //need some of jars there for https to work 205 Rule.suffixNeg("jre/lib/nibs"), 206 //keep core deploy APIs but strip plugin dll 207 //Rule.suffixNeg("jre/lib/deploy"), 208 //Rule.suffixNeg("jre/lib/deploy.jar"), 209 //Rule.suffixNeg("jre/lib/javaws.jar"), 210 //Rule.suffixNeg("jre/lib/libdeploy.dylib"), 211 //Rule.suffixNeg("jre/lib/plugin.jar"), 212 Rule.suffixNeg("jre/lib/libnpjp2.dylib"), 213 Rule.suffixNeg("jre/lib/security/javaws.policy"), 214 Rule.substrNeg("Contents/Info.plist") 215 }, 216 false, 217 (s, p) -> null 218 ); 219 220 public static final BundlerParamInfo<RelativeFileSet> MAC_RUNTIME = new StandardBundlerParam<>( 221 RUNTIME.getName(), 222 RUNTIME.getDescription(), 223 RUNTIME.getID(), 224 RelativeFileSet.class, 225 null, 226 params -> extractMacRuntime(System.getProperty("java.home"), params), 227 false, 228 MacAppBundler::extractMacRuntime 229 ); 230 231 public static RelativeFileSet extractMacRuntime(String base, Map<String, ? super Object> params) { 232 if (base.endsWith("/Home")) { 233 throw new IllegalArgumentException("Currently Macs require a JDK to package"); 234 } else if (base.endsWith("/Home/jre")) { 235 File baseDir = new File(base).getParentFile().getParentFile().getParentFile(); 236 return JreUtils.extractJreAsRelativeFileSet(baseDir.toString(), 237 MAC_JDK_RULES.fetchFrom(params)); 238 } else { 239 // for now presume we are pointed to the top of a JDK 240 return JreUtils.extractJreAsRelativeFileSet(base, 241 MAC_JDK_RULES.fetchFrom(params)); 242 } 243 } 244 245 public MacAppBundler() { 246 super(); 247 baseResourceLoader = MacResources.class; 248 } 249 250 @Override 251 public boolean validate(Map<String, ? super Object> params) throws UnsupportedPlatformException, ConfigException { 252 try { 253 logParameters(params); 254 return doValidate(params); 255 } catch (RuntimeException re) { 256 throw new ConfigException(re); 257 } 258 } 259 260 //to be used by chained bundlers, e.g. by EXE bundler to avoid 261 // skipping validation if p.type does not include "image" 262 public boolean doValidate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException { 263 if (!System.getProperty("os.name").toLowerCase().contains("os x")) { 264 throw new UnsupportedPlatformException(); 265 } 266 267 if (getPredefinedImage(p) != null) { 268 return true; 269 } 270 271 if (MAIN_JAR.fetchFrom(p) == null) { 272 throw new ConfigException( 273 "Main application jar is missing.", 274 "Make sure to use fx:jar task to create main application jar."); 275 } 276 277 //validate required inputs 278 if (USE_FX_PACKAGING.fetchFrom(p)) { 279 testRuntime(p, new String[] {"Contents/Home/jre/lib/ext/jfxrt.jar", "Contents/Home/jre/lib/jfxrt.jar"}); 280 } 281 282 return true; 283 } 284 285 286 private File getConfig_InfoPlist(Map<String, ? super Object> params) { 287 return new File(CONFIG_ROOT.fetchFrom(params), "Info.plist"); 288 } 289 290 private File getConfig_Icon(Map<String, ? super Object> params) { 291 return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + ".icns"); 292 } 293 294 private void prepareConfigFiles(Map<String, ? super Object> params) throws IOException { 295 File infoPlistFile = getConfig_InfoPlist(params); 296 infoPlistFile.createNewFile(); 297 writeInfoPlist(infoPlistFile, params); 298 299 // Copy icon to Resources folder 300 prepareIcon(params); 301 } 302 303 public File doBundle(Map<String, ? super Object> p, File outputDirectory, boolean dependentTask) { 304 File rootDirectory = null; 305 try { 306 final File predefinedImage = getPredefinedImage(p); 307 if (predefinedImage != null) { 308 return predefinedImage; 309 } 310 311 File file = BUILD_ROOT.fetchFrom(p); 312 313 //prepare config resources (we will copy them to the bundle later) 314 // NB: explicitly saving them to simplify customization 315 prepareConfigFiles(p); 316 317 // Create directory structure 318 rootDirectory = new File(outputDirectory, APP_NAME.fetchFrom(p) + ".app"); 319 IOUtils.deleteRecursive(rootDirectory); 320 rootDirectory.mkdirs(); 321 322 if (!dependentTask) { 323 Log.info("Creating app bundle: " + rootDirectory.getAbsolutePath()); 324 } 325 326 File contentsDirectory = new File(rootDirectory, "Contents"); 327 contentsDirectory.mkdirs(); 328 329 File macOSDirectory = new File(contentsDirectory, "MacOS"); 330 macOSDirectory.mkdirs(); 331 332 File javaDirectory = new File(contentsDirectory, "Java"); 333 javaDirectory.mkdirs(); 334 335 File plugInsDirectory = new File(contentsDirectory, "PlugIns"); 336 337 File resourcesDirectory = new File(contentsDirectory, "Resources"); 338 resourcesDirectory.mkdirs(); 339 340 // Generate PkgInfo 341 File pkgInfoFile = new File(contentsDirectory, "PkgInfo"); 342 pkgInfoFile.createNewFile(); 343 writePkgInfo(pkgInfoFile); 344 345 // Copy executable to MacOS folder 346 File executableFile = new File(macOSDirectory, getLauncherName(p)); 347 IOUtils.copyFromURL( 348 RAW_EXECUTABLE_URL.fetchFrom(p), 349 executableFile); 350 351 executableFile.setExecutable(true, false); 352 353 // Copy runtime to PlugIns folder 354 copyRuntime(plugInsDirectory, p); 355 356 // Copy class path entries to Java folder 357 copyClassPathEntries(javaDirectory, p); 358 359 //TODO: Need to support adding native libraries. 360 // Copy library path entries to MacOS folder 361 //copyLibraryPathEntries(macOSDirectory); 362 363 /*********** Take care of "config" files *******/ 364 // Copy icon to Resources folder 365 IOUtils.copyFile(getConfig_Icon(p), 366 new File(resourcesDirectory, getConfig_Icon(p).getName())); 367 // Generate Info.plist 368 IOUtils.copyFile(getConfig_InfoPlist(p), 369 new File(contentsDirectory, "Info.plist")); 370 } catch (IOException ex) { 371 Log.verbose(ex); 372 return null; 373 } finally { 374 if (!VERBOSE.fetchFrom(p)) { 375 //cleanup 376 cleanupConfigFiles(p); 377 } else { 378 Log.info("Config files are saved to " + 379 CONFIG_ROOT.fetchFrom(p).getAbsolutePath() + 380 ". Use them to customize package."); 381 } 382 } 383 return rootDirectory; 384 } 385 386 public String getAppName(Map<String, ? super Object> params) { 387 return APP_NAME.fetchFrom(params) + ".app"; 388 } 389 390 protected void cleanupConfigFiles(Map<String, ? super Object> params) { 391 //Since building the app can be bypassed, make sure configRoot was set 392 if (CONFIG_ROOT.fetchFrom(params) != null) { 393 if (getConfig_Icon(params) != null) { 394 getConfig_Icon(params).delete(); 395 } 396 if (getConfig_InfoPlist(params) != null) { 397 getConfig_InfoPlist(params).delete(); 398 } 399 } 400 } 401 402 403 private void copyClassPathEntries(File javaDirectory, Map<String, ? super Object> params) throws IOException { 404 RelativeFileSet classPath = APP_RESOURCES.fetchFrom(params); 405 if (classPath == null) { 406 throw new RuntimeException("Null app resources?"); 407 } 408 File srcdir = classPath.getBaseDirectory(); 409 for (String fname : classPath.getIncludedFiles()) { 410 IOUtils.copyFile( 411 new File(srcdir, fname), new File(javaDirectory, fname)); 412 } 413 } 414 415 private void copyRuntime(File plugInsDirectory, Map<String, ? super Object> params) throws IOException { 416 RelativeFileSet runTime = MAC_RUNTIME.fetchFrom(params); 417 if (runTime == null) { 418 //request to use system runtime => do not bundle 419 return; 420 } 421 plugInsDirectory.mkdirs(); 422 423 File srcdir = runTime.getBaseDirectory(); 424 File destDir = new File(plugInsDirectory, srcdir.getName()); 425 Set<String> filesToCopy = runTime.getIncludedFiles(); 426 427 for (String fname : filesToCopy) { 428 IOUtils.copyFile( 429 new File(srcdir, fname), new File(destDir, fname)); 430 } 431 } 432 433 private void prepareIcon(Map<String, ? super Object> params) throws IOException { 434 File icon = ICON.fetchFrom(params); 435 if (icon == null || !icon.exists()) { 436 fetchResource(MAC_BUNDLER_PREFIX+ APP_NAME.fetchFrom(params) +".icns", 437 "icon", 438 DEFAULT_ICNS_ICON.fetchFrom(params), 439 getConfig_Icon(params), 440 VERBOSE.fetchFrom(params)); 441 } else { 442 fetchResource(MAC_BUNDLER_PREFIX+ APP_NAME.fetchFrom(params) +".icns", 443 "icon", 444 icon, 445 getConfig_Icon(params), 446 VERBOSE.fetchFrom(params)); 447 } 448 } 449 450 private String getLauncherName(Map<String, ? super Object> params) { 451 if (APP_NAME.fetchFrom(params) != null) { 452 return APP_NAME.fetchFrom(params); 453 } else { 454 return MAIN_CLASS.fetchFrom(params); 455 } 456 } 457 458 private String getBundleName(Map<String, ? super Object> params) { 459 //TODO: Check to see what rules/limits are in place for CFBundleName 460 if (MAC_CF_BUNDLE_NAME.fetchFrom(params) != null) { 461 String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params); 462 if (bn.length() > 16) { 463 Log.info(MessageFormat.format(I18N.getString("message.bundle-name-too-long-warning"), MAC_CF_BUNDLE_NAME.getID(), bn)); 464 } 465 return MAC_CF_BUNDLE_NAME.fetchFrom(params); 466 } else if (APP_NAME.fetchFrom(params) != null) { 467 return APP_NAME.fetchFrom(params); 468 } else { 469 String nm = MAIN_CLASS.fetchFrom(params); 470 if (nm.length() > 16) { 471 nm = nm.substring(0, 16); 472 } 473 return nm; 474 } 475 } 476 477 private String getBundleIdentifier(Map<String, ? super Object> params) { 478 //TODO: Check to see what rules/limits are in place for CFBundleIdentifier 479 return IDENTIFIER.fetchFrom(params); 480 } 481 482 private void writeInfoPlist(File file, Map<String, ? super Object> params) throws IOException { 483 Log.verbose("Preparing Info.plist: "+file.getAbsolutePath()); 484 485 //prepare config for exe 486 //Note: do not need CFBundleDisplayName if we do not support localization 487 Map<String, String> data = new HashMap<>(); 488 data.put("DEPLOY_ICON_FILE", getConfig_Icon(params).getName()); 489 data.put("DEPLOY_BUNDLE_IDENTIFIER", 490 getBundleIdentifier(params)); 491 data.put("DEPLOY_BUNDLE_NAME", 492 getBundleName(params)); 493 data.put("DEPLOY_BUNDLE_COPYRIGHT", 494 COPYRIGHT.fetchFrom(params) != null ? COPYRIGHT.fetchFrom(params) : "Unknown"); 495 data.put("DEPLOY_LAUNCHER_NAME", getLauncherName(params)); 496 if (MAC_RUNTIME.fetchFrom(params) != null) { 497 data.put("DEPLOY_JAVA_RUNTIME_NAME", 498 MAC_RUNTIME.fetchFrom(params).getBaseDirectory().getName()); 499 } else { 500 data.put("DEPLOY_JAVA_RUNTIME_NAME", ""); 501 } 502 data.put("DEPLOY_BUNDLE_SHORT_VERSION", 503 VERSION.fetchFrom(params) != null ? VERSION.fetchFrom(params) : "1.0.0"); 504 data.put("DEPLOY_BUNDLE_CATEGORY", 505 //TODO parameters should provide set of values for IDEs 506 MAC_CATEGORY.validatedFetchFrom(params)); 507 508 //TODO NOT THE WAY TODO THIS but good enough for first pass 509 data.put("DEPLOY_MAIN_JAR_NAME", new BundleParams(params).getMainApplicationJar()); 510 // data.put("DEPLOY_MAIN_JAR_NAME", MAIN_JAR.fetchFrom(params).toString()); 511 512 data.put("DEPLOY_PREFERENCES_ID", PREFERENCES_ID.fetchFrom(params).toLowerCase()); 513 514 StringBuilder sb = new StringBuilder(); 515 List<String> jvmOptions = JVM_OPTIONS.fetchFrom(params); 516 517 String newline = ""; //So we don't add unneccessary extra line after last append 518 for (String o : jvmOptions) { 519 sb.append(newline).append(" <string>").append(o).append("</string>"); 520 newline = "\n"; 521 } 522 data.put("DEPLOY_JVM_OPTIONS", sb.toString()); 523 524 newline = ""; 525 sb = new StringBuilder(); 526 Map<String, String> overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); 527 for (Map.Entry<String, String> arg: overridableJVMOptions.entrySet()) { 528 sb.append(newline); 529 sb.append(" <key>").append(arg.getKey()).append("</key>\n"); 530 sb.append(" <string>").append(arg.getValue()).append("</string>"); 531 newline = "\n"; 532 } 533 data.put("DEPLOY_JVM_USER_OPTIONS", sb.toString()); 534 535 536 //TODO UNLESS we are supporting building for jre7, this is unnecessary 537 // if (params.useJavaFXPackaging()) { 538 // data.put("DEPLOY_LAUNCHER_CLASS", JAVAFX_LAUNCHER_CLASS); 539 // } else { 540 data.put("DEPLOY_LAUNCHER_CLASS", MAIN_CLASS.fetchFrom(params)); 541 // } 542 // This will be an empty string for correctly packaged JavaFX apps 543 data.put("DEPLOY_APP_CLASSPATH", MAIN_JAR_CLASSPATH.fetchFrom(params)); 544 545 //TODO: Add remainder of the classpath 546 547 Writer w = new BufferedWriter(new FileWriter(file)); 548 w.write(preprocessTextResource( 549 MAC_BUNDLER_PREFIX + getConfig_InfoPlist(params).getName(), 550 "Bundle config file", TEMPLATE_INFO_PLIST, data, 551 VERBOSE.fetchFrom(params))); 552 w.close(); 553 554 } 555 556 private void writePkgInfo(File file) throws IOException { 557 558 //hardcoded as it does not seem we need to change it ever 559 String signature = "????"; 560 561 try (Writer out = new BufferedWriter(new FileWriter(file))) { 562 out.write(OS_TYPE_CODE + signature); 563 out.flush(); 564 } 565 } 566 567 ////////////////////////////////////////////////////////////////////////////////// 568 // Implement Bundler 569 ////////////////////////////////////////////////////////////////////////////////// 570 571 @Override 572 public String getName() { 573 return "Mac Application Image"; 574 } 575 576 @Override 577 public String getDescription() { 578 return "A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers"; 579 } 580 581 @Override 582 public String getID() { 583 return "mac.app"; 584 } 585 586 @Override 587 public BundleType getBundleType() { 588 return BundleType.IMAGE; 589 } 590 591 @Override 592 public Collection<BundlerParamInfo<?>> getBundleParameters() { 593 return getAppBundleParameters(); 594 } 595 596 public static Collection<BundlerParamInfo<?>> getAppBundleParameters() { 597 return Arrays.asList( 598 APP_NAME, 599 APP_RESOURCES, 600 BUILD_ROOT, 601 JVM_OPTIONS, 602 MAIN_CLASS, 603 MAIN_JAR, 604 MAIN_JAR_CLASSPATH, 605 PREFERENCES_ID, 606 RAW_EXECUTABLE_URL, 607 MAC_RUNTIME, 608 USE_FX_PACKAGING, 609 USER_JVM_OPTIONS, 610 VERSION, 611 ICON, 612 MAC_CATEGORY 613 ); 614 } 615 616 617 @Override 618 public File execute(Map<String, ? super Object> params, File outputParentDir) { 619 return doBundle(params, outputParentDir, false); 620 } 621 }