1 /* 2 * Copyright (c) 2012, 2015, 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 26 package com.oracle.tools.packager.windows; 27 28 import com.oracle.tools.packager.AbstractImageBundler; 29 import com.oracle.tools.packager.BundlerParamInfo; 30 import com.oracle.tools.packager.StandardBundlerParam; 31 import com.oracle.tools.packager.Log; 32 import com.oracle.tools.packager.ConfigException; 33 import com.oracle.tools.packager.IOUtils; 34 import com.oracle.tools.packager.RelativeFileSet; 35 import com.oracle.tools.packager.UnsupportedPlatformException; 36 37 import java.io.*; 38 import java.net.MalformedURLException; 39 import java.net.URL; 40 import java.nio.file.Files; 41 import java.text.MessageFormat; 42 import java.util.*; 43 import java.util.concurrent.atomic.AtomicReference; 44 import java.util.regex.Pattern; 45 46 import static com.oracle.tools.packager.StandardBundlerParam.*; 47 import static com.oracle.tools.packager.windows.WindowsBundlerParam.BIT_ARCH_64; 48 import static com.oracle.tools.packager.windows.WindowsBundlerParam.BIT_ARCH_64_RUNTIME; 49 import static com.oracle.tools.packager.windows.WindowsBundlerParam.WIN_RUNTIME; 50 51 public class WinAppBundler extends AbstractImageBundler { 52 53 private static final ResourceBundle I18N = 54 ResourceBundle.getBundle(WinAppBundler.class.getName()); 55 56 public static final BundlerParamInfo<File> CONFIG_ROOT = new WindowsBundlerParam<>( 57 I18N.getString("param.config-root.name"), 58 I18N.getString("param.config-root.description"), 59 "configRoot", 60 File.class, 61 params -> { 62 File imagesRoot = new File(BUILD_ROOT.fetchFrom(params), "windows"); 63 imagesRoot.mkdirs(); 64 return imagesRoot; 65 }, 66 (s, p) -> null); 67 68 private final static String EXECUTABLE_NAME = "WinLauncher.exe"; 69 private final static String LIBRARY_NAME = "packager.dll"; 70 71 private final static String[] VS_VERS = {"100", "110", "120"}; 72 private final static String REDIST_MSVCR = "msvcrVS_VER.dll"; 73 private final static String REDIST_MSVCP = "msvcpVS_VER.dll"; 74 75 private static final String TOOL_ICON_SWAP="IconSwap.exe"; 76 private static final String TOOL_VERSION_INFO_SWAP="VersionInfoSwap.exe"; 77 78 private static final String EXECUTABLE_PROPERTIES_TEMPLATE = "WinLauncher.properties"; 79 80 public static final BundlerParamInfo<URL> RAW_EXECUTABLE_URL = new WindowsBundlerParam<>( 81 I18N.getString("param.raw-executable-url.name"), 82 I18N.getString("param.raw-executable-url.description"), 83 "win.launcher.url", 84 URL.class, 85 params -> WinResources.class.getResource(EXECUTABLE_NAME), 86 (s, p) -> { 87 try { 88 return new URL(s); 89 } catch (MalformedURLException e) { 90 Log.info(e.toString()); 91 return null; 92 } 93 }); 94 95 public static final BundlerParamInfo<Boolean> REBRAND_EXECUTABLE = new WindowsBundlerParam<>( 96 I18N.getString("param.rebrand-executable.name"), 97 I18N.getString("param.rebrand-executable.description"), 98 "win.launcher.rebrand", 99 Boolean.class, 100 params -> Boolean.TRUE, 101 (s, p) -> Boolean.valueOf(s)); 102 103 public static final BundlerParamInfo<File> ICON_ICO = new StandardBundlerParam<>( 104 I18N.getString("param.icon-ico.name"), 105 I18N.getString("param.icon-ico.description"), 106 "icon.ico", 107 File.class, 108 params -> { 109 File f = ICON.fetchFrom(params); 110 if (f != null && !f.getName().toLowerCase().endsWith(".ico")) { 111 Log.info(MessageFormat.format(I18N.getString("message.icon-not-ico"), f)); 112 return null; 113 } 114 return f; 115 }, 116 (s, p) -> new File(s)); 117 118 public WinAppBundler() { 119 super(); 120 baseResourceLoader = WinResources.class; 121 } 122 123 public final static String WIN_BUNDLER_PREFIX = 124 BUNDLER_PREFIX + "windows/"; 125 126 File getConfigRoot(Map<String, ? super Object> params) { 127 return CONFIG_ROOT.fetchFrom(params); 128 } 129 130 @Override 131 public boolean validate(Map<String, ? super Object> params) throws UnsupportedPlatformException, ConfigException { 132 try { 133 if (params == null) throw new ConfigException( 134 I18N.getString("error.parameters-null"), 135 I18N.getString("error.parameters-null.advice")); 136 137 return doValidate(params); 138 } catch (RuntimeException re) { 139 if (re.getCause() instanceof ConfigException) { 140 throw (ConfigException) re.getCause(); 141 } else { 142 throw new ConfigException(re); 143 } 144 } 145 } 146 147 //to be used by chained bundlers, e.g. by EXE bundler to avoid 148 // skipping validation if p.type does not include "image" 149 boolean doValidate(Map<String, ? super Object> p) throws UnsupportedPlatformException, ConfigException { 150 if (!System.getProperty("os.name").toLowerCase().startsWith("win")) { 151 throw new UnsupportedPlatformException(); 152 } 153 154 imageBundleValidation(p); 155 156 if (WinResources.class.getResource(TOOL_ICON_SWAP) == null || 157 WinResources.class.getResource(TOOL_VERSION_INFO_SWAP) == null) 158 { 159 throw new ConfigException( 160 I18N.getString("error.no-windows-resources"), 161 I18N.getString("error.no-windows-resources.advice")); 162 } 163 164 //validate required inputs 165 testRuntime(WIN_RUNTIME.fetchFrom(p), new String[] { 166 "bin\\\\[^\\\\]+\\\\jvm.dll", // most reliable 167 "lib\\\\rt.jar", // fallback canary for JDK 8 168 }); 169 if (USE_FX_PACKAGING.fetchFrom(p)) { 170 testRuntime(WIN_RUNTIME.fetchFrom(p), new String[] {"lib\\\\ext\\\\jfxrt.jar", "lib\\\\jfxrt.jar"}); 171 } 172 173 //validate runtime bit-architectire 174 testRuntimeBitArchitecture(p); 175 176 return true; 177 } 178 179 private static void testRuntimeBitArchitecture(Map<String, ? super Object> params) throws ConfigException { 180 if ("true".equalsIgnoreCase(System.getProperty("fxpackager.disableBitArchitectureMismatchCheck"))) { 181 Log.debug(I18N.getString("message.disable-bit-architecture-check")); 182 return; 183 } 184 185 if ((BIT_ARCH_64.fetchFrom(params) != BIT_ARCH_64_RUNTIME.fetchFrom(params)) && !"systemjre".equals(params.get(".runtime.autodetect"))) { 186 throw new ConfigException( 187 I18N.getString("error.bit-architecture-mismatch"), 188 I18N.getString("error.bit-architecture-mismatch.advice")); 189 } 190 } 191 192 //it is static for the sake of sharing with "Exe" bundles 193 // that may skip calls to validate/bundle in this class! 194 private static File getRootDir(File outDir, Map<String, ? super Object> p) { 195 return new File(outDir, APP_NAME.fetchFrom(p)); 196 } 197 198 public static String getLauncherName(Map<String, ? super Object> p) { 199 return APP_NAME.fetchFrom(p) +".exe"; 200 } 201 202 public static String getLauncherCfgName(Map<String, ? super Object> p) { 203 return "app\\" + APP_NAME.fetchFrom(p) +".cfg"; 204 } 205 206 private File getConfig_AppIcon(Map<String, ? super Object> params) { 207 return new File(getConfigRoot(params), APP_NAME.fetchFrom(params) + ".ico"); 208 } 209 210 private File getConfig_ExecutableProperties(Map<String, ? super Object> params) { 211 return new File(getConfigRoot(params), APP_NAME.fetchFrom(params) + ".properties"); 212 } 213 214 private final static String TEMPLATE_APP_ICON ="javalogo_white_48.ico"; 215 216 //remove 217 protected void cleanupConfigFiles(Map<String, ? super Object> params) { 218 getConfig_AppIcon(params).delete(); 219 getConfig_ExecutableProperties(params).delete(); 220 } 221 222 private void validateValueAndPut(Map<String, String> data, String key, 223 BundlerParamInfo<String> param, Map<String, ? super Object> params) 224 { 225 String value = param.fetchFrom(params); 226 if (value.contains("\r") || value.contains("\n")) { 227 Log.info("Configuration Parameter " + param.getID() + " contains multiple lines of text, ignore it"); 228 data.put(key, ""); 229 return; 230 } 231 data.put(key, value); 232 } 233 234 protected void prepareExecutableProperties(Map<String, ? super Object> params) 235 throws IOException 236 { 237 Map<String, String> data = new HashMap<>(); 238 239 // mapping Java parameters in strings for version resource 240 data.put("COMMENTS", ""); 241 validateValueAndPut(data, "COMPANY_NAME", VENDOR, params); 242 validateValueAndPut(data, "FILE_DESCRIPTION", DESCRIPTION, params); 243 validateValueAndPut(data, "FILE_VERSION", VERSION, params); 244 data.put("INTERNAL_NAME", getLauncherName(params)); 245 validateValueAndPut(data, "LEGAL_COPYRIGHT", COPYRIGHT, params); 246 data.put("LEGAL_TRADEMARK", ""); 247 data.put("ORIGINAL_FILENAME", getLauncherName(params)); 248 data.put("PRIVATE_BUILD", ""); 249 validateValueAndPut(data, "PRODUCT_NAME", APP_NAME, params); 250 validateValueAndPut(data, "PRODUCT_VERSION", VERSION, params); 251 data.put("SPECIAL_BUILD", ""); 252 253 Writer w = new BufferedWriter(new FileWriter(getConfig_ExecutableProperties(params))); 254 String content = preprocessTextResource( 255 WinAppBundler.WIN_BUNDLER_PREFIX + getConfig_ExecutableProperties(params).getName(), 256 I18N.getString("resource.executable-properties-template"), EXECUTABLE_PROPERTIES_TEMPLATE, data, 257 VERBOSE.fetchFrom(params), 258 DROP_IN_RESOURCES_ROOT.fetchFrom(params)); 259 w.write(content); 260 w.close(); 261 } 262 263 private void prepareConfigFiles(Map<String, ? super Object> params) throws IOException { 264 File iconTarget = getConfig_AppIcon(params); 265 266 File icon = ICON_ICO.fetchFrom(params); 267 if (icon != null && icon.exists()) { 268 fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(), 269 I18N.getString("resource.application-icon"), 270 icon, 271 iconTarget, 272 VERBOSE.fetchFrom(params), 273 DROP_IN_RESOURCES_ROOT.fetchFrom(params)); 274 } else { 275 fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(), 276 I18N.getString("resource.application-icon"), 277 WinAppBundler.TEMPLATE_APP_ICON, 278 iconTarget, 279 VERBOSE.fetchFrom(params), 280 DROP_IN_RESOURCES_ROOT.fetchFrom(params)); 281 } 282 283 prepareExecutableProperties(params); 284 } 285 286 public boolean bundle(Map<String, ? super Object> p, File outputDirectory) { 287 return doBundle(p, outputDirectory, false) != null; 288 } 289 290 File doBundle(Map<String, ? super Object> p, File outputDirectory, boolean dependentTask) { 291 Map<String, ? super Object> originalParams = new HashMap<>(p); 292 if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) { 293 throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-create-output-dir"), outputDirectory.getAbsolutePath())); 294 } 295 if (!outputDirectory.canWrite()) { 296 throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-write-to-output-dir"), outputDirectory.getAbsolutePath())); 297 } 298 try { 299 if (!dependentTask) { 300 Log.info(MessageFormat.format(I18N.getString("message.creating-app-bundle"), APP_NAME.fetchFrom(p), outputDirectory.getAbsolutePath())); 301 } 302 303 // Create directory structure 304 File rootDirectory = getRootDir(outputDirectory, p); 305 IOUtils.deleteRecursive(rootDirectory); 306 rootDirectory.mkdirs(); 307 308 File appDirectory = new File(rootDirectory, "app"); 309 appDirectory.mkdirs(); 310 311 // create the .exe launchers 312 createLauncherForEntryPoint(p, rootDirectory); 313 314 // copy the jars 315 copyApplication(p, appDirectory); 316 317 // Copy runtime 318 File runtimeDirectory = new File(rootDirectory, "runtime"); 319 copyRuntime(p, runtimeDirectory); 320 321 // copy in the needed libraries 322 IOUtils.copyFromURL( 323 WinResources.class.getResource(LIBRARY_NAME), 324 new File(rootDirectory, LIBRARY_NAME)); 325 326 copyMSVCDLLs(rootDirectory, runtimeDirectory); 327 328 // create the secondary launchers, if any 329 List<Map<String, ? super Object>> entryPoints = StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(p); 330 for (Map<String, ? super Object> entryPoint : entryPoints) { 331 Map<String, ? super Object> tmp = new HashMap<>(originalParams); 332 tmp.putAll(entryPoint); 333 createLauncherForEntryPoint(tmp, rootDirectory); 334 } 335 336 if (!dependentTask) { 337 Log.info(MessageFormat.format(I18N.getString("message.result-dir"), outputDirectory.getAbsolutePath())); 338 } 339 340 return rootDirectory; 341 } catch (IOException ex) { 342 Log.info("Exception: "+ex); 343 Log.debug(ex); 344 return null; 345 } finally { 346 if (VERBOSE.fetchFrom(p)) { 347 Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), getConfigRoot(p).getAbsolutePath())); 348 } else { 349 cleanupConfigFiles(p); 350 } 351 } 352 353 } 354 355 private void copyMSVCDLLs(File rootDirectory, File jreDir) throws IOException { 356 String vsVer = null; 357 if (jreDir == null || !jreDir.isDirectory()) { 358 jreDir = new File(System.getProperty("java.home")); 359 } 360 361 // first copy the ones needed for the launcher 362 for (String thisVer : VS_VERS) { 363 if (copyMSVCDLLs(rootDirectory, thisVer)) { 364 vsVer = thisVer; 365 break; 366 } 367 } 368 if (vsVer == null) { 369 throw new RuntimeException("Not found MSVC dlls"); 370 } 371 372 AtomicReference<IOException> ioe = new AtomicReference<>(); 373 final String finalVsVer = vsVer; 374 Files.list(jreDir.toPath().resolve("bin")) 375 .filter(p -> Pattern.matches("msvc(r|p)\\d\\d\\d.dll", p.toFile().getName().toLowerCase())) 376 .filter(p -> !p.toString().toLowerCase().endsWith(finalVsVer + ".dll")) 377 .forEach(p -> { 378 try { 379 IOUtils.copyFile(p.toFile(), new File(rootDirectory, p.toFile().getName())); 380 } catch (IOException e) { 381 ioe.set(e); 382 } 383 }); 384 385 IOException e = ioe.get(); 386 if (e != null) { 387 throw e; 388 } 389 } 390 391 private boolean copyMSVCDLLs(File rootDirectory, String VS_VER) throws IOException { 392 final URL REDIST_MSVCR_URL = WinResources.class.getResource( 393 REDIST_MSVCR.replaceAll("VS_VER", VS_VER)); 394 final URL REDIST_MSVCP_URL = WinResources.class.getResource( 395 REDIST_MSVCP.replaceAll("VS_VER", VS_VER)); 396 397 if (REDIST_MSVCR_URL != null && REDIST_MSVCP_URL != null) { 398 IOUtils.copyFromURL( 399 REDIST_MSVCR_URL, 400 new File(rootDirectory, REDIST_MSVCR.replaceAll("VS_VER", VS_VER))); 401 IOUtils.copyFromURL( 402 REDIST_MSVCP_URL, 403 new File(rootDirectory, REDIST_MSVCP.replaceAll("VS_VER", VS_VER))); 404 return true; 405 } 406 407 return false; // not found 408 } 409 410 private void createLauncherForEntryPoint(Map<String, ? super Object> p, File rootDirectory) throws IOException { 411 prepareConfigFiles(p); 412 413 // Generate launcher .cfg file 414 if (LAUNCHER_CFG_FORMAT.fetchFrom(p).equals(CFG_FORMAT_PROPERTIES)) { 415 writeCfgFile(p, rootDirectory); 416 } else { 417 writeCfgFile(p, new File(rootDirectory, getLauncherCfgName(p)), getRuntimeLocation(p)); 418 } 419 420 // Copy executable root folder 421 File executableFile = new File(rootDirectory, getLauncherName(p)); 422 IOUtils.copyFromURL( 423 RAW_EXECUTABLE_URL.fetchFrom(p), 424 executableFile); 425 executableFile.setExecutable(true, false); 426 427 //Update branding of exe file 428 if (REBRAND_EXECUTABLE.fetchFrom(p) && getConfig_AppIcon(p).exists()) { 429 //extract IconSwap helper tool 430 File iconSwapTool = File.createTempFile("iconswap", ".exe"); 431 IOUtils.copyFromURL( 432 WinResources.class.getResource(TOOL_ICON_SWAP), 433 iconSwapTool, 434 true); 435 iconSwapTool.setExecutable(true, false); 436 iconSwapTool.deleteOnExit(); 437 438 //run it on launcher file 439 executableFile.setWritable(true); 440 ProcessBuilder pb = new ProcessBuilder( 441 iconSwapTool.getAbsolutePath(), 442 getConfig_AppIcon(p).getAbsolutePath(), 443 executableFile.getAbsolutePath()); 444 IOUtils.exec(pb, VERBOSE.fetchFrom(p)); 445 executableFile.setReadOnly(); 446 iconSwapTool.delete(); 447 } 448 449 IOUtils.copyFile(getConfig_AppIcon(p), 450 new File(rootDirectory, APP_NAME.fetchFrom(p) + ".ico")); 451 452 if (REBRAND_EXECUTABLE.fetchFrom(p) && getConfig_ExecutableProperties(p).exists()) { 453 // extract VersionInfoHelper tool 454 File versionInfoTool = File.createTempFile("versioninfoswap", ".exe"); 455 IOUtils.copyFromURL( 456 WinResources.class.getResource(TOOL_VERSION_INFO_SWAP), 457 versionInfoTool, 458 true); 459 versionInfoTool.setExecutable(true, false); 460 versionInfoTool.deleteOnExit(); 461 462 // run it on launcher file 463 executableFile.setWritable(true); 464 ProcessBuilder pb = new ProcessBuilder( 465 versionInfoTool.getAbsolutePath(), 466 getConfig_ExecutableProperties(p).getAbsolutePath(), 467 executableFile.getAbsolutePath()); 468 IOUtils.exec(pb, VERBOSE.fetchFrom(p)); 469 executableFile.setReadOnly(); 470 versionInfoTool.delete(); 471 } 472 } 473 474 private void copyApplication(Map<String, ? super Object> params, File appDirectory) throws IOException { 475 List<RelativeFileSet> appResourcesList = APP_RESOURCES_LIST.fetchFrom(params); 476 if (appResourcesList == null) { 477 throw new RuntimeException("Null app resources?"); 478 } 479 for (RelativeFileSet appResources : appResourcesList) { 480 if (appResources == null) { 481 throw new RuntimeException("Null app resources?"); 482 } 483 File srcdir = appResources.getBaseDirectory(); 484 for (String fname : appResources.getIncludedFiles()) { 485 IOUtils.copyFile( 486 new File(srcdir, fname), new File(appDirectory, fname)); 487 } 488 } 489 } 490 491 private String getRuntimeLocation(Map<String, ? super Object> params) { 492 if (WIN_RUNTIME.fetchFrom(params) == null) { 493 return ""; 494 } else { 495 return "$APPDIR\\runtime"; 496 } 497 } 498 499 private void writeCfgFile(Map<String, ? super Object> params, File rootDir) throws FileNotFoundException { 500 File cfgFile = new File(rootDir, getLauncherCfgName(params)); 501 502 cfgFile.delete(); 503 504 PrintStream out = new PrintStream(cfgFile); 505 out.println("app.runtime=" + getRuntimeLocation(params)); 506 out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); 507 out.println("app.version=" + VERSION.fetchFrom(params)); 508 //for future AU support (to be able to find app in the registry) 509 out.println("app.id=" + IDENTIFIER.fetchFrom(params)); 510 out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); 511 out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); 512 513 out.println("app.mainclass=" + 514 MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/")); 515 out.println("app.classpath=" + CLASSPATH.fetchFrom(params)); 516 517 List<String> jvmargs = JVM_OPTIONS.fetchFrom(params); 518 int idx = 1; 519 for (String a : jvmargs) { 520 out.println("jvmarg."+idx+"="+a); 521 idx++; 522 } 523 Map<String, String> jvmProps = JVM_PROPERTIES.fetchFrom(params); 524 for (Map.Entry<String, String> entry : jvmProps.entrySet()) { 525 out.println("jvmarg."+idx+"=-D"+entry.getKey()+"="+entry.getValue()); 526 idx++; 527 } 528 529 String preloader = PRELOADER_CLASS.fetchFrom(params); 530 if (preloader != null) { 531 out.println("jvmarg."+idx+"=-Djavafx.preloader="+preloader); 532 } 533 534 Map<String, String> overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); 535 idx = 1; 536 for (Map.Entry<String, String> arg: overridableJVMOptions.entrySet()) { 537 if (arg.getKey() == null || arg.getValue() == null) { 538 Log.info(I18N.getString("message.jvm-user-arg-is-null")); 539 } 540 else { 541 out.println("jvmuserarg."+idx+".name="+arg.getKey()); 542 out.println("jvmuserarg."+idx+".value="+arg.getValue()); 543 } 544 idx++; 545 } 546 547 // add command line args 548 List<String> args = ARGUMENTS.fetchFrom(params); 549 idx = 1; 550 for (String a : args) { 551 out.println("arg."+idx+"="+a); 552 idx++; 553 } 554 555 out.close(); 556 } 557 558 private void copyRuntime(Map<String, ? super Object> params, File runtimeDirectory) throws IOException { 559 RelativeFileSet runtime = WIN_RUNTIME.fetchFrom(params); 560 if (runtime == null) { 561 //its ok, request to use system JRE 562 return; 563 } 564 runtimeDirectory.mkdirs(); 565 566 File srcdir = runtime.getBaseDirectory(); 567 Set<String> filesToCopy = runtime.getIncludedFiles(); 568 for (String fname : filesToCopy) { 569 IOUtils.copyFile( 570 new File(srcdir, fname), new File(runtimeDirectory, fname)); 571 } 572 } 573 574 public void extractRuntimeFlags(Map<String, ? super Object> params) { 575 extractFlagsFromRuntime(params); 576 } 577 578 public static void extractFlagsFromRuntime(Map<String, ? super Object> params) { 579 if (params.containsKey(".runtime.autodetect")) return; 580 581 params.put(".runtime.autodetect", "attempted"); 582 RelativeFileSet runtime = WIN_RUNTIME.fetchFrom(params); 583 String commandline; 584 if (runtime == null) { 585 //System JRE, report nothing useful 586 params.put(".runtime.autodetect", "systemjre"); 587 } else { 588 File runtimePath = runtime.getBaseDirectory(); 589 File launcherPath = new File(runtimePath, "bin\\java"); 590 591 ProcessBuilder pb = new ProcessBuilder(launcherPath.getAbsolutePath(), "-version"); 592 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 593 try (PrintStream pout = new PrintStream(baos)) { 594 IOUtils.exec(pb, Log.isDebug(), true, pout); 595 } 596 597 commandline = baos.toString(); 598 } catch (IOException e) { 599 e.printStackTrace(); 600 params.put(".runtime.autodetect", "failed"); 601 return; 602 } 603 AbstractImageBundler.extractFlagsFromVersion(params, commandline); 604 params.put(".runtime.autodetect", "succeeded"); 605 } 606 } 607 608 609 @Override 610 public String getName() { 611 return I18N.getString("bundler.name"); 612 } 613 614 @Override 615 public String getDescription() { 616 return I18N.getString("bundler.description"); 617 } 618 619 @Override 620 public String getID() { 621 return "windows.app"; 622 } 623 624 @Override 625 public String getBundleType() { 626 return "IMAGE"; 627 } 628 629 @Override 630 public Collection<BundlerParamInfo<?>> getBundleParameters() { 631 return getAppBundleParameters(); 632 } 633 634 public static Collection<BundlerParamInfo<?>> getAppBundleParameters() { 635 return Arrays.asList( 636 APP_NAME, 637 APP_RESOURCES, 638 // APP_RESOURCES_LIST, // ?? 639 ARGUMENTS, 640 CLASSPATH, 641 ICON_ICO, 642 JVM_OPTIONS, 643 JVM_PROPERTIES, 644 MAIN_CLASS, 645 MAIN_JAR, 646 PREFERENCES_ID, 647 PRELOADER_CLASS, 648 USER_JVM_OPTIONS, 649 VERSION, 650 WIN_RUNTIME 651 ); 652 } 653 654 @Override 655 public File execute(Map<String, ? super Object> params, File outputParentDir) { 656 return doBundle(params, outputParentDir, false); 657 } 658 659 @Override 660 protected String getCacheLocation(Map<String, ? super Object> params) { 661 return "$CACHEDIR/"; 662 } 663 }