# HG changeset patch # User kcr # Date 1458008399 25200 # Mon Mar 14 19:19:59 2016 -0700 # Node ID bd5f822c03f68bd1b4a1a3f4b2e19717c32e0368 # Parent 33e0a9be138307c079e179798babec55c483e160 imported patch 9-jake-fxpackager.patch diff --git a/modules/fxpackager/make/build.properties b/modules/fxpackager/make/build.properties new file mode 100644 --- /dev/null +++ b/modules/fxpackager/make/build.properties @@ -0,0 +1,31 @@ +# +# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# Build properties for this module + +include_in_jre=false +include_in_jdk=true +include_in_jdk_server=false +classloader=app diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/AbstractBundler.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/AbstractBundler.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/AbstractBundler.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/AbstractBundler.java @@ -29,9 +29,10 @@ import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URL; +import java.nio.file.Files; import java.text.MessageFormat; import java.util.*; @@ -58,9 +59,9 @@ String publicName, String category, String defaultName, File result, boolean verbose, File publicRoot) throws IOException { - URL u = locateResource(publicName, category, defaultName, verbose, publicRoot); - if (u != null) { - IOUtils.copyFromURL(u, result); + InputStream is = streamResource(publicName, category, defaultName, verbose, publicRoot); + if (is != null) { + Files.copy(is, result.toPath()); } else { if (verbose) { Log.info(MessageFormat.format(I18N.getString("message.using-default-resource"), category == null ? "" : "[" + category + "] ", publicName)); @@ -72,9 +73,9 @@ String publicName, String category, File defaultFile, File result, boolean verbose, File publicRoot) throws IOException { - URL u = locateResource(publicName, category, null, verbose, publicRoot); - if (u != null) { - IOUtils.copyFromURL(u, result); + InputStream is = streamResource(publicName, category, null, verbose, publicRoot); + if (is != null) { + Files.copy(is, result.toPath()); } else { IOUtils.copyFile(defaultFile, result); if (verbose) { @@ -83,41 +84,40 @@ } } - private URL locateResource(String publicName, String category, + private InputStream streamResource(String publicName, String category, String defaultName, boolean verbose, File publicRoot) throws IOException { - URL u = null; boolean custom = false; + InputStream is = null; if (publicName != null) { if (publicRoot != null) { File publicResource = new File(publicRoot, publicName); if (publicResource.exists() && publicResource.isFile()) { - u = publicResource.toURI().toURL(); + is = new FileInputStream(publicResource); } } else { - u = baseResourceLoader.getClassLoader().getResource(publicName); + is = baseResourceLoader.getClassLoader().getResourceAsStream(publicName); } - custom = (u != null); + custom = (is != null); } - if (u == null && defaultName != null) { - u = baseResourceLoader.getResource(defaultName); + if (is == null && defaultName != null) { + is = baseResourceLoader.getResourceAsStream(defaultName); } String msg = null; if (custom) { msg = MessageFormat.format(I18N.getString("message.using-custom-resource-from-classpath"), category == null ? "" : "[" + category + "] ", publicName); - } else if (u != null) { + } else if (is != null) { msg = MessageFormat.format(I18N.getString("message.using-default-resource-from-classpath"), category == null ? "" : "[" + category + "] ", publicName); } - if (verbose && u != null) { + if (verbose && is != null) { Log.info(msg); } - return u; + return is; } protected String preprocessTextResource(String publicName, String category, String defaultName, Map pairs, boolean verbose, File publicRoot) throws IOException { - URL u = locateResource(publicName, category, defaultName, verbose, publicRoot); - InputStream inp = u.openStream(); + InputStream inp = streamResource(publicName, category, defaultName, verbose, publicRoot); if (inp == null) { throw new RuntimeException("Jar corrupt? No "+defaultName+" resource!"); } diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/AbstractImageBundler.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/AbstractImageBundler.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/AbstractImageBundler.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/AbstractImageBundler.java @@ -24,15 +24,13 @@ */ package com.oracle.tools.packager; -import com.sun.javafx.tools.packager.bundlers.BundleParams; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.nio.file.Files; import java.text.MessageFormat; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.ResourceBundle; @@ -40,7 +38,6 @@ import java.util.regex.Pattern; import static com.oracle.tools.packager.StandardBundlerParam.*; -import static com.oracle.tools.packager.StandardBundlerParam.ARGUMENTS; /** * Common utility methods used by app image bundlers. @@ -50,35 +47,6 @@ private static final ResourceBundle I18N = ResourceBundle.getBundle(AbstractImageBundler.class.getName()); - public static final String CFG_FORMAT_PROPERTIES="prop"; - public static final String CFG_FORMAT_INI="ini"; - - public static final BundlerParamInfo LAUNCHER_CFG_FORMAT = - new StandardBundlerParam<>( - I18N.getString("param.launcher-cfg-format.name"), - I18N.getString("param.launcher-cfg-format.description"), - "launcher-cfg-format", - String.class, - params -> "ini", - (s, p) -> s); - - //helper method to test if required files are present in the runtime - public void testRuntime(RelativeFileSet runtime, String[] file) throws ConfigException { - if (runtime == null) { - return; //null runtime is ok (request to use system) - } - - Pattern[] weave = Arrays.stream(file).map(Pattern::compile).toArray(Pattern[]::new); - - if (!runtime.getIncludedFiles().stream().anyMatch(s -> - Arrays.stream(weave).anyMatch(pattern -> pattern.matcher(s).matches()) - )) { - throw new ConfigException( - MessageFormat.format(I18N.getString("error.jre-missing-file"), Arrays.toString(file)), - I18N.getString("error.jre-missing-file.advice")); - } - } - public void imageBundleValidation(Map p) throws ConfigException { StandardBundlerParam.validateMainClassInfoFromAppResources(p); @@ -99,162 +67,15 @@ I18N.getString("error.no-application-jar.advice")); } - extractRuntimeFlags(p); - if (ENABLE_APP_CDS.fetchFrom(p)) { - if (UNLOCK_COMMERCIAL_FEATURES.fetchFrom(p)) { - if (p.containsKey(BundleParams.PARAM_RUNTIME) - && (p.get(BundleParams.PARAM_RUNTIME) == null)) - { - throw new ConfigException( - I18N.getString("error.app-cds-requires-runtime"), - I18N.getString("error.app-cds-requires-runtime.advice")); - } - Object majorV = p.get(".runtime.version.major"); - Object minorV = p.get(".runtime.version.minor"); - if (majorV != null && minorV != null) { - try { - int major = Integer.parseInt(majorV.toString()); - int minor = Integer.parseInt(minorV.toString()); - if ((major < 8) || (major == 8 && minor < 40)) { - throw new ConfigException( - I18N.getString("error.app-cds-bad-version"), - I18N.getString("error.app-cds-bad-version.advice")); - } - } catch (NumberFormatException nfe) { - //maybe log a failure to check versions? - } - } - } else { + if (!UNLOCK_COMMERCIAL_FEATURES.fetchFrom(p)) { throw new ConfigException( I18N.getString("error.app-cds-no-commercial-unlock"), I18N.getString("error.app-cds-no-commercial-unlock.advice")); } } - } - public void writeCfgFile(Map params, File cfgFileName, String runtimeLocation) throws IOException { - cfgFileName.delete(); - - boolean appCDEnabled = UNLOCK_COMMERCIAL_FEATURES.fetchFrom(params) && ENABLE_APP_CDS.fetchFrom(params); - String appCDSCacheMode = APP_CDS_CACHE_MODE.fetchFrom(params); - - PrintStream out = new PrintStream(cfgFileName); - - out.println("[Application]"); - out.println("app.name=" + APP_NAME.fetchFrom(params)); - out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); - out.println("app.version=" + VERSION.fetchFrom(params)); - out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); - out.println("app.mainclass=" + - MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/")); - out.println("app.classpath=" + - String.join(File.pathSeparator, CLASSPATH.fetchFrom(params).split("[ :;]"))); - out.println("app.runtime=" + runtimeLocation); - out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); - if (appCDEnabled) { - out.println("app.appcds.cache=" + appCDSCacheMode.split("\\+")[0]); - } - - - out.println(); - out.println("[JVMOptions]"); - List jvmargs = JVM_OPTIONS.fetchFrom(params); - for (String arg : jvmargs) { - out.println(arg); - } - Map jvmProps = JVM_PROPERTIES.fetchFrom(params); - for (Map.Entry property : jvmProps.entrySet()) { - out.println("-D" + property.getKey() + "=" + property.getValue()); - } - String preloader = PRELOADER_CLASS.fetchFrom(params); - if (preloader != null) { - out.println("-Djavafx.preloader="+preloader); - } - - - out.println(); - out.println("[JVMUserOptions]"); - Map overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); - for (Map.Entry arg: overridableJVMOptions.entrySet()) { - if (arg.getKey() == null || arg.getValue() == null) { - Log.info(I18N.getString("message.jvm-user-arg-is-null")); - } else { - out.println(arg.getKey().replaceAll("([\\=])", "\\\\$1") + "=" + arg.getValue()); - } - } - - if (appCDEnabled) { - prepareAppCDS(params, out); - } - - out.println(); - out.println("[ArgOptions]"); - List args = ARGUMENTS.fetchFrom(params); - for (String arg : args) { - if (arg.endsWith("=") && (arg.indexOf("=") == arg.lastIndexOf("="))) { - out.print(arg.substring(0, arg.length() - 1)); - out.println("\\="); - } else { - out.println(arg); - } - } - - - out.close(); - } - - protected abstract String getCacheLocation(Map params); - - void prepareAppCDS(Map params, PrintStream out) throws IOException { - //TODO check 8u40 or later - - File tempDir = Files.createTempDirectory("javapackager").toFile(); - tempDir.deleteOnExit(); - File classList = new File(tempDir, APP_FS_NAME.fetchFrom(params) + ".classlist"); - - try (FileOutputStream fos = new FileOutputStream(classList); - PrintStream ps = new PrintStream(fos)) { - for (String className : APP_CDS_CLASS_ROOTS.fetchFrom(params)) { - String slashyName = className.replace(".", "/"); - ps.println(slashyName); - } - } - APP_RESOURCES_LIST.fetchFrom(params).add(new RelativeFileSet(classList.getParentFile(), Arrays.asList(classList))); - - out.println(); - out.println("[AppCDSJVMOptions]"); - out.println("-XX:+UnlockCommercialFeatures"); - out.print("-XX:SharedArchiveFile="); - out.print(getCacheLocation(params)); - out.print(APP_FS_NAME.fetchFrom(params)); - out.println(".jpa"); - out.println("-Xshare:auto"); - out.println("-XX:+UseAppCDS"); - if (Log.isDebug()) { - out.println("-verbose:class"); - out.println("-XX:+TraceClassPaths"); - out.println("-XX:+UnlockDiagnosticVMOptions"); - } - out.println(""); - - out.println("[AppCDSGenerateCacheJVMOptions]"); - out.println("-XX:+UnlockCommercialFeatures"); - out.println("-Xshare:dump"); - out.println("-XX:+UseAppCDS"); - out.print("-XX:SharedArchiveFile="); - out.print(getCacheLocation(params)); - out.print(APP_FS_NAME.fetchFrom(params)); - out.println(".jpa"); - out.println("-XX:SharedClassListFile=$PACKAGEDIR/" + APP_FS_NAME.fetchFrom(params) + ".classlist"); - if (Log.isDebug()) { - out.println("-XX:+UnlockDiagnosticVMOptions"); - } - } - - abstract public void extractRuntimeFlags(Map params); - public static void extractFlagsFromVersion(Map params, String versionOutput) { Pattern bitArchPattern = Pattern.compile("(\\d*)[- ]?[bB]it"); Matcher matcher = bitArchPattern.matcher(versionOutput); diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/BasicBundlers.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/BasicBundlers.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/BasicBundlers.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/BasicBundlers.java @@ -93,40 +93,8 @@ return null; } - /** - * Loads the bundlers common to OpenJFX. - *
    - *
  • Windows file image
  • - *
  • Mac .app
  • - *
  • Linux file image
  • - *
  • Windows MSI
  • - *
  • Windows EXE
  • - *
  • Mac DMG
  • - *
  • Mac PKG
  • - *
  • Linux DEB
  • - *
  • Linux RPM
  • - * - *
- */ public void loadDefaultBundlers() { - if (defaultsLoaded) return; - - bundlers.add(new WinAppBundler()); - bundlers.add(new WinExeBundler()); - bundlers.add(new WinMsiBundler()); - - bundlers.add(new LinuxAppBundler()); - bundlers.add(new LinuxDebBundler()); - bundlers.add(new LinuxRpmBundler()); - - bundlers.add(new MacAppBundler()); - bundlers.add(new MacDmgBundler()); - bundlers.add(new MacPkgBundler()); - bundlers.add(new MacAppStoreBundler()); - - bundlers.add(new JNLPBundler()); - - defaultsLoaded = true; + // no-op. We now load all bundlers from module system. } /** diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/BundlerParamInfo.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/BundlerParamInfo.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/BundlerParamInfo.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/BundlerParamInfo.java @@ -25,6 +25,7 @@ package com.oracle.tools.packager; +import java.util.HashMap; import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/Bundlers.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/Bundlers.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/Bundlers.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/Bundlers.java @@ -111,6 +111,7 @@ *
  • Windows MSI
  • *
  • Windows EXE
  • *
  • Mac DMG
  • + *
  • Mac PKG
  • *
  • Linux DEB
  • *
  • Linux RPM
  • * diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/JDepHelper.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/JDepHelper.java new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/JDepHelper.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.tools.packager; + +import com.sun.tools.jdeps.Main; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + + +public final class JDepHelper { + private JDepHelper() {} + + private static int invokeJdep(String[] args, PrintWriter out) { + return com.sun.tools.jdeps.Main.run(args, out); + } + + public static List getResourceFileJarList(Map params) { + List files = new ArrayList(); + + for (RelativeFileSet rfs : StandardBundlerParam.APP_RESOURCES_LIST.fetchFrom(params)) { + for (String s : rfs.files) { + if (s.endsWith(".jar")) { + files.add(rfs.getBaseDirectory() + File.separator + s); + } + } + } + + return files; + } + + public static Set calculateModules(List Files, List modulePath) { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintWriter writer = new PrintWriter(baos)) { + + List arguments = new ArrayList<>(); + arguments.add("-s"); + + if (modulePath != null || !modulePath.isEmpty()) { + arguments.add("-modulepath"); + arguments.add(ListOfPathToString(modulePath)); + } + + arguments.addAll(Files); + + invokeJdep(arguments.toArray(new String[arguments.size()]), writer); + + // output format is multiple lines of "this.jar -> that.module.name" + // we only care about what is to the right of the arrow + return Arrays.stream(baos.toString().split("\\s*\\S+\\s+->\\s+")) + .map(String::trim) + .filter(s -> !s.isEmpty() && !arguments.contains(s) && !"not found".equals(s)) + .collect(Collectors.toSet()); + } catch (IOException ioe) { + Log.verbose(ioe); + return new LinkedHashSet(); + } + } + + private static String ListOfPathToString(List Value) { + String result = ""; + + for (Path path : Value) { + if (result.isEmpty()) { + result = path.toString(); + } + else { + result = File.pathSeparator + path.toString(); + } + } + + return result; + } +} diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/JLinkBundlerHelper.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/JLinkBundlerHelper.java new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/JLinkBundlerHelper.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.tools.packager; + +import jdk.tools.jlink.Jlink; +import jdk.tools.jlink.builder.ImageBuilder; +import jdk.tools.jlink.plugin.Plugin; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringReader; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; + + +public class JLinkBundlerHelper { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle(JLinkBundlerHelper.class.getName()); + + //TODO Remove and replace with programmatic implementation JDK-8149975 + private static final String[] JRE_MODULES = {"java.se", + "java.smartcardio", + "javafx.base", + "javafx.controls", + "javafx.deploy", + "javafx.fxml", + "javafx.graphics", + "javafx.media", + "javafx.swing", + "javafx.web", + "javafx.base", + "javafx.deploy", + "javafx.graphics", + "javafx.swing", + "javafx.controls", + "javafx.fxml", + "javafx.media", + "javafx.web", + "jdk.packager.services", //TODO rename to jdk.packager.runtime JDK-8148482 + "jdk.accessibility", + "jdk.charsets", + "jdk.crypto.ec", + "jdk.crypto.pkcs11", + "jdk.dynalink", + "jdk.httpserver", + "jdk.internal.le", + "jdk.jfr", + "jdk.jvmstat", + "jdk.jvmstat.rmi", + "jdk.localedata", + "jdk.management", + "jdk.management.cmm", + "jdk.management.resource", + "jdk.naming.dns", + "jdk.naming.rmi", + "jdk.pack200", + "jdk.scripting.nashorn", + "jdk.scripting.nashorn.shell", + "jdk.sctp", + "jdk.security.auth", + "jdk.security.jgss", + "jdk.snmp", + "jdk.vm.cds", + "jdk.vm.ci", + "jdk.xml.dom", + "jdk.zipfs", + "jdk.crypto.mscapi", + "jdk.crypto.ucrypto", + "jdk.deploy.osx"}; // going away JDK-8148187 + + @SuppressWarnings("unchecked") + public static final BundlerParamInfo> MODULE_PATH = + new StandardBundlerParam<>( + I18N.getString("param.module-path.name"), + I18N.getString("param.module-path.description"), + "modulepath", + (Class>) (Object)List.class, + p -> new ArrayList(), + (s, p) -> Arrays.asList(s.split("(\\s" + File.pathSeparator + ")+")).stream() + .map(ss -> new File(ss).toPath()) + .collect(Collectors.toList())); + + @SuppressWarnings("unchecked") + public static final BundlerParamInfo JDK_MODULE_PATH = + new StandardBundlerParam<>( + I18N.getString("param.jdk-module-path.name"), + I18N.getString("param.jdk-module-path.description"), + "jdkmodulepath", + String.class, + p -> Paths.get(System.getProperty("java.home"), "jmods").toAbsolutePath().toString(), + (s, p) -> String.valueOf(s)); + + @SuppressWarnings("unchecked") + public static final BundlerParamInfo> ADD_MODULES = + new StandardBundlerParam<>( + I18N.getString("param.add-modules.name"), + I18N.getString("param.add-modules.description"), + "addmods", + (Class>) (Object) Set.class, + p -> new LinkedHashSet(), + (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split("[,;: ]+")))); + + @SuppressWarnings("unchecked") + public static final BundlerParamInfo> LIMIT_MODULES = + new StandardBundlerParam<>( + I18N.getString("param.limit-modules.name"), + I18N.getString("param.limit-modules.description"), + "limitmods", + (Class>) (Object) Set.class, + p -> new LinkedHashSet(), + (s, p) -> new LinkedHashSet<>(Arrays.asList(s.split("[,;: ]+")))); + + @SuppressWarnings("unchecked") + public static final BundlerParamInfo DETECT_MODULES = + new StandardBundlerParam<>( + I18N.getString("param.auto-modules.name"), + I18N.getString("param.auto-modules.description"), + "detectmods", + Boolean.class, + p -> Boolean.FALSE, + (s, p) -> Boolean.valueOf(s)); + + @SuppressWarnings("unchecked") + public static final BundlerParamInfo STRIP_NATIVE_COMMANDS = + new StandardBundlerParam<>( + I18N.getString("param.strip-executables.name"), + I18N.getString("param.strip-executables.description"), + "stripexecutables", + Boolean.class, + p -> Boolean.TRUE, + (s, p) -> Boolean.valueOf(s)); + + @SuppressWarnings("unchecked") + public static final BundlerParamInfo> JLINK_OPTIONS = + new StandardBundlerParam<>( + I18N.getString("param.jlink-options.name"), + I18N.getString("param.jlink-options.description"), + "jlinkOptions", + (Class>) (Object) Map.class, + p -> Collections.emptyMap(), + (s, p) -> { + try { + Properties props = new Properties(); + props.load(new StringReader(s)); + return new LinkedHashMap<>((Map)props); + } catch (IOException e) { + return new LinkedHashMap<>(); + } + }); + + @SuppressWarnings("unchecked") + public static final BundlerParamInfo JLINK_BUILDER = + new StandardBundlerParam<>( + I18N.getString("param.jlink-builder.name"), + I18N.getString("param.jlink-builder.description"), + "jlink.builder", + String.class, + null, + (s, p) -> s); + + + public static void execute(Map params, File outputParentDir, ImageBuilder imageBuilder) { + String jdkmodulePath = JDK_MODULE_PATH.fetchFrom(params); + List modulePath = MODULE_PATH.fetchFrom(params); + Set addModules = ADD_MODULES.fetchFrom(params); + Set limitModules = LIMIT_MODULES.fetchFrom(params); + File jdkModulePathFile = new File(jdkmodulePath); + + if (!jdkModulePathFile.exists() || !jdkModulePathFile.isDirectory()) { + Log.info("JDK Module path doesn't exist: " + jdkmodulePath); + //TODO fail? + jdkModulePathFile = null; + } + + if (DETECT_MODULES.fetchFrom(params)) { + // Add JDK modules to the module path. + if (jdkModulePathFile != null) { + modulePath.add(jdkModulePathFile.toPath()); + } + + // Get App Jars. + List appJars = JDepHelper.getResourceFileJarList(params); + + // Ask Jdeps for the list of dependent modules. + Collection detectedModules = JDepHelper.calculateModules(appJars, modulePath); + addModules.addAll(detectedModules); + Log.info("Automatically adding detected modules " + detectedModules); + } else if (addModules.isEmpty()) { + // Add all modules on user specified path (-modulepath). + addModules.addAll(getModuleNamesFromPath(modulePath)); + + // Only retain Java SE Modules. + if (jdkModulePathFile != null) { + Set jdkModuleNames = getModuleNamesFromPath(jdkModulePathFile.toPath()); + Set javaseModules = new HashSet<>(Arrays.asList(JRE_MODULES)); + + //TODO JDK-8149975 programmatically determine JRE vs JDK modules + jdkModuleNames.retainAll(javaseModules); // strip out JDK modules + addModules.addAll(jdkModuleNames); + + // Add JDK modules to the module path. + modulePath.add(jdkModulePathFile.toPath()); + } + } + + Path output = outputParentDir.toPath(); + + // jlink main arguments + Jlink.JlinkConfiguration jlinkConfig = new Jlink.JlinkConfiguration(output, + modulePath, + addModules, + limitModules); + + // plugin configuration + List plugins = new ArrayList<>(); + + if (STRIP_NATIVE_COMMANDS.fetchFrom(params)) { + plugins.add(Jlink.newPlugin( + "strip-native-commands", + Collections.singletonMap("strip-native-commands", "on"), + null)); + } + + plugins.add(Jlink.newPlugin( + "exclude-files", + Collections.singletonMap("exclude-files", getExcludeFileList()), + null)); + + // add user supplied jlink arguments + for (Map.Entry entry : JLINK_OPTIONS.fetchFrom(params).entrySet()) { + Object o = entry.getValue(); + if (o instanceof String) { + String key = entry.getKey(); + String value = (String)entry.getValue(); + plugins.add(Jlink.newPlugin(key, + Collections.singletonMap(key, value), + null)); + } + } + + plugins.add(Jlink.newPlugin("installed-modules", Collections.emptyMap(), null)); + + //TODO --compress-resources + + Jlink.PluginsConfiguration pluginConfig = new Jlink.PluginsConfiguration(plugins, imageBuilder, null); + + // Build the image + Jlink jlink = new Jlink(); + + try { + jlink.build(jlinkConfig, pluginConfig); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static Set getModuleNamesFromPath(List Value) { + Set result = new LinkedHashSet(); + ModuleManager mm = new ModuleManager(Value); + List modules = mm.getModules(EnumSet.of(ModuleManager.SearchType.ModularJar, + ModuleManager.SearchType.Jmod, + ModuleManager.SearchType.ExplodedModule)); + + for (Module module : modules) { + result.add(module.getModuleName()); + } + + return result; + } + + private static Set getModuleNamesFromPath(Path Value) { + return getModuleNamesFromPath(new ArrayList(Arrays.asList(Value))); + } + + //TODO + private static String getExcludeFileList() { + // strip debug symbols + String result = "*diz"; + + if (System.getProperty("os.name").toLowerCase().indexOf("mac") >= 0) { + // strip mac osx quicktime + result += ",*libjfxmedia_qtkit.dylib"; + } + + return result; + } +} diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/Module.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/Module.java new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/Module.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.tools.packager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + + +public final class Module { + private String FFileName; + private ModuleType FModuleType; + + private enum JarType {Unknown, UnnamedJar, ModularJar} + + + public enum ModuleType {Unknown, UnnamedJar, ModularJar, Jmod, ExplodedModule} + + public Module(File AFile) { + super(); + FFileName = AFile.getPath(); + FModuleType = getModuleType(AFile); + } + + public String getFileName() { + return FFileName; + } + + public String getModulePath() { + File file = new File(getFileName()); + return file.getParent(); + } + + public String getModuleName() { + File file = new File(getFileName()); + return getFileWithoutExtension(file.getName()); + } + + public ModuleType getModuleType() { + return FModuleType; + } + + public List getRequiredModules() { + List result = new ArrayList(); + + List files = new ArrayList(); + files.add(getFileName()); + Collection detectedModules = JDepHelper.calculateModules(files, null); + + for (String filename : detectedModules) { + Module module = new Module(new File(filename)); + result.add(module); + } + + return result; + } + + private static ModuleType getModuleType(File AFile) { + ModuleType result = ModuleType.Unknown; + String filename = AFile.getAbsolutePath(); + + if (AFile.isFile()) { + if (filename.endsWith(".jmod")) { + result = ModuleType.Jmod; + } + else if (filename.endsWith(".jar")) { + JarType status = isModularJar(filename); + + if (status == JarType.ModularJar) { + result = ModuleType.ModularJar; + } + else if (status == JarType.UnnamedJar) { + result = ModuleType.UnnamedJar; + } + } + } + else if (AFile.isDirectory()) { + File moduleInfo = new File(filename + File.separator + "module-info.class"); + + if (moduleInfo.exists()) { + result = ModuleType.ExplodedModule; + } + } + + return result; + } + + private static JarType isModularJar(String FileName) { + JarType result = JarType.Unknown; + List classNames = new ArrayList(); + + try { + ZipInputStream zip = new ZipInputStream(new FileInputStream(FileName)); + result = JarType.UnnamedJar; + + try { + for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) { + if (entry.getName().matches("module-info.class")) { + result = JarType.ModularJar; + break; + } + } + + zip.close(); + } catch (IOException ex) { + } + } catch (FileNotFoundException e) { + } + + return result; + } + + private static String getFileWithoutExtension(String FileName) { + return FileName.replaceFirst("[.][^.]+$", ""); + } +} diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/ModuleManager.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/ModuleManager.java new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/ModuleManager.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.tools.packager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + + +public final class ModuleManager { + private List FFolders = new ArrayList(); + + public enum SearchType {UnnamedJar, ModularJar, Jmod, ExplodedModule} + + public ModuleManager(String Folders) { + super(); + String lfolders = Folders.replaceAll("^\"|\"$", ""); + List paths = new ArrayList(); + + for (String folder : Arrays.asList(lfolders.split(File.pathSeparator))) { + File file = new File(folder); + paths.add(file.toPath()); + } + + initialize(paths); + } + + public ModuleManager(List Paths) { + super(); + initialize(Paths); + } + + private void initialize(List Paths) { + for (Path path : Paths) { + FFolders.add(path.toString().replaceAll("^\"|\"$", "")); + } + } + + public List getModules() { + return getModules(EnumSet.of(SearchType.UnnamedJar, + SearchType.ModularJar, SearchType.Jmod, SearchType.ExplodedModule)); + } + + public List getModules(EnumSet Search) { + List result = new ArrayList(); + + for (String folder : FFolders) { + result.addAll(getAllModulesInDirectory(folder, Search)); + } + + return result; + } + + private static List getAllModulesInDirectory(String Folder, EnumSet Search) { + List result = new ArrayList(); + File lfolder = new File(Folder); + File[] files = lfolder.listFiles(); + + for (File file : files) { + Module module = new Module(file); + + switch (module.getModuleType()) { + case Unknown: + break; + case UnnamedJar: + if (Search.contains(SearchType.UnnamedJar)) { + result.add(module); + } + break; + case ModularJar: + if (Search.contains(SearchType.ModularJar)) { + result.add(module); + } + break; + case Jmod: + if (Search.contains(SearchType.Jmod)) { + result.add(module); + } + break; + case ExplodedModule: + if (Search.contains(SearchType.ExplodedModule)) { + result.add(module); + } + break; + } + } + + return result; + } +} \ No newline at end of file diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/StandardBundlerParam.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/StandardBundlerParam.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/StandardBundlerParam.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/StandardBundlerParam.java @@ -32,7 +32,19 @@ import java.io.StringReader; import java.nio.file.Files; import java.text.MessageFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; import java.util.jar.Attributes; @@ -77,10 +89,34 @@ I18N.getString("param.app-resource-list.description"), BundleParams.PARAM_APP_RESOURCES + "List", (Class>) (Object) List.class, - p -> new ArrayList<>(Arrays.asList(APP_RESOURCES.fetchFrom(p))), // Default is appResources, as a single item list - null // no string translation, tool must provide complex type + p -> new ArrayList<>(Collections.singletonList(APP_RESOURCES.fetchFrom(p))), // Default is appResources, as a single item list + StandardBundlerParam::createAppResourcesListFromString ); + private static List createAppResourcesListFromString(String s, Map objectObjectMap) { + List result = new ArrayList<>(); + for (String path : s.split("[:;]")) { + File f = new File(path); + if (f.getName().equals("*") || path.endsWith("/") || path.endsWith("\\")) { + if (f.getName().equals("*")) { + f = f.getParentFile(); + } + Set theFiles = new HashSet<>(); + try { + Files.walk(f.toPath()) + .filter(Files::isRegularFile) + .forEach(p -> theFiles.add(p.toFile())); + } catch (IOException e) { + e.printStackTrace(); + } + result.add(new RelativeFileSet(f, theFiles)); + } else { + result.add(new RelativeFileSet(f.getParentFile(), Collections.singleton(f))); + } + } + return result; + } + public static final StandardBundlerParam ICON = new StandardBundlerParam<>( I18N.getString("param.icon-file.name"), @@ -99,6 +135,7 @@ BundleParams.PARAM_APPLICATION_CLASS, String.class, params -> { + //FIXME sniff modules extractMainClassInfoFromAppResources(params); return (String) params.get(BundleParams.PARAM_APPLICATION_CLASS); }, @@ -195,7 +232,7 @@ File appResourcesRoot = rfs.getBaseDirectory(); File f = new File(appResourcesRoot, s); if (f.exists()) { - return new RelativeFileSet(appResourcesRoot, new LinkedHashSet<>(Arrays.asList(f))); + return new RelativeFileSet(appResourcesRoot, new LinkedHashSet<>(Collections.singletonList(f))); } } throw new IllegalArgumentException( @@ -419,7 +456,7 @@ ); public static final BundlerParamInfo LICENSE_TYPE = - new StandardBundlerParam<> ( + new StandardBundlerParam<>( I18N.getString("param.license-type.name"), I18N.getString("param.license-type.description"), BundleParams.PARAM_LICENSE_TYPE, @@ -607,7 +644,7 @@ I18N.getString("param.com-app-cds-root.description"), "commercial.AppCDS.classRoots", (Class>)((Object)List.class), - p -> Arrays.asList(MAIN_CLASS.fetchFrom(p)), + p -> Collections.singletonList(MAIN_CLASS.fetchFrom(p)), (s, p) -> Arrays.asList(s.split("[ ,:]")) ); @@ -698,7 +735,7 @@ if (fnames[0] == null) { fnames[0] = file.getParentFile().toString(); } - params.put(MAIN_JAR.getID(), new RelativeFileSet(new File(fnames[0]), new LinkedHashSet<>(Arrays.asList(file)))); + params.put(MAIN_JAR.getID(), new RelativeFileSet(new File(fnames[0]), new LinkedHashSet<>(Collections.singletonList(file)))); } if (!hasMainJarClassPath) { String cp = attrs.getValue(Attributes.Name.CLASS_PATH); @@ -763,5 +800,4 @@ l.add(current.toString()); return l; } - } diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/linux/LinuxAppBundler.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/linux/LinuxAppBundler.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/linux/LinuxAppBundler.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/linux/LinuxAppBundler.java @@ -27,25 +27,28 @@ import com.oracle.tools.packager.AbstractImageBundler; import com.oracle.tools.packager.BundlerParamInfo; +import com.oracle.tools.packager.ConfigException; +import com.oracle.tools.packager.IOUtils; +import com.oracle.tools.packager.JLinkBundlerHelper; import com.oracle.tools.packager.JreUtils; import com.oracle.tools.packager.JreUtils.Rule; +import com.oracle.tools.packager.Log; +import com.oracle.tools.packager.RelativeFileSet; import com.oracle.tools.packager.StandardBundlerParam; -import com.oracle.tools.packager.Log; -import com.oracle.tools.packager.ConfigException; -import com.oracle.tools.packager.IOUtils; -import com.oracle.tools.packager.RelativeFileSet; import com.oracle.tools.packager.UnsupportedPlatformException; import com.sun.javafx.tools.packager.bundlers.BundleParams; +import jdk.tools.jlink.builder.ImageBuilder; +import jdk.packager.builders.linux.LinuxAppImageBuilder; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.PrintStream; import java.net.MalformedURLException; import java.net.URL; import java.text.MessageFormat; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.ResourceBundle; import static com.oracle.tools.packager.StandardBundlerParam.*; @@ -57,7 +60,6 @@ protected static final String LINUX_BUNDLER_PREFIX = BUNDLER_PREFIX + "linux" + File.separator; private static final String EXECUTABLE_NAME = "JavaAppLauncher"; - private static final String LIBRARY_NAME = "libpackager.so"; public static final BundlerParamInfo ICON_PNG = new StandardBundlerParam<>( I18N.getString("param.icon-png.name"), @@ -151,15 +153,6 @@ I18N.getString("error.no-linux-resources.advice")); } - //validate required inputs - testRuntime(LINUX_RUNTIME.fetchFrom(p), new String[] { - "lib/[^/]+/[^/]+/libjvm.so", // most reliable - "lib/rt.jar", // fallback canary for JDK 8 - }); - if (USE_FX_PACKAGING.fetchFrom(p)) { - testRuntime(LINUX_RUNTIME.fetchFrom(p), new String[] {"lib/ext/jfxrt.jar", "lib/jfxrt.jar"}); - } - return true; } @@ -169,16 +162,11 @@ return new File(outDir, APP_FS_NAME.fetchFrom(p)); } - public static String getLauncherName(Map p) { - return APP_FS_NAME.fetchFrom(p); - } - public static String getLauncherCfgName(Map p) { return "app/" + APP_FS_NAME.fetchFrom(p) +".cfg"; } File doBundle(Map p, File outputDirectory, boolean dependentTask) { - Map originalParams = new HashMap<>(p); try { if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) { throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-create-output-dir"), outputDirectory.getAbsolutePath())); @@ -196,35 +184,12 @@ Log.info(MessageFormat.format(I18N.getString("message.creating-bundle-location"), rootDirectory.getAbsolutePath())); } - File runtimeDirectory = new File(rootDirectory, "runtime"); - - File appDirectory = new File(rootDirectory, "app"); - appDirectory.mkdirs(); - - // create the primary launcher - createLauncherForEntryPoint(p, rootDirectory); - - // Copy library to the launcher folder - IOUtils.copyFromURL( - LinuxResources.class.getResource(LIBRARY_NAME), - new File(rootDirectory, LIBRARY_NAME)); - - // create the secondary launchers, if any - List> entryPoints = StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(p); - for (Map entryPoint : entryPoints) { - Map tmp = new HashMap<>(originalParams); - tmp.putAll(entryPoint); - createLauncherForEntryPoint(tmp, rootDirectory); + if (!p.containsKey(JLinkBundlerHelper.JLINK_BUILDER.getID())) { + p.put(JLinkBundlerHelper.JLINK_BUILDER.getID(), "linuxapp-image-builder"); } - // Copy runtime to PlugIns folder - copyRuntime(p, runtimeDirectory); - - // Copy class path entries to Java folder - copyApplication(p, appDirectory); - - // Copy icon to Resources folder -//FIXME copyIcon(resourcesDirectory); + ImageBuilder imageBuilder = new LinuxAppImageBuilder(p, outputDirectory.toPath()); + JLinkBundlerHelper.execute(p, outputDirectory, imageBuilder); return rootDirectory; } catch (IOException ex) { @@ -234,131 +199,6 @@ } } - private void createLauncherForEntryPoint(Map p, File rootDir) throws IOException { - // Copy executable to Linux folder - File executableFile = new File(rootDir, getLauncherName(p)); - IOUtils.copyFromURL( - RAW_EXECUTABLE_URL.fetchFrom(p), - executableFile); - - executableFile.setExecutable(true, false); - executableFile.setWritable(true, true); //for str - - // Generate launcher .cfg file - if (LAUNCHER_CFG_FORMAT.fetchFrom(p).equals(CFG_FORMAT_PROPERTIES)) { - writeCfgFile(p, rootDir); - } else { - writeCfgFile(p, new File(rootDir, getLauncherCfgName(p)), getRuntimeLocation(p)); - } - } - - private void copyApplication(Map params, File appDirectory) throws IOException { - List appResourcesList = APP_RESOURCES_LIST.fetchFrom(params); - if (appResourcesList == null) { - throw new RuntimeException("Null app resources?"); - } - for (RelativeFileSet appResources : appResourcesList) { - if (appResources == null) { - throw new RuntimeException("Null app resources?"); - } - File srcdir = appResources.getBaseDirectory(); - for (String fname : appResources.getIncludedFiles()) { - IOUtils.copyFile( - new File(srcdir, fname), new File(appDirectory, fname)); - } - } - } - - private String getRuntimeLocation(Map params) { - if (LINUX_RUNTIME.fetchFrom(params) == null) { - return ""; - } else { - return "$APPDIR/runtime"; - } - } - - private void writeCfgFile(Map params, File rootDir) throws FileNotFoundException { - File cfgFile = new File(rootDir, getLauncherCfgName(params)); - - cfgFile.delete(); - PrintStream out = new PrintStream(cfgFile); - out.println("app.runtime=" + getRuntimeLocation(params)); - out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); - out.println("app.version=" + VERSION.fetchFrom(params)); - - //use '/' in the class name (instead of '.' to simplify native code - out.println("app.mainclass=" + - MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/")); - - StringBuilder macroedPath = new StringBuilder(); - for (String s : CLASSPATH.fetchFrom(params).split("[ ;:]+")) { - macroedPath.append(s); - macroedPath.append(":"); - } - macroedPath.deleteCharAt(macroedPath.length() - 1); - out.println("app.classpath=" + macroedPath.toString()); - - List jvmargs = JVM_OPTIONS.fetchFrom(params); - int idx = 1; - for (String a : jvmargs) { - out.println("jvmarg."+idx+"="+a); - idx++; - } - Map jvmProps = JVM_PROPERTIES.fetchFrom(params); - for (Map.Entry entry : jvmProps.entrySet()) { - out.println("jvmarg."+idx+"=-D"+entry.getKey()+"="+entry.getValue()); - idx++; - } - - String preloader = PRELOADER_CLASS.fetchFrom(params); - if (preloader != null) { - out.println("jvmarg."+idx+"=-Djavafx.preloader="+preloader); - } - - //app.id required for setting user preferences (Java Preferences API) - out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); - out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); - - Map overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); - idx = 1; - for (Map.Entry arg: overridableJVMOptions.entrySet()) { - if (arg.getKey() == null || arg.getValue() == null) { - Log.info(I18N.getString("message.jvm-user-arg-is-null")); - } - else { - out.println("jvmuserarg."+idx+".name="+arg.getKey()); - out.println("jvmuserarg."+idx+".value="+arg.getValue()); - } - idx++; - } - - // add command line args - List args = ARGUMENTS.fetchFrom(params); - idx = 1; - for (String a : args) { - out.println("arg."+idx+"="+a); - idx++; - } - - out.close(); - } - - private void copyRuntime(Map params, File runtimeDirectory) throws IOException { - RelativeFileSet runtime = LINUX_RUNTIME.fetchFrom(params); - if (runtime == null) { - //request to use system runtime - return; - } - runtimeDirectory.mkdirs(); - - File srcdir = runtime.getBaseDirectory(); - Set filesToCopy = runtime.getIncludedFiles(); - for (String fname : filesToCopy) { - IOUtils.copyFile( - new File(srcdir, fname), new File(runtimeDirectory, fname)); - } - } - @Override public String getName() { return I18N.getString("bundler.name"); @@ -407,40 +247,4 @@ public File execute(Map params, File outputParentDir) { return doBundle(params, outputParentDir, false); } - - @Override - protected String getCacheLocation(Map params) { - return "$CACHEDIR/"; - } - - @Override - public void extractRuntimeFlags(Map params) { - if (params.containsKey(".runtime.autodetect")) return; - - params.put(".runtime.autodetect", "attempted"); - RelativeFileSet runtime = LINUX_RUNTIME.fetchFrom(params); - String commandline; - if (runtime == null) { - //System JRE, report nothing useful - params.put(".runtime.autodetect", "systemjre"); - } else { - File runtimePath = runtime.getBaseDirectory(); - File launcherPath = new File(runtimePath, "bin/java"); - - ProcessBuilder pb = new ProcessBuilder(launcherPath.getAbsolutePath(), "-version"); - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - try (PrintStream pout = new PrintStream(baos)) { - IOUtils.exec(pb, Log.isDebug(), true, pout); - } - - commandline = baos.toString(); - } catch (IOException e) { - e.printStackTrace(); - params.put(".runtime.autodetect", "failed"); - return; - } - AbstractImageBundler.extractFlagsFromVersion(params, commandline); - params.put(".runtime.autodetect", "succeeded"); - } - } } diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacAppBundler.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacAppBundler.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacAppBundler.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacAppBundler.java @@ -26,32 +26,29 @@ import com.oracle.tools.packager.AbstractImageBundler; import com.oracle.tools.packager.BundlerParamInfo; +import com.oracle.tools.packager.ConfigException; import com.oracle.tools.packager.EnumeratedBundlerParam; -import com.oracle.tools.packager.JreUtils; -import com.oracle.tools.packager.JreUtils.Rule; +import com.oracle.tools.packager.IOUtils; +import com.oracle.tools.packager.JLinkBundlerHelper; +import com.oracle.tools.packager.Log; import com.oracle.tools.packager.StandardBundlerParam; -import com.oracle.tools.packager.Log; -import com.sun.javafx.tools.packager.bundlers.BundleParams; -import com.oracle.tools.packager.ConfigException; -import com.oracle.tools.packager.IOUtils; -import com.oracle.tools.packager.RelativeFileSet; import com.oracle.tools.packager.UnsupportedPlatformException; +import jdk.tools.jlink.builder.ImageBuilder; +import jdk.packager.builders.mac.MacAppImageBuilder; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.math.BigInteger; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; import java.text.MessageFormat; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; import static com.oracle.tools.packager.StandardBundlerParam.*; -import static com.oracle.tools.packager.mac.MacBaseInstallerBundler.SIGNING_KEYCHAIN; -import static com.oracle.tools.packager.mac.MacBaseInstallerBundler.SIGNING_KEY_USER; -import static com.oracle.tools.packager.mac.MacBaseInstallerBundler.getPredefinedImage; +import static com.oracle.tools.packager.mac.MacBaseInstallerBundler.*; public class MacAppBundler extends AbstractImageBundler { @@ -61,12 +58,7 @@ public final static String MAC_BUNDLER_PREFIX = BUNDLER_PREFIX + "macosx" + File.separator; - private static final String EXECUTABLE_NAME = "JavaAppLauncher"; - private final static String LIBRARY_NAME = "libpackager.dylib"; private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns"; - private static final String OS_TYPE_CODE = "APPL"; - private static final String TEMPLATE_INFO_PLIST_LEGACY = "Info.plist.template"; - private static final String TEMPLATE_INFO_PLIST_LITE = "Info-lite.plist.template"; private static Map getMacCategories() { Map map = new HashMap<>(); @@ -115,15 +107,6 @@ return map; } - public static final BundlerParamInfo MAC_CONFIGURE_LAUNCHER_IN_PLIST = - new StandardBundlerParam<>( - I18N.getString("param.configure-launcher-in-plist"), - I18N.getString("param.configure-launcher-in-plist.description"), - "mac.configure-launcher-in-plist", - Boolean.class, - params -> Boolean.FALSE, - (s, p) -> Boolean.valueOf(s)); - public static final EnumeratedBundlerParam MAC_CATEGORY = new EnumeratedBundlerParam<>( I18N.getString("param.category-name"), @@ -184,21 +167,6 @@ }, (s, p) -> new File(s)); - public static final BundlerParamInfo RAW_EXECUTABLE_URL = new StandardBundlerParam<>( - I18N.getString("param.raw-executable-url.name"), - I18N.getString("param.raw-executable-url.description"), - "mac.launcher.url", - URL.class, - params -> MacResources.class.getResource(EXECUTABLE_NAME), - (s, p) -> { - try { - return new URL(s); - } catch (MalformedURLException e) { - Log.info(e.toString()); - return null; - } - }); - public static final BundlerParamInfo DEFAULT_ICNS_ICON = new StandardBundlerParam<>( I18N.getString("param.default-icon-icns"), I18N.getString("param.default-icon-icns.description"), @@ -207,24 +175,6 @@ params -> TEMPLATE_BUNDLE_ICON, (s, p) -> s); - public static final BundlerParamInfo MAC_RULES = new StandardBundlerParam<>( - "", - "", - ".mac.runtime.rules", - Rule[].class, - MacAppBundler::createMacRuntimeRules, - (s, p) -> null - ); - - public static final BundlerParamInfo MAC_RUNTIME = new StandardBundlerParam<>( - I18N.getString("param.runtime.name"), - I18N.getString("param.runtime.description"), - BundleParams.PARAM_RUNTIME, - RelativeFileSet.class, - params -> extractMacRuntime(System.getProperty("java.home"), params), - MacAppBundler::extractMacRuntime - ); - public static final BundlerParamInfo DEVELOPER_ID_APP_SIGNING_KEY = new StandardBundlerParam<>( I18N.getString("param.signing-key-developer-id-app.name"), I18N.getString("param.signing-key-developer-id-app.description"), @@ -256,43 +206,11 @@ }, (s, p) -> new File(s)); - public static RelativeFileSet extractMacRuntime(String base, Map params) { - if (base.isEmpty()) { - return null; - } - - File workingBase = new File(base); - workingBase = workingBase.getAbsoluteFile(); - try { - workingBase = workingBase.getCanonicalFile(); - } catch (IOException ignore) { - // we tried, workingBase will remain absolute and not canonical. - } - - if (workingBase.getName().equals("jre")) { - workingBase = workingBase.getParentFile(); - } - if (workingBase.getName().equals("Home")) { - workingBase = workingBase.getParentFile(); - } - if (workingBase.getName().equals("Contents")) { - workingBase = workingBase.getParentFile(); - } - return JreUtils.extractJreAsRelativeFileSet(workingBase.toString(), - MAC_RULES.fetchFrom(params), true); - } - public MacAppBundler() { super(); baseResourceLoader = MacResources.class; } - @Override - protected String getCacheLocation(Map params) { - return "$CACHEDIR/"; - } - - public static boolean validCFBundleVersion(String v) { // CFBundleVersion (String - iOS, OS X) specifies the build version // number of the bundle, which identifies an iteration (released or @@ -368,29 +286,7 @@ return true; } - // make sure we are pointing at the right JDK. - RelativeFileSet runtime = MAC_RUNTIME.fetchFrom(p); - if (runtime != null) { - runtime = new RelativeFileSet(runtime); - if ("jre".equals(runtime.getBaseDirectory().getName())) { - runtime.upshift(); - } - if ("Home".equals(runtime.getBaseDirectory().getName())) { - runtime.upshift(); - } - if ("Contents".equals(runtime.getBaseDirectory().getName())) { - runtime.upshift(); - } - } - - //validate required inputs - testRuntime(runtime, new String[] { - "Contents/Home/(jre/)?lib/[^/]+/libjvm.dylib", // most reliable - "Contents/Home/(jre/)?lib/rt.jar", // fallback canary for JDK 8 - }); - if (USE_FX_PACKAGING.fetchFrom(p)) { - testRuntime(runtime, new String[] {"Contents/Home/(jre/)?lib/ext/jfxrt.jar", "Contents/Home/(jre/)?lib/jfxrt.jar"}); - } + //TODO warn if MAC_RUNTIME is set // validate short version if (!validCFBundleVersion(MAC_CF_BUNDLE_VERSION.fetchFrom(p))) { @@ -420,41 +316,18 @@ return new File(CONFIG_ROOT.fetchFrom(params), APP_NAME.fetchFrom(params) + ".icns"); } - private void prepareConfigFiles(Map params) throws IOException { - File infoPlistFile = getConfig_InfoPlist(params); - infoPlistFile.createNewFile(); - writeInfoPlist(infoPlistFile, params); - // Copy icon to Resources folder - prepareIcon(params); - } - - public File doBundle(Map p, File outputDirectory, boolean dependentTask) { - File rootDirectory = null; - Map originalParams = new HashMap<>(p); - - if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) { - throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-create-output-dir"), outputDirectory.getAbsolutePath())); - } - if (!outputDirectory.canWrite()) { - throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-write-to-output-dir"), outputDirectory.getAbsolutePath())); - } - + File doBundle(Map p, File outputDirectory, boolean dependentTask) { try { - final File predefinedImage = getPredefinedImage(p); - if (predefinedImage != null) { - return predefinedImage; + if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) { + throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-create-output-dir"), outputDirectory.getAbsolutePath())); + } + if (!outputDirectory.canWrite()) { + throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-write-to-output-dir"), outputDirectory.getAbsolutePath())); } - // side effect is temp dir is created if not specified - BUILD_ROOT.fetchFrom(p); - - //prepare config resources (we will copy them to the bundle later) - // NB: explicitly saving them to simplify customization - prepareConfigFiles(p); - // Create directory structure - rootDirectory = new File(outputDirectory, APP_NAME.fetchFrom(p) + ".app"); + File rootDirectory = new File(outputDirectory, APP_NAME.fetchFrom(p) + ".app"); IOUtils.deleteRecursive(rootDirectory); rootDirectory.mkdirs(); @@ -462,103 +335,18 @@ Log.info(MessageFormat.format(I18N.getString("message.creating-app-bundle"), rootDirectory.getAbsolutePath())); } - File contentsDirectory = new File(rootDirectory, "Contents"); - contentsDirectory.mkdirs(); - - File macOSDirectory = new File(contentsDirectory, "MacOS"); - macOSDirectory.mkdirs(); - - File javaDirectory = new File(contentsDirectory, "Java"); - javaDirectory.mkdirs(); - - File plugInsDirectory = new File(contentsDirectory, "PlugIns"); - - File resourcesDirectory = new File(contentsDirectory, "Resources"); - resourcesDirectory.mkdirs(); - - // Generate PkgInfo - File pkgInfoFile = new File(contentsDirectory, "PkgInfo"); - pkgInfoFile.createNewFile(); - writePkgInfo(pkgInfoFile); - - // Copy executable to MacOS folder - File executableFile = new File(macOSDirectory, getLauncherName(p)); - IOUtils.copyFromURL( - RAW_EXECUTABLE_URL.fetchFrom(p), - executableFile); - - // Copy library to the MacOS folder - IOUtils.copyFromURL( - MacResources.class.getResource(LIBRARY_NAME), - new File(macOSDirectory, LIBRARY_NAME)); - - // maybe generate launcher config - if (!MAC_CONFIGURE_LAUNCHER_IN_PLIST.fetchFrom(p)) { - if (LAUNCHER_CFG_FORMAT.fetchFrom(p).equals(CFG_FORMAT_PROPERTIES)) { - writeCfgFile(p, rootDirectory); - } else { - writeCfgFile(p, new File(rootDirectory, getLauncherCfgName(p)), getRuntimeLocation(p)); - } + if (!p.containsKey(JLinkBundlerHelper.JLINK_BUILDER.getID())) { + p.put(JLinkBundlerHelper.JLINK_BUILDER.getID(), "macapp-image-builder"); } - executableFile.setExecutable(true, false); - - // Copy runtime to PlugIns folder - copyRuntime(plugInsDirectory, p); - - // Copy class path entries to Java folder - copyClassPathEntries(javaDirectory, p); - - //TODO: Need to support adding native libraries. - // Copy library path entries to MacOS folder - //copyLibraryPathEntries(macOSDirectory); - - /*********** Take care of "config" files *******/ - // Copy icon to Resources folder - IOUtils.copyFile(getConfig_Icon(p), - new File(resourcesDirectory, getConfig_Icon(p).getName())); - - // copy file association icons - for (Map fa : FILE_ASSOCIATIONS.fetchFrom(p)) { - File f = FA_ICON.fetchFrom(fa); - if (f != null && f.exists()) { - IOUtils.copyFile(f, - new File(resourcesDirectory, f.getName())); - } - } - - // Generate Info.plist - IOUtils.copyFile(getConfig_InfoPlist(p), - new File(contentsDirectory, "Info.plist")); - - // create the secondary launchers, if any - List> entryPoints = StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(p); - for (Map entryPoint : entryPoints) { - Map tmp = new HashMap<>(originalParams); - tmp.putAll(entryPoint); - createLauncherForEntryPoint(tmp, rootDirectory); - } - - // maybe sign - if (Optional.ofNullable(SIGN_BUNDLE.fetchFrom(p)).orElse(Boolean.TRUE)) { - String signingIdentity = DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(p); - if (signingIdentity != null) { - MacBaseInstallerBundler.signAppBundle(p, rootDirectory, signingIdentity, BUNDLE_ID_SIGNING_PREFIX.fetchFrom(p)); - } - } + ImageBuilder imageBuilder = new MacAppImageBuilder(p, outputDirectory.toPath()); + JLinkBundlerHelper.execute(p, outputDirectory, imageBuilder); + return rootDirectory; } catch (IOException ex) { Log.info(ex.toString()); Log.verbose(ex); return null; - } finally { - if (!VERBOSE.fetchFrom(p)) { - //cleanup - cleanupConfigFiles(p); - } else { - Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), CONFIG_ROOT.fetchFrom(p).getAbsolutePath())); - } } - return rootDirectory; } public void cleanupConfigFiles(Map params) { @@ -569,479 +357,6 @@ } } - private void copyClassPathEntries(File javaDirectory, Map params) throws IOException { - List resourcesList = APP_RESOURCES_LIST.fetchFrom(params); - if (resourcesList == null) { - throw new RuntimeException(I18N.getString("message.null-classpath")); - } - - for (RelativeFileSet classPath : resourcesList) { - File srcdir = classPath.getBaseDirectory(); - for (String fname : classPath.getIncludedFiles()) { - IOUtils.copyFile( - new File(srcdir, fname), new File(javaDirectory, fname)); - } - } - } - - private void copyRuntime(File plugInsDirectory, Map params) throws IOException { - RelativeFileSet runtime = MAC_RUNTIME.fetchFrom(params); - if (runtime == null) { - //request to use system runtime => do not bundle - return; - } - runtime = new RelativeFileSet(runtime); - if ("jre".equals(runtime.getBaseDirectory().getName())) { - runtime.upshift(); - } - if ("Home".equals(runtime.getBaseDirectory().getName())) { - runtime.upshift(); - } - if ("Contents".equals(runtime.getBaseDirectory().getName())) { - runtime.upshift(); - } - - - plugInsDirectory.mkdirs(); - - File srcdir = runtime.getBaseDirectory(); - // the name in .../Contents/PlugIns/ must have a dot to be verified - // properly by the Mac App Store. - File destDir = new File(plugInsDirectory, "Java.runtime"); - Set filesToCopy = runtime.getIncludedFiles(); - - for (String fname : filesToCopy) { - IOUtils.copyFile( - new File(srcdir, fname), new File(destDir, fname)); - } - } - - private void prepareIcon(Map params) throws IOException { - File icon = ICON_ICNS.fetchFrom(params); - if (icon == null || !icon.exists()) { - fetchResource(MAC_BUNDLER_PREFIX+ APP_NAME.fetchFrom(params) +".icns", - "icon", - DEFAULT_ICNS_ICON.fetchFrom(params), - getConfig_Icon(params), - VERBOSE.fetchFrom(params), - DROP_IN_RESOURCES_ROOT.fetchFrom(params)); - } else { - fetchResource(MAC_BUNDLER_PREFIX+ APP_NAME.fetchFrom(params) +".icns", - "icon", - icon, - getConfig_Icon(params), - VERBOSE.fetchFrom(params), - DROP_IN_RESOURCES_ROOT.fetchFrom(params)); - } - } - - private String getLauncherName(Map params) { - if (APP_NAME.fetchFrom(params) != null) { - return APP_NAME.fetchFrom(params); - } else { - return MAIN_CLASS.fetchFrom(params); - } - } - - private String getBundleName(Map params) { - //TODO: Check to see what rules/limits are in place for CFBundleName - if (MAC_CF_BUNDLE_NAME.fetchFrom(params) != null) { - String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params); - if (bn.length() > 16) { - Log.info(MessageFormat.format(I18N.getString("message.bundle-name-too-long-warning"), MAC_CF_BUNDLE_NAME.getID(), bn)); - } - return MAC_CF_BUNDLE_NAME.fetchFrom(params); - } else if (APP_NAME.fetchFrom(params) != null) { - return APP_NAME.fetchFrom(params); - } else { - String nm = MAIN_CLASS.fetchFrom(params); - if (nm.length() > 16) { - nm = nm.substring(0, 16); - } - return nm; - } - } - - private String getRuntimeLocation(Map params) { - if (MAC_RUNTIME.fetchFrom(params) == null) { - return ""; - } else { - return "$APPDIR/PlugIns/Java.runtime"; - } - } - - private void writeInfoPlist(File file, Map params) throws IOException { - Log.verbose(MessageFormat.format(I18N.getString("message.preparing-info-plist"), file.getAbsolutePath())); - - //prepare config for exe - //Note: do not need CFBundleDisplayName if we do not support localization - Map data = new HashMap<>(); - data.put("DEPLOY_ICON_FILE", getConfig_Icon(params).getName()); - data.put("DEPLOY_BUNDLE_IDENTIFIER", - MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params)); - data.put("DEPLOY_BUNDLE_NAME", - getBundleName(params)); - data.put("DEPLOY_BUNDLE_COPYRIGHT", - COPYRIGHT.fetchFrom(params) != null ? COPYRIGHT.fetchFrom(params) : "Unknown"); - data.put("DEPLOY_LAUNCHER_NAME", getLauncherName(params)); - if (MAC_RUNTIME.fetchFrom(params) != null) { - data.put("DEPLOY_JAVA_RUNTIME_NAME", "$APPDIR/PlugIns/Java.runtime"); - } else { - data.put("DEPLOY_JAVA_RUNTIME_NAME", ""); - } - data.put("DEPLOY_BUNDLE_SHORT_VERSION", - VERSION.fetchFrom(params) != null ? VERSION.fetchFrom(params) : "1.0.0"); - data.put("DEPLOY_BUNDLE_CFBUNDLE_VERSION", - MAC_CF_BUNDLE_VERSION.fetchFrom(params) != null ? MAC_CF_BUNDLE_VERSION.fetchFrom(params) : "100"); - data.put("DEPLOY_BUNDLE_CATEGORY", - //TODO parameters should provide set of values for IDEs - MAC_CATEGORY.validatedFetchFrom(params)); - - data.put("DEPLOY_MAIN_JAR_NAME", MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); - - data.put("DEPLOY_PREFERENCES_ID", PREFERENCES_ID.fetchFrom(params).toLowerCase()); - - StringBuilder sb = new StringBuilder(); - List jvmOptions = JVM_OPTIONS.fetchFrom(params); - - String newline = ""; //So we don't add unneccessary extra line after last append - for (String o : jvmOptions) { - sb.append(newline).append(" ").append(o).append(""); - newline = "\n"; - } - - Map jvmProps = JVM_PROPERTIES.fetchFrom(params); - for (Map.Entry entry : jvmProps.entrySet()) { - sb.append(newline) - .append(" -D") - .append(entry.getKey()) - .append("=") - .append(entry.getValue()) - .append(""); - newline = "\n"; - } - - String preloader = PRELOADER_CLASS.fetchFrom(params); - if (preloader != null) { - sb.append(newline) - .append(" -Djavafx.preloader=") - .append(preloader) - .append(""); - //newline = "\n"; - } - - data.put("DEPLOY_JVM_OPTIONS", sb.toString()); - - sb = new StringBuilder(); - List args = ARGUMENTS.fetchFrom(params); - newline = ""; //So we don't add unneccessary extra line after last append - for (String o : args) { - sb.append(newline).append(" ").append(o).append(""); - newline = "\n"; - } - data.put("DEPLOY_ARGUMENTS", sb.toString()); - - newline = ""; - sb = new StringBuilder(); - Map overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); - for (Map.Entry arg: overridableJVMOptions.entrySet()) { - sb.append(newline) - .append(" ").append(arg.getKey()).append("\n") - .append(" ").append(arg.getValue()).append(""); - newline = "\n"; - } - data.put("DEPLOY_JVM_USER_OPTIONS", sb.toString()); - - - data.put("DEPLOY_LAUNCHER_CLASS", MAIN_CLASS.fetchFrom(params)); - - StringBuilder macroedPath = new StringBuilder(); - for (String s : CLASSPATH.fetchFrom(params).split("[ ;:]+")) { - macroedPath.append(s); - macroedPath.append(":"); - } - macroedPath.deleteCharAt(macroedPath.length() - 1); - - data.put("DEPLOY_APP_CLASSPATH", macroedPath.toString()); - - //TODO: Add remainder of the classpath - - StringBuilder bundleDocumentTypes = new StringBuilder(); - StringBuilder exportedTypes = new StringBuilder(); - for (Map fileAssociation : FILE_ASSOCIATIONS.fetchFrom(params)) { - - List extensions = FA_EXTENSIONS.fetchFrom(fileAssociation); - - if (extensions == null) { - Log.info(I18N.getString("message.creating-association-with-null-extension")); - } - - List mimeTypes = FA_CONTENT_TYPE.fetchFrom(fileAssociation); - String itemContentType = MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) + "." + ((extensions == null || extensions.isEmpty()) - ? "mime" - : extensions.get(0)); - String description = FA_DESCRIPTION.fetchFrom(fileAssociation); - File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICNS - - bundleDocumentTypes.append(" \n") - .append(" LSItemContentTypes\n") - .append(" \n") - .append(" ") - .append(itemContentType) - .append("\n") - .append(" \n") - .append("\n") - .append(" CFBundleTypeName\n") - .append(" ") - .append(description) - .append("\n") - .append("\n") - .append(" LSHandlerRank\n") - .append(" Owner\n") //TODO make a bundler arg - .append("\n") - .append(" CFBundleTypeRole\n") - .append(" Editor\n") // TODO make a bundler arg - .append("\n") - .append(" LSIsAppleDefaultForType\n") - .append(" \n") // TODO make a bundler arg - .append("\n"); - - if (icon != null && icon.exists()) { - //? - bundleDocumentTypes.append(" CFBundleTypeIconFile\n") - .append(" ") - .append(icon.getName()) - .append("\n"); - } - bundleDocumentTypes.append(" \n"); - - exportedTypes.append(" \n") - .append(" UTTypeIdentifier\n") - .append(" ") - .append(itemContentType) - .append("\n") - .append("\n") - .append(" UTTypeDescription\n") - .append(" ") - .append(description) - .append("\n") - .append(" UTTypeConformsTo\n") - .append(" \n") - .append(" public.data\n") //TODO expose this? - .append(" \n") - .append("\n"); - - if (icon != null && icon.exists()) { - exportedTypes.append(" UTTypeIconFile\n") - .append(" ") - .append(icon.getName()) - .append("\n") - .append("\n"); - } - - exportedTypes.append("\n") - .append(" UTTypeTagSpecification\n") - .append(" \n") - //TODO expose via param? .append(" com.apple.ostype\n"); - //TODO expose via param? .append(" ABCD\n") - .append("\n"); - - if (extensions != null && !extensions.isEmpty()) { - exportedTypes.append(" public.filename-extension\n") - .append(" \n"); - - for (String ext : extensions) { - exportedTypes.append(" ") - .append(ext) - .append("\n"); - } - exportedTypes.append(" \n"); - } - if (mimeTypes != null && !mimeTypes.isEmpty()) { - exportedTypes.append(" public.mime-type\n") - .append(" \n"); - - for (String mime : mimeTypes) { - exportedTypes.append(" ") - .append(mime) - .append("\n"); - } - exportedTypes.append(" \n"); - } - exportedTypes.append(" \n") - .append(" \n"); - } - String associationData; - if (bundleDocumentTypes.length() > 0) { - associationData = "\n CFBundleDocumentTypes\n \n" - + bundleDocumentTypes.toString() - + " \n\n UTExportedTypeDeclarations\n \n" - + exportedTypes.toString() - + " \n"; - } else { - associationData = ""; - } - data.put("DEPLOY_FILE_ASSOCIATIONS", associationData); - - - Writer w = new BufferedWriter(new FileWriter(file)); - w.write(preprocessTextResource( - MAC_BUNDLER_PREFIX + getConfig_InfoPlist(params).getName(), - I18N.getString("resource.bundle-config-file"), - MAC_CONFIGURE_LAUNCHER_IN_PLIST.fetchFrom(params) - ? TEMPLATE_INFO_PLIST_LEGACY - : TEMPLATE_INFO_PLIST_LITE, - data, VERBOSE.fetchFrom(params), - DROP_IN_RESOURCES_ROOT.fetchFrom(params))); - w.close(); - - } - - private void writePkgInfo(File file) throws IOException { - - //hardcoded as it does not seem we need to change it ever - String signature = "????"; - - try (Writer out = new BufferedWriter(new FileWriter(file))) { - out.write(OS_TYPE_CODE + signature); - out.flush(); - } - } - - public static Rule[] createMacRuntimeRules(Map params) { - if (!System.getProperty("os.name").toLowerCase().contains("os x")) { - // we will never get a sensible answer unless we are running on OSX, - // so quit now and return null indicating 'no sensible value' - return null; - } - - //Subsetting of JRE is restricted. - //JRE README defines what is allowed to strip: - // http://www.oracle.com/technetwork/java/javase/jre-8-readme-2095710.html - // - - List rules = new ArrayList<>(); - - File baseDir; - - if (params.containsKey(MAC_RUNTIME.getID())) { - Object o = params.get(MAC_RUNTIME.getID()); - if (o instanceof RelativeFileSet) { - baseDir = ((RelativeFileSet)o).getBaseDirectory(); - } else { - baseDir = new File(o.toString()); - } - } else { - baseDir = new File(System.getProperty("java.home")); - } - - // we accept either pointing at the directories typically installed at: - // /Libraries/Java/JavaVirtualMachine/jdk1.8.0_40/ - // * . - // * Contents/Home - // * Contents/Home/jre - // /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin/ - // * . - // * /Contents/Home - // version may change, and if we don't detect any Contents/Home or Contents/Home/jre we will - // presume we are at a root. - - if (!baseDir.exists()) { - throw new RuntimeException(I18N.getString("error.non-existent-runtime"), - new ConfigException(I18N.getString("error.non-existent-runtime"), - I18N.getString("error.non-existent-runtime.advice"))); - } - - boolean isJRE; - boolean isJDK; - - try { - String path = baseDir.getCanonicalPath(); - if (path.endsWith("/Contents/Home/jre")) { - baseDir = baseDir.getParentFile().getParentFile().getParentFile(); - } else if (path.endsWith("/Contents/Home")) { - baseDir = baseDir.getParentFile().getParentFile(); - } - - isJRE = new File(baseDir, "Contents/Home/lib/jli/libjli.dylib").exists(); - isJDK = new File(baseDir, "Contents/Home/jre/lib/jli/libjli.dylib").exists(); - - } catch (IOException e) { - throw new RuntimeException(e); - } - - if (!(isJRE || isJDK)) { - throw new RuntimeException(I18N.getString("error.cannot-detect-runtime-in-directory"), - new ConfigException(I18N.getString("error.cannot-detect-runtime-in-directory"), - I18N.getString("error.cannot-detect-runtime-in-directory.advice"))); - } - - // we need the Info.plist for signing - rules.add(Rule.suffix("/contents/info.plist")); - - // Strip some JRE specific stuff - if (isJRE) { - rules.add(Rule.suffixNeg("/contents/disabled.plist")); - rules.add(Rule.suffixNeg("/contents/enabled.plist")); - rules.add(Rule.substrNeg("/contents/frameworks/")); - } - - // strip out command line tools - rules.add(Rule.suffixNeg("home/bin")); - if (isJDK) { - rules.add(Rule.suffixNeg("home/jre/bin")); - } - - // strip out JRE stuff - if (isJRE) { - // update helper - rules.add(Rule.suffixNeg("resources")); - // interfacebuilder files - rules.add(Rule.suffixNeg("lib/nibs")); - // browser integration - rules.add(Rule.suffixNeg("lib/libnpjp2.dylib")); - // java webstart - rules.add(Rule.suffixNeg("lib/security/javaws.policy")); - rules.add(Rule.suffixNeg("lib/shortcuts")); - - // general deploy libraries - rules.add(Rule.suffixNeg("lib/deploy")); - rules.add(Rule.suffixNeg("lib/deploy.jar")); - rules.add(Rule.suffixNeg("lib/javaws.jar")); - rules.add(Rule.suffixNeg("lib/libdeploy.dylib")); - rules.add(Rule.suffixNeg("lib/plugin.jar")); - } - - // strip out man pages - rules.add(Rule.suffixNeg("home/man")); - - // this is the build hashes, strip or keep? - //rules.add(Rule.suffixNeg("home/release")); - - // strip out JDK stuff like JavaDB, JNI Headers, etc - if (isJDK) { - rules.add(Rule.suffixNeg("home/db")); - rules.add(Rule.suffixNeg("home/demo")); - rules.add(Rule.suffixNeg("home/include")); - rules.add(Rule.suffixNeg("home/lib")); - rules.add(Rule.suffixNeg("home/sample")); - rules.add(Rule.suffixNeg("home/src.zip")); - rules.add(Rule.suffixNeg("home/javafx-src.zip")); - } - - //"home/rt" is not part of the official builds - // but we may be creating this symlink to make older NB projects - // happy. Make sure to not include it into final artifact - rules.add(Rule.suffixNeg("home/rt")); - - //rules.add(Rule.suffixNeg("jre/lib/ext")); //need some of jars there for https to work - - // strip out flight recorder - rules.add(Rule.suffixNeg("lib/jfr.jar")); - - return rules.toArray(new Rule[rules.size()]); - } - ////////////////////////////////////////////////////////////////////////////////// // Implement Bundler ////////////////////////////////////////////////////////////////////////////////// @@ -1087,7 +402,7 @@ MAC_CF_BUNDLE_IDENTIFIER, MAC_CF_BUNDLE_NAME, MAC_CF_BUNDLE_VERSION, - MAC_RUNTIME, +// MAC_RUNTIME, MAIN_CLASS, MAIN_JAR, PREFERENCES_ID, @@ -1104,125 +419,23 @@ return doBundle(params, outputParentDir, false); } - private void createLauncherForEntryPoint(Map p, File rootDirectory) throws IOException { - prepareConfigFiles(p); +// private void createLauncherForEntryPoint(Map p, File rootDirectory) throws IOException { +// prepareConfigFiles(p); +// +// if (LAUNCHER_CFG_FORMAT.fetchFrom(p).equals(CFG_FORMAT_PROPERTIES)) { +// writeCfgFile(p, rootDirectory); +// } else { +// writeCfgFile(p, new File(rootDirectory, getLauncherCfgName(p)), "$APPDIR/PlugIns/Java.runtime"); +// } +// +// // Copy executable root folder +// File executableFile = new File(rootDirectory, "Contents/MacOS/" + getLauncherName(p)); +// IOUtils.copyFromURL( +// RAW_EXECUTABLE_URL.fetchFrom(p), +// executableFile); +// executableFile.setExecutable(true, false); +// +// } +// - if (LAUNCHER_CFG_FORMAT.fetchFrom(p).equals(CFG_FORMAT_PROPERTIES)) { - writeCfgFile(p, rootDirectory); - } else { - writeCfgFile(p, new File(rootDirectory, getLauncherCfgName(p)), "$APPDIR/PlugIns/Java.runtime"); - } - - // Copy executable root folder - File executableFile = new File(rootDirectory, "Contents/MacOS/" + getLauncherName(p)); - IOUtils.copyFromURL( - RAW_EXECUTABLE_URL.fetchFrom(p), - executableFile); - executableFile.setExecutable(true, false); - - } - - public static String getLauncherCfgName(Map p) { - return "Contents/Java/" + APP_NAME.fetchFrom(p) +".cfg"; - } - - private void writeCfgFile(Map params, File rootDir) throws FileNotFoundException { - File pkgInfoFile = new File(rootDir, getLauncherCfgName(params)); - - pkgInfoFile.delete(); - - PrintStream out = new PrintStream(pkgInfoFile); - out.println("app.runtime=" + getRuntimeLocation(params)); - out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); - out.println("app.version=" + VERSION.fetchFrom(params)); - //for future AU support (to be able to find app in the registry) - out.println("app.id=" + IDENTIFIER.fetchFrom(params)); - out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); - out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); - - out.println("app.mainclass=" + - MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/")); - out.println("app.classpath=" + CLASSPATH.fetchFrom(params)); - - List jvmargs = JVM_OPTIONS.fetchFrom(params); - int idx = 1; - for (String a : jvmargs) { - out.println("jvmarg."+idx+"="+a); - idx++; - } - Map jvmProps = JVM_PROPERTIES.fetchFrom(params); - for (Map.Entry entry : jvmProps.entrySet()) { - out.println("jvmarg."+idx+"=-D"+entry.getKey()+"="+entry.getValue()); - idx++; - } - - String preloader = PRELOADER_CLASS.fetchFrom(params); - if (preloader != null) { - out.println("jvmarg."+idx+"=-Djavafx.preloader="+preloader); - } - - Map overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); - idx = 1; - for (Map.Entry arg: overridableJVMOptions.entrySet()) { - if (arg.getKey() == null || arg.getValue() == null) { - Log.info(I18N.getString("message.jvm-user-arg-is-null")); - } - else { - out.println("jvmuserarg."+idx+".name="+arg.getKey()); - out.println("jvmuserarg."+idx+".value="+arg.getValue()); - } - idx++; - } - - // add command line args - List args = ARGUMENTS.fetchFrom(params); - idx = 1; - for (String a : args) { - out.println("arg."+idx+"="+a); - idx++; - } - - out.close(); - } - @Override - public void extractRuntimeFlags(Map params) { - if (params.containsKey(".runtime.autodetect")) return; - - params.put(".runtime.autodetect", "attempted"); - RelativeFileSet runtime = MAC_RUNTIME.fetchFrom(params); - String commandline; - if (runtime == null) { - //System JRE, report nothing useful - params.put(".runtime.autodetect", "systemjre"); - } else { - File workingBase = runtime.getBaseDirectory(); - if (workingBase.getName().equals("jre")) { - workingBase = workingBase.getParentFile(); - } - if (workingBase.getName().equals("Home")) { - workingBase = workingBase.getParentFile(); - } - if (workingBase.getName().equals("Contents")) { - workingBase = workingBase.getParentFile(); - } - - - try { - byte[] infoPlistBytes = Files.readAllBytes(workingBase.toPath().resolve(Paths.get("Contents", "Info.plist"))); - String infoPlist = new String(infoPlistBytes); - - Pattern cfBundleVersionMatcher = Pattern.compile("CFBundleVersion\\s*([^<]+)"); - Matcher m = cfBundleVersionMatcher.matcher(infoPlist); - if (m.find()) { - AbstractImageBundler.extractFlagsFromVersion(params, "java version \"" + m.group(1) + "\"\n"); - params.put(".runtime.autodetect", "succeeded"); - } else { - params.put(".runtime.autodetect", "failed"); - } - } catch (IOException e) { - e.printStackTrace(); - params.put(".runtime.autodetect", "failed"); - } - } - } } diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacAppStoreBundler.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacAppStoreBundler.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacAppStoreBundler.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacAppStoreBundler.java @@ -26,13 +26,12 @@ package com.oracle.tools.packager.mac; import com.oracle.tools.packager.BundlerParamInfo; -import com.oracle.tools.packager.JreUtils; -import com.oracle.tools.packager.RelativeFileSet; import com.oracle.tools.packager.StandardBundlerParam; import com.oracle.tools.packager.Log; import com.oracle.tools.packager.ConfigException; import com.oracle.tools.packager.IOUtils; import com.oracle.tools.packager.UnsupportedPlatformException; +import jdk.packager.builders.mac.MacAppImageBuilder; import java.io.File; import java.io.FileNotFoundException; @@ -111,7 +110,7 @@ p.put(DEFAULT_ICNS_ICON.getID(), TEMPLATE_BUNDLE_ICON_HIDPI); // next we need to change the jdk/jre stripping to strip gstreamer - p.put(MAC_RULES.getID(), createMacAppStoreRuntimeRules(p)); +// p.put(MAC_RULES.getID(), createMacAppStoreRuntimeRules(p)); // now we create the app File appImageDir = APP_IMAGE_BUILD_ROOT.fetchFrom(p); @@ -129,7 +128,7 @@ String entitlementsFile = getConfig_Entitlements(p).toString(); String inheritEntitlements = getConfig_Inherit_Entitlements(p).toString(); - signAppBundle(p, appLocation, signingIdentity, identifierPrefix, entitlementsFile, inheritEntitlements); + MacAppImageBuilder.signAppBundle(p, appLocation.toPath(), signingIdentity, identifierPrefix, entitlementsFile, inheritEntitlements); ProcessBuilder pb; // create the final pkg file @@ -237,69 +236,6 @@ } - public static JreUtils.Rule[] createMacAppStoreRuntimeRules(Map params) { - //Subsetting of JRE is restricted. - //JRE README defines what is allowed to strip: - // http://www.oracle.com/technetwork/java/javase/jre-8-readme-2095710.html - // - - List rules = new ArrayList<>(); - - rules.addAll(Arrays.asList(createMacRuntimeRules(params))); - - File baseDir; - - if (params.containsKey(MAC_RUNTIME.getID())) { - Object o = params.get(MAC_RUNTIME.getID()); - if (o instanceof RelativeFileSet) { - - baseDir = ((RelativeFileSet) o).getBaseDirectory(); - } else { - baseDir = new File(o.toString()); - } - } else { - baseDir = new File(System.getProperty("java.home")); - } - - // we accept either pointing at the directories typically installed at: - // /Libraries/Java/JavaVirtualMachine/jdk1.8.0_40/ - // * . - // * Contents/Home - // * Contents/Home/jre - // /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin/ - // * . - // * /Contents/Home - // version may change, and if we don't detect any Contents/Home or Contents/Home/jre we will - // presume we are at a root. - - - try { - String path = baseDir.getCanonicalPath(); - if (path.endsWith("/Contents/Home/jre")) { - baseDir = baseDir.getParentFile().getParentFile().getParentFile(); - } else if (path.endsWith("/Contents/Home")) { - baseDir = baseDir.getParentFile().getParentFile(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - - if (!baseDir.exists()) { - throw new RuntimeException(I18N.getString("error.non-existent-runtime"), - new ConfigException(I18N.getString("error.non-existent-runtime"), - I18N.getString("error.non-existent-runtime.advice"))); - } - - if (new File(baseDir, "Contents/Home/lib/libjfxmedia_qtkit.dylib").exists() - || new File(baseDir, "Contents/Home/jre/lib/libjfxmedia_qtkit.dylib").exists()) - { - rules.add(JreUtils.Rule.suffixNeg("/lib/libjfxmedia_qtkit.dylib")); - } else { - rules.add(JreUtils.Rule.suffixNeg("/lib/libjfxmedia.dylib")); - } - return rules.toArray(new JreUtils.Rule[rules.size()]); - } - ////////////////////////////////////////////////////////////////////////////////// // Implement Bundler ////////////////////////////////////////////////////////////////////////////////// @@ -360,14 +296,7 @@ //run basic validation to ensure requirements are met // Mac App Store apps cannot use the system runtime - if (params.containsKey(MAC_RUNTIME.getID()) && params.get(MAC_RUNTIME.getID()) == null) { - throw new ConfigException( - I18N.getString("error.no-system-runtime"), - I18N.getString("error.no-system-runtime.advice")); - } - - //we need to change the jdk/jre stripping to strip qtkit code - params.put(MAC_RULES.getID(), createMacAppStoreRuntimeRules(params)); + //TODO //we are not interested in return code, only possible exception validateAppImageAndBundeler(params); diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacBaseInstallerBundler.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacBaseInstallerBundler.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacBaseInstallerBundler.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/mac/MacBaseInstallerBundler.java @@ -215,153 +215,11 @@ return DAEMON_BUNDLER.fetchFrom(p).doBundle(p, daemonImageRoot, true); } - public static void signAppBundle(Map params, File appLocation, String signingIdentity, String identifierPrefix) throws IOException { - signAppBundle(params, appLocation, signingIdentity, identifierPrefix, null, null); - } +// public static void signAppBundle(Map params, File appLocation, String signingIdentity, String identifierPrefix) throws IOException { +// signAppBundle(params, appLocation, signingIdentity, identifierPrefix, null, null); +// } +// - public static void signAppBundle(Map params, File appLocation, String signingIdentity, String identifierPrefix, String entitlementsFile, String inheritedEntitlements) throws IOException { - AtomicReference toThrow = new AtomicReference<>(); - String appExecutable = "/Contents/MacOS/" + APP_NAME.fetchFrom(params); - String keyChain = SIGNING_KEYCHAIN.fetchFrom(params); - - // sign all dylibs and jars - Files.walk(appLocation.toPath()) - // while we are searching let's fix permissions - .peek(path -> { - try { - Set pfp = Files.getPosixFilePermissions(path); - if (!pfp.contains(PosixFilePermission.OWNER_WRITE)) { - pfp = EnumSet.copyOf(pfp); - pfp.add(PosixFilePermission.OWNER_WRITE); - Files.setPosixFilePermissions(path, pfp); - } - } catch (IOException e) { - Log.debug(e); - } - }) - .filter(p -> Files.isRegularFile(p) && - !(p.toString().contains("/Contents/MacOS/libjli.dylib") - || p.toString().contains("/Contents/MacOS/JavaAppletPlugin") - || p.toString().endsWith(appExecutable)) - ).forEach(p -> { - //noinspection ThrowableResultOfMethodCallIgnored - if (toThrow.get() != null) return; - - List args = new ArrayList<>(); - args.addAll(Arrays.asList("codesign", - "-s", signingIdentity, // sign with this key - "--prefix", identifierPrefix, // use the identifier as a prefix - "-vvvv")); - if (entitlementsFile != null && - (p.toString().endsWith(".jar") - || p.toString().endsWith(".dylib"))) - { - args.add("--entitlements"); - args.add(entitlementsFile); // entitlements - } else if (inheritedEntitlements != null && Files.isExecutable(p)) { - args.add("--entitlements"); - args.add(inheritedEntitlements); // inherited entitlements for executable processes - } - if (keyChain != null && !keyChain.isEmpty()) { - args.add("--keychain"); - args.add(keyChain); - } - args.add(p.toString()); - - try { - Set oldPermissions = Files.getPosixFilePermissions(p); - File f = p.toFile(); - f.setWritable(true, true); - - ProcessBuilder pb = new ProcessBuilder(args); - IOUtils.exec(pb, VERBOSE.fetchFrom(params)); - - Files.setPosixFilePermissions(p, oldPermissions); - } catch (IOException ioe) { - toThrow.set(ioe); - } - }); - - IOException ioe = toThrow.get(); - if (ioe != null) { - throw ioe; - } - - // sign all plugins and frameworks - Consumer signIdentifiedByPList = path -> { - //noinspection ThrowableResultOfMethodCallIgnored - if (toThrow.get() != null) return; - - try { - List args = new ArrayList<>(); - args.addAll(Arrays.asList("codesign", - "-s", signingIdentity, // sign with this key - "--prefix", identifierPrefix, // use the identifier as a prefix - "-vvvv")); - if (keyChain != null && !keyChain.isEmpty()) { - args.add("--keychain"); - args.add(keyChain); - } - args.add(path.toString()); - ProcessBuilder pb = new ProcessBuilder(args); - IOUtils.exec(pb, VERBOSE.fetchFrom(params)); - - args = new ArrayList<>(); - args.addAll(Arrays.asList("codesign", - "-s", signingIdentity, // sign with this key - "--prefix", identifierPrefix, // use the identifier as a prefix - "-vvvv")); - if (keyChain != null && !keyChain.isEmpty()) { - args.add("--keychain"); - args.add(keyChain); - } - args.add(path.toString() + "/Contents/_CodeSignature/CodeResources"); - pb = new ProcessBuilder(args); - IOUtils.exec(pb, VERBOSE.fetchFrom(params)); - } catch (IOException e) { - toThrow.set(e); - } - }; - - Path pluginsPath = appLocation.toPath().resolve("Contents/PlugIns"); - if (Files.isDirectory(pluginsPath)) { - Files.list(pluginsPath) - .forEach(signIdentifiedByPList); - - ioe = toThrow.get(); - if (ioe != null) { - throw ioe; - } - } - Path frameworkPath = appLocation.toPath().resolve("Contents/Frameworks"); - if (Files.isDirectory(frameworkPath)) { - Files.list(frameworkPath) - .forEach(signIdentifiedByPList); - - ioe = toThrow.get(); - if (ioe != null) { - throw ioe; - } - } - - // sign the app itself - List args = new ArrayList<>(); - args.addAll(Arrays.asList("codesign", - "-s", signingIdentity, // sign with this key - "-vvvv")); // super verbose output - if (entitlementsFile != null) { - args.add("--entitlements"); - args.add(entitlementsFile); // entitlements - } - if (keyChain != null && !keyChain.isEmpty()) { - args.add("--keychain"); - args.add(keyChain); - } - args.add(appLocation.toString()); - - ProcessBuilder pb = new ProcessBuilder(args.toArray(new String[args.size()])); - IOUtils.exec(pb, VERBOSE.fetchFrom(params)); - } @Override public Collection> getBundleParameters() { diff --git a/modules/fxpackager/src/main/java/com/oracle/tools/packager/windows/WinAppBundler.java b/modules/fxpackager/src/main/java/com/oracle/tools/packager/windows/WinAppBundler.java --- a/modules/fxpackager/src/main/java/com/oracle/tools/packager/windows/WinAppBundler.java +++ b/modules/fxpackager/src/main/java/com/oracle/tools/packager/windows/WinAppBundler.java @@ -27,78 +27,35 @@ import com.oracle.tools.packager.AbstractImageBundler; import com.oracle.tools.packager.BundlerParamInfo; -import com.oracle.tools.packager.StandardBundlerParam; -import com.oracle.tools.packager.Log; import com.oracle.tools.packager.ConfigException; import com.oracle.tools.packager.IOUtils; +import com.oracle.tools.packager.JLinkBundlerHelper; +import com.oracle.tools.packager.Log; import com.oracle.tools.packager.RelativeFileSet; +import com.oracle.tools.packager.StandardBundlerParam; import com.oracle.tools.packager.UnsupportedPlatformException; +import jdk.packager.builders.windows.WindowsAppImageBuilder; +import jdk.tools.jlink.builder.ImageBuilder; -import java.io.*; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; import java.text.MessageFormat; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Pattern; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.ResourceBundle; import static com.oracle.tools.packager.StandardBundlerParam.*; -import static com.oracle.tools.packager.windows.WindowsBundlerParam.BIT_ARCH_64; -import static com.oracle.tools.packager.windows.WindowsBundlerParam.BIT_ARCH_64_RUNTIME; -import static com.oracle.tools.packager.windows.WindowsBundlerParam.WIN_RUNTIME; +import static com.oracle.tools.packager.windows.WindowsBundlerParam.*; public class WinAppBundler extends AbstractImageBundler { private static final ResourceBundle I18N = ResourceBundle.getBundle(WinAppBundler.class.getName()); - public static final BundlerParamInfo CONFIG_ROOT = new WindowsBundlerParam<>( - I18N.getString("param.config-root.name"), - I18N.getString("param.config-root.description"), - "configRoot", - File.class, - params -> { - File imagesRoot = new File(BUILD_ROOT.fetchFrom(params), "windows"); - imagesRoot.mkdirs(); - return imagesRoot; - }, - (s, p) -> null); - - private final static String EXECUTABLE_NAME = "WinLauncher.exe"; - private final static String LIBRARY_NAME = "packager.dll"; - - private final static String[] VS_VERS = {"100", "110", "120"}; - private final static String REDIST_MSVCR = "msvcrVS_VER.dll"; - private final static String REDIST_MSVCP = "msvcpVS_VER.dll"; - private static final String TOOL_ICON_SWAP="IconSwap.exe"; - private static final String TOOL_VERSION_INFO_SWAP="VersionInfoSwap.exe"; - - private static final String EXECUTABLE_PROPERTIES_TEMPLATE = "WinLauncher.properties"; - - public static final BundlerParamInfo RAW_EXECUTABLE_URL = new WindowsBundlerParam<>( - I18N.getString("param.raw-executable-url.name"), - I18N.getString("param.raw-executable-url.description"), - "win.launcher.url", - URL.class, - params -> WinResources.class.getResource(EXECUTABLE_NAME), - (s, p) -> { - try { - return new URL(s); - } catch (MalformedURLException e) { - Log.info(e.toString()); - return null; - } - }); - - public static final BundlerParamInfo REBRAND_EXECUTABLE = new WindowsBundlerParam<>( - I18N.getString("param.rebrand-executable.name"), - I18N.getString("param.rebrand-executable.description"), - "win.launcher.rebrand", - Boolean.class, - params -> Boolean.TRUE, - (s, p) -> Boolean.valueOf(s)); public static final BundlerParamInfo ICON_ICO = new StandardBundlerParam<>( I18N.getString("param.icon-ico.name"), @@ -123,10 +80,6 @@ public final static String WIN_BUNDLER_PREFIX = BUNDLER_PREFIX + "windows/"; - File getConfigRoot(Map params) { - return CONFIG_ROOT.fetchFrom(params); - } - @Override public boolean validate(Map params) throws UnsupportedPlatformException, ConfigException { try { @@ -153,23 +106,12 @@ imageBundleValidation(p); - if (WinResources.class.getResource(TOOL_ICON_SWAP) == null || - WinResources.class.getResource(TOOL_VERSION_INFO_SWAP) == null) - { + if (WinResources.class.getResource(TOOL_ICON_SWAP) == null) { throw new ConfigException( I18N.getString("error.no-windows-resources"), I18N.getString("error.no-windows-resources.advice")); } - //validate required inputs - testRuntime(WIN_RUNTIME.fetchFrom(p), new String[] { - "bin\\\\[^\\\\]+\\\\jvm.dll", // most reliable - "lib\\\\rt.jar", // fallback canary for JDK 8 - }); - if (USE_FX_PACKAGING.fetchFrom(p)) { - testRuntime(WIN_RUNTIME.fetchFrom(p), new String[] {"lib\\\\ext\\\\jfxrt.jar", "lib\\\\jfxrt.jar"}); - } - //validate runtime bit-architectire testRuntimeBitArchitecture(p); @@ -203,92 +145,11 @@ return "app\\" + APP_NAME.fetchFrom(p) +".cfg"; } - private File getConfig_AppIcon(Map params) { - return new File(getConfigRoot(params), APP_NAME.fetchFrom(params) + ".ico"); - } - - private File getConfig_ExecutableProperties(Map params) { - return new File(getConfigRoot(params), APP_NAME.fetchFrom(params) + ".properties"); - } - - private final static String TEMPLATE_APP_ICON ="javalogo_white_48.ico"; - - //remove - protected void cleanupConfigFiles(Map params) { - getConfig_AppIcon(params).delete(); - getConfig_ExecutableProperties(params).delete(); - } - - private void validateValueAndPut(Map data, String key, - BundlerParamInfo param, Map params) - { - String value = param.fetchFrom(params); - if (value.contains("\r") || value.contains("\n")) { - Log.info("Configuration Parameter " + param.getID() + " contains multiple lines of text, ignore it"); - data.put(key, ""); - return; - } - data.put(key, value); - } - - protected void prepareExecutableProperties(Map params) - throws IOException - { - Map data = new HashMap<>(); - - // mapping Java parameters in strings for version resource - data.put("COMMENTS", ""); - validateValueAndPut(data, "COMPANY_NAME", VENDOR, params); - validateValueAndPut(data, "FILE_DESCRIPTION", DESCRIPTION, params); - validateValueAndPut(data, "FILE_VERSION", VERSION, params); - data.put("INTERNAL_NAME", getLauncherName(params)); - validateValueAndPut(data, "LEGAL_COPYRIGHT", COPYRIGHT, params); - data.put("LEGAL_TRADEMARK", ""); - data.put("ORIGINAL_FILENAME", getLauncherName(params)); - data.put("PRIVATE_BUILD", ""); - validateValueAndPut(data, "PRODUCT_NAME", APP_NAME, params); - validateValueAndPut(data, "PRODUCT_VERSION", VERSION, params); - data.put("SPECIAL_BUILD", ""); - - Writer w = new BufferedWriter(new FileWriter(getConfig_ExecutableProperties(params))); - String content = preprocessTextResource( - WinAppBundler.WIN_BUNDLER_PREFIX + getConfig_ExecutableProperties(params).getName(), - I18N.getString("resource.executable-properties-template"), EXECUTABLE_PROPERTIES_TEMPLATE, data, - VERBOSE.fetchFrom(params), - DROP_IN_RESOURCES_ROOT.fetchFrom(params)); - w.write(content); - w.close(); - } - - private void prepareConfigFiles(Map params) throws IOException { - File iconTarget = getConfig_AppIcon(params); - - File icon = ICON_ICO.fetchFrom(params); - if (icon != null && icon.exists()) { - fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(), - I18N.getString("resource.application-icon"), - icon, - iconTarget, - VERBOSE.fetchFrom(params), - DROP_IN_RESOURCES_ROOT.fetchFrom(params)); - } else { - fetchResource(WIN_BUNDLER_PREFIX + iconTarget.getName(), - I18N.getString("resource.application-icon"), - WinAppBundler.TEMPLATE_APP_ICON, - iconTarget, - VERBOSE.fetchFrom(params), - DROP_IN_RESOURCES_ROOT.fetchFrom(params)); - } - - prepareExecutableProperties(params); - } - public boolean bundle(Map p, File outputDirectory) { return doBundle(p, outputDirectory, false) != null; } File doBundle(Map p, File outputDirectory, boolean dependentTask) { - Map originalParams = new HashMap<>(p); if (!outputDirectory.isDirectory() && !outputDirectory.mkdirs()) { throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-create-output-dir"), outputDirectory.getAbsolutePath())); } @@ -305,33 +166,12 @@ IOUtils.deleteRecursive(rootDirectory); rootDirectory.mkdirs(); - File appDirectory = new File(rootDirectory, "app"); - appDirectory.mkdirs(); + if (!p.containsKey(JLinkBundlerHelper.JLINK_BUILDER.getID())) { + p.put(JLinkBundlerHelper.JLINK_BUILDER.getID(), "windowsapp-image-builder"); + } - // create the .exe launchers - createLauncherForEntryPoint(p, rootDirectory); - - // copy the jars - copyApplication(p, appDirectory); - - // Copy runtime - File runtimeDirectory = new File(rootDirectory, "runtime"); - copyRuntime(p, runtimeDirectory); - - // copy in the needed libraries - IOUtils.copyFromURL( - WinResources.class.getResource(LIBRARY_NAME), - new File(rootDirectory, LIBRARY_NAME)); - - copyMSVCDLLs(rootDirectory, runtimeDirectory); - - // create the secondary launchers, if any - List> entryPoints = StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(p); - for (Map entryPoint : entryPoints) { - Map tmp = new HashMap<>(originalParams); - tmp.putAll(entryPoint); - createLauncherForEntryPoint(tmp, rootDirectory); - } + ImageBuilder imageBuilder = new WindowsAppImageBuilder(p, outputDirectory.toPath()); + JLinkBundlerHelper.execute(p, outputDirectory, imageBuilder); if (!dependentTask) { Log.info(MessageFormat.format(I18N.getString("message.result-dir"), outputDirectory.getAbsolutePath())); @@ -339,241 +179,12 @@ return rootDirectory; } catch (IOException ex) { - Log.info("Exception: "+ex); - Log.debug(ex); + Log.info(ex.toString()); + Log.verbose(ex); return null; - } finally { - if (VERBOSE.fetchFrom(p)) { - Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), getConfigRoot(p).getAbsolutePath())); - } else { - cleanupConfigFiles(p); - } - } - - } - - private void copyMSVCDLLs(File rootDirectory, File jreDir) throws IOException { - String vsVer = null; - if (jreDir == null || !jreDir.isDirectory()) { - jreDir = new File(System.getProperty("java.home")); - } - - // first copy the ones needed for the launcher - for (String thisVer : VS_VERS) { - if (copyMSVCDLLs(rootDirectory, thisVer)) { - vsVer = thisVer; - break; - } - } - if (vsVer == null) { - throw new RuntimeException("Not found MSVC dlls"); - } - - AtomicReference ioe = new AtomicReference<>(); - final String finalVsVer = vsVer; - Files.list(jreDir.toPath().resolve("bin")) - .filter(p -> Pattern.matches("msvc(r|p)\\d\\d\\d.dll", p.toFile().getName().toLowerCase())) - .filter(p -> !p.toString().toLowerCase().endsWith(finalVsVer + ".dll")) - .forEach(p -> { - try { - IOUtils.copyFile(p.toFile(), new File(rootDirectory, p.toFile().getName())); - } catch (IOException e) { - ioe.set(e); - } - }); - - IOException e = ioe.get(); - if (e != null) { - throw e; } } - private boolean copyMSVCDLLs(File rootDirectory, String VS_VER) throws IOException { - final URL REDIST_MSVCR_URL = WinResources.class.getResource( - REDIST_MSVCR.replaceAll("VS_VER", VS_VER)); - final URL REDIST_MSVCP_URL = WinResources.class.getResource( - REDIST_MSVCP.replaceAll("VS_VER", VS_VER)); - - if (REDIST_MSVCR_URL != null && REDIST_MSVCP_URL != null) { - IOUtils.copyFromURL( - REDIST_MSVCR_URL, - new File(rootDirectory, REDIST_MSVCR.replaceAll("VS_VER", VS_VER))); - IOUtils.copyFromURL( - REDIST_MSVCP_URL, - new File(rootDirectory, REDIST_MSVCP.replaceAll("VS_VER", VS_VER))); - return true; - } - - return false; // not found - } - - private void createLauncherForEntryPoint(Map p, File rootDirectory) throws IOException { - prepareConfigFiles(p); - - // Generate launcher .cfg file - if (LAUNCHER_CFG_FORMAT.fetchFrom(p).equals(CFG_FORMAT_PROPERTIES)) { - writeCfgFile(p, rootDirectory); - } else { - writeCfgFile(p, new File(rootDirectory, getLauncherCfgName(p)), getRuntimeLocation(p)); - } - - // Copy executable root folder - File executableFile = new File(rootDirectory, getLauncherName(p)); - IOUtils.copyFromURL( - RAW_EXECUTABLE_URL.fetchFrom(p), - executableFile); - executableFile.setExecutable(true, false); - - //Update branding of exe file - if (REBRAND_EXECUTABLE.fetchFrom(p) && getConfig_AppIcon(p).exists()) { - //extract IconSwap helper tool - File iconSwapTool = File.createTempFile("iconswap", ".exe"); - IOUtils.copyFromURL( - WinResources.class.getResource(TOOL_ICON_SWAP), - iconSwapTool, - true); - iconSwapTool.setExecutable(true, false); - iconSwapTool.deleteOnExit(); - - //run it on launcher file - executableFile.setWritable(true); - ProcessBuilder pb = new ProcessBuilder( - iconSwapTool.getAbsolutePath(), - getConfig_AppIcon(p).getAbsolutePath(), - executableFile.getAbsolutePath()); - IOUtils.exec(pb, VERBOSE.fetchFrom(p)); - executableFile.setReadOnly(); - iconSwapTool.delete(); - } - - IOUtils.copyFile(getConfig_AppIcon(p), - new File(rootDirectory, APP_NAME.fetchFrom(p) + ".ico")); - - if (REBRAND_EXECUTABLE.fetchFrom(p) && getConfig_ExecutableProperties(p).exists()) { - // extract VersionInfoHelper tool - File versionInfoTool = File.createTempFile("versioninfoswap", ".exe"); - IOUtils.copyFromURL( - WinResources.class.getResource(TOOL_VERSION_INFO_SWAP), - versionInfoTool, - true); - versionInfoTool.setExecutable(true, false); - versionInfoTool.deleteOnExit(); - - // run it on launcher file - executableFile.setWritable(true); - ProcessBuilder pb = new ProcessBuilder( - versionInfoTool.getAbsolutePath(), - getConfig_ExecutableProperties(p).getAbsolutePath(), - executableFile.getAbsolutePath()); - IOUtils.exec(pb, VERBOSE.fetchFrom(p)); - executableFile.setReadOnly(); - versionInfoTool.delete(); - } - } - - private void copyApplication(Map params, File appDirectory) throws IOException { - List appResourcesList = APP_RESOURCES_LIST.fetchFrom(params); - if (appResourcesList == null) { - throw new RuntimeException("Null app resources?"); - } - for (RelativeFileSet appResources : appResourcesList) { - if (appResources == null) { - throw new RuntimeException("Null app resources?"); - } - File srcdir = appResources.getBaseDirectory(); - for (String fname : appResources.getIncludedFiles()) { - IOUtils.copyFile( - new File(srcdir, fname), new File(appDirectory, fname)); - } - } - } - - private String getRuntimeLocation(Map params) { - if (WIN_RUNTIME.fetchFrom(params) == null) { - return ""; - } else { - return "$APPDIR\\runtime"; - } - } - - private void writeCfgFile(Map params, File rootDir) throws FileNotFoundException { - File cfgFile = new File(rootDir, getLauncherCfgName(params)); - - cfgFile.delete(); - - PrintStream out = new PrintStream(cfgFile); - out.println("app.runtime=" + getRuntimeLocation(params)); - out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); - out.println("app.version=" + VERSION.fetchFrom(params)); - //for future AU support (to be able to find app in the registry) - out.println("app.id=" + IDENTIFIER.fetchFrom(params)); - out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); - out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); - - out.println("app.mainclass=" + - MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/")); - out.println("app.classpath=" + CLASSPATH.fetchFrom(params)); - - List jvmargs = JVM_OPTIONS.fetchFrom(params); - int idx = 1; - for (String a : jvmargs) { - out.println("jvmarg."+idx+"="+a); - idx++; - } - Map jvmProps = JVM_PROPERTIES.fetchFrom(params); - for (Map.Entry entry : jvmProps.entrySet()) { - out.println("jvmarg."+idx+"=-D"+entry.getKey()+"="+entry.getValue()); - idx++; - } - - String preloader = PRELOADER_CLASS.fetchFrom(params); - if (preloader != null) { - out.println("jvmarg."+idx+"=-Djavafx.preloader="+preloader); - } - - Map overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); - idx = 1; - for (Map.Entry arg: overridableJVMOptions.entrySet()) { - if (arg.getKey() == null || arg.getValue() == null) { - Log.info(I18N.getString("message.jvm-user-arg-is-null")); - } - else { - out.println("jvmuserarg."+idx+".name="+arg.getKey()); - out.println("jvmuserarg."+idx+".value="+arg.getValue()); - } - idx++; - } - - // add command line args - List args = ARGUMENTS.fetchFrom(params); - idx = 1; - for (String a : args) { - out.println("arg."+idx+"="+a); - idx++; - } - - out.close(); - } - - private void copyRuntime(Map params, File runtimeDirectory) throws IOException { - RelativeFileSet runtime = WIN_RUNTIME.fetchFrom(params); - if (runtime == null) { - //its ok, request to use system JRE - return; - } - runtimeDirectory.mkdirs(); - - File srcdir = runtime.getBaseDirectory(); - Set filesToCopy = runtime.getIncludedFiles(); - for (String fname : filesToCopy) { - IOUtils.copyFile( - new File(srcdir, fname), new File(runtimeDirectory, fname)); - } - } - - public void extractRuntimeFlags(Map params) { - extractFlagsFromRuntime(params); - } public static void extractFlagsFromRuntime(Map params) { if (params.containsKey(".runtime.autodetect")) return; @@ -655,9 +266,4 @@ public File execute(Map params, File outputParentDir) { return doBundle(params, outputParentDir, false); } - - @Override - protected String getCacheLocation(Map params) { - return "$CACHEDIR/"; - } } diff --git a/modules/fxpackager/src/main/java/com/sun/javafx/tools/ant/Application.java b/modules/fxpackager/src/main/java/com/sun/javafx/tools/ant/Application.java --- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/ant/Application.java +++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/ant/Application.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,6 +61,11 @@ String version = null; Boolean daemon = null; + public List addModule = new LinkedList(); + public List limitModule = new LinkedList(); + String jdkModulePath; + boolean detectModules; + public void setVersion(String v) { version = v; } @@ -180,6 +185,94 @@ daemon = b; } + + /** + * "addModule" declaration for the application's runtime. + * + * Modules can be specified per-element, or comma/colon/semi-colon/space separated + * + * @ant.not-required Default is to bundle the whole platform + */ + public Argument createAddModule() { + Argument a = new Argument(); + addModule.add(a); + return a; + } + + /** + * "addModule" declaration for the application's runtime + * + * @ant.not-required Default is to bundle the whole platform + */ + List getAddModule() { + List lst = new LinkedList(); + for(Argument a: arguments) { + for (String s : a.value.split("[:;,\\s]+")) { + lst.add(s); + } + } + return lst; + } + + /** + * "limitModule" declaration for the application's runtime. + * + * Modules can be specified per-element, or comma/colon/semi-colon/space separated + * + * @ant.not-required Default is to bundle the whole platform + */ + public Argument createLimitModule() { + Argument a = new Argument(); + addModule.add(a); + return a; + } + + /** + * "limitModule" declaration for the application's runtime + * + * @ant.not-required Default is to bundle the whole platform + */ + List getLimitModule() { + List lst = new LinkedList(); + for(Argument a: arguments) { + for (String s : a.value.split("[:;,\\s]+")) { + lst.add(s); + } + } + return lst; + } + + /** + * Whether or not the bundler should attempt to detect and add used modules + */ + public boolean getDetectModules() { + return detectModules; + } + + /** + * Whether or not the bundler should attempt to detect and add used modules + * @ant.not-required default is false + */ + public void setDetectModules(boolean Value) { + this.detectModules = Value; + } + + /** + * Module path within the running applicaiton + */ + public String getJdkModulePath() { + return jdkModulePath; + } + + /** + * Module path within the running applicaiton + * + * @ant.not-required default is $PACKAGEPATH/modules + */ + public void setJdkModulePath(String Value) { + this.jdkModulePath = Value; + } + //return instance that actually has data. Could be referenced object ... public Application get() { return isReference() ? diff --git a/modules/fxpackager/src/main/java/com/sun/javafx/tools/ant/DeployFXTask.java b/modules/fxpackager/src/main/java/com/sun/javafx/tools/ant/DeployFXTask.java --- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/ant/DeployFXTask.java +++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/ant/DeployFXTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,6 +108,7 @@ private Resources resources = null; private Preferences prefs = null; private String codebase = null; + private String modulePath = null; //container to embed application into //could be either string id or js code. If it is string id then it needs to @@ -135,6 +136,10 @@ codebase = str; } + public void setLinkModulePath(String str) { + modulePath = str; + } + public DeployFXTask() { packager = new PackagerLib(); deployParams = new DeployParams(); @@ -147,6 +152,7 @@ deployParams.setOfflineAllowed(offlineAllowed); deployParams.setVerbose(verbose); deployParams.setCodebase(codebase); + deployParams.setModulePath(modulePath); deployParams.setSignBundle(signBundle); if (width != null) { @@ -180,6 +186,15 @@ deployParams.setVersion(app.get().version); deployParams.setId(app.get().id); deployParams.setServiceHint(app.get().daemon); + + for (String s : app.getAddModule()) { + deployParams.addAddModule(s); + } + for (String s : app.getLimitModule()) { + deployParams.addLimitModule(s); + } + deployParams.setDetectModules(app.getDetectModules()); + deployParams.setJdkModulePath(app.getJdkModulePath()); } if (appInfo != null) { diff --git a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/DeployParams.java b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/DeployParams.java --- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/DeployParams.java +++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/DeployParams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,6 +74,14 @@ List htmlParams; List arguments; //unnamed arguments + // Java 9 modules support + Set addModules = new LinkedHashSet<>(); + Set limitModules = new LinkedHashSet<>(); + boolean detectModules = false; + boolean stripExecutables = false; + String modulePath; + String jdkModulePath; + int width; int height; String embeddedWidth = null; @@ -241,6 +249,30 @@ this.arguments = args; } + public void addAddModule(String module) { + addModules.add(module); + } + + public void addLimitModule(String module) { + limitModules.add(module); + } + + public void setModulePath(String value) { + this.modulePath = value; + } + + public void setJdkModulePath(String value) { + this.jdkModulePath = value; + } + + public void setDetectModules(boolean value) { + this.detectModules = value; + } + + public void setStripExecutables(boolean value) { + this.stripExecutables = value; + } + public void setDescription(String description) { this.description = description; } @@ -523,7 +555,13 @@ StandardBundlerParam.JVM_PROPERTIES.getID(), StandardBundlerParam.JVM_OPTIONS.getID(), StandardBundlerParam.USER_JVM_OPTIONS.getID(), - StandardBundlerParam.ARGUMENTS.getID() + StandardBundlerParam.ARGUMENTS.getID(), + //StandardBundlerParam.MODULE_NAME.getID(), + JLinkBundlerHelper.MODULE_PATH.getID(), + JLinkBundlerHelper.JDK_MODULE_PATH.getID(), + JLinkBundlerHelper.ADD_MODULES.getID(), + JLinkBundlerHelper.LIMIT_MODULES.getID(), + JLinkBundlerHelper.STRIP_NATIVE_COMMANDS.getID() )); @SuppressWarnings("unchecked") @@ -604,6 +642,13 @@ bundleParams.setJvmUserArgs(jvmUserArgs); bundleParams.setArguments(arguments); + bundleParams.setAddModules(addModules); + bundleParams.setLimitModules(limitModules); + bundleParams.setDetectModules(detectModules); + bundleParams.setStripExecutables(stripExecutables); + bundleParams.setAppModulePath(jdkModulePath); + bundleParams.setLinkModulePath(modulePath); + File appIcon = null; List> bundlerIcons = new ArrayList<>(); for (Icon ic: icons) { diff --git a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Main.java b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Main.java --- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Main.java +++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -229,6 +229,9 @@ boolean srcfilesSet = false; File templateInFile = null; File templateOutFile = null; + deployParams.setBundleType(BundleType.JNLP); + deployParams.setTargetFormat("jnlp"); + //can only set it to true with command line, reset default deployParams.setEmbedJNLP(false); @@ -274,7 +277,6 @@ } else { //assume it is request to build only specific format // (like exe or msi) - type = BundleType.INSTALLER; format = (v != null) ? v.toLowerCase() : null; } } @@ -338,6 +340,32 @@ addArgument(deployParams, nextArg(args, i++)); } else if (arg.equalsIgnoreCase("-nosign")) { deployParams.setSignBundle(false); + } else if (arg.equals("-addmods")) { + deployParams.addModules.add(nextArg(args, i++)); + } else if (arg.equals("-limitmods")) { + deployParams.limitModules.add(nextArg(args, i++)); + } else if (arg.equals("-detectmods")) { + deployParams.detectModules = true; + } else if (arg.equals("-stripexecutables")) { + deployParams.stripExecutables = true; + } else if (arg.equals("-modulepath")) { + if (deployParams.modulePath == null) { + deployParams.modulePath = nextArg(args, i++); + } else { + deployParams.modulePath = + deployParams.modulePath + + File.pathSeparator + + nextArg(args, i++); + } + } else if (arg.equals("-jdkmodulepath")) { + if (deployParams.jdkModulePath == null) { + deployParams.jdkModulePath = nextArg(args, i++); + } else { + deployParams.jdkModulePath = + deployParams.jdkModulePath + + File.pathSeparator + + nextArg(args, i++); + } } else { throw new PackagerException("ERR_UnknownArgument", arg); } diff --git a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/PackagerLib.java b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/PackagerLib.java --- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/PackagerLib.java +++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/PackagerLib.java @@ -28,7 +28,6 @@ import com.oracle.tools.packager.Bundlers; import com.oracle.tools.packager.ConfigException; import com.oracle.tools.packager.Log; -import com.oracle.tools.packager.RelativeFileSet; import com.oracle.tools.packager.UnsupportedPlatformException; import com.sun.javafx.tools.packager.JarSignature.InputStreamSource; import com.sun.javafx.tools.packager.bundlers.BundleParams; @@ -187,16 +186,16 @@ //NOTE: This should be a save-to-temp file, then rename operation File applicationJar = new File(createJarParams.outdir, - createJarParams.outfile.endsWith(".jar") - ? createJarParams.outfile - : createJarParams.outfile + ".jar"); + createJarParams.outfile.endsWith(".jar") + ? createJarParams.outfile + : createJarParams.outfile + ".jar"); if (jarToUpdate != null && applicationJar.getAbsoluteFile().equals(jarToUpdate.getAbsoluteFile())) { try { File newInputJar = File.createTempFile("tempcopy", ".jar"); Files.move(jarToUpdate.toPath(), newInputJar.toPath(), - StandardCopyOption.REPLACE_EXISTING); + StandardCopyOption.REPLACE_EXISTING); jarToUpdate = newInputJar; } catch (IOException ioe) { throw new PackagerException( @@ -315,8 +314,26 @@ try { BundleParams bp = deployParams.getBundleParams(); if (bp != null) { - generateNativeBundles(deployParams.outdir, bp.getBundleParamsAsMap(), "JNLP", "jnlp"); - generateNativeBundles(new File(deployParams.outdir, "bundles"), bp.getBundleParamsAsMap(), deployParams.getBundleType().toString(), deployParams.getTargetFormat()); + if (deployParams.getBundleType().equals(BundleType.ALL) && deployParams.getTargetFormat() == null) { + // generate everything. + + // generate JNLP in the main directory + generateNativeBundles(deployParams.outdir, bp.getBundleParamsAsMap(), BundleType.JNLP.toString(), "jnlp"); + // generate the rest in .../bundles + + // generate disk images + generateNativeBundles(new File(deployParams.outdir, "bundles"), bp.getBundleParamsAsMap(), BundleType.IMAGE.toString(), deployParams.getTargetFormat()); + + //TODO generate installers referencing disk image + // for now just generate all images + generateNativeBundles(new File(deployParams.outdir, "bundles"), bp.getBundleParamsAsMap(), BundleType.INSTALLER.toString(), deployParams.getTargetFormat()); + } else if (deployParams.getBundleType().equals(BundleType.NONE) && deployParams.getTargetFormat() == null) { + // old school default. Just generate JNLP. + generateNativeBundles(deployParams.outdir, bp.getBundleParamsAsMap(), BundleType.JNLP.toString(), "jnlp"); + } else { + // a specefic output format, just generate that. + generateNativeBundles(deployParams.outdir, bp.getBundleParamsAsMap(), deployParams.getBundleType().toString(), deployParams.getTargetFormat()); + } } } catch (PackagerException ex) { throw ex; @@ -327,19 +344,7 @@ } private void generateNativeBundles(File outdir, Map params, String bundleType, String bundleFormat) throws PackagerException { - if (params.containsKey(BundleParams.PARAM_RUNTIME)) { - RelativeFileSet runtime = BundleParams.getRuntime(params); - if (runtime == null) { - com.oracle.tools.packager.Log.info(bundle.getString("MSG_NoJREPackaged")); - } else { - com.oracle.tools.packager.Log.info(MessageFormat.format(bundle.getString("MSG_UserProvidedJRE"), runtime.getBaseDirectory().getAbsolutePath())); - if (com.oracle.tools.packager.Log.isDebug()) { - runtime.dump(); - } - } - } else { - com.oracle.tools.packager.Log.info(bundle.getString("MSG_UseSystemJRE")); - } + //FIXME //TODO check for system JRE for (com.oracle.tools.packager.Bundler bundler : Bundlers.createBundlersInstance().getBundlers(bundleType)) { @@ -432,7 +437,7 @@ private void signFile( PackagerResource pr, JarSignature signature, File outdir, boolean verbose) - throws NoSuchAlgorithmException, IOException, SignatureException { + throws NoSuchAlgorithmException, IOException, SignatureException { if (pr.getFile().isDirectory()) { File[] children = pr.getFile().listFiles(); if (children != null) { @@ -463,7 +468,7 @@ destJar.getParentFile().mkdirs(); signedJar.renameTo(destJar); if (verbose) { - System.out.println("Signed as " + destJar.getPath()); + System.out.println("Signed as " + destJar.getPath()); } } } @@ -523,7 +528,7 @@ try (FileWriter sources = new FileWriter(tmpFile)) { scanAndCopy(new PackagerResource(new File(srcDirName), "."), sources, compiledDir); } - String classpath = jfxHome + "/../rt/lib/ext/jfxrt.jar"; + String classpath = jfxHome + "/../lib/jfxrt.jar"; if (makeAllParams.classpath != null) { classpath += File.pathSeparator + makeAllParams.classpath; } @@ -635,8 +640,8 @@ } else { copyFileToOutDir(new FileInputStream(f), new File(outdir.getPath() + File.separator - + dir.getRelativePath() + File.separator - + f.getName())); + + dir.getRelativePath() + File.separator + + f.getName())); } } } catch (IOException ex) { @@ -669,7 +674,7 @@ private void jar( Manifest manifest, List files, File importJarFile, JarOutputStream jar, Filter filter) - throws IOException, PackagerException { + throws IOException, PackagerException { try { jar.putNextEntry(new ZipEntry("META-INF/")); jar.closeEntry(); @@ -755,7 +760,7 @@ || (filter == Filter.RESOURCES && isResource(f.getAbsolutePath()))) { final String absPath = f.getAbsolutePath(); if (absPath.endsWith("META-INF\\MANIFEST.MF") - || absPath.endsWith("META-INF/MANIFEST.MF")) { + || absPath.endsWith("META-INF/MANIFEST.MF")) { return; } createParentEntries(absPath.substring(cut).replace('\\', '/'), jar); @@ -763,8 +768,8 @@ // generate bss file into temporary directory int startOfExt = absPath.lastIndexOf(".") + 1; String bssFileName = absPath - .substring(cut, startOfExt) - .concat("bss"); + .substring(cut, startOfExt) + .concat("bss"); File bssFile = new File(bssTmpDir, bssFileName); bssFile.getParentFile().mkdirs(); @@ -809,8 +814,8 @@ } else if (f.getName().endsWith(".css")) { String cssFileName = f.getAbsolutePath(); String bssFileName = new File(outdir.getAbsolutePath(), - replaceExtensionByBSS(relPath)) - .getAbsolutePath(); + replaceExtensionByBSS(relPath)) + .getAbsolutePath(); createBinaryCss(cssFileName, bssFileName); } } @@ -831,7 +836,7 @@ int lastIndexOfSlash = Math.max(classUrl.lastIndexOf("/"), classUrl.lastIndexOf("\\")); return classUrl.substring(0, lastIndexOfSlash) - + "/../rt/lib/ext/jfxrt.jar!/"; + + "/../lib/jfxrt.jar!/"; } private Class loadClassFromRuntime(String className) throws PackagerException { @@ -845,8 +850,8 @@ private void createBinaryCss(String cssFile, String binCssFile) throws PackagerException { String ofname = (binCssFile != null) - ? binCssFile - : replaceExtensionByBSS(cssFile); + ? binCssFile + : replaceExtensionByBSS(cssFile); // create parent directories File of = new File(ofname); @@ -872,7 +877,7 @@ } catch (Exception ex) { Throwable causeEx = ex.getCause(); String cause = (causeEx != null) ? causeEx.getMessage() - : bundle.getString("ERR_UnknownReason"); + : bundle.getString("ERR_UnknownReason"); throw new PackagerException(ex, "ERR_BSSConversionFailed", cssFile, cause); } diff --git a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/BundleParams.java b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/BundleParams.java --- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/BundleParams.java +++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/BundleParams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,6 @@ package com.sun.javafx.tools.packager.bundlers; import com.oracle.tools.packager.*; -import com.oracle.tools.packager.linux.LinuxAppBundler; -import com.oracle.tools.packager.mac.MacAppBundler; -import com.oracle.tools.packager.windows.WindowsBundlerParam; import com.sun.javafx.tools.packager.bundlers.Bundler.BundleType; import java.io.File; @@ -39,6 +36,7 @@ import java.util.jar.Manifest; import static com.oracle.tools.packager.StandardBundlerParam.*; +import static com.oracle.tools.packager.JLinkBundlerHelper.*; public class BundleParams { @@ -170,6 +168,31 @@ putUnlessNullOrEmpty(ARGUMENTS.getID(), arguments); } + public void setAddModules(Set addModules) { + putUnlessNullOrEmpty(ADD_MODULES.getID(), addModules); + } + + public void setLimitModules(Set limitModules) { + putUnlessNullOrEmpty(LIMIT_MODULES.getID(), limitModules); + } + + public void setDetectModules(Boolean detectModules) { + putUnlessNull(DETECT_MODULES.getID(), detectModules); + } + + public void setStripExecutables(Boolean value) { + putUnlessNull(STRIP_NATIVE_COMMANDS.getID(), value); + } + + public void setAppModulePath(String appModulePath) { + putUnlessNull(JDK_MODULE_PATH.getID(), appModulePath); + } + + public void setLinkModulePath(String linkModulePath) { + putUnlessNull(MODULE_PATH.getID(), linkModulePath); + } + + public String getApplicationID() { return fetchParam(IDENTIFIER); } @@ -250,23 +273,6 @@ public void setSignBundle(Boolean b) { putUnlessNull(SIGN_BUNDLE.getID(), b); } - public com.oracle.tools.packager.RelativeFileSet getRuntime() { - return getRuntime(params); - } - - public static com.oracle.tools.packager.RelativeFileSet getRuntime(Map params) { - String os = System.getProperty("os.name").toLowerCase(); - if (os.startsWith("linux")) { - return LinuxAppBundler.LINUX_RUNTIME.fetchFrom(params); - } else if (os.contains("os x")) { - return MacAppBundler.MAC_RUNTIME.fetchFrom(params); - } else if (os.startsWith("win")) { - return WindowsBundlerParam.WIN_RUNTIME.fetchFrom(params); - } else { - return null; - } - } - public boolean isShortcutHint() { return fetchParam(SHORTCUT_HINT); } diff --git a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/Bundler.java b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/Bundler.java --- a/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/Bundler.java +++ b/modules/fxpackager/src/main/java/com/sun/javafx/tools/packager/bundlers/Bundler.java @@ -33,7 +33,7 @@ */ @Deprecated public enum BundleType { - NONE, IMAGE, INSTALLER, ALL + NONE, IMAGE, INSTALLER, ALL, JNLP } } diff --git a/modules/fxpackager/src/main/java/jdk/packager/builders/AbstractAppImageBuilder.java b/modules/fxpackager/src/main/java/jdk/packager/builders/AbstractAppImageBuilder.java new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/java/jdk/packager/builders/AbstractAppImageBuilder.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.packager.builders; + +import com.oracle.tools.packager.JLinkBundlerHelper; +import com.oracle.tools.packager.RelativeFileSet; +import jdk.tools.jlink.builder.*; +import jdk.tools.jlink.plugin.Pool; + +import com.oracle.tools.packager.Log; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Set; + +import static com.oracle.tools.packager.StandardBundlerParam.*; +import static com.oracle.tools.packager.StandardBundlerParam.ARGUMENTS; +import static com.oracle.tools.packager.StandardBundlerParam.USER_JVM_OPTIONS; + +/** + * + * Created by dferrin on 9/1/15. + */ +public abstract class AbstractAppImageBuilder extends DefaultImageBuilder { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle(AbstractAppImageBuilder.class.getName()); + + //do not use file separator - + // we use it for classpath lookup and there / are not platform specific + public final static String BUNDLER_PREFIX = "package/"; + + public AbstractAppImageBuilder(Map properties, Path root) throws IOException { + super(true, root); + } + + abstract protected void prepareApplicationFiles(Pool files, Set modules) throws IOException; + + abstract protected InputStream getResourceAsStream(String name); + + protected InputStream locateResource(String publicName, String category, + String defaultName, File customFile, + boolean verbose, File publicRoot) throws IOException { + InputStream is = null; + boolean customFromClasspath = false; + boolean customFromFile = false; + if (publicName != null) { + if (publicRoot != null) { + File publicResource = new File(publicRoot, publicName); + if (publicResource.exists() && publicResource.isFile()) { + is = new FileInputStream(publicResource); + } + } else { + is = getResourceAsStream(publicName); + } + customFromClasspath = (is != null); + } + if (is == null && customFile != null) { + is = new FileInputStream(customFile); + customFromFile = (is != null); + } + if (is == null && defaultName != null) { + is = getResourceAsStream(defaultName); + } + String msg = null; + if (customFromClasspath) { + msg = MessageFormat.format(I18N.getString("message.using-custom-resource-from-classpath"), category == null ? "" : "[" + category + "] ", publicName); + } else if (customFromFile) { + msg = MessageFormat.format(I18N.getString("message.using-custom-resource-from-file"), category == null ? "" : "[" + category + "] ", customFile.getAbsoluteFile()); + } else if (is != null) { + msg = MessageFormat.format(I18N.getString("message.using-default-resource-from-classpath"), category == null ? "" : "[" + category + "] ", publicName); + } else { + msg = MessageFormat.format(I18N.getString("message.using-default-resource"), category == null ? "" : "[" + category + "] ", publicName); + } + if (verbose) { + Log.info(msg); + } + return is; + } + + + protected String preprocessTextResource(String publicName, String category, + String defaultName, Map pairs, + boolean verbose, File publicRoot) throws IOException { + InputStream inp = locateResource(publicName, category, defaultName, null, verbose, publicRoot); + if (inp == null) { + throw new RuntimeException("Module corrupt? No "+defaultName+" resource!"); + } + + try (InputStream is = inp) { + //read fully into memory + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = is.read(buffer)) != -1) { + baos.write(buffer, 0, length); + } + + //substitute + String result = new String(baos.toByteArray()); + for (Map.Entry e : pairs.entrySet()) { + if (e.getValue() != null) { + result = result.replace(e.getKey(), e.getValue()); + } + } + return result; + } + } + + public void writeCfgFile(Map params, File cfgFileName, String runtimeLocation) throws IOException { + cfgFileName.delete(); + + boolean appCDEnabled = UNLOCK_COMMERCIAL_FEATURES.fetchFrom(params) && ENABLE_APP_CDS.fetchFrom(params); + String appCDSCacheMode = APP_CDS_CACHE_MODE.fetchFrom(params); + + PrintStream out = new PrintStream(cfgFileName); + + out.println("[Application]"); + out.println("app.name=" + APP_NAME.fetchFrom(params)); + out.println("app.mainjar=" + MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); + out.println("app.version=" + VERSION.fetchFrom(params)); + out.println("app.preferences.id=" + PREFERENCES_ID.fetchFrom(params)); + out.println("app.mainclass=" + + MAIN_CLASS.fetchFrom(params).replaceAll("\\.", "/")); + out.println("app.classpath=" + + String.join(File.pathSeparator, CLASSPATH.fetchFrom(params).split("[ :;]"))); + out.println("app.modulepath=" + + String.join(File.pathSeparator, JLinkBundlerHelper.JDK_MODULE_PATH.fetchFrom(params))); + out.println("app.runtime=" + runtimeLocation); + out.println("app.identifier=" + IDENTIFIER.fetchFrom(params)); + if (appCDEnabled) { + out.println("app.appcds.cache=" + appCDSCacheMode.split("\\+")[0]); + } + + + out.println(); + out.println("[JVMOptions]"); + List jvmargs = JVM_OPTIONS.fetchFrom(params); + for (String arg : jvmargs) { + out.println(arg); + } + Map jvmProps = JVM_PROPERTIES.fetchFrom(params); + for (Map.Entry property : jvmProps.entrySet()) { + out.println("-D" + property.getKey() + "=" + property.getValue()); + } + String preloader = PRELOADER_CLASS.fetchFrom(params); + if (preloader != null) { + out.println("-Djavafx.preloader="+preloader); + } + + + out.println(); + out.println("[JVMUserOptions]"); + Map overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); + for (Map.Entry arg: overridableJVMOptions.entrySet()) { + if (arg.getKey() == null || arg.getValue() == null) { + Log.info(I18N.getString("message.jvm-user-arg-is-null")); + } else { + out.println(arg.getKey().replaceAll("([\\=])", "\\\\$1") + "=" + arg.getValue()); + } + } + + if (appCDEnabled) { + prepareAppCDS(params, out); + } + + out.println(); + out.println("[ArgOptions]"); + List args = ARGUMENTS.fetchFrom(params); + for (String arg : args) { + if (arg.endsWith("=") && (arg.indexOf("=") == arg.lastIndexOf("="))) { + out.print(arg.substring(0, arg.length() - 1)); + out.println("\\="); + } else { + out.println(arg); + } + } + + + out.close(); + } + + protected abstract String getCacheLocation(Map params); + + void prepareAppCDS(Map params, PrintStream out) throws IOException { + //TODO check 8u40 or later + + File tempDir = Files.createTempDirectory("javapackager").toFile(); + tempDir.deleteOnExit(); + File classList = new File(tempDir, APP_FS_NAME.fetchFrom(params) + ".classlist"); + + try (FileOutputStream fos = new FileOutputStream(classList); + PrintStream ps = new PrintStream(fos)) { + for (String className : APP_CDS_CLASS_ROOTS.fetchFrom(params)) { + String slashyName = className.replace(".", "/"); + ps.println(slashyName); + } + } + APP_RESOURCES_LIST.fetchFrom(params).add(new RelativeFileSet(classList.getParentFile(), Arrays.asList(classList))); + + out.println(); + out.println("[AppCDSJVMOptions]"); + out.println("-XX:+UnlockCommercialFeatures"); + out.print("-XX:SharedArchiveFile="); + out.print(getCacheLocation(params)); + out.print(APP_FS_NAME.fetchFrom(params)); + out.println(".jpa"); + out.println("-Xshare:auto"); + out.println("-XX:+UseAppCDS"); + if (Log.isDebug()) { + out.println("-verbose:class"); + out.println("-XX:+TraceClassPaths"); + out.println("-XX:+UnlockDiagnosticVMOptions"); + } + out.println(""); + + out.println("[AppCDSGenerateCacheJVMOptions]"); + out.println("-XX:+UnlockCommercialFeatures"); + out.println("-Xshare:dump"); + out.println("-XX:+UseAppCDS"); + out.print("-XX:SharedArchiveFile="); + out.print(getCacheLocation(params)); + out.print(APP_FS_NAME.fetchFrom(params)); + out.println(".jpa"); + out.println("-XX:SharedClassListFile=$PACKAGEDIR/" + APP_FS_NAME.fetchFrom(params) + ".classlist"); + if (Log.isDebug()) { + out.println("-XX:+UnlockDiagnosticVMOptions"); + } + } + + +} diff --git a/modules/fxpackager/src/main/java/jdk/packager/builders/linux/LinuxAppImageBuilder.java b/modules/fxpackager/src/main/java/jdk/packager/builders/linux/LinuxAppImageBuilder.java new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/java/jdk/packager/builders/linux/LinuxAppImageBuilder.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.packager.builders.linux; + +import com.oracle.tools.packager.BundlerParamInfo; +import com.oracle.tools.packager.Log; +import com.oracle.tools.packager.RelativeFileSet; +import com.oracle.tools.packager.StandardBundlerParam; +import com.oracle.tools.packager.linux.LinuxResources; +import jdk.packager.builders.AbstractAppImageBuilder; +import jdk.tools.jlink.plugin.Pool; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.Set; + +import static com.oracle.tools.packager.StandardBundlerParam.*; + +/** + * + */ +public class LinuxAppImageBuilder extends AbstractAppImageBuilder { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle(LinuxAppImageBuilder.class.getName()); + + protected static final String LINUX_BUNDLER_PREFIX = + BUNDLER_PREFIX + "linux" + File.separator; + private static final String EXECUTABLE_NAME = "JavaAppLauncher"; + private static final String LIBRARY_NAME = "libpackager.so"; + + private final Path root; + private final Path appDir; + private final Path runtimeRoot; + private final Path mdir; + + private final Map params; + + public static final BundlerParamInfo ICON_PNG = new StandardBundlerParam<>( + I18N.getString("param.icon-png.name"), + I18N.getString("param.icon-png.description"), + "icon.png", + File.class, + params -> { + File f = ICON.fetchFrom(params); + if (f != null && !f.getName().toLowerCase().endsWith(".png")) { + Log.info(MessageFormat.format(I18N.getString("message.icon-not-png"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + +// public static final BundlerParamInfo RAW_EXECUTABLE_URL = new StandardBundlerParam<>( +// I18N.getString("param.raw-executable-url.name"), +// I18N.getString("param.raw-executable-url.description"), +// "linux.launcher.url", +// URL.class, +// params -> LinuxResources.class.getResource(EXECUTABLE_NAME), +// (s, p) -> { +// try { +// return new URL(s); +// } catch (MalformedURLException e) { +// Log.info(e.toString()); +// return null; +// } +// }); +// + + public LinuxAppImageBuilder(Map config, Path imageOutDir) throws IOException { + super(config, imageOutDir.resolve(APP_NAME.fetchFrom(config) + "/runtime")); + + Objects.requireNonNull(imageOutDir); + + //@SuppressWarnings("unchecked") + //String img = (String) config.get("jimage.name"); // FIXME constant + + this.root = imageOutDir.resolve(APP_NAME.fetchFrom(config)); + this.appDir = root.resolve("app"); + this.runtimeRoot = root.resolve("runtime"); + this.mdir = runtimeRoot.resolve("lib"); + this.params = new HashMap(); + config.entrySet().stream().forEach(e -> params.put(e.getKey().toString(), e.getValue())); + Files.createDirectories(appDir); + } + + private Path destFile(String dir, String filename) { + return runtimeRoot.resolve(dir).resolve(filename); + } + + private void writeEntry(InputStream in, Path dstFile) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.copy(in, dstFile); + } + + private void writeSymEntry(Path dstFile, Path target) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.createLink(dstFile, target); + } + + /** + * chmod ugo+x file + */ + private void setExecutable(Path file) { + try { + Set perms = Files.getPosixFilePermissions(file); + perms.add(PosixFilePermission.OWNER_EXECUTE); + perms.add(PosixFilePermission.GROUP_EXECUTE); + perms.add(PosixFilePermission.OTHERS_EXECUTE); + Files.setPosixFilePermissions(file, perms); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + private static void createUtf8File(File file, String content) throws IOException { + try (OutputStream fout = new FileOutputStream(file); + Writer output = new OutputStreamWriter(fout, "UTF-8")) { + output.write(content); + } + } + + + //it is static for the sake of sharing with "installer" bundlers + // that may skip calls to validate/bundle in this class! + public static File getRootDir(File outDir, Map p) { + return new File(outDir, APP_FS_NAME.fetchFrom(p)); + } + + public static String getLauncherName(Map p) { + return APP_FS_NAME.fetchFrom(p); + } + + public static String getLauncherCfgName(Map p) { + return "app/" + APP_FS_NAME.fetchFrom(p) + ".cfg"; + } + + @Override + protected InputStream getResourceAsStream(String name) { + return LinuxResources.class.getResourceAsStream(name); + } + + @Override + protected void prepareApplicationFiles(Pool files, Set modules) throws IOException { + Map originalParams = new HashMap<>(params); + try { + + // create the primary launcher + createLauncherForEntryPoint(params, root); + + // Copy library to the launcher folder + writeEntry(LinuxResources.class.getResourceAsStream(LIBRARY_NAME), root.resolve(LIBRARY_NAME)); + + // create the secondary launchers, if any + List> entryPoints = StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(params); + for (Map entryPoint : entryPoints) { + Map tmp = new HashMap<>(originalParams); + tmp.putAll(entryPoint); + createLauncherForEntryPoint(tmp, root); + } + + // Copy class path entries to Java folder + copyApplication(); + + // Copy icon to Resources folder +//FIXME copyIcon(resourcesDirectory); + + } catch (IOException ex) { + Log.info("Exception: " + ex); + Log.debug(ex); + } + } + + private void createLauncherForEntryPoint(Map p, Path rootDir) throws IOException { + // Copy executable to Linux folder + Path executableFile = root.resolve(getLauncherName(p)); + + writeEntry(LinuxResources.class.getResourceAsStream(EXECUTABLE_NAME), executableFile); + + executableFile.toFile().setExecutable(true, false); + executableFile.toFile().setWritable(true, true); + + writeCfgFile(p, root.resolve(getLauncherCfgName(p)).toFile(), "$APPDIR/runtime"); + } + + private void copyApplication() throws IOException { + List appResourcesList = APP_RESOURCES_LIST.fetchFrom(params); + if (appResourcesList == null) { + throw new RuntimeException("Null app resources?"); + } + for (RelativeFileSet appResources : appResourcesList) { + if (appResources == null) { + throw new RuntimeException("Null app resources?"); + } + File srcdir = appResources.getBaseDirectory(); + for (String fname : appResources.getIncludedFiles()) { + writeEntry( + new FileInputStream(new File(srcdir, fname)), + new File(appDir.toFile(), fname).toPath() + ); + } + } + } + + @Override + protected String getCacheLocation(Map params) { + return "$CACHEDIR/"; + } + +} diff --git a/modules/fxpackager/src/main/java/jdk/packager/builders/mac/MacAppImageBuilder.java b/modules/fxpackager/src/main/java/jdk/packager/builders/mac/MacAppImageBuilder.java new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/java/jdk/packager/builders/mac/MacAppImageBuilder.java @@ -0,0 +1,873 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.packager.builders.mac; + +import com.oracle.tools.packager.BundlerParamInfo; +import com.oracle.tools.packager.IOUtils; +import com.oracle.tools.packager.Log; +import com.oracle.tools.packager.RelativeFileSet; +import com.oracle.tools.packager.StandardBundlerParam; +import com.oracle.tools.packager.mac.MacResources; +import jdk.packager.builders.AbstractAppImageBuilder; +import jdk.tools.jlink.plugin.Pool; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import static com.oracle.tools.packager.StandardBundlerParam.*; +import static com.oracle.tools.packager.mac.MacBaseInstallerBundler.*; +import static com.oracle.tools.packager.mac.MacAppBundler.*; +/** + * + */ +public class MacAppImageBuilder extends AbstractAppImageBuilder { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle(MacAppImageBuilder.class.getName()); + + private static final String EXECUTABLE_NAME = "JavaAppLauncher"; + private static final String LIBRARY_NAME = "libpackager.dylib"; + private static final String TEMPLATE_BUNDLE_ICON = "GenericApp.icns"; + private static final String OS_TYPE_CODE = "APPL"; + private static final String TEMPLATE_INFO_PLIST_LITE = "Info-lite.plist.template"; + private static final String TEMPLATE_RUNTIME_INFO_PLIST = "Runtime-Info.plist.template"; + + private final Path root; + private final Path contentsDir; + private final Path javaDir; + private final Path resourcesDir; + private final Path macOSDir; + private final Path runtimeDir; + private final Path runtimeRoot; + private final Path mdir; + + private final Map params; + + private static Map getMacCategories() { + Map map = new HashMap<>(); + map.put("Business", "public.app-category.business"); + map.put("Developer Tools", "public.app-category.developer-tools"); + map.put("Education", "public.app-category.education"); + map.put("Entertainment", "public.app-category.entertainment"); + map.put("Finance", "public.app-category.finance"); + map.put("Games", "public.app-category.games"); + map.put("Graphics & Design", "public.app-category.graphics-design"); + map.put("Healthcare & Fitness", "public.app-category.healthcare-fitness"); + map.put("Lifestyle", "public.app-category.lifestyle"); + map.put("Medical", "public.app-category.medical"); + map.put("Music", "public.app-category.music"); + map.put("News", "public.app-category.news"); + map.put("Photography", "public.app-category.photography"); + map.put("Productivity", "public.app-category.productivity"); + map.put("Reference", "public.app-category.reference"); + map.put("Social Networking", "public.app-category.social-networking"); + map.put("Sports", "public.app-category.sports"); + map.put("Travel", "public.app-category.travel"); + map.put("Utilities", "public.app-category.utilities"); + map.put("Video", "public.app-category.video"); + map.put("Weather", "public.app-category.weather"); + + map.put("Action Games", "public.app-category.action-games"); + map.put("Adventure Games", "public.app-category.adventure-games"); + map.put("Arcade Games", "public.app-category.arcade-games"); + map.put("Board Games", "public.app-category.board-games"); + map.put("Card Games", "public.app-category.card-games"); + map.put("Casino Games", "public.app-category.casino-games"); + map.put("Dice Games", "public.app-category.dice-games"); + map.put("Educational Games", "public.app-category.educational-games"); + map.put("Family Games", "public.app-category.family-games"); + map.put("Kids Games", "public.app-category.kids-games"); + map.put("Music Games", "public.app-category.music-games"); + map.put("Puzzle Games", "public.app-category.puzzle-games"); + map.put("Racing Games", "public.app-category.racing-games"); + map.put("Role Playing Games", "public.app-category.role-playing-games"); + map.put("Simulation Games", "public.app-category.simulation-games"); + map.put("Sports Games", "public.app-category.sports-games"); + map.put("Strategy Games", "public.app-category.strategy-games"); + map.put("Trivia Games", "public.app-category.trivia-games"); + map.put("Word Games", "public.app-category.word-games"); + + return map; + } + + public static final BundlerParamInfo MAC_CONFIGURE_LAUNCHER_IN_PLIST = + new StandardBundlerParam<>( + I18N.getString("param.configure-launcher-in-plist"), + I18N.getString("param.configure-launcher-in-plist.description"), + "mac.configure-launcher-in-plist", + Boolean.class, + params -> Boolean.FALSE, + (s, p) -> Boolean.valueOf(s)); + + public static final BundlerParamInfo MAC_CATEGORY = + new StandardBundlerParam<>( + I18N.getString("param.category-name"), + I18N.getString("param.category-name.description"), + "mac.category", + String.class, + params -> "public.app-category.developer-tools", // this category is almost certianly wrong, encouraging the user to set a value + (s, p) -> s + ); + + public static final BundlerParamInfo MAC_CF_BUNDLE_NAME = + new StandardBundlerParam<>( + I18N.getString("param.cfbundle-name.name"), + I18N.getString("param.cfbundle-name.description"), + "mac.CFBundleName", + String.class, + params -> null, + (s, p) -> s); + + public static final BundlerParamInfo MAC_CF_BUNDLE_IDENTIFIER = + new StandardBundlerParam<>( + I18N.getString("param.cfbundle-identifier.name"), + I18N.getString("param.cfbundle-identifier.description"), + "mac.CFBundleIdentifier", + String.class, + params -> "com.example.bad", //FIXME + (s, p) -> s); + + public static final BundlerParamInfo MAC_CF_BUNDLE_VERSION = + new StandardBundlerParam<>( + I18N.getString("param.cfbundle-version.name"), + I18N.getString("param.cfbundle-version.description"), + "mac.CFBundleVersion", + String.class, + p -> { + String s = VERSION.fetchFrom(p); + if (validCFBundleVersion(s)) { + return s; + } else { + return "100"; + } + }, + (s, p) -> s); + + public static final BundlerParamInfo CONFIG_ROOT = new StandardBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> { + File configRoot = new File(BUILD_ROOT.fetchFrom(params), "macosx"); + configRoot.mkdirs(); + return configRoot; + }, + (s, p) -> new File(s)); + + public static final BundlerParamInfo DEFAULT_ICNS_ICON = new StandardBundlerParam<>( + I18N.getString("param.default-icon-icns"), + I18N.getString("param.default-icon-icns.description"), + ".mac.default.icns", + String.class, + params -> TEMPLATE_BUNDLE_ICON, + (s, p) -> s); + +// public static final BundlerParamInfo DEVELOPER_ID_APP_SIGNING_KEY = new StandardBundlerParam<>( +// I18N.getString("param.signing-key-developer-id-app.name"), +// I18N.getString("param.signing-key-developer-id-app.description"), +// "mac.signing-key-developer-id-app", +// String.class, +// params -> MacBaseInstallerBundler.findKey("Developer ID Application: " + SIGNING_KEY_USER.fetchFrom(params), SIGNING_KEYCHAIN.fetchFrom(params), VERBOSE.fetchFrom(params)), +// (s, p) -> s); + + // public static final BundlerParamInfo BUNDLE_ID_SIGNING_PREFIX = new StandardBundlerParam<>( +// I18N.getString("param.bundle-id-signing-prefix.name"), +// I18N.getString("param.bundle-id-signing-prefix.description"), +// "mac.bundle-id-signing-prefix", +// String.class, +// params -> IDENTIFIER.fetchFrom(params) + ".", +// (s, p) -> s); +// + public static final BundlerParamInfo ICON_ICNS = new StandardBundlerParam<>( + I18N.getString("param.icon-icns.name"), + I18N.getString("param.icon-icns.description"), + "icon.icns", + File.class, + params -> { + File f = ICON.fetchFrom(params); + if (f != null && !f.getName().toLowerCase().endsWith(".icns")) { + Log.info(MessageFormat.format(I18N.getString("message.icon-not-icns"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + private static String extractAppName() { + return ""; + } + + public MacAppImageBuilder(Map config, Path imageOutDir) throws IOException { + super(config, imageOutDir.resolve(APP_NAME.fetchFrom(config) + ".app/Contents/PlugIns/Java.runtime/Contents/Home")); + + Objects.requireNonNull(imageOutDir); + + //@SuppressWarnings("unchecked") + //String img = (String) config.get("jimage.name"); // FIXME constant + + this.params = config; + + this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params) + ".app"); + + this.contentsDir = root.resolve("Contents"); + this.javaDir = contentsDir.resolve("Java"); + this.resourcesDir = contentsDir.resolve("Resources"); + this.macOSDir = contentsDir.resolve("MacOS"); + this.runtimeDir = contentsDir.resolve("PlugIns/Java.runtime"); + this.runtimeRoot = runtimeDir.resolve("Contents/Home"); + this.mdir = runtimeRoot.resolve("lib"); + Files.createDirectories(javaDir); + Files.createDirectories(resourcesDir); + Files.createDirectories(macOSDir); + } + + private void writeEntry(InputStream in, Path dstFile) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.copy(in, dstFile); + } + + /** + * chmod ugo+x file + */ + private void setExecutable(Path file) { + try { + Set perms = Files.getPosixFilePermissions(file); + perms.add(PosixFilePermission.OWNER_EXECUTE); + perms.add(PosixFilePermission.GROUP_EXECUTE); + perms.add(PosixFilePermission.OTHERS_EXECUTE); + Files.setPosixFilePermissions(file, perms); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + private static void createUtf8File(File file, String content) throws IOException { + try (OutputStream fout = new FileOutputStream(file); + Writer output = new OutputStreamWriter(fout, "UTF-8")) { + output.write(content); + } + } + + @Override + protected String getCacheLocation(Map params) { + return "$CACHEDIR/"; + } + + public static boolean validCFBundleVersion(String v) { + // CFBundleVersion (String - iOS, OS X) specifies the build version + // number of the bundle, which identifies an iteration (released or + // unreleased) of the bundle. The build version number should be a + // string comprised of three non-negative, period-separated integers + // with the first integer being greater than zero. The string should + // only contain numeric (0-9) and period (.) characters. Leading zeros + // are truncated from each integer and will be ignored (that is, + // 1.02.3 is equivalent to 1.2.3). This key is not localizable. + + if (v == null) { + return false; + } + + String p[] = v.split("\\."); + if (p.length > 3 || p.length < 1) { + Log.verbose(I18N.getString("message.version-string-too-many-components")); + return false; + } + + try { + BigInteger n = new BigInteger(p[0]); + if (BigInteger.ONE.compareTo(n) > 0) { + Log.verbose(I18N.getString("message.version-string-first-number-not-zero")); + return false; + } + if (p.length > 1) { + n = new BigInteger(p[1]); + if (BigInteger.ZERO.compareTo(n) > 0) { + Log.verbose(I18N.getString("message.version-string-no-negative-numbers")); + return false; + } + } + if (p.length > 2) { + n = new BigInteger(p[2]); + if (BigInteger.ZERO.compareTo(n) > 0) { + Log.verbose(I18N.getString("message.version-string-no-negative-numbers")); + return false; + } + } + } catch (NumberFormatException ne) { + Log.verbose(I18N.getString("message.version-string-numbers-only")); + Log.verbose(ne); + return false; + } + + return true; + } + + @Override + protected InputStream getResourceAsStream(String name) { + return MacResources.class.getResourceAsStream(name); + } + + + @Override + protected void prepareApplicationFiles(Pool files, Set modules) throws IOException { + File f; + + // Generate PkgInfo + File pkgInfoFile = new File(contentsDir.toFile(), "PkgInfo"); + pkgInfoFile.createNewFile(); + writePkgInfo(pkgInfoFile); + + + // Copy executable to MacOS folder + Path executable = macOSDir.resolve(getLauncherName(params)); + writeEntry(MacResources.class.getResourceAsStream(EXECUTABLE_NAME), executable); + executable.toFile().setExecutable(true, false); + + // Copy library to the MacOS folder + writeEntry( + MacResources.class.getResourceAsStream(LIBRARY_NAME), + macOSDir.resolve(LIBRARY_NAME) + ); + + // generate launcher config + + writeCfgFile(params, new File(root.toFile(), getLauncherCfgName(params)), "$APPDIR/PlugIns/Java.runtime"); + + // Copy class path entries to Java folder + copyClassPathEntries(javaDir); + + //TODO: Need to support adding native libraries. + // Copy library path entries to MacOS folder + //copyLibraryPathEntries(macOSDirectory); + + /*********** Take care of "config" files *******/ + // Copy icon to Resources folder + File icon = ICON_ICNS.fetchFrom(params); + InputStream in = locateResource("package/macosx/" + APP_NAME.fetchFrom(params) + ".icns", + "icon", + DEFAULT_ICNS_ICON.fetchFrom(params), + icon, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + Files.copy(in, resourcesDir.resolve(APP_NAME.fetchFrom(params) + ".icns")); + + // copy file association icons + for (Map fa : FILE_ASSOCIATIONS.fetchFrom(params)) { + f = FA_ICON.fetchFrom(fa); + if (f != null && f.exists()) { + try (InputStream in2 = new FileInputStream(f)) { + Files.copy(in2, resourcesDir.resolve(f.getName())); + } + + } + } + + // Generate Info.plist + writeInfoPlist(contentsDir.resolve("Info.plist").toFile()); + + // generate java runtime info.plist + writeRuntimeInfoPlist(runtimeDir.resolve("Contents/Info.plist").toFile()); + + // copy library + Path runtimeMacOSDir = Files.createDirectories(runtimeDir.resolve("Contents/MacOS")); + Files.copy(runtimeRoot.resolve("lib/jli/libjli.dylib"), runtimeMacOSDir.resolve("libjli.dylib")); + + // maybe sign + if (Optional.ofNullable(SIGN_BUNDLE.fetchFrom(params)).orElse(Boolean.TRUE)) { + String signingIdentity = DEVELOPER_ID_APP_SIGNING_KEY.fetchFrom(params); + if (signingIdentity != null) { + signAppBundle(params, root, signingIdentity, BUNDLE_ID_SIGNING_PREFIX.fetchFrom(params), null, null); + } + } + } + + + private String getLauncherName(Map params) { + if (APP_NAME.fetchFrom(params) != null) { + return APP_NAME.fetchFrom(params); + } else { + return MAIN_CLASS.fetchFrom(params); + } + } + + public static String getLauncherCfgName(Map p) { + return "Contents/Java/" + APP_NAME.fetchFrom(p) + ".cfg"; + } + + private void copyClassPathEntries(Path javaDirectory) throws IOException { + List resourcesList = APP_RESOURCES_LIST.fetchFrom(params); + if (resourcesList == null) { + throw new RuntimeException(I18N.getString("message.null-classpath")); + } + + for (RelativeFileSet classPath : resourcesList) { + File srcdir = classPath.getBaseDirectory(); + for (String fname : classPath.getIncludedFiles()) { + // use new File since fname can have file separators + Files.copy(new File(srcdir, fname).toPath(), new File(javaDirectory.toFile(), fname).toPath()); + } + } + } + + private String getBundleName(Map params) { + //TODO: Check to see what rules/limits are in place for CFBundleName + if (MAC_CF_BUNDLE_NAME.fetchFrom(params) != null) { + String bn = MAC_CF_BUNDLE_NAME.fetchFrom(params); + if (bn.length() > 16) { + Log.info(MessageFormat.format(I18N.getString("message.bundle-name-too-long-warning"), MAC_CF_BUNDLE_NAME.getID(), bn)); + } + return MAC_CF_BUNDLE_NAME.fetchFrom(params); + } else if (APP_NAME.fetchFrom(params) != null) { + return APP_NAME.fetchFrom(params); + } else { + String nm = MAIN_CLASS.fetchFrom(params); + if (nm.length() > 16) { + nm = nm.substring(0, 16); + } + return nm; + } + } + + private void writeRuntimeInfoPlist(File file) throws IOException { + //FIXME //TODO these values are bogus. + Map data = new HashMap<>(); + data.put("CF_BUNDLE_INFO", "bundle info"); + data.put("CF_BUNDLE_IDENTIFIER", "com.oracle.java.8u60.jdk"); + data.put("CF_BUNDLE_NAME", "Java SE 9"); + data.put("CF_BUNDLE_VERSION", "1.8.0_60"); + + Writer w = new BufferedWriter(new FileWriter(file)); + w.write(preprocessTextResource( + "package/macosx/Runtime-Info.plist", + I18N.getString("resource.runtime-info-plist"), + TEMPLATE_RUNTIME_INFO_PLIST, + data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params))); + w.close(); + } + + private void writeInfoPlist(File file) throws IOException { + Log.verbose(MessageFormat.format(I18N.getString("message.preparing-info-plist"), file.getAbsolutePath())); + + //prepare config for exe + //Note: do not need CFBundleDisplayName if we do not support localization + Map data = new HashMap<>(); + data.put("DEPLOY_ICON_FILE", APP_NAME.fetchFrom(params) + ".icns"); + data.put("DEPLOY_BUNDLE_IDENTIFIER", + MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params)); + data.put("DEPLOY_BUNDLE_NAME", + getBundleName(params)); + data.put("DEPLOY_BUNDLE_COPYRIGHT", + COPYRIGHT.fetchFrom(params) != null ? COPYRIGHT.fetchFrom(params) : "Unknown"); + data.put("DEPLOY_LAUNCHER_NAME", getLauncherName(params)); + data.put("DEPLOY_JAVA_RUNTIME_NAME", "$APPDIR/PlugIns/Java.runtime"); + data.put("DEPLOY_BUNDLE_SHORT_VERSION", + VERSION.fetchFrom(params) != null ? VERSION.fetchFrom(params) : "1.0.0"); + data.put("DEPLOY_BUNDLE_CFBUNDLE_VERSION", + MAC_CF_BUNDLE_VERSION.fetchFrom(params) != null ? MAC_CF_BUNDLE_VERSION.fetchFrom(params) : "100"); + data.put("DEPLOY_BUNDLE_CATEGORY", + //TODO parameters should provide set of values for IDEs + MAC_CATEGORY.fetchFrom(params)); + + data.put("DEPLOY_MAIN_JAR_NAME", MAIN_JAR.fetchFrom(params).getIncludedFiles().iterator().next()); + + data.put("DEPLOY_PREFERENCES_ID", PREFERENCES_ID.fetchFrom(params).toLowerCase()); + + StringBuilder sb = new StringBuilder(); + List jvmOptions = JVM_OPTIONS.fetchFrom(params); + + String newline = ""; //So we don't add unneccessary extra line after last append + for (String o : jvmOptions) { + sb.append(newline).append(" ").append(o).append(""); + newline = "\n"; + } + + Map jvmProps = JVM_PROPERTIES.fetchFrom(params); + for (Map.Entry entry : jvmProps.entrySet()) { + sb.append(newline) + .append(" -D") + .append(entry.getKey()) + .append("=") + .append(entry.getValue()) + .append(""); + newline = "\n"; + } + + String preloader = PRELOADER_CLASS.fetchFrom(params); + if (preloader != null) { + sb.append(newline) + .append(" -Djavafx.preloader=") + .append(preloader) + .append(""); + } + + data.put("DEPLOY_JVM_OPTIONS", sb.toString()); + + sb = new StringBuilder(); + List args = ARGUMENTS.fetchFrom(params); + newline = ""; //So we don't add unneccessary extra line after last append + for (String o : args) { + sb.append(newline).append(" ").append(o).append(""); + newline = "\n"; + } + data.put("DEPLOY_ARGUMENTS", sb.toString()); + + newline = ""; + sb = new StringBuilder(); + Map overridableJVMOptions = USER_JVM_OPTIONS.fetchFrom(params); + for (Map.Entry arg : overridableJVMOptions.entrySet()) { + sb.append(newline) + .append(" ").append(arg.getKey()).append("\n") + .append(" ").append(arg.getValue()).append(""); + newline = "\n"; + } + data.put("DEPLOY_JVM_USER_OPTIONS", sb.toString()); + + + data.put("DEPLOY_LAUNCHER_CLASS", MAIN_CLASS.fetchFrom(params)); + + StringBuilder macroedPath = new StringBuilder(); + for (String s : CLASSPATH.fetchFrom(params).split("[ ;:]+")) { + macroedPath.append(s); + macroedPath.append(":"); + } + macroedPath.deleteCharAt(macroedPath.length() - 1); + + data.put("DEPLOY_APP_CLASSPATH", macroedPath.toString()); + + //TODO: Add remainder of the classpath + + StringBuilder bundleDocumentTypes = new StringBuilder(); + StringBuilder exportedTypes = new StringBuilder(); + for (Map fileAssociation : FILE_ASSOCIATIONS.fetchFrom(params)) { + + List extensions = FA_EXTENSIONS.fetchFrom(fileAssociation); + + if (extensions == null) { + Log.info(I18N.getString("message.creating-association-with-null-extension")); + } + + List mimeTypes = FA_CONTENT_TYPE.fetchFrom(fileAssociation); + String itemContentType = MAC_CF_BUNDLE_IDENTIFIER.fetchFrom(params) + "." + ((extensions == null || extensions.isEmpty()) + ? "mime" + : extensions.get(0)); + String description = FA_DESCRIPTION.fetchFrom(fileAssociation); + File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICNS + + bundleDocumentTypes.append(" \n") + .append(" LSItemContentTypes\n") + .append(" \n") + .append(" ") + .append(itemContentType) + .append("\n") + .append(" \n") + .append("\n") + .append(" CFBundleTypeName\n") + .append(" ") + .append(description) + .append("\n") + .append("\n") + .append(" LSHandlerRank\n") + .append(" Owner\n") //TODO make a bundler arg + .append("\n") + .append(" CFBundleTypeRole\n") + .append(" Editor\n") // TODO make a bundler arg + .append("\n") + .append(" LSIsAppleDefaultForType\n") + .append(" \n") // TODO make a bundler arg + .append("\n"); + + if (icon != null && icon.exists()) { + //? + bundleDocumentTypes.append(" CFBundleTypeIconFile\n") + .append(" ") + .append(icon.getName()) + .append("\n"); + } + bundleDocumentTypes.append(" \n"); + + exportedTypes.append(" \n") + .append(" UTTypeIdentifier\n") + .append(" ") + .append(itemContentType) + .append("\n") + .append("\n") + .append(" UTTypeDescription\n") + .append(" ") + .append(description) + .append("\n") + .append(" UTTypeConformsTo\n") + .append(" \n") + .append(" public.data\n") //TODO expose this? + .append(" \n") + .append("\n"); + + if (icon != null && icon.exists()) { + exportedTypes.append(" UTTypeIconFile\n") + .append(" ") + .append(icon.getName()) + .append("\n") + .append("\n"); + } + + exportedTypes.append("\n") + .append(" UTTypeTagSpecification\n") + .append(" \n") + //TODO expose via param? .append(" com.apple.ostype\n"); + //TODO expose via param? .append(" ABCD\n") + .append("\n"); + + if (extensions != null && !extensions.isEmpty()) { + exportedTypes.append(" public.filename-extension\n") + .append(" \n"); + + for (String ext : extensions) { + exportedTypes.append(" ") + .append(ext) + .append("\n"); + } + exportedTypes.append(" \n"); + } + if (mimeTypes != null && !mimeTypes.isEmpty()) { + exportedTypes.append(" public.mime-type\n") + .append(" \n"); + + for (String mime : mimeTypes) { + exportedTypes.append(" ") + .append(mime) + .append("\n"); + } + exportedTypes.append(" \n"); + } + exportedTypes.append(" \n") + .append(" \n"); + } + String associationData; + if (bundleDocumentTypes.length() > 0) { + associationData = "\n CFBundleDocumentTypes\n \n" + + bundleDocumentTypes.toString() + + " \n\n UTExportedTypeDeclarations\n \n" + + exportedTypes.toString() + + " \n"; + } else { + associationData = ""; + } + data.put("DEPLOY_FILE_ASSOCIATIONS", associationData); + + + Writer w = new BufferedWriter(new FileWriter(file)); + w.write(preprocessTextResource( + //MAC_BUNDLER_PREFIX + getConfig_InfoPlist(params).getName(), + "package/macosx/Info.plist", + I18N.getString("resource.app-info-plist"), + TEMPLATE_INFO_PLIST_LITE, + data, VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params))); + w.close(); + + } + + private void writePkgInfo(File file) throws IOException { + //hardcoded as it does not seem we need to change it ever + String signature = "????"; + + try (Writer out = new BufferedWriter(new FileWriter(file))) { + out.write(OS_TYPE_CODE + signature); + out.flush(); + } + } + + public static void signAppBundle(Map params, Path appLocation, String signingIdentity, String identifierPrefix, String entitlementsFile, String inheritedEntitlements) throws IOException { + AtomicReference toThrow = new AtomicReference<>(); + String appExecutable = "/Contents/MacOS/" + APP_NAME.fetchFrom(params); + String keyChain = SIGNING_KEYCHAIN.fetchFrom(params); + + // sign all dylibs and jars + Files.walk(appLocation) + // while we are searching let's fix permissions + .peek(path -> { + try { + Set pfp = Files.getPosixFilePermissions(path); + if (!pfp.contains(PosixFilePermission.OWNER_WRITE)) { + pfp = EnumSet.copyOf(pfp); + pfp.add(PosixFilePermission.OWNER_WRITE); + Files.setPosixFilePermissions(path, pfp); + } + } catch (IOException e) { + Log.debug(e); + } + }) + .filter(p -> Files.isRegularFile(p) && + !(p.toString().contains("/Contents/MacOS/libjli.dylib") + || p.toString().contains("/Contents/MacOS/JavaAppletPlugin") + || p.toString().endsWith(appExecutable)) + ).forEach(p -> { + //noinspection ThrowableResultOfMethodCallIgnored + if (toThrow.get() != null) return; + + List args = new ArrayList<>(); + args.addAll(Arrays.asList("codesign", + "-s", signingIdentity, // sign with this key + "--prefix", identifierPrefix, // use the identifier as a prefix + "-vvvv")); + if (entitlementsFile != null && + (p.toString().endsWith(".jar") + || p.toString().endsWith(".dylib"))) { + args.add("--entitlements"); + args.add(entitlementsFile); // entitlements + } else if (inheritedEntitlements != null && Files.isExecutable(p)) { + args.add("--entitlements"); + args.add(inheritedEntitlements); // inherited entitlements for executable processes + } + if (keyChain != null && !keyChain.isEmpty()) { + args.add("--keychain"); + args.add(keyChain); + } + args.add(p.toString()); + + try { + Set oldPermissions = Files.getPosixFilePermissions(p); + File f = p.toFile(); + f.setWritable(true, true); + + ProcessBuilder pb = new ProcessBuilder(args); + IOUtils.exec(pb, VERBOSE.fetchFrom(params)); + + Files.setPosixFilePermissions(p, oldPermissions); + } catch (IOException ioe) { + toThrow.set(ioe); + } + }); + + IOException ioe = toThrow.get(); + if (ioe != null) { + throw ioe; + } + + // sign all plugins and frameworks + Consumer signIdentifiedByPList = path -> { + //noinspection ThrowableResultOfMethodCallIgnored + if (toThrow.get() != null) return; + + try { + List args = new ArrayList<>(); + args.addAll(Arrays.asList("codesign", + "-s", signingIdentity, // sign with this key + "--prefix", identifierPrefix, // use the identifier as a prefix + "-vvvv")); + if (keyChain != null && !keyChain.isEmpty()) { + args.add("--keychain"); + args.add(keyChain); + } + args.add(path.toString()); + ProcessBuilder pb = new ProcessBuilder(args); + IOUtils.exec(pb, VERBOSE.fetchFrom(params)); + + args = new ArrayList<>(); + args.addAll(Arrays.asList("codesign", + "-s", signingIdentity, // sign with this key + "--prefix", identifierPrefix, // use the identifier as a prefix + "-vvvv")); + if (keyChain != null && !keyChain.isEmpty()) { + args.add("--keychain"); + args.add(keyChain); + } + args.add(path.toString() + "/Contents/_CodeSignature/CodeResources"); + pb = new ProcessBuilder(args); + IOUtils.exec(pb, VERBOSE.fetchFrom(params)); + } catch (IOException e) { + toThrow.set(e); + } + }; + + Path pluginsPath = appLocation.resolve("Contents/PlugIns"); + if (Files.isDirectory(pluginsPath)) { + Files.list(pluginsPath) + .forEach(signIdentifiedByPList); + + ioe = toThrow.get(); + if (ioe != null) { + throw ioe; + } + } + Path frameworkPath = appLocation.resolve("Contents/Frameworks"); + if (Files.isDirectory(frameworkPath)) { + Files.list(frameworkPath) + .forEach(signIdentifiedByPList); + + ioe = toThrow.get(); + if (ioe != null) { + throw ioe; + } + } + + // sign the app itself + List args = new ArrayList<>(); + args.addAll(Arrays.asList("codesign", + "-s", signingIdentity, // sign with this key + "-vvvv")); // super verbose output + if (entitlementsFile != null) { + args.add("--entitlements"); + args.add(entitlementsFile); // entitlements + } + if (keyChain != null && !keyChain.isEmpty()) { + args.add("--keychain"); + args.add(keyChain); + } + args.add(appLocation.toString()); + + ProcessBuilder pb = new ProcessBuilder(args.toArray(new String[args.size()])); + IOUtils.exec(pb, VERBOSE.fetchFrom(params)); + } + +} diff --git a/modules/fxpackager/src/main/java/jdk/packager/builders/windows/WindowsAppImageBuilder.java b/modules/fxpackager/src/main/java/jdk/packager/builders/windows/WindowsAppImageBuilder.java new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/java/jdk/packager/builders/windows/WindowsAppImageBuilder.java @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.packager.builders.windows; + +import com.oracle.tools.packager.BundlerParamInfo; +import com.oracle.tools.packager.Log; +import com.oracle.tools.packager.RelativeFileSet; +import com.oracle.tools.packager.IOUtils; +import com.oracle.tools.packager.StandardBundlerParam; +import com.oracle.tools.packager.windows.WinResources; +import com.oracle.tools.packager.windows.WindowsBundlerParam; +import jdk.packager.builders.AbstractAppImageBuilder; + +import jdk.tools.jlink.plugin.Pool; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; + +import static com.oracle.tools.packager.StandardBundlerParam.*; + +/** + * + */ +public class WindowsAppImageBuilder extends AbstractAppImageBuilder { + + private static final ResourceBundle I18N = + ResourceBundle.getBundle(WindowsAppImageBuilder.class.getName()); + + protected static final String WINDOWS_BUNDLER_PREFIX = + BUNDLER_PREFIX + "windows" + File.separator; + + private final static String EXECUTABLE_NAME = "WinLauncher.exe"; + private final static String LIBRARY_NAME = "packager.dll"; + + private final static String[] VS_VERS = {"100", "110", "120"}; + private final static String REDIST_MSVCR = "msvcrVS_VER.dll"; + private final static String REDIST_MSVCP = "msvcpVS_VER.dll"; + + private final static String TEMPLATE_APP_ICON ="javalogo_white_48.ico"; + private static final String TOOL_ICON_SWAP="IconSwap.exe"; + private static final String TOOL_VERSION_INFO_SWAP="VersionInfoSwap.exe"; + + private static final String EXECUTABLE_PROPERTIES_TEMPLATE = "WinLauncher.properties"; + + private final Path root; + private final Path appDir; + private final Path runtimeRoot; + private final Path mdir; + + private final Map params; + + public static final BundlerParamInfo CONFIG_ROOT = new WindowsBundlerParam<>( + I18N.getString("param.config-root.name"), + I18N.getString("param.config-root.description"), + "configRoot", + File.class, + params -> { + File imagesRoot = new File(BUILD_ROOT.fetchFrom(params), "windows"); + imagesRoot.mkdirs(); + return imagesRoot; + }, + (s, p) -> null); + + public static final BundlerParamInfo REBRAND_EXECUTABLE = new WindowsBundlerParam<>( + I18N.getString("param.rebrand-executable.name"), + I18N.getString("param.rebrand-executable.description"), + "win.launcher.rebrand", + Boolean.class, + params -> Boolean.TRUE, + (s, p) -> Boolean.valueOf(s)); + + public static final BundlerParamInfo ICON_ICO = new StandardBundlerParam<>( + I18N.getString("param.icon-ico.name"), + I18N.getString("param.icon-ico.description"), + "icon.ico", + File.class, + params -> { + File f = ICON.fetchFrom(params); + if (f != null && !f.getName().toLowerCase().endsWith(".ico")) { + Log.info(MessageFormat.format(I18N.getString("message.icon-not-ico"), f)); + return null; + } + return f; + }, + (s, p) -> new File(s)); + + + + public WindowsAppImageBuilder(Map config, Path imageOutDir) throws IOException { + super(config, imageOutDir.resolve(APP_NAME.fetchFrom(config) + "/runtime")); + + Objects.requireNonNull(imageOutDir); + + //@SuppressWarnings("unchecked") + //String img = (String) config.get("jimage.name"); // FIXME constant + + this.params = config; + + this.root = imageOutDir.resolve(APP_NAME.fetchFrom(params)); + this.appDir = root.resolve("app"); + this.runtimeRoot = root.resolve("runtime"); + this.mdir = runtimeRoot.resolve("lib"); + Files.createDirectories(appDir); + } + + private Path destFile(String dir, String filename) { + return runtimeRoot.resolve(dir).resolve(filename); + } + + private void writeEntry(InputStream in, Path dstFile) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.copy(in, dstFile); + } + + private void writeSymEntry(Path dstFile, Path target) throws IOException { + Files.createDirectories(dstFile.getParent()); + Files.createLink(dstFile, target); + } + + /** + * chmod ugo+x file + */ + private void setExecutable(Path file) { + try { + Set perms = Files.getPosixFilePermissions(file); + perms.add(PosixFilePermission.OWNER_EXECUTE); + perms.add(PosixFilePermission.GROUP_EXECUTE); + perms.add(PosixFilePermission.OTHERS_EXECUTE); + Files.setPosixFilePermissions(file, perms); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + private static void createUtf8File(File file, String content) throws IOException { + try (OutputStream fout = new FileOutputStream(file); + Writer output = new OutputStreamWriter(fout, "UTF-8")) { + output.write(content); + } + } + + + + //it is static for the sake of sharing with "installer" bundlers + // that may skip calls to validate/bundle in this class! + public static File getRootDir(File outDir, Map p) { + return new File(outDir, APP_FS_NAME.fetchFrom(p)); + } + + public static String getLauncherName(Map p) { + return APP_FS_NAME.fetchFrom(p) + ".exe"; + } + + public static String getLauncherCfgName(Map p) { + return "app/" + APP_FS_NAME.fetchFrom(p) +".cfg"; + } + + private File getConfig_AppIcon(Map params) { + return new File(getConfigRoot(params), APP_FS_NAME.fetchFrom(params) + ".ico"); + } + + private File getConfig_ExecutableProperties(Map params) { + return new File(getConfigRoot(params), APP_FS_NAME.fetchFrom(params) + ".properties"); + } + + File getConfigRoot(Map params) { + return CONFIG_ROOT.fetchFrom(params); + } + + protected void cleanupConfigFiles(Map params) { + getConfig_AppIcon(params).delete(); + getConfig_ExecutableProperties(params).delete(); + } + + @Override + protected InputStream getResourceAsStream(String name) { + return WinResources.class.getResourceAsStream(name); + } + + @Override + protected void prepareApplicationFiles(Pool files, Set modules) throws IOException { + Map originalParams = new HashMap<>(params); + File rootFile = root.toFile(); + if (!rootFile.isDirectory() && !rootFile.mkdirs()) { + throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-create-output-dir"), rootFile.getAbsolutePath())); + } + if (!rootFile.canWrite()) { + throw new RuntimeException(MessageFormat.format(I18N.getString("error.cannot-write-to-output-dir"), rootFile.getAbsolutePath())); + } + try { +// if (!dependentTask) { +// Log.info(MessageFormat.format(I18N.getString("message.creating-app-bundle"), APP_NAME.fetchFrom(p), outputDirectory.getAbsolutePath())); +// } + + // Create directory structure +// IOUtils.deleteRecursive(rootDirectory); +// rootDirectory.mkdirs(); + + + // create the .exe launchers + createLauncherForEntryPoint(params); + + // copy the jars + copyApplication(params); + + // copy in the needed libraries + Files.copy(WinResources.class.getResourceAsStream(LIBRARY_NAME), + root.resolve(LIBRARY_NAME)); + + copyMSVCDLLs(); + + // create the secondary launchers, if any + List> entryPoints = StandardBundlerParam.SECONDARY_LAUNCHERS.fetchFrom(params); + for (Map entryPoint : entryPoints) { + Map tmp = new HashMap<>(originalParams); + tmp.putAll(entryPoint); + createLauncherForEntryPoint(tmp); + } + + } catch (IOException ex) { + Log.info("Exception: "+ex); + Log.debug(ex); + } finally { + + if (VERBOSE.fetchFrom(params)) { + Log.info(MessageFormat.format(I18N.getString("message.config-save-location"), getConfigRoot(params).getAbsolutePath())); + } else { + cleanupConfigFiles(params); + } + } + + } + + private void copyMSVCDLLs() throws IOException { + String vsVer = null; + + // first copy the ones needed for the launcher + for (String thisVer : VS_VERS) { + if (copyMSVCDLLs(thisVer)) { + vsVer = thisVer; + break; + } + } + if (vsVer == null) { + throw new RuntimeException("Not found MSVC dlls"); + } + + AtomicReference ioe = new AtomicReference<>(); + final String finalVsVer = vsVer; + Files.list(runtimeRoot.resolve("bin")) + .filter(p -> Pattern.matches("msvc(r|p)\\d\\d\\d.dll", p.toFile().getName().toLowerCase())) + .filter(p -> !p.toString().toLowerCase().endsWith(finalVsVer + ".dll")) + .forEach(p -> { + try { + Files.copy(p, root.resolve((p.toFile().getName()))); + } catch (IOException e) { + ioe.set(e); + } + }); + + IOException e = ioe.get(); + if (e != null) { + throw e; + } + } + + private boolean copyMSVCDLLs(String VS_VER) throws IOException { + final InputStream REDIST_MSVCR_URL = WinResources.class.getResourceAsStream( + REDIST_MSVCR.replaceAll("VS_VER", VS_VER)); + final InputStream REDIST_MSVCP_URL = WinResources.class.getResourceAsStream( + REDIST_MSVCP.replaceAll("VS_VER", VS_VER)); + + if (REDIST_MSVCR_URL != null && REDIST_MSVCP_URL != null) { + Files.copy( + REDIST_MSVCR_URL, + root.resolve(REDIST_MSVCR.replaceAll("VS_VER", VS_VER))); + Files.copy( + REDIST_MSVCP_URL, + root.resolve(REDIST_MSVCP.replaceAll("VS_VER", VS_VER))); + return true; + } + + return false; // not found + } + + private void validateValueAndPut(Map data, String key, + BundlerParamInfo param, Map params) + { + String value = param.fetchFrom(params); + if (value.contains("\r") || value.contains("\n")) { + Log.info("Configuration Parameter " + param.getID() + " contains multiple lines of text, ignore it"); + data.put(key, ""); + return; + } + data.put(key, value); + } + + protected void prepareExecutableProperties(Map params) + throws IOException + { + Map data = new HashMap<>(); + + // mapping Java parameters in strings for version resource + data.put("COMMENTS", ""); + validateValueAndPut(data, "COMPANY_NAME", VENDOR, params); + validateValueAndPut(data, "FILE_DESCRIPTION", DESCRIPTION, params); + validateValueAndPut(data, "FILE_VERSION", VERSION, params); + data.put("INTERNAL_NAME", getLauncherName(params)); + validateValueAndPut(data, "LEGAL_COPYRIGHT", COPYRIGHT, params); + data.put("LEGAL_TRADEMARK", ""); + data.put("ORIGINAL_FILENAME", getLauncherName(params)); + data.put("PRIVATE_BUILD", ""); + validateValueAndPut(data, "PRODUCT_NAME", APP_NAME, params); + validateValueAndPut(data, "PRODUCT_VERSION", VERSION, params); + data.put("SPECIAL_BUILD", ""); + + Writer w = new BufferedWriter(new FileWriter(getConfig_ExecutableProperties(params))); + String content = preprocessTextResource( + WINDOWS_BUNDLER_PREFIX + getConfig_ExecutableProperties(params).getName(), + I18N.getString("resource.executable-properties-template"), EXECUTABLE_PROPERTIES_TEMPLATE, data, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + w.write(content); + w.close(); + } + + private void createLauncherForEntryPoint(Map p) throws IOException { + + File icon = ICON_ICO.fetchFrom(params); + File iconTarget = getConfig_AppIcon(params); + + InputStream in = locateResource("package/windows/" + APP_NAME.fetchFrom(params) + ".ico", + "icon", + TEMPLATE_APP_ICON, + icon, + VERBOSE.fetchFrom(params), + DROP_IN_RESOURCES_ROOT.fetchFrom(params)); + Files.copy(in, iconTarget.toPath()); + + writeCfgFile(p, root.resolve(getLauncherCfgName(p)).toFile(), "$APPDIR\\runtime"); + + prepareExecutableProperties(p); + + // Copy executable root folder + Path executableFile = root.resolve(getLauncherName(p)); + writeEntry(WinResources.class.getResourceAsStream(EXECUTABLE_NAME), executableFile); + executableFile.toFile().setWritable(true, true); + + //Update branding of exe file + if (REBRAND_EXECUTABLE.fetchFrom(p) && iconTarget.exists()) { + //extract IconSwap helper tool + File iconSwapTool = File.createTempFile("iconswap", ".exe"); + IOUtils.copyFromURL( + WinResources.class.getResource(TOOL_ICON_SWAP), + iconSwapTool, + true); + iconSwapTool.setExecutable(true, false); + iconSwapTool.deleteOnExit(); + + //run it on launcher file + executableFile.toFile().setWritable(true); + ProcessBuilder pb = new ProcessBuilder( + iconSwapTool.getAbsolutePath(), + getConfig_AppIcon(p).getAbsolutePath(), + executableFile.toFile().getAbsolutePath()); + IOUtils.exec(pb, VERBOSE.fetchFrom(p)); + executableFile.toFile().setReadOnly(); + iconSwapTool.delete(); + } + + Files.copy(iconTarget.toPath(), root.resolve(APP_NAME.fetchFrom(p) + ".ico")); + + if (REBRAND_EXECUTABLE.fetchFrom(p) && getConfig_ExecutableProperties(p).exists()) { + // extract VersionInfoHelper tool + File versionInfoTool = File.createTempFile("versioninfoswap", ".exe"); + IOUtils.copyFromURL( + WinResources.class.getResource(TOOL_VERSION_INFO_SWAP), + versionInfoTool, + true); + versionInfoTool.setExecutable(true, false); + versionInfoTool.deleteOnExit(); + + // run it on launcher file + executableFile.toFile().setWritable(true); + ProcessBuilder pb = new ProcessBuilder( + versionInfoTool.getAbsolutePath(), + getConfig_ExecutableProperties(p).getAbsolutePath(), + executableFile.toFile().getAbsolutePath()); + IOUtils.exec(pb, VERBOSE.fetchFrom(p)); + executableFile.toFile().setReadOnly(); + versionInfoTool.delete(); + } + } + + private void copyApplication(Map params) throws IOException { + List appResourcesList = APP_RESOURCES_LIST.fetchFrom(params); + if (appResourcesList == null) { + throw new RuntimeException("Null app resources?"); + } + for (RelativeFileSet appResources : appResourcesList) { + if (appResources == null) { + throw new RuntimeException("Null app resources?"); + } + File srcdir = appResources.getBaseDirectory(); + for (String fname : appResources.getIncludedFiles()) { + Files.copy(new File(srcdir, fname).toPath(), new File(appDir.toFile(), fname).toPath()); + } + } + } + + @Override + protected String getCacheLocation(Map params) { + return "$CACHEDIR/"; + } + +} diff --git a/modules/fxpackager/src/main/module-info/module-info.java b/modules/fxpackager/src/main/module-info/module-info.java new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/module-info/module-info.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +module jdk.packager { + requires jdk.jlink; + requires jdk.jdeps; + + requires java.xml; + + exports com.oracle.tools.packager; + exports com.sun.javafx.tools.packager; + exports com.sun.javafx.tools.packager.bundlers; + exports com.sun.javafx.tools.resource; + + uses com.oracle.tools.packager.Bundler; + uses com.oracle.tools.packager.Bundlers; + + provides com.oracle.tools.packager.Bundlers with com.oracle.tools.packager.BasicBundlers; + + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.jnlp.JNLPBundler; + + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.linux.LinuxAppBundler; + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.linux.LinuxDebBundler; + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.linux.LinuxRpmBundler; + + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.mac.MacAppBundler; + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.mac.MacAppStoreBundler; + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.mac.MacDmgBundler; + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.mac.MacPkgBundler; + + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.windows.WinAppBundler; + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.windows.WinExeBundler; + provides com.oracle.tools.packager.Bundler with com.oracle.tools.packager.windows.WinMsiBundler; +} diff --git a/modules/fxpackager/src/main/native/javapackager/shell/javapackager b/modules/fxpackager/src/main/native/javapackager/shell/javapackager --- a/modules/fxpackager/src/main/native/javapackager/shell/javapackager +++ b/modules/fxpackager/src/main/native/javapackager/shell/javapackager @@ -76,8 +76,8 @@ if [ -e "$javafx_home/jre/lib/ext/jfxrt.jar" ] ; then classpath="$classpath:$javafx_home/jre/lib/ext/jfxrt.jar" -elif [ -e "$javafx_home/rt/lib/ext/jfxrt.jar" ] ; then - classpath="$classpath:$javafx_home/rt/lib/ext/jfxrt.jar" +elif [ -e "$javafx_home/lib/jfxrt.jar" ] ; then + classpath="$classpath:$javafx_home/lib/jfxrt.jar" fi if $cygwin ; then diff --git a/modules/fxpackager/src/main/native/library/common/GenericPlatform.cpp b/modules/fxpackager/src/main/native/library/common/GenericPlatform.cpp --- a/modules/fxpackager/src/main/native/library/common/GenericPlatform.cpp +++ b/modules/fxpackager/src/main/native/library/common/GenericPlatform.cpp @@ -166,6 +166,7 @@ keys.insert(std::map::value_type(CONFIG_MAINJAR_KEY, _T("app.mainjar"))); keys.insert(std::map::value_type(CONFIG_MAINCLASSNAME_KEY, _T("app.mainclass"))); keys.insert(std::map::value_type(CONFIG_CLASSPATH_KEY, _T("app.classpath"))); + keys.insert(std::map::value_type(CONFIG_MODULEPATH_KEY, _T("app.modulepath"))); keys.insert(std::map::value_type(APP_NAME_KEY, _T("app.name"))); keys.insert(std::map::value_type(CONFIG_APP_ID_KEY, _T("app.preferences.id"))); keys.insert(std::map::value_type(JVM_RUNTIME_KEY, _T("app.runtime"))); diff --git a/modules/fxpackager/src/main/native/library/common/Helpers.cpp b/modules/fxpackager/src/main/native/library/common/Helpers.cpp --- a/modules/fxpackager/src/main/native/library/common/Helpers.cpp +++ b/modules/fxpackager/src/main/native/library/common/Helpers.cpp @@ -295,3 +295,43 @@ return result; } + +std::list Helpers::StringToArray(TString Value) { + std::list result; + TString line; + + for (int index = 0; index < Value.length(); index++) { + TCHAR c = Value[index]; + +// Environment::NewLine; + + switch (c) { + case '\n': { + result.push_back(line); + line = _T(""); + break; + } + + case '\r': { + result.push_back(line); + line = _T(""); + + if (Value[index + 1] == '\n') + index++; + + break; + } + + default: { + line += c; + } + } + } + + // The buffer may not have ended with a Carriage Return/Line Feed. + if (line.length() > 0) { + result.push_back(line); + } + + return result; +} \ No newline at end of file diff --git a/modules/fxpackager/src/main/native/library/common/Helpers.h b/modules/fxpackager/src/main/native/library/common/Helpers.h --- a/modules/fxpackager/src/main/native/library/common/Helpers.h +++ b/modules/fxpackager/src/main/native/library/common/Helpers.h @@ -69,6 +69,8 @@ static std::list MapToNameValueList(OrderedMap Map); static TString NameValueToString(TString name, TString value); + + static std::list StringToArray(TString Value); }; #endif //HELPERS_H diff --git a/modules/fxpackager/src/main/native/library/common/JavaVirtualMachine.cpp b/modules/fxpackager/src/main/native/library/common/JavaVirtualMachine.cpp --- a/modules/fxpackager/src/main/native/library/common/JavaVirtualMachine.cpp +++ b/modules/fxpackager/src/main/native/library/common/JavaVirtualMachine.cpp @@ -229,6 +229,10 @@ AppendValue(Key, Value, NULL); } + void AppendValue(const TString Key) { + AppendValue(Key, _T(""), NULL); + } + void AppendValues(OrderedMap Values) { std::vector orderedKeys = Values.GetKeys(); @@ -360,9 +364,13 @@ Package& package = Package::GetInstance(); TString classpath = package.GetClassPath(); + TString modulepath = package.GetModulePath(); + JavaOptions options; - JavaOptions options; - options.AppendValue(_T("-Djava.class.path"), classpath); + if (modulepath.empty() == false) { + options.AppendValue(_T("-Djava.module.path"), modulepath); + } + options.AppendValue(_T("-Djava.library.path"), package.GetPackageAppDirectory() + FilePath::PathSeparator() + package.GetPackageLauncherDirectory()); options.AppendValue(_T("-Djava.launcher.path"), package.GetPackageLauncherDirectory()); options.AppendValue(_T("-Dapp.preferences.id"), package.GetAppID()); @@ -418,6 +426,8 @@ javaLibrary.Load(package.GetJVMLibraryFileName()); #ifndef USE_JLI_LAUNCH + options.AppendValue(_T("-Djava.class.path"), classpath); + if (package.HasSplashScreen() == true) { options.AppendValue(TString(_T("-splash:")) + package.GetSplashScreenFileName(), _T("")); } @@ -459,6 +469,9 @@ // will treat them as command line args provided by the user ... // Only propagate original set of args first time. + options.AppendValue(_T("-classpath")); + options.AppendValue(classpath); + std::list vmargs; vmargs.push_back(package.GetCommandName()); diff --git a/modules/fxpackager/src/main/native/library/common/Package.cpp b/modules/fxpackager/src/main/native/library/common/Package.cpp --- a/modules/fxpackager/src/main/native/library/common/Package.cpp +++ b/modules/fxpackager/src/main/native/library/common/Package.cpp @@ -100,6 +100,10 @@ FBootFields->FClassPath = GetMainJar() + FilePath::PathSeparator() + FBootFields->FClassPath; } + // Modulepath. + config->GetValue(keys[CONFIG_SECTION_APPLICATION], keys[CONFIG_MODULEPATH_KEY], FBootFields->FModulePath); + FBootFields->FModulePath = FilePath::FixPathSeparatorForPlatform(FBootFields->FModulePath); + // Main Class. config->GetValue(keys[CONFIG_SECTION_APPLICATION], keys[CONFIG_MAINCLASSNAME_KEY], FBootFields->FMainClassName); @@ -562,6 +566,11 @@ return FBootFields->FClassPath; } +TString Package::GetModulePath() { + assert(FBootFields != NULL); + return FBootFields->FModulePath; +} + TString Package::GetMainJar() { assert(FBootFields != NULL); return FBootFields->FMainJar; diff --git a/modules/fxpackager/src/main/native/library/common/Package.h b/modules/fxpackager/src/main/native/library/common/Package.h --- a/modules/fxpackager/src/main/native/library/common/Package.h +++ b/modules/fxpackager/src/main/native/library/common/Package.h @@ -58,6 +58,7 @@ TString FAppID; TString FPackageAppDataDirectory; TString FClassPath; + TString FModulePath; TString FMainJar; TString FMainClassName; bool FIsRuntimeBundled; @@ -131,6 +132,7 @@ TString GetAppID(); TString GetPackageAppDataDirectory(); TString GetClassPath(); + TString GetModulePath(); TString GetMainClassName(); bool IsRuntimeBundled(); TString GetJVMLibraryFileName(); diff --git a/modules/fxpackager/src/main/native/library/common/Platform.cpp b/modules/fxpackager/src/main/native/library/common/Platform.cpp --- a/modules/fxpackager/src/main/native/library/common/Platform.cpp +++ b/modules/fxpackager/src/main/native/library/common/Platform.cpp @@ -40,6 +40,10 @@ #include "MacPlatform.h" +// Environment +StaticReadProperty Environment::NewLine; + + //-------------------------------------------------------------------------------------------------- Platform& Platform::GetInstance() { diff --git a/modules/fxpackager/src/main/native/library/common/Platform.h b/modules/fxpackager/src/main/native/library/common/Platform.h --- a/modules/fxpackager/src/main/native/library/common/Platform.h +++ b/modules/fxpackager/src/main/native/library/common/Platform.h @@ -137,6 +137,7 @@ #define CONFIG_MAINJAR_KEY _T("CONFIG_MAINJAR_KEY") #define CONFIG_MAINCLASSNAME_KEY _T("CONFIG_MAINCLASSNAME_KEY") #define CONFIG_CLASSPATH_KEY _T("CONFIG_CLASSPATH_KEY") +#define CONFIG_MODULEPATH_KEY _T("CONFIG_MODULEPATH_KEY") #define APP_NAME_KEY _T("APP_NAME_KEY") #define CONFIG_SPLASH_KEY _T("CONFIG_SPLASH_KEY") #define CONFIG_APP_ID_KEY _T("CONFIG_APP_ID_KEY") @@ -151,9 +152,131 @@ typedef void* Procedure; +template +class Property { +private: + ObjectType* FObject; + +public: + Property() { + FObject = NULL; + } + + void SetInstance(ObjectType* Value) { + FObject = Value; + } + + // To set the value using the set method. + ValueType operator =(const ValueType& Value) { + assert(FObject != NULL); + (FObject->*setter)(Value); + return Value; + } + + // The Property class is treated as the internal type. + operator ValueType() { + assert(FObject != NULL); + return (FObject->*getter)(); + } +}; + +template +class ReadProperty { +private: + ObjectType* FObject; + +public: + ReadProperty() { + FObject = NULL; + } + + void SetInstance(ObjectType* Value) { + FObject = Value; + } + + // The Property class is treated as the internal type. + operator ValueType() { + assert(FObject != NULL); + return (FObject->*getter)(); + } +}; + +template +class WriteProperty { +private: + ObjectType* FObject; + +public: + WriteProperty() { + FObject = NULL; + } + + void SetInstance(ObjectType* Value) { + FObject = Value; + } + + // To set the value using the set method. + ValueType operator =(const ValueType& Value) { + assert(FObject != NULL); + (FObject->*setter)(Value); + return Value; + } +}; + +template +class StaticProperty { +public: + StaticProperty() { + } + + // To set the value using the set method. + ValueType operator =(const ValueType& Value) { + (*getter)(Value); + return Value; + } + + // The Property class is treated as the internal type which is the getter. + operator ValueType() { + return (*setter)(); + } +}; + +template +class StaticReadProperty { +public: + StaticReadProperty() { + } + + // The Property class is treated as the internal type which is the getter. + operator ValueType() { + return (*getter)(); + } +}; + +template +class StaticWriteProperty { +public: + StaticWriteProperty() { + } + + // To set the value using the set method. + ValueType operator =(const ValueType& Value) { + (*setter)(Value); + return Value; + } +}; + + class Process { +protected: + std::list FOutput; + public: - Process() {} + Process() { + Output.SetInstance(this); + Input.SetInstance(this); + } + virtual ~Process() {} virtual bool IsRunning() = 0; @@ -162,6 +285,12 @@ bool AWait = false) = 0; virtual bool Wait() = 0; virtual TProcessID GetProcessID() = 0; + + virtual std::list GetOutput() { return FOutput; } + virtual void SetInput(TString Value) = 0; + + ReadProperty, &Process::GetOutput> Output; + WriteProperty Input; }; @@ -227,6 +356,24 @@ virtual bool GetSection(const TString SectionName, OrderedMap &Data) = 0; }; +class Environment { +private: + Environment() { + } + +public: + static TString GetNewLine() { +#ifdef WINDOWS + return _T("\r\n"); +#endif //WINDOWS +#ifdef POSIX + return _T("\n"); +#endif //POSIX + } + + static StaticReadProperty NewLine; +}; + enum DebugState {dsNone, dsNative, dsJava}; enum MessageResponse {mrOK, mrCancel}; diff --git a/modules/fxpackager/src/main/native/library/common/PosixPlatform.cpp b/modules/fxpackager/src/main/native/library/common/PosixPlatform.cpp --- a/modules/fxpackager/src/main/native/library/common/PosixPlatform.cpp +++ b/modules/fxpackager/src/main/native/library/common/PosixPlatform.cpp @@ -37,6 +37,7 @@ #include "PlatformString.h" #include "FilePath.h" +#include "Helpers.h" #include #include @@ -112,6 +113,8 @@ PosixProcess::PosixProcess() : Process() { FChildPID = 0; FRunning = false; + FOutputHandle = 0; + FInputHandle = 0; } PosixProcess::~PosixProcess() { @@ -119,6 +122,16 @@ } void PosixProcess::Cleanup() { + if (FOutputHandle != 0) { + close(FOutputHandle); + FOutputHandle = 0; + } + + if (FInputHandle != 0) { + close(FInputHandle); + FInputHandle = 0; + } + #ifdef MAC sigaction(SIGINT, &savintr, (struct sigaction *)0); sigaction(SIGQUIT, &savequit, (struct sigaction *)0); @@ -126,6 +139,38 @@ #endif //MAC } +bool PosixProcess::ReadOutput() { + bool result = false; + + if (FOutputHandle != 0 && IsRunning() == true) { + char buffer[4096]; + //select(p[0] + 1, &rfds, NULL, NULL, NULL); + + ssize_t count = read(FOutputHandle, buffer, sizeof(buffer)); + + if (count == -1) { + if (errno == EINTR) { + //continue; + } else { + perror("read"); + exit(1); + } + } else if (count == 0) { + //break; + } else { + if (buffer[count] == EOF) { + buffer[count] = '\0'; + } + + std::list output = Helpers::StringToArray(buffer); + FOutput.splice(FOutput.end(), output, output.begin(), output.end()); + result = true; + } + } + + return false; +} + bool PosixProcess::IsRunning() { bool result = false; @@ -169,12 +214,23 @@ return result; } +#define PIPE_READ 0 +#define PIPE_WRITE 1 + bool PosixProcess::Execute(const TString Application, const std::vector Arguments, bool AWait) { bool result = false; if (FRunning == false) { FRunning = true; + int handles[2]; + + if (pipe(handles) == -1) { + //perror("pipe"); + //exit(1); + return false; + } + struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); @@ -206,10 +262,35 @@ #ifdef DEBUG printf("%s\n", command.data()); #endif //DEBUG +// dup2(FOutputHandle, STDOUT_FILENO); +// dup2(FInputHandle, STDIN_FILENO); +// close(FOutputHandle); +// close(FInputHandle); + + dup2(handles[PIPE_READ], STDIN_FILENO); + dup2(handles[PIPE_WRITE], STDOUT_FILENO); +// setvbuf(stdout,NULL,_IONBF,0); +// setvbuf(stdin,NULL,_IONBF,0); + + close(handles[PIPE_READ]); + close(handles[PIPE_WRITE]); + execl("/bin/sh", "sh", "-c", command.data(), (char *)0); + _exit(127); } else { +// close(handles[PIPE_READ]); +// close(handles[PIPE_WRITE]); + +// close(output[1]); +// int nbytes = read(output[0], foo, sizeof(foo)); +// printf("Output: (%.*s)\n", nbytes, foo); +// wait(NULL); + FOutputHandle = handles[PIPE_READ]; + FInputHandle = handles[PIPE_WRITE]; + if (AWait == true) { + ReadOutput(); Wait(); Cleanup(); FRunning = false; @@ -271,4 +352,15 @@ return FChildPID; } +void PosixProcess::SetInput(TString Value) { + if (FInputHandle != 0) { + write(FInputHandle, Value.data(), Value.size()); + } +} + +std::list PosixProcess::GetOutput() { + ReadOutput(); + return Process::GetOutput(); +} + #endif //POSIX diff --git a/modules/fxpackager/src/main/native/library/common/PosixPlatform.h b/modules/fxpackager/src/main/native/library/common/PosixPlatform.h --- a/modules/fxpackager/src/main/native/library/common/PosixPlatform.h +++ b/modules/fxpackager/src/main/native/library/common/PosixPlatform.h @@ -64,12 +64,16 @@ private: pid_t FChildPID; sigset_t saveblock; + int FOutputHandle; + int FInputHandle; #ifdef MAC struct sigaction savintr, savequit; #endif //MAC bool FRunning; void Cleanup(); + bool ReadOutput(); + //static void ProcessOutput(Process *Instance, std::vector Output); public: PosixProcess(); @@ -81,6 +85,8 @@ bool AWait = false); virtual bool Wait(); virtual TProcessID GetProcessID(); + virtual void SetInput(TString Value); + virtual std::list GetOutput(); }; #endif //POSIXPLATFORM_H diff --git a/modules/fxpackager/src/main/native/library/common/WindowsPlatform.cpp b/modules/fxpackager/src/main/native/library/common/WindowsPlatform.cpp --- a/modules/fxpackager/src/main/native/library/common/WindowsPlatform.cpp +++ b/modules/fxpackager/src/main/native/library/common/WindowsPlatform.cpp @@ -826,4 +826,19 @@ return FProcessInfo.dwProcessId; } +bool WindowsProcess::ReadOutput() { + bool result = false; + //TODO implement + return result; +} + +void WindowsProcess::SetInput(TString Value) { + //TODO implement +} + +std::list WindowsProcess::GetOutput() { + ReadOutput(); + return Process::GetOutput(); +} + #endif //WINDOWS diff --git a/modules/fxpackager/src/main/native/library/common/WindowsPlatform.h b/modules/fxpackager/src/main/native/library/common/WindowsPlatform.h --- a/modules/fxpackager/src/main/native/library/common/WindowsPlatform.h +++ b/modules/fxpackager/src/main/native/library/common/WindowsPlatform.h @@ -177,6 +177,7 @@ static WindowsJob FJob; void Cleanup(); + bool ReadOutput(); public: WindowsProcess(); @@ -188,6 +189,8 @@ bool AWait = false); virtual bool Wait(); virtual TProcessID GetProcessID(); + virtual void SetInput(TString Value); + virtual std::list GetOutput(); }; diff --git a/modules/fxpackager/src/main/resources/com/oracle/tools/packager/JLinkBundlerHelper.properties b/modules/fxpackager/src/main/resources/com/oracle/tools/packager/JLinkBundlerHelper.properties new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/resources/com/oracle/tools/packager/JLinkBundlerHelper.properties @@ -0,0 +1,23 @@ +param.jdk-module-path.name=JDK Module Path +param.jdk-module-path.description=When packaging the Java Runtime, this defaults the Packager's JDK modules directory but can be overriden with a different JDK modules directory. + +param.module-path.name=Module Path +param.module-path.description=When packaging the Java Runtime, this is the path JLink will look in for modules. + +param.add-modules.name=Add Modules +param.add-modules.description=List of Modules to add to JImage creation, including possible services. + +param.limit-modules.name=Limit Modules +param.limit-modules.description=Modules to Limit JImage creation to. + +param.auto-modules.name=Auto Modules +param.auto-modules.description=Automatically calculate modules to Limit JImage creation to. + +param.jlink-options.name=JLink Options +param.jlink-options.description=Options to be added to JLink invocation. + +param.jlink-builder.name=JLink Builder +param.jlink-builder.description=Name of the JLink Builder to build the applicaiton image with. + +param.strip-executables.name=Strip Native Executables +param.strip-executables.description=Removes native executables from the JImage creation. diff --git a/modules/fxpackager/src/main/resources/com/oracle/tools/packager/mac/Runtime-Info.plist.template b/modules/fxpackager/src/main/resources/com/oracle/tools/packager/mac/Runtime-Info.plist.template new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/resources/com/oracle/tools/packager/mac/Runtime-Info.plist.template @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + libjli.dylib + CFBundleGetInfoString + CF_BUNDLE_INFO + CFBundleIdentifier + CF_BUNDLE_IDENTIFIER + CFBundleInfoDictionaryVersion + 7.0 + CFBundleName + CF_BUNDLE_NAME + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + CF_BUNDLE_VERSION + + diff --git a/modules/fxpackager/src/main/resources/com/oracle/tools/packager/windows/WinAppBundler.properties b/modules/fxpackager/src/main/resources/com/oracle/tools/packager/windows/WinAppBundler.properties --- a/modules/fxpackager/src/main/resources/com/oracle/tools/packager/windows/WinAppBundler.properties +++ b/modules/fxpackager/src/main/resources/com/oracle/tools/packager/windows/WinAppBundler.properties @@ -14,7 +14,6 @@ param.icon-ico.description = Icon for the application, in ICO format. resource.application-icon=application icon -resource.executable-properties-template=Template for creating executable properties file. error.parameters-null=Parameters map is null. error.parameters-null.advice=Pass in a non-null parameters map. diff --git a/modules/fxpackager/src/main/resources/jdk/packager/builders/AbstractAppImageBuilder.properties b/modules/fxpackager/src/main/resources/jdk/packager/builders/AbstractAppImageBuilder.properties new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/resources/jdk/packager/builders/AbstractAppImageBuilder.properties @@ -0,0 +1,5 @@ +message.using-default-resource=Using default package resource {0} (add {1} to the class path to customize) +message.using-custom-resource-from-file=\ Using custom package resource {0} (loaded from file {1}) +message.using-custom-resource-from-classpath=\ Using custom package resource {0} (loaded from {1}) +message.using-default-resource-from-classpath=\ Using default package resource {0} (add {1} to the class path to customize) +message.jvm-user-arg-is-null=WARNING\: a jvmuserarg has a null name or value. diff --git a/modules/fxpackager/src/main/resources/jdk/packager/builders/linux/LinuxAppImageBuilder.properties b/modules/fxpackager/src/main/resources/jdk/packager/builders/linux/LinuxAppImageBuilder.properties new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/resources/jdk/packager/builders/linux/LinuxAppImageBuilder.properties @@ -0,0 +1,4 @@ +param.icon-png.name = .png Icon +param.icon-png.description = Icon for the application, in PNG format. + +message.icon-not-png= The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place. diff --git a/modules/fxpackager/src/main/resources/jdk/packager/builders/mac/MacAppImageBuilder.properties b/modules/fxpackager/src/main/resources/jdk/packager/builders/mac/MacAppImageBuilder.properties new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/resources/jdk/packager/builders/mac/MacAppImageBuilder.properties @@ -0,0 +1,72 @@ +bundler.name=Mac Application Image +bundler.description=A Directory based image of a mac Application with an optionally co-bundled JRE. Used as a base for the Installer bundlers + +param.signing-key-developer-id-app.name=Apple Developer ID Application Signing Key +param.signing-key-developer-id-app.description=The full name of the Apple Developer ID Application signing key. + +param.icon-icns.name= .icns Icon +param.icon-icns.description= Icon for the application, in ICNS format. + +param.config-root.name= +param.config-root.description= + +param.configure-launcher-in-plist=Configure Launcher in Info.plist +param.configure-launcher-in-plist.description=Should the legacy method of configuring hte launcher in the Info.plist be used. + +param.category-name=Category +param.category-name.description=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. + +param.cfbundle-name.name=CFBundleName +param.cfbundle-name.description=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. + +param.cfbundle-identifier.name=CFBundleIdentifier +param.cfbundle-identifier.description=An identifier that uniquely identifies the application for MacOSX (and on the Mac App Store). May only use alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.) characters. + +param.cfbundle-version.name=CFBundleVersion +param.cfbundle-version.description=An computer readable version for the CFBundle. May contain only digits and from zero to two dots, such as "1.8.1" or "100". + +param.bundle-id-signing-prefix.name=Bundle Signing Prefix +param.bundle-id-signing-prefix.description=When signing the application bundle this value is prefixed to all components that need to be signed that don't have an existing CFBundleIdentifier. + +param.raw-executable-url.name=Launcher URL +param.raw-executable-url.description=Override the packager default launcher with a custom launcher. + +param.default-icon-icns=Default Icon +param.default-icon-icns.description=The Default Icon for when a user does not specify an icns file. + +param.runtime.name=JRE +param.runtime.description=The Java Runtime to co-bundle. The default value is the current JRE running the bundler. A value of null will cause no JRE to be co-bundled and the system JRE will be used to launch the application. + +param.images-root.name= +param.images-root.description= + +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. +error.invalid-cfbundle-version=Invalid CFBundleVersion - ''{0}'' +error.invalid-cfbundle-version.advice=Set a compatible 'appVersion' or set a 'mac.CFBundleVersion'. Valid versions are one to three integers separated by dots. +error.explicit-sign-no-cert=Signature explicitly requested but no signing certificate specified. +error.explicit-sign-no-cert.advice=Either specify a valid cert in 'mac.signing-key-developer-id-app' or unset 'signBundle' or set 'signBundle' to false. +error.non-existent-runtime=The file for the Runtime/JRE directory does not exist. +error.non-existent-runtime.advice=Point the runtime parameter to a directory that containes the JRE. +error.cannot-detect-runtime-in-directory=Cannot determine which JRE/JDK exists in the specified runtime directory. +error.cannot-detect-runtime-in-directory.advice=Point the runtime directory to one of the JDK/JRE root, the Contents/Home directory of that root, or the Contents/Home/jre directory of the JDK. +resource.app-info-plist=Application Info.plist +resource.runtime-info-plist=Java Runtime Info.plist + +message.config-save-location=\ Config files are saved to {0}. Use them to customize package. +message.bundle-name-too-long-warning={0} is set to ''{1}'', which is longer than 16 characters. For a better Mac experience consider shortening it. +message.no-mac-jre-support=Currently Macs require a JDK to package +message.creating-app-bundle=Creating app bundle\: {0} +message.null-classpath=Null app resources? +message.preparing-info-plist=Preparing Info.plist\: {0} +message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not be used. The default icon will be used in it's place. +message.version-string-too-many-components=Version sting may have between 1 and 3 numbers: 1, 1.2, 1.2.3. +message.version-string-first-number-not-zero=The first number in a CFBundleVersion cannot be zero or negative. +message.version-string-no-negative-numbers=Negative numbers are not allowed in version strings. +message.version-string-numbers-only=Version strings can consist of only numbers and up to two dots. +message.creating-association-with-null-extension=Creating association with null extension. + +message.using-default-resource=Using default package resource {0} (add {1} to the class path to customize) +message.using-custom-resource-from-file=\ Using custom package resource {0} (loaded from file {1}) +message.using-custom-resource-from-classpath=\ Using custom package resource {0} (loaded from {1}) +message.using-default-resource-from-classpath=\ Using default package resource {0} (add {1} to the class path to customize) diff --git a/modules/fxpackager/src/main/resources/jdk/packager/builders/windows/WindowsAppImageBuilder.properties b/modules/fxpackager/src/main/resources/jdk/packager/builders/windows/WindowsAppImageBuilder.properties new file mode 100644 --- /dev/null +++ b/modules/fxpackager/src/main/resources/jdk/packager/builders/windows/WindowsAppImageBuilder.properties @@ -0,0 +1,15 @@ +param.rebrand-executable.name = Rebrand Launcher +param.rebrand-executable.description = Update the launcher with the application icon and update ownership information. + +param.icon-ico.name = .ico Icon +param.icon-ico.description = Icon for the application, in ICO format. + +param.config-root.name= +param.config-root.description= + +error.cannot-create-output-dir=Output directory {0} cannot be created. +error.cannot-write-to-output-dir=Output directory {0} is not writable. + +message.config-save-location=\ Config files are saved to {0}. Use them to customize package. + +resource.executable-properties-template=Template for creating executable properties file. diff --git a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacAppBundlerTest.java b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacAppBundlerTest.java --- a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacAppBundlerTest.java +++ b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacAppBundlerTest.java @@ -203,7 +203,7 @@ params.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); if (runtimeJdk != null) { - params.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME params.put(MAC_RUNTIME.getID(), runtimeJdk); } params.put(VERSION.getID(), v); @@ -220,7 +220,7 @@ params.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); if (runtimeJdk != null) { - params.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME params.put(MAC_RUNTIME.getID(), runtimeJdk); } params.put(MAC_CF_BUNDLE_VERSION.getID(), v); @@ -241,7 +241,7 @@ params.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); if (runtimeJdk != null) { - params.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME params.put(MAC_RUNTIME.getID(), runtimeJdk); } params.put(VERSION.getID(), v); @@ -259,7 +259,7 @@ params.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); if (runtimeJdk != null) { - params.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME params.put(MAC_RUNTIME.getID(), runtimeJdk); } params.put(MAC_CF_BUNDLE_VERSION.getID(), v); @@ -302,7 +302,7 @@ bundleParams.put(SIGN_BUNDLE.getID(), false); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = bundler.validate(bundleParams); @@ -402,7 +402,7 @@ bundleParams.put(BUILD_ROOT.getID(), tmpBase); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } bundleParams.put(APP_NAME.getID(), appName); @@ -463,7 +463,7 @@ bundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } if (!signingKeysPresent) { @@ -510,7 +510,7 @@ bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } String keychain = null; @@ -549,7 +549,7 @@ bundleParams.put(DESCRIPTION.getID(), "крайне большое описание со странными символами"); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } String keychain = null; @@ -584,7 +584,7 @@ bundleParams.put(VERBOSE.getID(), true); bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); - bundleParams.put(MAC_RUNTIME.getID(), APP_RESOURCES.fetchFrom(bundleParams)); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), APP_RESOURCES.fetchFrom(bundleParams)); bundler.validate(bundleParams); } @@ -629,7 +629,7 @@ bundleParams.put(MAC_CF_BUNDLE_IDENTIFIER.getID(), "com.example.everything.cf-bundle-identifier"); bundleParams.put(MAC_CF_BUNDLE_NAME.getID(), "Everything CF Bundle Name"); bundleParams.put(MAC_CF_BUNDLE_VERSION.getID(), "8.2.0"); - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk); bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle"); bundleParams.put(MAIN_JAR.getID(), "mainApp.jar"); bundleParams.put(PREFERENCES_ID.getID(), "everything/preferences/id"); @@ -707,7 +707,7 @@ bundleParams.put(SIGN_BUNDLE.getID(), false); // force no signing if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = bundler.validate(bundleParams); @@ -737,7 +737,7 @@ bundleParams.put(BUILD_ROOT.getID(), tmpBase); bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); - bundleParams.put(MAC_RUNTIME.getID(), jre); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), jre); String keychain = null; if (!signingKeysPresent) { @@ -779,7 +779,7 @@ bundleParams.put(ENABLE_APP_CDS.getID(), true); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = bundler.validate(bundleParams); diff --git a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacAppStoreBundlerTest.java b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacAppStoreBundlerTest.java --- a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacAppStoreBundlerTest.java +++ b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacAppStoreBundlerTest.java @@ -191,7 +191,7 @@ bundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = bundler.validate(bundleParams); @@ -243,7 +243,7 @@ bundleParams.put(MAC_CF_BUNDLE_IDENTIFIER.getID(), "com.example.everything.cf-bundle-identifier"); bundleParams.put(MAC_CF_BUNDLE_NAME.getID(), "Everything CF Bundle Name"); bundleParams.put(MAC_CF_BUNDLE_VERSION.getID(), "8.2.0"); - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk); bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle"); bundleParams.put(MAIN_JAR.getID(), "mainApp.jar"); bundleParams.put(PREFERENCES_ID.getID(), "everything/preferences/id"); @@ -324,7 +324,7 @@ bundleParams.put(MacAppBundler.MAC_CATEGORY.getID(), "public.app-category.developer-tools"); bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); bundleParams.put(VERBOSE.getID(), true); - bundleParams.put(MAC_RUNTIME.getID(), jre); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), jre); boolean valid = bundler.validate(bundleParams); assertTrue(valid); @@ -361,7 +361,7 @@ bundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } bundleParams.put(SIGN_BUNDLE.getID(), false); diff --git a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacDaemonBundlerTest.java b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacDaemonBundlerTest.java --- a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacDaemonBundlerTest.java +++ b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacDaemonBundlerTest.java @@ -48,7 +48,6 @@ import org.junit.BeforeClass; import org.junit.Test; -import static com.oracle.tools.packager.mac.MacAppBundler.MAC_RUNTIME; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -138,7 +137,7 @@ bundleParams.put(SYSTEM_WIDE.getID(), true); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = bundler.validate(bundleParams); diff --git a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacDmgBundlerTest.java b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacDmgBundlerTest.java --- a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacDmgBundlerTest.java +++ b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacDmgBundlerTest.java @@ -168,7 +168,7 @@ bundleParams.put(SIMPLE_DMG.getID(), simple_dmg); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = bundler.validate(bundleParams); @@ -205,7 +205,7 @@ bundleParams.put(SIMPLE_DMG.getID(), simple_dmg); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = bundler.validate(bundleParams); @@ -237,7 +237,7 @@ bundleParams.put(DESCRIPTION.getID(), "крайне большое описание со странными символами"); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } bundler.validate(bundleParams); @@ -269,7 +269,7 @@ appBundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = appBundler.validate(appBundleParams); @@ -295,7 +295,7 @@ dmgBundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - dmgBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME dmgBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } valid = dmgBundler.validate(dmgBundleParams); @@ -326,7 +326,7 @@ appBundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = appBundler.validate(appBundleParams); @@ -353,7 +353,7 @@ dmgBundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - dmgBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME dmgBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } valid = dmgBundler.validate(dmgBundleParams); @@ -429,7 +429,7 @@ bundleParams.put(MAC_CATEGORY.getID(), "public.app-category.developer-tools"); bundleParams.put(MAC_CF_BUNDLE_IDENTIFIER.getID(), "com.example.everything.cf-bundle-identifier"); bundleParams.put(MAC_CF_BUNDLE_NAME.getID(), "Everything CF Bundle Name"); - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk); bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle"); bundleParams.put(MAIN_JAR.getID(), "mainApp.jar"); bundleParams.put(PREFERENCES_ID.getID(), "everything/preferences/id"); diff --git a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacPkgBundlerTest.java b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacPkgBundlerTest.java --- a/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacPkgBundlerTest.java +++ b/modules/fxpackager/src/test/java/com/oracle/tools/packager/mac/MacPkgBundlerTest.java @@ -253,7 +253,7 @@ bundleParams.put(SIGN_BUNDLE.getID(), false); // force no signing if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = bundler.validate(bundleParams); @@ -290,7 +290,7 @@ bundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } if (!signingKeysPresent) { @@ -337,7 +337,7 @@ bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } String keychain = null; @@ -380,7 +380,7 @@ bundleParams.put(DESCRIPTION.getID(), "крайне большое описание со странными символами"); if (runtimeJdk != null) { - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } String keychain = null; @@ -423,7 +423,7 @@ appBundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } String keychain = null; @@ -456,7 +456,7 @@ pkgBundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - pkgBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME pkgBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } if (keychain != null) { pkgBundleParams.put(SIGNING_KEYCHAIN.getID(), keychain); @@ -543,7 +543,7 @@ appBundleParams.put(VERBOSE.getID(), true); if (runtimeJdk != null) { - appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); +//FIXME appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); } boolean valid = appBundler.validate(appBundleParams); @@ -596,7 +596,7 @@ bundleParams.put(MAC_CATEGORY.getID(), "public.app-category.developer-tools"); bundleParams.put(MAC_CF_BUNDLE_IDENTIFIER.getID(), "com.example.everything.cf-bundle-identifier"); bundleParams.put(MAC_CF_BUNDLE_NAME.getID(), "Everything CF Bundle Name"); - bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk); +//FIXME bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk); bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle"); bundleParams.put(MAIN_JAR.getID(), "mainApp.jar"); bundleParams.put(PREFERENCES_ID.getID(), "everything/preferences/id"); diff --git a/modules/fxpackager/src/test/java/com/oracle/tools/packager/windows/RuntimeFlagsParserTest.java b/modules/fxpackager/src/test/java/com/oracle/tools/packager/windows/RuntimeFlagsParserTest.java deleted file mode 100644 --- a/modules/fxpackager/src/test/java/com/oracle/tools/packager/windows/RuntimeFlagsParserTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.oracle.tools.packager.windows; - -import com.oracle.tools.packager.AbstractImageBundler; -import junit.framework.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -@RunWith(value = Parameterized.class) -public class RuntimeFlagsParserTest { - - @Parameterized.Parameters - public static Collection data() { - return Arrays.asList(new String[][] { - { - "java version \"1.7.0_51\"\nJava(TM) SE Runtime Environment (build 1.7.0_51-b13)\nJava HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)\n", - "64", "1.7.0_51", "1.7.0", "51", null, "7", "51", "51", "0" - }, - { - "java version \"1.8.0_05-ea\"\nJava(TM) SE Runtime Environment (build 1.8.0_05-ea-b03)\nJava HotSpot(TM) Client VM (build 25.5-b01, mixed mode)\n", - "32", "1.8.0_05", "1.8.0", "05", "ea", "8", "05", "05", "0" - }, - { - "java version \"1.8.0_40\"\nJava(TM) SE Runtime Environment (build 1.8.0_40-b25)\nJava HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)", - "64", "1.8.0_40", "1.8.0", "40", null, "8", "40", "40", "0" - }, - { - "java version \"9.1.2.3-ea\"\nJava(TM) SE Runtime Environment (build 9.1.2.3-ea-4+2015-03-14-092653.pi.jdk9)\nJava HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)", - "64", "9.1.2.3", "9.1.2.3", "1", "ea", "9", "1", "2", "3" - } - }); - } - - String versionText; - String bitArch; - String fullVersion; - String releaseVersion; - String updateVersion; - String versionModifiers; - String versionMajor; - String versionMinor; - String versionSecurity; - String versionPatch; - - public RuntimeFlagsParserTest(String versionText, String bitArch, String fullVersion, String releaseVersion, String updateVersion, String versionModifiers, String versionMajor, String versionMinor, String versionSecurity, String versionPatch) { - this.versionText = versionText; - this.bitArch = bitArch; - this.fullVersion = fullVersion; - this.releaseVersion = releaseVersion; - this.updateVersion = updateVersion; - this.versionModifiers = versionModifiers; - this.versionMajor = versionMajor; - this.versionMinor = versionMinor; - this.versionSecurity = versionSecurity; - this.versionPatch = versionPatch; - } - - @Test - public void validateVersionText() { - Map params = new HashMap<>(); - AbstractImageBundler.extractFlagsFromVersion(params, versionText); - - Assert.assertEquals(bitArch, params.get(".runtime.bit-arch")); - Assert.assertEquals(fullVersion, params.get(".runtime.version")); - Assert.assertEquals(releaseVersion, params.get(".runtime.version.release")); - Assert.assertEquals(updateVersion, params.get(".runtime.version.update")); - Assert.assertEquals(versionModifiers, params.get(".runtime.version.modifiers")); - Assert.assertEquals(versionMajor, params.get(".runtime.version.major")); - Assert.assertEquals(versionMinor, params.get(".runtime.version.minor")); - Assert.assertEquals(versionSecurity, params.get(".runtime.version.security")); - Assert.assertEquals(versionPatch, params.get(".runtime.version.patch")); - } -} diff --git a/modules/fxpackager/src/test/java/hello/SimpleBundle.java b/modules/fxpackager/src/test/java/hello/SimpleBundle.java --- a/modules/fxpackager/src/test/java/hello/SimpleBundle.java +++ b/modules/fxpackager/src/test/java/hello/SimpleBundle.java @@ -28,6 +28,7 @@ import com.oracle.tools.packager.Bundler; import com.oracle.tools.packager.Bundlers; import com.oracle.tools.packager.ConfigException; +import com.oracle.tools.packager.JLinkBundlerHelper; import com.oracle.tools.packager.Log; import com.oracle.tools.packager.RelativeFileSet; import com.oracle.tools.packager.StandardBundlerParam; @@ -77,6 +78,10 @@ params.put(argsQ.remove(), argsQ.remove()); break; + case "-modulepath": + params.put(JLinkBundlerHelper.MODULE_PATH.getID(), argsQ.remove()); + break; + case "-all": // JVM args params.put(StandardBundlerParam.JVM_OPTIONS.getID(), "-Doption.1=bundlerargs\n-Doption.2=bundlerargs\n-Dcollide=jvmoptions"); @@ -132,8 +137,7 @@ File appResourcesDir = new File("."); File fakeMainJar = new File(appResourcesDir, "mainApp.jar"); - File packagerJar = new File(appResourcesDir, "packager.jar"); - Set appResources = new HashSet<>(Arrays.asList(fakeMainJar, packagerJar)); + Set appResources = new HashSet<>(Arrays.asList(fakeMainJar)); bundleParams.put(APP_NAME.getID(), "DevTest"); bundleParams.put(MAIN_CLASS.getID(), "hello.TestPackager"); @@ -141,7 +145,7 @@ new RelativeFileSet(fakeMainJar.getParentFile(), new HashSet<>(Arrays.asList(fakeMainJar))) ); - bundleParams.put(CLASSPATH.getID(), fakeMainJar.getName() + " " + packagerJar.getName()); + bundleParams.put(CLASSPATH.getID(), fakeMainJar.getName()); bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); bundleParams.put(VERBOSE.getID(), true); bundleParams.put(ICON.getID(), "java-logo2.gif"); diff --git a/modules/fxpackager/src/test/java/hello/TestPackager.java b/modules/fxpackager/src/test/java/hello/TestPackager.java --- a/modules/fxpackager/src/test/java/hello/TestPackager.java +++ b/modules/fxpackager/src/test/java/hello/TestPackager.java @@ -156,7 +156,24 @@ label.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0)); panel.add(label, BorderLayout.NORTH); - panel.add(box, BorderLayout.CENTER); + DefaultTableModel propsModel = new DefaultTableModel(); + propsModel.addColumn("Key"); + propsModel.addColumn("Value"); + + for (Map.Entry entry : System.getProperties().entrySet()) { + propsModel.addRow(new Object[] {entry.getKey(), entry.getValue()}); + } + JTable props = new JTable(propsModel); + props.setAutoCreateRowSorter(true); + JScrollPane propsScroller = new JScrollPane(props); + propsScroller.setPreferredSize(new Dimension(400, 100)); + + + JTabbedPane tabbedPane = new JTabbedPane(); + tabbedPane.addTab("Launch", box); + tabbedPane.addTab("System", propsScroller); + + panel.add(tabbedPane, BorderLayout.CENTER); panel.setSize(panel.getPreferredSize()); frame.getContentPane().add(panel); diff --git a/modules/fxpackagerservices/make/build.properties b/modules/fxpackagerservices/make/build.properties new file mode 100644 --- /dev/null +++ b/modules/fxpackagerservices/make/build.properties @@ -0,0 +1,31 @@ +# +# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# Build properties for this module + +include_in_jre=false +include_in_jdk=true +include_in_jdk_server=false +classloader=app diff --git a/modules/fxpackager/src/main/java/jdk/packager/services/UserJvmOptionsService.java b/modules/fxpackagerservices/src/main/java/jdk/packager/services/UserJvmOptionsService.java rename from modules/fxpackager/src/main/java/jdk/packager/services/UserJvmOptionsService.java rename to modules/fxpackagerservices/src/main/java/jdk/packager/services/UserJvmOptionsService.java --- a/modules/fxpackager/src/main/java/jdk/packager/services/UserJvmOptionsService.java +++ b/modules/fxpackagerservices/src/main/java/jdk/packager/services/UserJvmOptionsService.java @@ -25,7 +25,7 @@ package jdk.packager.services; -import jdk.packager.services.userjvmoptions.LauncherUserJvmOptions; +import jdk.packager.services.userjvmoptions.PreferencesUserJvmOptions; import java.util.Iterator; import java.util.Map; import java.util.ServiceLoader; @@ -34,10 +34,10 @@ * Runtime access to the UserJVMOptions. * * This class is not typically available in the Java Runtime, you must - * explicitly include the 'packager.jar' file from the lib directory + * explicitly include the 'jdk.packager.services' module from the jmod directory * of the JDK as part of your application bundle. * - * @since 8u40 + * @since 9 */ public interface UserJvmOptionsService { @@ -55,8 +55,7 @@ if (iter.hasNext()) { return iter.next(); } else { - return new LauncherUserJvmOptions(); - //return new PreferencesUserJvmOptions(); + return new PreferencesUserJvmOptions(); } } @@ -96,5 +95,4 @@ * @throws UnsupportedOperationException if the defaults cannot be calculated. */ Map getUserJVMOptionDefaults(); - } diff --git a/modules/fxpackager/src/main/java/jdk/packager/services/userjvmoptions/LauncherUserJvmOptions.java b/modules/fxpackagerservices/src/main/java/jdk/packager/services/userjvmoptions/LauncherUserJvmOptions.java rename from modules/fxpackager/src/main/java/jdk/packager/services/userjvmoptions/LauncherUserJvmOptions.java rename to modules/fxpackagerservices/src/main/java/jdk/packager/services/userjvmoptions/LauncherUserJvmOptions.java --- a/modules/fxpackager/src/main/java/jdk/packager/services/userjvmoptions/LauncherUserJvmOptions.java +++ b/modules/fxpackagerservices/src/main/java/jdk/packager/services/userjvmoptions/LauncherUserJvmOptions.java @@ -52,6 +52,7 @@ checkAllPermissions(); System.loadLibrary("packager"); } catch (SecurityException se) { + se.printStackTrace(System.err); // fail to load, we will also throw on all public methods } } diff --git a/modules/fxpackager/src/main/java/jdk/packager/services/userjvmoptions/PreferencesUserJvmOptions.java b/modules/fxpackagerservices/src/main/java/jdk/packager/services/userjvmoptions/PreferencesUserJvmOptions.java rename from modules/fxpackager/src/main/java/jdk/packager/services/userjvmoptions/PreferencesUserJvmOptions.java rename to modules/fxpackagerservices/src/main/java/jdk/packager/services/userjvmoptions/PreferencesUserJvmOptions.java diff --git a/modules/fxpackagerservices/src/main/module-info/module-info.java b/modules/fxpackagerservices/src/main/module-info/module-info.java new file mode 100644 --- /dev/null +++ b/modules/fxpackagerservices/src/main/module-info/module-info.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +module jdk.packager.services { + exports jdk.packager.services; + + requires java.prefs; + + uses jdk.packager.services.UserJvmOptionsService; + + provides jdk.packager.services.UserJvmOptionsService with jdk.packager.services.userjvmoptions.LauncherUserJvmOptions; +// service loaders are not preserving order, so delete this and provide only one +// provides jdk.packager.services.UserJvmOptionsService with jdk.packager.services.userjvmoptions.PreferencesUserJvmOptions; +} \ No newline at end of file