src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java
branchJDK-8200758-branch
changeset 57404 a477b26bf888
parent 57397 89549ecec1c7
child 57407 2c14fbeff1dc
--- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java	Thu Jun 13 19:04:29 2019 -0400
+++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiBundler.java	Thu Jun 13 19:32:24 2019 -0400
@@ -28,12 +28,66 @@
 import java.io.*;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.text.MessageFormat;
 import java.util.*;
 import java.util.regex.Pattern;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
 import static jdk.jpackage.internal.WindowsBundlerParam.*;
 
+/**
+ * WinMsiBundler
+ *
+ * Produces .msi installer from application image. Uses WiX Toolkit to build
+ * .msi installer.
+ * <p>
+ * {@link #execute} method creates a number of source files with the description
+ * of installer to be processed by WiX tools. Generated source files are stored
+ * in "config" subdirectory next to "app" subdirectory in the root work
+ * directory. The following WiX source files are generated:
+ * <ul>
+ * <li>main.wxs. Main source file with the installer description
+ * <li>bundle.wxi. Source file with application and Java run-time directory tree
+ * description. This source file is included from main.wxs
+ * <li>icons.wxi. Source file with the list of icons used by the application.
+ * This source file is included from main.wxs
+ * </ul>
+ * <p>
+ * main.wxs file is a copy of main.wxs resource from
+ * jdk.jpackage.internal.resources package. It is parametrized with the
+ * following WiX variables:
+ * <ul>
+ * <li>JpAppName. Name of the application. Set to the value of --name command
+ * line option
+ * <li>JpAppVersion. Version of the application. Set to the value of
+ * --app-version command line option
+ * <li>JpAppVendor. Vendor of the application. Set to the value of --vendor
+ * command line option
+ * <li>JpAppDescription. Description of the application. Set to the value of
+ * --description command line option
+ * <li>JpProductCode. Set to product code UUID of the application. Random value
+ * generated by jpackage every time {@link #execute} method is called
+ * <li>JpProductUpgradeCode. Set to upgrade code UUID of the application. Random
+ * value generated by jpackage every time {@link #execute} method is called if
+ * --win-upgrade-uuid command line option is not specified. Otherwise this
+ * variable is set to the value of --win-upgrade-uuid command line option
+ * <li>JpAllowDowngrades. Set to "yes" if --win-upgrade-uuid command line option
+ * was specified. Undefined otherwise
+ * <li>JpLicenseRtf. Set to the value of --license-file command line option.
+ * Undefined is --license-file command line option was not specified
+ * <li>JpInstallDirChooser. Set to "yes" if --win-dir-chooser command line
+ * option was specified. Undefined otherwise
+ * <li>JpConfigDir. Absolute path to the directory with generated WiX source
+ * files.
+ * <li>JpIsSystemWide. Set to "yes" if --win-per-user-install command line
+ * option was not specified. Undefined otherwise
+ * <li>JpWixVersion36OrNewer. Set to "yes" if WiX Toolkit v3.6 or newer is used.
+ * Undefined otherwise
+ * </ul>
+ */
 public class WinMsiBundler  extends AbstractBundler {
 
     private static final ResourceBundle I18N = ResourceBundle.getBundle(
@@ -403,6 +457,7 @@
             File destFile = new File(CONFIG_ROOT.fetchFrom(params),
                     lfile.getName());
             IOUtils.copyFile(lfile, destFile);
+            destFile.setWritable(true);
             ensureByMutationFileIsRTF(destFile);
         }
 
@@ -452,6 +507,8 @@
             throw new PackagerException("error.no-wix-tools");
         }
 
+        Map<String, String> wixVars = null;
+
         File imageDir = MSI_IMAGE_DIR.fetchFrom(params);
         try {
             imageDir.mkdirs();
@@ -464,8 +521,10 @@
                 params.put(MENU_HINT.getID(), true);
             }
 
-            if (prepareProto(params) && prepareWiXConfig(params)
-                    && prepareBasicProjectConfig(params)) {
+            prepareBasicProjectConfig(params);
+            if (prepareProto(params)) {
+                wixVars = prepareWiXConfig(params);
+
                 File configScriptSrc = getConfig_Script(params);
                 if (configScriptSrc.exists()) {
                     // we need to be running post script in the image folder
@@ -482,7 +541,7 @@
                             configScript.getAbsolutePath()));
                     IOUtils.run("wscript", configScript);
                 }
-                return buildMSI(params, outdir);
+                return buildMSI(params, wixVars, outdir);
             }
             return null;
         } catch (IOException ex) {
@@ -497,7 +556,7 @@
                 APP_NAME.fetchFrom(params) + "-post-image.wsf");
     }
 
-    private boolean prepareBasicProjectConfig(
+    private void prepareBasicProjectConfig(
         Map<String, ? super Object> params) throws IOException {
         fetchResource(getConfig_Script(params).getName(),
                 I18N.getString("resource.post-install-script"),
@@ -505,15 +564,74 @@
                 getConfig_Script(params),
                 VERBOSE.fetchFrom(params),
                 RESOURCE_DIR.fetchFrom(params));
-        return true;
     }
 
-    private String relativePath(File basedir, File file) {
+    private static String relativePath(File basedir, File file) {
         return file.getAbsolutePath().substring(
                 basedir.getAbsolutePath().length() + 1);
     }
 
-    boolean prepareMainProjectFile(
+    private void prepareIconsFile(
+            Map<String, ? super Object> params) throws IOException {
+
+        File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
+
+        List<Map<String, ? super Object>> addLaunchers =
+                ADD_LAUNCHERS.fetchFrom(params);
+
+        XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
+        try (Writer w = new BufferedWriter(new FileWriter(new File(
+                CONFIG_ROOT.fetchFrom(params), "icons.wxi")))) {
+            XMLStreamWriter xml = xmlFactory.createXMLStreamWriter(w);
+
+            xml.writeStartDocument();
+            xml.writeStartElement("Include");
+
+            File launcher = new File(imageRootDir, WinAppBundler.getLauncherName(params));
+            if (launcher.exists()) {
+                String iconPath = launcher.getAbsolutePath().replace(".exe", ".ico");
+                if (MENU_HINT.fetchFrom(params)) {
+                    xml.writeStartElement("Icon");
+                    xml.writeAttribute("Id", "StartMenuIcon.exe");
+                    xml.writeAttribute("SourceFile", iconPath);
+                    xml.writeEndElement();
+                }
+                if (SHORTCUT_HINT.fetchFrom(params)) {
+                    xml.writeStartElement("Icon");
+                    xml.writeAttribute("Id", "DesktopIcon.exe");
+                    xml.writeAttribute("SourceFile", iconPath);
+                    xml.writeEndElement();
+                }
+            }
+
+            for (int i = 0; i < addLaunchers.size(); i++) {
+                Map<String, ? super Object> sl = addLaunchers.get(i);
+                if (SHORTCUT_HINT.fetchFrom(sl) || MENU_HINT.fetchFrom(sl)) {
+                    File addLauncher = new File(imageRootDir,
+                            WinAppBundler.getLauncherName(sl));
+                    String addLauncherPath
+                            = relativePath(imageRootDir, addLauncher);
+                    String addLauncherIconPath
+                            = addLauncherPath.replace(".exe", ".ico");
+
+                    xml.writeStartElement("Icon");
+                    xml.writeAttribute("Id", "Launcher" + i + ".exe");
+                    xml.writeAttribute("SourceFile", addLauncherIconPath);
+                    xml.writeEndElement();
+                }
+            }
+
+            xml.writeEndElement();
+            xml.writeEndDocument();
+            xml.flush();
+            xml.close();
+        } catch (XMLStreamException ex) {
+            Log.verbose(ex);
+            throw new IOException(ex);
+        }
+    }
+
+    Map<String, String> prepareMainProjectFile(
             Map<String, ? super Object> params) throws IOException {
         Map<String, String> data = new HashMap<>();
 
@@ -528,193 +646,65 @@
         // Upgrade guid is important to decide whether it is an upgrade of
         // installed app.  I.e. we need it to be the same for
         // 2 different versions of app if possible
-        data.put("PRODUCT_GUID", productGUID.toString());
-        data.put("PRODUCT_UPGRADE_GUID",
+        data.put("JpProductCode", productGUID.toString());
+        data.put("JpProductUpgradeCode",
                 UPGRADE_UUID.fetchFrom(params).toString());
-        data.put("UPGRADE_BLOCK", getUpgradeBlock(params));
 
-        data.put("APPLICATION_NAME", APP_NAME.fetchFrom(params));
-        data.put("APPLICATION_DESCRIPTION", DESCRIPTION.fetchFrom(params));
-        data.put("APPLICATION_VENDOR", VENDOR.fetchFrom(params));
-        data.put("APPLICATION_VERSION", PRODUCT_VERSION.fetchFrom(params));
-
-        // WinAppBundler will add application folder again => step out
-        File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
-        File launcher = new File(imageRootDir,
-                WinAppBundler.getLauncherName(params));
+        if (!UPGRADE_UUID.getIsDefaultValue()) {
+            data.put("JpAllowDowngrades", "yes");
+        }
 
-        String launcherPath = relativePath(imageRootDir, launcher);
-        data.put("APPLICATION_LAUNCHER", launcherPath);
-
-        String iconPath = launcherPath.replace(".exe", ".ico");
-
-        data.put("APPLICATION_ICON", iconPath);
+        if (CAN_USE_WIX36.fetchFrom(params)) {
+            data.put("JpWixVersion36OrNewer", "yes");
+        }
 
-        data.put("REGISTRY_ROOT", getRegistryRoot(params));
+        data.put("JpAppName", APP_NAME.fetchFrom(params));
+        data.put("JpAppDescription", DESCRIPTION.fetchFrom(params));
+        data.put("JpAppVendor", VENDOR.fetchFrom(params));
+        data.put("JpAppVersion", PRODUCT_VERSION.fetchFrom(params));
 
-        boolean canUseWix36Features = CAN_USE_WIX36.fetchFrom(params);
-        data.put("WIX36_ONLY_START",
-                canUseWix36Features ? "" : "<!--");
-        data.put("WIX36_ONLY_END",
-                canUseWix36Features ? "" : "-->");
+        data.put("JpConfigDir", CONFIG_ROOT.fetchFrom(params).getAbsolutePath());
+
+        File imageRootDir = WIN_APP_IMAGE.fetchFrom(params);
 
         if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
-            data.put("INSTALL_SCOPE", "perMachine");
-        } else {
-            data.put("INSTALL_SCOPE", "perUser");
+            data.put("JpIsSystemWide", "yes");
         }
 
-        data.put("PLATFORM", "x64");
-        data.put("WIN64", "yes");
-
-        data.put("UI_BLOCK", getUIBlock(params));
+        if (LICENSE_FILE.fetchFrom(params) != null) {
+            data.put("JpLicenseRtf", LICENSE_FILE.fetchFrom(params));
+        }
 
-        // Add CA to check install dir
+        // Copy CA dll to include with installer
         if (INSTALLDIR_CHOOSER.fetchFrom(params)) {
-            data.put("CA_BLOCK", CA_BLOCK);
-            data.put("INVALID_INSTALL_DIR_DLG_BLOCK", INVALID_INSTALL_DIR_DLG_BLOCK);
-        } else {
-            data.put("CA_BLOCK", "");
-            data.put("INVALID_INSTALL_DIR_DLG_BLOCK", "");
+            data.put("JpInstallDirChooser", "yes");
+            String fname = "wixhelper.dll";
+            try (InputStream is = getResourceAsStream(fname)) {
+                Files.copy(is, Paths.get(
+                        CONFIG_ROOT.fetchFrom(params).getAbsolutePath(), fname));
+            }
         }
 
-        List<Map<String, ? super Object>> addLaunchers =
-                ADD_LAUNCHERS.fetchFrom(params);
-
-        StringBuilder addLauncherIcons = new StringBuilder();
-        for (int i = 0; i < addLaunchers.size(); i++) {
-            Map<String, ? super Object> sl = addLaunchers.get(i);
-            // <Icon Id="DesktopIcon.exe" SourceFile="APPLICATION_ICON" />
-            if (SHORTCUT_HINT.fetchFrom(sl) || MENU_HINT.fetchFrom(sl)) {
-                File addLauncher = new File(imageRootDir,
-                        WinAppBundler.getLauncherName(sl));
-                String addLauncherPath =
-                        relativePath(imageRootDir, addLauncher);
-                String addLauncherIconPath =
-                        addLauncherPath.replace(".exe", ".ico");
-
-                addLauncherIcons.append("        <Icon Id=\"Launcher");
-                addLauncherIcons.append(i);
-                addLauncherIcons.append(".exe\" SourceFile=\"");
-                addLauncherIcons.append(addLauncherIconPath);
-                addLauncherIcons.append("\" />\r\n");
+        // Copy l10n files.
+        for (String loc : Arrays.asList("en", "ja", "zh_CN")) {
+            String fname = "MsiInstallerStrings_" + loc + ".wxl";
+            try (InputStream is = getResourceAsStream(fname)) {
+                Files.copy(is, Paths.get(
+                        CONFIG_ROOT.fetchFrom(params).getAbsolutePath(), fname));
             }
         }
-        data.put("ADD_LAUNCHER_ICONS", addLauncherIcons.toString());
-
-        String wxs = StandardBundlerParam.isRuntimeInstaller(params) ?
-                MSI_PROJECT_TEMPLATE_SERVER_JRE : MSI_PROJECT_TEMPLATE;
-
-        try (Writer w = Files.newBufferedWriter(
-                getConfig_ProjectFile(params).toPath())) {
 
-            String content = preprocessTextResource(
-                    getConfig_ProjectFile(params).getName(),
-                    I18N.getString("resource.wix-config-file"),
-                    wxs, data, VERBOSE.fetchFrom(params),
-                    RESOURCE_DIR.fetchFrom(params));
-            w.write(content);
+        try (InputStream is = getResourceAsStream("main.wxs")) {
+            Files.copy(is, Paths.get(
+                    getConfig_ProjectFile(params).getAbsolutePath()));
         }
-        return true;
+
+        return data;
     }
     private int id;
     private int compId;
     private final static String LAUNCHER_ID = "LauncherId";
 
-    private static final String CA_BLOCK =
-            "<Binary Id=\"CustomActionDLL\" SourceFile=\"wixhelper.dll\" />\n" +
-            "<CustomAction Id=\"CHECK_INSTALLDIR\" BinaryKey=\"CustomActionDLL\" " +
-            "DllEntry=\"CheckInstallDir\" />";
-
-    private static final String INVALID_INSTALL_DIR_DLG_BLOCK =
-            "<Dialog Id=\"InvalidInstallDir\" Width=\"300\" Height=\"85\" " +
-            "Title=\"[ProductName] Setup\" NoMinimize=\"yes\">\n" +
-            "<Control Id=\"InvalidInstallDirYes\" Type=\"PushButton\" X=\"100\" Y=\"55\" " +
-            "Width=\"50\" Height=\"15\" Default=\"no\" Cancel=\"no\" Text=\"Yes\">\n" +
-            "<Publish Event=\"NewDialog\" Value=\"VerifyReadyDlg\">1</Publish>\n" +
-            "</Control>\n" +
-            "<Control Id=\"InvalidInstallDirNo\" Type=\"PushButton\" X=\"150\" Y=\"55\" " +
-            "Width=\"50\" Height=\"15\" Default=\"yes\" Cancel=\"yes\" Text=\"No\">\n" +
-            "<Publish Event=\"NewDialog\" Value=\"InstallDirDlg\">1</Publish>\n" +
-            "</Control>\n" +
-            "<Control Id=\"Text\" Type=\"Text\" X=\"25\" Y=\"15\" Width=\"250\" Height=\"30\" " +
-            "TabSkip=\"no\">\n" +
-            "<Text>" + I18N.getString("message.install.dir.exist") + "</Text>\n" +
-            "</Control>\n" +
-            "</Dialog>";
-
-    /**
-     * Overrides the dialog sequence in built-in dialog set "WixUI_InstallDir"
-     * to exclude license dialog
-     */
-    private static final String TWEAK_FOR_EXCLUDING_LICENSE =
-              "     <Publish Dialog=\"WelcomeDlg\" Control=\"Next\""
-            + "              Event=\"NewDialog\" Value=\"InstallDirDlg\""
-            + " Order=\"2\">1</Publish>\n"
-            + "     <Publish Dialog=\"InstallDirDlg\" Control=\"Back\""
-            + "              Event=\"NewDialog\" Value=\"WelcomeDlg\""
-            + " Order=\"2\">1</Publish>\n";
-
-    private static final String CHECK_INSTALL_DLG_CTRL =
-              "     <Publish Dialog=\"InstallDirDlg\" Control=\"Next\""
-            + "              Event=\"DoAction\" Value=\"CHECK_INSTALLDIR\""
-            + " Order=\"3\">1</Publish>\n"
-            + "     <Publish Dialog=\"InstallDirDlg\" Control=\"Next\""
-            + "              Event=\"NewDialog\" Value=\"InvalidInstallDir\""
-            + " Order=\"5\">INSTALLDIR_VALID=\"0\"</Publish>\n"
-            + "     <Publish Dialog=\"InstallDirDlg\" Control=\"Next\""
-            + "              Event=\"NewDialog\" Value=\"VerifyReadyDlg\""
-            + " Order=\"5\">INSTALLDIR_VALID=\"1\"</Publish>\n";
-
-    // Required upgrade element for installers which support major upgrade (when user
-    // specifies --win-upgrade-uuid). We will allow downgrades.
-    private static final String UPGRADE_BLOCK =
-            "<MajorUpgrade AllowDowngrades=\"yes\"/>";
-
-    private String getUpgradeBlock(Map<String, ? super Object> params) {
-        if (UPGRADE_UUID.getIsDefaultValue()) {
-            return "";
-        } else {
-            return UPGRADE_BLOCK;
-        }
-    }
-
-    /**
-     * Creates UI element using WiX built-in dialog sets
-     *     - WixUI_InstallDir/WixUI_Minimal.
-     * The dialog sets are the closest to what we want to implement.
-     *
-     * WixUI_Minimal for license dialog only
-     * WixUI_InstallDir for installdir dialog only or for both
-     * installdir/license dialogs
-     */
-    private String getUIBlock(Map<String, ? super Object> params) throws IOException {
-        String uiBlock = ""; // UI-less element
-
-        // Copy CA dll to include with installer
-        if (INSTALLDIR_CHOOSER.fetchFrom(params)) {
-            File helper = new File(CONFIG_ROOT.fetchFrom(params), "wixhelper.dll");
-            try (InputStream is_lib = getResourceAsStream("wixhelper.dll")) {
-                Files.copy(is_lib, helper.toPath());
-            }
-        }
-
-        if (INSTALLDIR_CHOOSER.fetchFrom(params)) {
-            boolean enableTweakForExcludingLicense =
-                    (getLicenseFile(params) == null);
-            uiBlock = "     <Property Id=\"WIXUI_INSTALLDIR\""
-                    + " Value=\"APPLICATIONFOLDER\" />\n"
-                    + "     <UIRef Id=\"WixUI_InstallDir\" />\n"
-                    + (enableTweakForExcludingLicense ?
-                            TWEAK_FOR_EXCLUDING_LICENSE : "")
-                    + CHECK_INSTALL_DLG_CTRL;
-        } else if (getLicenseFile(params) != null) {
-            uiBlock = "     <UIRef Id=\"WixUI_Minimal\" />\n";
-        }
-
-        return uiBlock;
-    }
-
     private void walkFileTree(Map<String, ? super Object> params,
             File root, PrintStream out, String prefix) {
         List<File> dirs = new ArrayList<>();
@@ -936,15 +926,7 @@
         }
     }
 
-    String getRegistryRoot(Map<String, ? super Object> params) {
-        if (MSI_SYSTEM_WIDE.fetchFrom(params)) {
-            return "HKLM";
-        } else {
-            return "HKCU";
-        }
-    }
-
-    boolean prepareContentList(Map<String, ? super Object> params)
+    void prepareContentList(Map<String, ? super Object> params)
             throws FileNotFoundException {
         File f = new File(
                 CONFIG_ROOT.fetchFrom(params), MSI_PROJECT_CONTENT_FILE);
@@ -1024,14 +1006,13 @@
             for (int j = 0; j < compId; j++) {
                 out.println("    <ComponentRef Id=\"comp" + j + "\" />");
             }
-            // component is defined in the template.wsx
+            // component is defined in the main.wsx
             out.println(
                     "    <ComponentRef Id=\"CleanupMainApplicationFolder\" />");
             out.println(" </Feature>");
             out.println("</Include>");
 
         }
-        return true;
     }
 
     private File getConfig_ProjectFile(Map<String, ? super Object> params) {
@@ -1039,38 +1020,21 @@
                 APP_NAME.fetchFrom(params) + ".wxs");
     }
 
-    private String getLicenseFile(Map<String, ? super Object> params) {
-        String licenseFile = LICENSE_FILE.fetchFrom(params);
-        if (licenseFile != null) {
-            File lfile = new File(licenseFile);
-            File destFile = new File(CONFIG_ROOT.fetchFrom(params),
-                lfile.getName());
-            String filePath = destFile.getAbsolutePath();
-            if (filePath.contains(" ")) {
-                return "\"" + filePath + "\"";
-            } else {
-                return filePath;
-            }
-        }
-
-        return null;
+    private Map<String, String> prepareWiXConfig(
+            Map<String, ? super Object> params) throws IOException {
+        prepareContentList(params);
+        prepareIconsFile(params);
+        return prepareMainProjectFile(params);
     }
 
-    private boolean prepareWiXConfig(
-            Map<String, ? super Object> params) throws IOException {
-        return prepareMainProjectFile(params) && prepareContentList(params);
-
-    }
-    private final static String MSI_PROJECT_TEMPLATE = "template.wxs";
-    private final static String MSI_PROJECT_TEMPLATE_SERVER_JRE =
-            "template.jre.wxs";
     private final static String MSI_PROJECT_CONTENT_FILE = "bundle.wxi";
 
-    private File buildMSI(Map<String, ? super Object> params, File outdir)
+    private File buildMSI(Map<String, ? super Object> params,
+            Map<String, String> wixVars, File outdir)
             throws IOException {
         File tmpDir = new File(TEMP_ROOT.fetchFrom(params), "tmp");
         File candleOut = new File(
-                tmpDir, APP_NAME.fetchFrom(params) +".wixobj");
+                tmpDir, APP_NAME.fetchFrom(params) + ".wixobj");
         File msiOut = new File(
                 outdir, INSTALLER_FILE_NAME.fetchFrom(params) + ".msi");
 
@@ -1079,28 +1043,30 @@
 
         msiOut.getParentFile().mkdirs();
 
-        // run candle
-        ProcessBuilder pb = new ProcessBuilder(
+        List<String> commandLine = new ArrayList<>(Arrays.asList(
                 TOOL_CANDLE_EXECUTABLE.fetchFrom(params),
                 "-nologo",
                 getConfig_ProjectFile(params).getAbsolutePath(),
                 "-ext", "WixUtilExtension",
-                "-out", candleOut.getAbsolutePath());
+                "-out", candleOut.getAbsolutePath()));
+        for(Map.Entry<String, String> wixVar: wixVars.entrySet()) {
+            String v = "-d" + wixVar.getKey() + "=" + wixVar.getValue();
+            commandLine.add(v);
+        }
+        ProcessBuilder pb = new ProcessBuilder(commandLine);
         pb = pb.directory(WIN_APP_IMAGE.fetchFrom(params));
         IOUtils.exec(pb);
 
         Log.verbose(MessageFormat.format(I18N.getString(
                 "message.generating-msi"), msiOut.getAbsolutePath()));
 
-        boolean enableLicenseUI = (getLicenseFile(params) != null);
+        boolean enableLicenseUI = (LICENSE_FILE.fetchFrom(params) != null);
         boolean enableInstalldirUI = INSTALLDIR_CHOOSER.fetchFrom(params);
 
-        List<String> commandLine = new ArrayList<>();
+        commandLine = new ArrayList<>();
 
         commandLine.add(TOOL_LIGHT_EXECUTABLE.fetchFrom(params));
-        if (enableLicenseUI) {
-            commandLine.add("-dWixUILicenseRtf="+getLicenseFile(params));
-        }
+
         commandLine.add("-nologo");
         commandLine.add("-spdb");
         commandLine.add("-sice:60");
@@ -1110,9 +1076,13 @@
         commandLine.add("WixUtilExtension");
         if (enableLicenseUI || enableInstalldirUI) {
             commandLine.add("-ext");
-            commandLine.add("WixUIExtension.dll");
+            commandLine.add("WixUIExtension");
         }
 
+        commandLine.add("-loc");
+        commandLine.add(new File(CONFIG_ROOT.fetchFrom(params), I18N.getString(
+                "resource.wxl-file-name")).getAbsolutePath());
+
         // Only needed if we using CA dll, so Wix can find it
         if (enableInstalldirUI) {
             commandLine.add("-b");