--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.packager/windows/classes/jdk/packager/internal/windows/WinExeBundler.java Mon Oct 08 18:17:37 2018 -0400
@@ -0,0 +1,1024 @@
+/*
+ * Copyright (c) 2017, 2018, 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.internal.windows;
+
+import jdk.packager.internal.*;
+import jdk.packager.internal.ConfigException;
+import jdk.packager.internal.Arguments;
+import jdk.packager.internal.UnsupportedPlatformException;
+import jdk.packager.internal.resources.windows.WinResources;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static jdk.packager.internal.windows.WindowsBundlerParam.*;
+
+public class WinExeBundler extends AbstractBundler {
+
+ private static final ResourceBundle I18N = ResourceBundle.getBundle(
+ "jdk.packager.internal.resources.windows.WinExeBundler");
+
+ public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER =
+ new WindowsBundlerParam<>(
+ getString("param.app-bundler.name"),
+ getString("param.app-bundler.description"),
+ "win.app.bundler",
+ WinAppBundler.class,
+ params -> new WinAppBundler(),
+ null);
+
+ public static final BundlerParamInfo<File> CONFIG_ROOT =
+ new WindowsBundlerParam<>(
+ getString("param.config-root.name"),
+ 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<File> EXE_IMAGE_DIR =
+ new WindowsBundlerParam<>(
+ getString("param.image-dir.name"),
+ getString("param.image-dir.description"),
+ "win.exe.imageDir",
+ File.class,
+ params -> {
+ File imagesRoot = IMAGES_ROOT.fetchFrom(params);
+ if (!imagesRoot.exists()) imagesRoot.mkdirs();
+ return new File(imagesRoot, "win-exe.image");
+ },
+ (s, p) -> null);
+
+ public static final BundlerParamInfo<File> WIN_APP_IMAGE =
+ new WindowsBundlerParam<>(
+ getString("param.app-dir.name"),
+ getString("param.app-dir.description"),
+ "win.app.image",
+ File.class,
+ null,
+ (s, p) -> null);
+
+
+ public static final StandardBundlerParam<Boolean> EXE_SYSTEM_WIDE =
+ new StandardBundlerParam<>(
+ getString("param.system-wide.name"),
+ getString("param.system-wide.description"),
+ Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
+ Boolean.class,
+ params -> true, // default to system wide
+ (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null
+ : Boolean.valueOf(s)
+ );
+ public static final StandardBundlerParam<String> PRODUCT_VERSION =
+ new StandardBundlerParam<>(
+ getString("param.product-version.name"),
+ getString("param.product-version.description"),
+ "win.msi.productVersion",
+ String.class,
+ VERSION::fetchFrom,
+ (s, p) -> s
+ );
+
+ public static final StandardBundlerParam<Boolean> MENU_HINT =
+ new WindowsBundlerParam<>(
+ getString("param.menu-shortcut-hint.name"),
+ getString("param.menu-shortcut-hint.description"),
+ Arguments.CLIOptions.WIN_MENU_HINT.getId(),
+ Boolean.class,
+ params -> false,
+ (s, p) -> (s == null ||
+ "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
+ );
+
+ public static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
+ new WindowsBundlerParam<>(
+ getString("param.desktop-shortcut-hint.name"),
+ getString("param.desktop-shortcut-hint.description"),
+ Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(),
+ Boolean.class,
+ params -> false,
+ (s, p) -> (s == null ||
+ "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
+ );
+
+
+
+ private final static String DEFAULT_EXE_PROJECT_TEMPLATE = "template.iss";
+ private static final String TOOL_INNO_SETUP_COMPILER = "iscc.exe";
+
+ public static final BundlerParamInfo<String>
+ TOOL_INNO_SETUP_COMPILER_EXECUTABLE = new WindowsBundlerParam<>(
+ getString("param.iscc-path.name"),
+ getString("param.iscc-path.description"),
+ "win.exe.iscc.exe",
+ String.class,
+ params -> {
+ for (String dirString : (System.getenv("PATH") + ";C:\\Program Files (x86)\\Inno Setup 5;C:\\Program Files\\Inno Setup 5").split(";")) {
+ File f = new File(dirString.replace("\"", ""),
+ TOOL_INNO_SETUP_COMPILER);
+ if (f.isFile()) {
+ return f.toString();
+ }
+ }
+ return null;
+ },
+ null);
+
+ public WinExeBundler() {
+ super();
+ baseResourceLoader = WinResources.class;
+ }
+
+ @Override
+ public String getName() {
+ return getString("bundler.name");
+ }
+
+ @Override
+ public String getDescription() {
+ return getString("bundler.description");
+ }
+
+ @Override
+ public String getID() {
+ return "exe";
+ }
+
+ @Override
+ public String getBundleType() {
+ return "INSTALLER";
+ }
+
+ @Override
+ public Collection<BundlerParamInfo<?>> getBundleParameters() {
+ Collection<BundlerParamInfo<?>> results = new LinkedHashSet<>();
+ results.addAll(WinAppBundler.getAppBundleParameters());
+ results.addAll(getExeBundleParameters());
+ return results;
+ }
+
+ public static Collection<BundlerParamInfo<?>> getExeBundleParameters() {
+ return Arrays.asList(
+ DESCRIPTION,
+ COPYRIGHT,
+ LICENSE_FILE,
+ MENU_GROUP,
+ MENU_HINT,
+ // RUN_AT_STARTUP,
+ SHORTCUT_HINT,
+ // SERVICE_HINT,
+ // START_ON_INSTALL,
+ // STOP_ON_UNINSTALL,
+ EXE_SYSTEM_WIDE,
+ TITLE,
+ VENDOR,
+ INSTALLDIR_CHOOSER
+ );
+ }
+
+ @Override
+ public File execute(
+ Map<String, ? super Object> p, File outputParentDir) {
+ return bundle(p, outputParentDir);
+ }
+
+ @Override
+ public boolean supported() {
+ return (Platform.getPlatform() == Platform.WINDOWS);
+ }
+
+ static class VersionExtractor extends PrintStream {
+ double version = 0f;
+
+ public VersionExtractor() {
+ super(new ByteArrayOutputStream());
+ }
+
+ double getVersion() {
+ if (version == 0f) {
+ String content =
+ new String(((ByteArrayOutputStream) out).toByteArray());
+ Pattern pattern = Pattern.compile("Inno Setup (\\d+.?\\d*)");
+ Matcher matcher = pattern.matcher(content);
+ if (matcher.find()) {
+ String v = matcher.group(1);
+ version = new Double(v);
+ }
+ }
+ return version;
+ }
+ }
+
+ private static double findToolVersion(String toolName) {
+ try {
+ if (toolName == null || "".equals(toolName)) return 0f;
+
+ ProcessBuilder pb = new ProcessBuilder(
+ toolName,
+ "/?");
+ VersionExtractor ve = new VersionExtractor();
+ IOUtils.exec(pb, Log.isDebug(), true, ve);
+ // not interested in the output
+ double version = ve.getVersion();
+ Log.verbose(MessageFormat.format(
+ getString("message.tool-version"), toolName, version));
+ return version;
+ } catch (Exception e) {
+ if (Log.isDebug()) {
+ Log.verbose(e);
+ }
+ return 0f;
+ }
+ }
+
+ @Override
+ public boolean validate(Map<String, ? super Object> p)
+ throws UnsupportedPlatformException, ConfigException {
+ try {
+ if (p == null) throw new ConfigException(
+ getString("error.parameters-null"),
+ getString("error.parameters-null.advice"));
+
+ // run basic validation to ensure requirements are met
+ // we are not interested in return code, only possible exception
+ APP_BUNDLER.fetchFrom(p).validate(p);
+
+ // make sure some key values don't have newlines
+ for (BundlerParamInfo<String> pi : Arrays.asList(
+ APP_NAME,
+ COPYRIGHT,
+ DESCRIPTION,
+ MENU_GROUP,
+ TITLE,
+ VENDOR,
+ VERSION)
+ ) {
+ String v = pi.fetchFrom(p);
+ if (v.contains("\n") | v.contains("\r")) {
+ throw new ConfigException("Parmeter '" + pi.getID() +
+ "' cannot contain a newline.",
+ " Change the value of '" + pi.getID() +
+ " so that it does not contain any newlines");
+ }
+ }
+
+ // exe bundlers trim the copyright to 100 characters,
+ // tell them this will happen
+ if (COPYRIGHT.fetchFrom(p).length() > 100) {
+ throw new ConfigException(
+ getString("error.copyright-is-too-long"),
+ getString("error.copyright-is-too-long.advice"));
+ }
+
+ // validate license file, if used, exists in the proper place
+ if (p.containsKey(LICENSE_FILE.getID())) {
+ List<RelativeFileSet> appResourcesList =
+ APP_RESOURCES_LIST.fetchFrom(p);
+ for (String license : LICENSE_FILE.fetchFrom(p)) {
+ boolean found = false;
+ for (RelativeFileSet appResources : appResourcesList) {
+ found = found || appResources.contains(license);
+ }
+ if (!found) {
+ throw new ConfigException(
+ getString("error.license-missing"),
+ MessageFormat.format(getString(
+ "error.license-missing.advice"), license));
+ }
+ }
+ }
+//
+// if (SERVICE_HINT.fetchFrom(p)) {
+// SERVICE_BUNDLER.fetchFrom(p).validate(p);
+// }
+
+ double innoVersion = findToolVersion(
+ TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p));
+
+ //Inno Setup 5+ is required
+ double minVersion = 5.0f;
+
+ if (innoVersion < minVersion) {
+ Log.info(MessageFormat.format(
+ getString("message.tool-wrong-version"),
+ TOOL_INNO_SETUP_COMPILER, innoVersion, minVersion));
+ throw new ConfigException(
+ getString("error.iscc-not-found"),
+ getString("error.iscc-not-found.advice"));
+ }
+
+ /********* validate bundle parameters *************/
+
+ // only one mime type per association, at least one file extension
+ List<Map<String, ? super Object>> associations =
+ FILE_ASSOCIATIONS.fetchFrom(p);
+ if (associations != null) {
+ for (int i = 0; i < associations.size(); i++) {
+ Map<String, ? super Object> assoc = associations.get(i);
+ List<String> mimes = FA_CONTENT_TYPE.fetchFrom(assoc);
+ if (mimes.size() > 1) {
+ throw new ConfigException(MessageFormat.format(
+ getString("error.too-many-content-"
+ + "types-for-file-association"), i),
+ getString("error.too-many-content-"
+ + "types-for-file-association.advice"));
+ }
+ }
+ }
+
+ // validate license file, if used, exists in the proper place
+ if (p.containsKey(LICENSE_FILE.getID())) {
+ List<RelativeFileSet> appResourcesList =
+ APP_RESOURCES_LIST.fetchFrom(p);
+ for (String license : LICENSE_FILE.fetchFrom(p)) {
+ boolean found = false;
+ for (RelativeFileSet appResources : appResourcesList) {
+ found = found || appResources.contains(license);
+ }
+ if (!found) {
+ throw new ConfigException(
+ MessageFormat.format(getString(
+ "error.license-missing"), license),
+ MessageFormat.format(getString(
+ "error.license-missing.advice"), license));
+ }
+ }
+ }
+
+ return true;
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof ConfigException) {
+ throw (ConfigException) re.getCause();
+ } else {
+ throw new ConfigException(re);
+ }
+ }
+ }
+
+ private boolean prepareProto(Map<String, ? super Object> p)
+ throws IOException {
+ File appImage = StandardBundlerParam.getPredefinedAppImage(p);
+ File appDir = null;
+
+ // we either have an application image or need to build one
+ if (appImage != null) {
+ appDir = new File(
+ EXE_IMAGE_DIR.fetchFrom(p), APP_NAME.fetchFrom(p));
+ // copy everything from appImage dir into appDir/name
+ IOUtils.copyRecursive(appImage.toPath(), appDir.toPath());
+ } else {
+ appDir = APP_BUNDLER.fetchFrom(p).doBundle(p,
+ EXE_IMAGE_DIR.fetchFrom(p), true);
+ }
+
+ if (appDir == null) {
+ return false;
+ }
+
+ p.put(WIN_APP_IMAGE.getID(), appDir);
+
+ List<String> licenseFiles = LICENSE_FILE.fetchFrom(p);
+ if (licenseFiles != null) {
+ // need to copy license file to the root of win.app.image
+ outerLoop:
+ for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(p)) {
+ for (String s : licenseFiles) {
+ if (rfs.contains(s)) {
+ File lfile = new File(rfs.getBaseDirectory(), s);
+ File destFile = new File(appDir, lfile.getName());
+ IOUtils.copyFile(lfile, destFile);
+ ensureByMutationFileIsRTF(destFile);
+ break outerLoop;
+ }
+ }
+ }
+ }
+
+ // copy file association icons
+ List<Map<String, ? super Object>> fileAssociations =
+ FILE_ASSOCIATIONS.fetchFrom(p);
+
+ for (Map<String, ? super Object> fa : fileAssociations) {
+ File icon = FA_ICON.fetchFrom(fa); // TODO FA_ICON_ICO
+ if (icon == null) {
+ continue;
+ }
+
+ File faIconFile = new File(appDir, icon.getName());
+
+ if (icon.exists()) {
+ try {
+ IOUtils.copyFile(icon, faIconFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+//
+// if (SERVICE_HINT.fetchFrom(p)) {
+// // copies the service launcher to the app root folder
+// appDir = SERVICE_BUNDLER.fetchFrom(p).doBundle(
+// p, appOutputDir, true);
+// if (appDir == null) {
+// return false;
+// }
+// }
+ return true;
+ }
+
+ public File bundle(Map<String, ? super Object> p, File outdir) {
+ if (!outdir.isDirectory() && !outdir.mkdirs()) {
+ throw new RuntimeException(MessageFormat.format(
+ getString("error.cannot-create-output-dir"),
+ outdir.getAbsolutePath()));
+ }
+ if (!outdir.canWrite()) {
+ throw new RuntimeException(MessageFormat.format(
+ getString("error.cannot-write-to-output-dir"),
+ outdir.getAbsolutePath()));
+ }
+
+ if (WindowsDefender.isThereAPotentialWindowsDefenderIssue()) {
+ Log.info(MessageFormat.format(
+ getString("message.potential.windows.defender.issue"),
+ WindowsDefender.getUserTempDirectory()));
+ }
+
+ // validate we have valid tools before continuing
+ String iscc = TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p);
+ if (iscc == null || !new File(iscc).isFile()) {
+ Log.info(getString("error.iscc-not-found"));
+ Log.info(MessageFormat.format(
+ getString("message.iscc-file-string"), iscc));
+ return null;
+ }
+
+ File imageDir = EXE_IMAGE_DIR.fetchFrom(p);
+ try {
+ imageDir.mkdirs();
+
+ boolean menuShortcut = MENU_HINT.fetchFrom(p);
+ boolean desktopShortcut = SHORTCUT_HINT.fetchFrom(p);
+ if (!menuShortcut && !desktopShortcut) {
+ // both can not be false - user will not find the app
+ Log.verbose(getString("message.one-shortcut-required"));
+ p.put(MENU_HINT.getID(), true);
+ }
+
+ if (prepareProto(p) && prepareProjectConfig(p)) {
+ File configScript = getConfig_Script(p);
+ if (configScript.exists()) {
+ Log.info(MessageFormat.format(
+ getString("message.running-wsh-script"),
+ configScript.getAbsolutePath()));
+ IOUtils.run("wscript", configScript, VERBOSE.fetchFrom(p));
+ }
+ return buildEXE(p, outdir);
+ }
+ return null;
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ return null;
+ } finally {
+ try {
+ if (VERBOSE.fetchFrom(p)) {
+ saveConfigFiles(p);
+ }
+ if (imageDir != null && !Log.isDebug()) {
+ IOUtils.deleteRecursive(imageDir);
+ } else if (imageDir != null) {
+ Log.info(MessageFormat.format(
+ getString("message.debug-working-directory"),
+ imageDir.getAbsolutePath()));
+ }
+ } catch (IOException ex) {
+ // noinspection ReturnInsideFinallyBlock
+ Log.debug(ex.getMessage());
+ return null;
+ }
+ }
+ }
+
+ // name of post-image script
+ private File getConfig_Script(Map<String, ? super Object> p) {
+ return new File(EXE_IMAGE_DIR.fetchFrom(p),
+ APP_NAME.fetchFrom(p) + "-post-image.wsf");
+ }
+
+ protected void saveConfigFiles(Map<String, ? super Object> p) {
+ try {
+ File configRoot = CONFIG_ROOT.fetchFrom(p);
+ if (getConfig_ExeProjectFile(p).exists()) {
+ IOUtils.copyFile(getConfig_ExeProjectFile(p),
+ new File(configRoot,
+ getConfig_ExeProjectFile(p).getName()));
+ }
+ if (getConfig_Script(p).exists()) {
+ IOUtils.copyFile(getConfig_Script(p),
+ new File(configRoot,
+ getConfig_Script(p).getName()));
+ }
+ if (getConfig_SmallInnoSetupIcon(p).exists()) {
+ IOUtils.copyFile(getConfig_SmallInnoSetupIcon(p),
+ new File(configRoot,
+ getConfig_SmallInnoSetupIcon(p).getName()));
+ }
+ Log.info(MessageFormat.format(
+ getString("message.config-save-location"),
+ configRoot.getAbsolutePath()));
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+
+ private String getAppIdentifier(Map<String, ? super Object> p) {
+ String nm = IDENTIFIER.fetchFrom(p);
+
+ //limitation of innosetup
+ if (nm.length() > 126)
+ nm = nm.substring(0, 126);
+
+ return nm;
+ }
+
+
+ private String getLicenseFile(Map<String, ? super Object> p) {
+ List<String> licenseFiles = LICENSE_FILE.fetchFrom(p);
+ if (licenseFiles == null || licenseFiles.isEmpty()) {
+ return "";
+ } else {
+ return licenseFiles.get(0);
+ }
+ }
+
+ void validateValueAndPut(Map<String, String> data, String key,
+ BundlerParamInfo<String> param,
+ Map<String, ? super Object> p) throws IOException {
+ String value = param.fetchFrom(p);
+ if (value.contains("\r") || value.contains("\n")) {
+ throw new IOException("Configuration Parameter " +
+ param.getID() + " cannot contain multiple lines of text");
+ }
+ data.put(key, innosetupEscape(value));
+ }
+
+ private String innosetupEscape(String value) {
+ if (value.contains("\"") || !value.trim().equals(value)) {
+ value = "\"" + value.replace("\"", "\"\"") + "\"";
+ }
+ return value;
+ }
+
+ boolean prepareMainProjectFile(Map<String, ? super Object> p)
+ throws IOException {
+ Map<String, String> data = new HashMap<>();
+ data.put("PRODUCT_APP_IDENTIFIER",
+ innosetupEscape(getAppIdentifier(p)));
+
+ validateValueAndPut(data, "APPLICATION_NAME", APP_NAME, p);
+
+ validateValueAndPut(data, "APPLICATION_VENDOR", VENDOR, p);
+ validateValueAndPut(data, "APPLICATION_VERSION", VERSION, p);
+ validateValueAndPut(data, "INSTALLER_FILE_NAME",
+ INSTALLER_FILE_NAME, p);
+
+ data.put("APPLICATION_LAUNCHER_FILENAME",
+ innosetupEscape(WinAppBundler.getLauncherName(p)));
+
+ data.put("APPLICATION_DESKTOP_SHORTCUT",
+ SHORTCUT_HINT.fetchFrom(p) ? "returnTrue" : "returnFalse");
+ data.put("APPLICATION_MENU_SHORTCUT",
+ MENU_HINT.fetchFrom(p) ? "returnTrue" : "returnFalse");
+ validateValueAndPut(data, "APPLICATION_GROUP", MENU_GROUP, p);
+ validateValueAndPut(data, "APPLICATION_COMMENTS",
+ TITLE, p); // TODO this seems strange, at least in name
+ validateValueAndPut(data, "APPLICATION_COPYRIGHT", COPYRIGHT, p);
+
+ data.put("APPLICATION_LICENSE_FILE",
+ innosetupEscape(getLicenseFile(p)));
+ data.put("DISABLE_DIR_PAGE",
+ INSTALLDIR_CHOOSER.fetchFrom(p) ? "No" : "Yes");
+
+ Boolean isSystemWide = EXE_SYSTEM_WIDE.fetchFrom(p);
+
+ if (isSystemWide) {
+ data.put("APPLICATION_INSTALL_ROOT", "{pf}");
+ data.put("APPLICATION_INSTALL_PRIVILEGE", "admin");
+ } else {
+ data.put("APPLICATION_INSTALL_ROOT", "{localappdata}");
+ data.put("APPLICATION_INSTALL_PRIVILEGE", "lowest");
+ }
+
+ if (BIT_ARCH_64.fetchFrom(p)) {
+ data.put("ARCHITECTURE_BIT_MODE", "x64");
+ } else {
+ data.put("ARCHITECTURE_BIT_MODE", "");
+ }
+//
+// if (SERVICE_HINT.fetchFrom(p)) {
+// data.put("RUN_FILENAME",
+// innosetupEscape(WinServiceBundler.getAppSvcName(p)));
+// } else {
+ validateValueAndPut(data, "RUN_FILENAME", APP_NAME, p);
+// }
+
+ validateValueAndPut(data, "APPLICATION_DESCRIPTION",
+ DESCRIPTION, p);
+
+// data.put("APPLICATION_SERVICE",
+// SERVICE_HINT.fetchFrom(p) ? "returnTrue" : "returnFalse");
+ data.put("APPLICATION_SERVICE", "returnFalse");
+
+// data.put("APPLICATION_NOT_SERVICE",
+// SERVICE_HINT.fetchFrom(p) ? "returnFalse" : "returnTrue");
+ data.put("APPLICATION_NOT_SERVICE", "returnFalse");
+
+// data.put("APPLICATION_APP_CDS_INSTALL",
+// (UNLOCK_COMMERCIAL_FEATURES.fetchFrom(p) &&
+// ENABLE_APP_CDS.fetchFrom(p) &&
+// ("install".equals(APP_CDS_CACHE_MODE.fetchFrom(p)) ||
+// "auto+install".equals(APP_CDS_CACHE_MODE.fetchFrom(p))))
+// ? "returnTrue" : "returnFalse");
+ data.put("APPLICATION_APP_CDS_INSTALL", "returnFalse");
+
+// data.put("START_ON_INSTALL",
+// START_ON_INSTALL.fetchFrom(p) ? "-startOnInstall" : "");
+// data.put("STOP_ON_UNINSTALL",
+// STOP_ON_UNINSTALL.fetchFrom(p) ? "-stopOnUninstall" : "");
+// data.put("RUN_AT_STARTUP",
+// RUN_AT_STARTUP.fetchFrom(p) ? "-runAtStartup" : "");
+ data.put("START_ON_INSTALL", "");
+ data.put("STOP_ON_UNINSTALL", "");
+ data.put("RUN_AT_STARTUP", "");
+
+ StringBuilder secondaryLaunchersCfg = new StringBuilder();
+ for (Map<String, ? super Object>
+ launcher : SECONDARY_LAUNCHERS.fetchFrom(p)) {
+ String application_name = APP_NAME.fetchFrom(launcher);
+ if (MENU_HINT.fetchFrom(launcher)) {
+ // Name: "{group}\APPLICATION_NAME";
+ // Filename: "{app}\APPLICATION_NAME.exe";
+ // IconFilename: "{app}\APPLICATION_NAME.ico"
+ secondaryLaunchersCfg.append("Name: \"{group}\\");
+ secondaryLaunchersCfg.append(application_name);
+ secondaryLaunchersCfg.append("\"; Filename: \"{app}\\");
+ secondaryLaunchersCfg.append(application_name);
+ secondaryLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\");
+ secondaryLaunchersCfg.append(application_name);
+ secondaryLaunchersCfg.append(".ico\"\r\n");
+ }
+ if (SHORTCUT_HINT.fetchFrom(launcher)) {
+ // Name: "{commondesktop}\APPLICATION_NAME";
+ // Filename: "{app}\APPLICATION_NAME.exe";
+ // IconFilename: "{app}\APPLICATION_NAME.ico"
+ secondaryLaunchersCfg.append("Name: \"{commondesktop}\\");
+ secondaryLaunchersCfg.append(application_name);
+ secondaryLaunchersCfg.append("\"; Filename: \"{app}\\");
+ secondaryLaunchersCfg.append(application_name);
+ secondaryLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\");
+ secondaryLaunchersCfg.append(application_name);
+ secondaryLaunchersCfg.append(".ico\"\r\n");
+ }
+ }
+ data.put("SECONDARY_LAUNCHERS", secondaryLaunchersCfg.toString());
+
+ StringBuilder registryEntries = new StringBuilder();
+ String regName = APP_REGISTRY_NAME.fetchFrom(p);
+ List<Map<String, ? super Object>> fetchFrom =
+ FILE_ASSOCIATIONS.fetchFrom(p);
+ for (int i = 0; i < fetchFrom.size(); i++) {
+ Map<String, ? super Object> fileAssociation = fetchFrom.get(i);
+ String description = FA_DESCRIPTION.fetchFrom(fileAssociation);
+ File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICO
+
+ List<String> extensions = FA_EXTENSIONS.fetchFrom(fileAssociation);
+ String entryName = regName + "File";
+ if (i > 0) {
+ entryName += "." + i;
+ }
+
+ if (extensions == null) {
+ Log.info(getString(
+ "message.creating-association-with-null-extension"));
+ } else {
+ for (String ext : extensions) {
+ if (isSystemWide) {
+ // "Root: HKCR; Subkey: \".myp\";
+ // ValueType: string; ValueName: \"\";
+ // ValueData: \"MyProgramFile\";
+ // Flags: uninsdeletevalue"
+ registryEntries.append("Root: HKCR; Subkey: \".")
+ .append(ext)
+ .append("\"; ValueType: string; ValueName: \"\"; ValueData: \"")
+ .append(entryName)
+ .append("\"; Flags: uninsdeletevalue\r\n");
+ } else {
+ registryEntries.append("Root: HKCU; Subkey: \"Software\\Classes\\.")
+ .append(ext)
+ .append("\"; ValueType: string; ValueName: \"\"; ValueData: \"")
+ .append(entryName)
+ .append("\"; Flags: uninsdeletevalue\r\n");
+ }
+ }
+ }
+
+ if (extensions != null && !extensions.isEmpty()) {
+ String ext = extensions.get(0);
+ List<String> mimeTypes = FA_CONTENT_TYPE.fetchFrom(fileAssociation);
+ for (String mime : mimeTypes) {
+ if (isSystemWide) {
+ // "Root: HKCR;
+ // Subkey: HKCR\\Mime\\Database\\Content Type\\application/chaos;
+ // ValueType: string;
+ // ValueName: Extension;
+ // ValueData: .chaos;
+ // Flags: uninsdeletevalue"
+ registryEntries.append("Root: HKCR; Subkey: " +
+ "\"Mime\\Database\\Content Type\\")
+ .append(mime)
+ .append("\"; ValueType: string; ValueName: " +
+ "\"Extension\"; ValueData: \".")
+ .append(ext)
+ .append("\"; Flags: uninsdeletevalue\r\n");
+ } else {
+ registryEntries.append(
+ "Root: HKCU; Subkey: \"Software\\" +
+ "Classes\\Mime\\Database\\Content Type\\")
+ .append(mime)
+ .append("\"; ValueType: string; " +
+ "ValueName: \"Extension\"; ValueData: \".")
+ .append(ext)
+ .append("\"; Flags: uninsdeletevalue\r\n");
+ }
+ }
+ }
+
+ if (isSystemWide) {
+ // "Root: HKCR;
+ // Subkey: \"MyProgramFile\";
+ // ValueType: string;
+ // ValueName: \"\";
+ // ValueData: \"My Program File\";
+ // Flags: uninsdeletekey"
+ registryEntries.append("Root: HKCR; Subkey: \"")
+ .append(entryName)
+ .append(
+ "\"; ValueType: string; ValueName: \"\"; ValueData: \"")
+ .append(description)
+ .append("\"; Flags: uninsdeletekey\r\n");
+ } else {
+ registryEntries.append(
+ "Root: HKCU; Subkey: \"Software\\Classes\\")
+ .append(entryName)
+ .append(
+ "\"; ValueType: string; ValueName: \"\"; ValueData: \"")
+ .append(description)
+ .append("\"; Flags: uninsdeletekey\r\n");
+ }
+
+ if (icon != null && icon.exists()) {
+ if (isSystemWide) {
+ // "Root: HKCR;
+ // Subkey: \"MyProgramFile\\DefaultIcon\";
+ // ValueType: string;
+ // ValueName: \"\";
+ // ValueData: \"{app}\\MYPROG.EXE,0\"\n" +
+ registryEntries.append("Root: HKCR; Subkey: \"")
+ .append(entryName)
+ .append("\\DefaultIcon\"; ValueType: string; " +
+ "ValueName: \"\"; ValueData: \"{app}\\")
+ .append(icon.getName())
+ .append("\"\r\n");
+ } else {
+ registryEntries.append(
+ "Root: HKCU; Subkey: \"Software\\Classes\\")
+ .append(entryName)
+ .append("\\DefaultIcon\"; ValueType: string; " +
+ "ValueName: \"\"; ValueData: \"{app}\\")
+ .append(icon.getName())
+ .append("\"\r\n");
+ }
+ }
+
+ if (isSystemWide) {
+ // "Root: HKCR;
+ // Subkey: \"MyProgramFile\\shell\\open\\command\";
+ // ValueType: string;
+ // ValueName: \"\";
+ // ValueData: \"\"\"{app}\\MYPROG.EXE\"\" \"\"%1\"\"\"\n"
+ registryEntries.append("Root: HKCR; Subkey: \"")
+ .append(entryName)
+ .append("\\shell\\open\\command\"; ValueType: " +
+ "string; ValueName: \"\"; ValueData: \"\"\"{app}\\")
+ .append(APP_NAME.fetchFrom(p))
+ .append("\"\" \"\"%1\"\"\"\r\n");
+ } else {
+ registryEntries.append(
+ "Root: HKCU; Subkey: \"Software\\Classes\\")
+ .append(entryName)
+ .append("\\shell\\open\\command\"; ValueType: " +
+ "string; ValueName: \"\"; ValueData: \"\"\"{app}\\")
+ .append(APP_NAME.fetchFrom(p))
+ .append("\"\" \"\"%1\"\"\"\r\n");
+ }
+ }
+ if (registryEntries.length() > 0) {
+ data.put("FILE_ASSOCIATIONS",
+ "ChangesAssociations=yes\r\n\r\n[Registry]\r\n" +
+ registryEntries.toString());
+ } else {
+ data.put("FILE_ASSOCIATIONS", "");
+ }
+
+ // TODO - alternate template for JRE installer
+ String iss = Arguments.CREATE_JRE_INSTALLER.fetchFrom(p) ?
+ DEFAULT_EXE_PROJECT_TEMPLATE : DEFAULT_EXE_PROJECT_TEMPLATE;
+
+ Writer w = new BufferedWriter(new FileWriter(
+ getConfig_ExeProjectFile(p)));
+
+ String content = preprocessTextResource(
+ WinAppBundler.WIN_BUNDLER_PREFIX +
+ getConfig_ExeProjectFile(p).getName(),
+ getString("resource.inno-setup-project-file"),
+ iss, data, VERBOSE.fetchFrom(p),
+ DROP_IN_RESOURCES_ROOT.fetchFrom(p));
+ w.write(content);
+ w.close();
+ return true;
+ }
+
+ private final static String DEFAULT_INNO_SETUP_ICON =
+ "icon_inno_setup.bmp";
+
+ private boolean prepareProjectConfig(Map<String, ? super Object> p)
+ throws IOException {
+ prepareMainProjectFile(p);
+
+ //prepare installer icon
+ File iconTarget = getConfig_SmallInnoSetupIcon(p);
+ fetchResource(WinAppBundler.WIN_BUNDLER_PREFIX + iconTarget.getName(),
+ getString("resource.setup-icon"),
+ DEFAULT_INNO_SETUP_ICON,
+ iconTarget,
+ VERBOSE.fetchFrom(p),
+ DROP_IN_RESOURCES_ROOT.fetchFrom(p));
+
+ fetchResource(WinAppBundler.WIN_BUNDLER_PREFIX +
+ getConfig_Script(p).getName(),
+ getString("resource.post-install-script"),
+ (String) null,
+ getConfig_Script(p),
+ VERBOSE.fetchFrom(p),
+ DROP_IN_RESOURCES_ROOT.fetchFrom(p));
+ return true;
+ }
+
+ private File getConfig_SmallInnoSetupIcon(
+ Map<String, ? super Object> p) {
+ return new File(EXE_IMAGE_DIR.fetchFrom(p),
+ APP_NAME.fetchFrom(p) + "-setup-icon.bmp");
+ }
+
+ private File getConfig_ExeProjectFile(Map<String, ? super Object> p) {
+ return new File(EXE_IMAGE_DIR.fetchFrom(p),
+ APP_NAME.fetchFrom(p) + ".iss");
+ }
+
+
+ private File buildEXE(Map<String, ? super Object> p, File outdir)
+ throws IOException {
+ Log.verbose(MessageFormat.format(
+ getString("message.outputting-to-location"),
+ outdir.getAbsolutePath()));
+
+ outdir.mkdirs();
+
+ //run candle
+ ProcessBuilder pb = new ProcessBuilder(
+ TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p),
+ "/o"+outdir.getAbsolutePath(),
+ getConfig_ExeProjectFile(p).getAbsolutePath());
+ pb = pb.directory(EXE_IMAGE_DIR.fetchFrom(p));
+ IOUtils.exec(pb, VERBOSE.fetchFrom(p));
+
+ Log.info(MessageFormat.format(
+ getString("message.output-location"),
+ outdir.getAbsolutePath()));
+
+ // presume the result is the ".exe" file with the newest modified time
+ // not the best solution, but it is the most reliable
+ File result = null;
+ long lastModified = 0;
+ File[] list = outdir.listFiles();
+ if (list != null) {
+ for (File f : list) {
+ if (f.getName().endsWith(".exe") &&
+ f.lastModified() > lastModified) {
+ result = f;
+ lastModified = f.lastModified();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static void ensureByMutationFileIsRTF(File f) {
+ if (f == null || !f.isFile()) return;
+
+ try {
+ boolean existingLicenseIsRTF = false;
+
+ try (FileInputStream fin = new FileInputStream(f)) {
+ byte[] firstBits = new byte[7];
+
+ if (fin.read(firstBits) == firstBits.length) {
+ String header = new String(firstBits);
+ existingLicenseIsRTF = "{\\rtf1\\".equals(header);
+ }
+ }
+
+ if (!existingLicenseIsRTF) {
+ List<String> oldLicense = Files.readAllLines(f.toPath());
+ try (Writer w = Files.newBufferedWriter(
+ f.toPath(), Charset.forName("Windows-1252"))) {
+ w.write("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033"
+ + "{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}}\n"
+ + "\\viewkind4\\uc1\\pard\\sa200\\sl276"
+ + "\\slmult1\\lang9\\fs20 ");
+ oldLicense.forEach(l -> {
+ try {
+ for (char c : l.toCharArray()) {
+ if (c < 0x10) {
+ w.write("\\'0");
+ w.write(Integer.toHexString(c));
+ } else if (c > 0xff) {
+ w.write("\\ud");
+ w.write(Integer.toString(c));
+ w.write("?");
+ } else if ((c < 0x20) || (c >= 0x80) ||
+ (c == 0x5C) || (c == 0x7B) ||
+ (c == 0x7D)) {
+ w.write("\\'");
+ w.write(Integer.toHexString(c));
+ } else {
+ w.write(c);
+ }
+ }
+ if (l.length() < 1) {
+ w.write("\\par");
+ } else {
+ w.write(" ");
+ }
+ w.write("\r\n");
+ } catch (IOException e) {
+ Log.verbose(e);
+ }
+ });
+ w.write("}\r\n");
+ }
+ }
+ } catch (IOException e) {
+ Log.verbose(e);
+ }
+ }
+
+ private static String getString(String key)
+ throws MissingResourceException {
+ return I18N.getString(key);
+ }
+}