8291978: jpackage: allow to override primary l10n files on Windows

Reviewed-by: asemenyuk, almatvee, naoto
This commit is contained in:
Alex Kasko 2022-08-10 20:34:51 +00:00 committed by Alexey Semenyuk
parent 68af7c1365
commit 543163a03b
11 changed files with 135 additions and 52 deletions

View File

@ -513,11 +513,11 @@ public class WinMsiBundler extends AbstractBundler {
}
// Copy standard l10n files.
for (String loc : Arrays.asList("en", "ja", "zh_CN")) {
for (String loc : Arrays.asList("de", "en", "ja", "zh_CN")) {
String fname = "MsiInstallerStrings_" + loc + ".wxl";
try (InputStream is = OverridableResource.readDefault(fname)) {
Files.copy(is, CONFIG_ROOT.fetchFrom(params).resolve(fname));
}
createResource(fname, params)
.setCategory(I18N.getString("resource.wxl-file"))
.saveToFile(configDir.resolve(fname));
}
createResource("main.wxs", params)
@ -563,16 +563,36 @@ public class WinMsiBundler extends AbstractBundler {
wixPipeline.addLightOptions("-sice:ICE91");
}
final Path primaryWxlFile = CONFIG_ROOT.fetchFrom(params).resolve(
I18N.getString("resource.wxl-file-name")).toAbsolutePath();
// Filter out custom l10n files that were already used to
// override primary l10n files. Ignore case filename comparison,
// both lists are expected to be short.
List<Path> primaryWxlFiles = getWxlFilesFromDir(params, CONFIG_ROOT);
List<Path> customWxlFiles = getWxlFilesFromDir(params, RESOURCE_DIR).stream()
.filter(custom -> primaryWxlFiles.stream().noneMatch(primary ->
primary.getFileName().toString().equalsIgnoreCase(
custom.getFileName().toString())))
.peek(custom -> Log.verbose(MessageFormat.format(
I18N.getString("message.using-custom-resource"),
String.format("[%s]", I18N.getString("resource.wxl-file")),
custom.getFileName().toString())))
.toList();
wixPipeline.addLightOptions("-loc", primaryWxlFile.toString());
// All l10n files are supplied to WiX with "-loc", but only
// Cultures from custom files and a single primary Culture are
// included into "-cultures" list
for (var wxl : primaryWxlFiles) {
wixPipeline.addLightOptions("-loc", wxl.toAbsolutePath().normalize().toString());
}
List<String> cultures = new ArrayList<>();
for (var wxl : getCustomWxlFiles(params)) {
wixPipeline.addLightOptions("-loc", wxl.toAbsolutePath().toString());
for (var wxl : customWxlFiles) {
wixPipeline.addLightOptions("-loc", wxl.toAbsolutePath().normalize().toString());
cultures.add(getCultureFromWxlFile(wxl));
}
// Append a primary culture bases on runtime locale.
final Path primaryWxlFile = CONFIG_ROOT.fetchFrom(params).resolve(
I18N.getString("resource.wxl-file-name"));
cultures.add(getCultureFromWxlFile(primaryWxlFile));
// Build ordered list of unique cultures.
@ -586,10 +606,10 @@ public class WinMsiBundler extends AbstractBundler {
return msiOut;
}
private static List<Path> getCustomWxlFiles(Map<String, ? super Object> params)
throws IOException {
Path resourceDir = RESOURCE_DIR.fetchFrom(params);
if (resourceDir == null) {
private static List<Path> getWxlFilesFromDir(Map<String, ? super Object> params,
StandardBundlerParam<Path> pathParam) throws IOException {
Path dir = pathParam.fetchFrom(params);
if (dir == null) {
return Collections.emptyList();
}
@ -597,7 +617,7 @@ public class WinMsiBundler extends AbstractBundler {
final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(
glob);
try (var walk = Files.walk(resourceDir, 1)) {
try (var walk = Files.walk(dir, 1)) {
return walk
.filter(Files::isReadable)
.filter(pathMatcher::matches)

View File

@ -1,5 +1,5 @@
<?xml version = '1.0' encoding = 'utf-8'?>
<WixLocalization Culture="de" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
<WixLocalization Culture="de-de" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="1252">
<String Id="message.install.dir.exist">Der Ordner [INSTALLDIR] ist bereits vorhanden. Möchten Sie diesen Ordner trotzdem installieren?</String>
<String Id="MainFeatureTitle">Hauptfeature</String>
<String Id="DowngradeErrorMessage">Eine höhere Version von [ProductName] ist bereits installiert. Downgrades sind deaktiviert. Setup wird jetzt beendet.</String>

View File

@ -1,5 +1,5 @@
<?xml version = '1.0' encoding = 'utf-8'?>
<WixLocalization Culture="ja" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="932">
<WixLocalization Culture="ja-jp" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="932">
<String Id="message.install.dir.exist">フォルダ[INSTALLDIR]はすでに存在します。そのフォルダにインストールしますか?</String>
<String Id="MainFeatureTitle">主な機能</String>
<String Id="DowngradeErrorMessage">[ProductName]のより上位のバージョンがすでにインストールされています。ダウングレードは無効です。セットアップを終了します。</String>

View File

@ -1,5 +1,5 @@
<?xml version = '1.0' encoding = 'utf-8'?>
<WixLocalization Culture="zh-CN" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="936">
<WixLocalization Culture="zh-cn" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="936">
<String Id="message.install.dir.exist">文件夹 [INSTALLDIR] 已存在。是否仍要安装到该文件夹?</String>
<String Id="MainFeatureTitle">主要功能</String>
<String Id="DowngradeErrorMessage">已安装更高版本的 [ProductName]。降级已禁用。现在将退出安装。</String>

View File

@ -34,6 +34,7 @@ resource.executable-properties-template=Template for creating executable propert
resource.setup-icon=setup dialog icon
resource.post-app-image-script=script to run after application image is populated
resource.post-msi-script=script to run after msi file for exe installer is created
resource.wxl-file=WiX localization file
resource.wxl-file-name=MsiInstallerStrings_en.wxl
resource.main-wix-file=Main WiX project file
resource.overrides-wix-file=Overrides WiX project file

View File

@ -34,7 +34,7 @@ resource.executable-properties-template=Vorlage f\u00FCr das Erstellen der ausf\
resource.setup-icon=Symbol f\u00FCr Dialogfeld "Setup"
resource.post-app-image-script=Auszuf\u00FChrendes Skript nach dem Auff\u00FCllen des Anwendungsimages
resource.post-msi-script=Auszuf\u00FChrendes Skript nach dem Erstellen der MSI-Datei f\u00FCr das EXE-Installationsprogramm
resource.wxl-file-name=MsiInstallerStrings_en.wxl
resource.wxl-file-name=MsiInstallerStrings_de.wxl
resource.main-wix-file=Haupt-WiX-Projektdatei
resource.overrides-wix-file=\u00DCberschreibt WiX-Projektdatei
resource.shortcutpromptdlg-wix-file=Dialogfeld f\u00FCr Verkn\u00FCpfungs-Prompt der WiX-Projektdatei

View File

@ -34,7 +34,7 @@ resource.executable-properties-template=\u5B9F\u884C\u53EF\u80FD\u306A\u30D7\u30
resource.setup-icon=\u8A2D\u5B9A\u30C0\u30A4\u30A2\u30ED\u30B0\u30FB\u30A2\u30A4\u30B3\u30F3
resource.post-app-image-script=\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u30FB\u30A4\u30E1\u30FC\u30B8\u3092\u79FB\u5165\u3057\u305F\u5F8C\u306B\u5B9F\u884C\u3059\u308B\u30B9\u30AF\u30EA\u30D7\u30C8
resource.post-msi-script=exe\u30A4\u30F3\u30B9\u30C8\u30FC\u30E9\u306Emsi\u30D5\u30A1\u30A4\u30EB\u304C\u4F5C\u6210\u3055\u308C\u305F\u5F8C\u306B\u5B9F\u884C\u3059\u308B\u30B9\u30AF\u30EA\u30D7\u30C8
resource.wxl-file-name=MsiInstallerStrings_en.wxl
resource.wxl-file-name=MsiInstallerStrings_ja.wxl
resource.main-wix-file=\u30E1\u30A4\u30F3WiX\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30FB\u30D5\u30A1\u30A4\u30EB
resource.overrides-wix-file=WiX\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30FB\u30D5\u30A1\u30A4\u30EB\u306E\u30AA\u30FC\u30D0\u30FC\u30E9\u30A4\u30C9
resource.shortcutpromptdlg-wix-file=\u30B7\u30E7\u30FC\u30C8\u30AB\u30C3\u30C8\u30FB\u30D7\u30ED\u30F3\u30D7\u30C8\u30FB\u30C0\u30A4\u30A2\u30ED\u30B0WiX\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u30FB\u30D5\u30A1\u30A4\u30EB

View File

@ -34,7 +34,7 @@ resource.executable-properties-template=\u7528\u4E8E\u521B\u5EFA\u53EF\u6267\u88
resource.setup-icon=\u8BBE\u7F6E\u5BF9\u8BDD\u6846\u56FE\u6807
resource.post-app-image-script=\u8981\u5728\u586B\u5145\u5E94\u7528\u7A0B\u5E8F\u6620\u50CF\u4E4B\u540E\u8FD0\u884C\u7684\u811A\u672C
resource.post-msi-script=\u5728\u4E3A exe \u5B89\u88C5\u7A0B\u5E8F\u521B\u5EFA msi \u6587\u4EF6\u4E4B\u540E\u8981\u8FD0\u884C\u7684\u811A\u672C
resource.wxl-file-name=MsiInstallerStrings_en.wxl
resource.wxl-file-name=MsiInstallerStrings_zh_CN.wxl
resource.main-wix-file=\u4E3B WiX \u9879\u76EE\u6587\u4EF6
resource.overrides-wix-file=\u8986\u76D6 WiX \u9879\u76EE\u6587\u4EF6
resource.shortcutpromptdlg-wix-file=\u5FEB\u6377\u65B9\u5F0F\u63D0\u793A\u5BF9\u8BDD\u6846 WiX \u9879\u76EE\u6587\u4EF6

View File

@ -61,6 +61,22 @@ public class WindowsHelper {
return Path.of(cmd.getArgumentValue("--install-dir", cmd::name));
}
// Tests have problems on windows where path in the temp dir are too long
// for the wix tools. We can't use a tempDir outside the TKit's WorkDir, so
// we minimize both the tempRoot directory name (above) and the tempDir name
// (below) to the extension part (which is necessary to differenciate between
// the multiple PackageTypes that will be run for one JPackageCommand).
// It might be beter if the whole work dir name was shortened from:
// jtreg_open_test_jdk_tools_jpackage_share_jdk_jpackage_tests_BasicTest_java.
public static Path getTempDirectory(JPackageCommand cmd, Path tempRoot) {
String ext = cmd.outputBundle().getFileName().toString();
int i = ext.lastIndexOf(".");
if (i > 0 && i < (ext.length() - 1)) {
ext = ext.substring(i+1);
}
return tempRoot.resolve(ext);
}
private static void runMsiexecWithRetries(Executor misexec) {
Executor.Result result = null;
for (int attempt = 0; attempt < 8; ++attempt) {

View File

@ -43,6 +43,8 @@ import jdk.jpackage.test.JavaTool;
import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.test.Annotations.Parameter;
import static jdk.jpackage.test.WindowsHelper.getTempDirectory;
/*
* @test
* @summary jpackage basic testing
@ -273,21 +275,6 @@ public final class BasicTest {
@Parameter("false")
public void testTemp(boolean withExistingTempDir) throws IOException {
final Path tempRoot = TKit.createTempDirectory("tmp");
// This Test has problems on windows where path in the temp dir are too long
// for the wix tools. We can't use a tempDir outside the TKit's WorkDir, so
// we minimize both the tempRoot directory name (above) and the tempDir name
// (below) to the extension part (which is necessary to differenciate between
// the multiple PackageTypes that will be run for one JPackageCommand).
// It might be beter if the whole work dir name was shortened from:
// jtreg_open_test_jdk_tools_jpackage_share_jdk_jpackage_tests_BasicTest_java.
Function<JPackageCommand, Path> getTempDir = cmd -> {
String ext = cmd.outputBundle().getFileName().toString();
int i = ext.lastIndexOf(".");
if (i > 0 && i < (ext.length() - 1)) {
ext = ext.substring(i+1);
}
return tempRoot.resolve(ext);
};
Supplier<PackageTest> createTest = () -> {
return new PackageTest()
@ -295,7 +282,7 @@ public final class BasicTest {
// Force save of package bundle in test work directory.
.addInitializer(JPackageCommand::setDefaultInputOutput)
.addInitializer(cmd -> {
Path tempDir = getTempDir.apply(cmd);
Path tempDir = getTempDirectory(cmd, tempRoot);
if (withExistingTempDir) {
Files.createDirectories(tempDir);
} else {
@ -308,7 +295,7 @@ public final class BasicTest {
createTest.get()
.addBundleVerifier(cmd -> {
// Check jpackage actually used the supplied directory.
Path tempDir = getTempDir.apply(cmd);
Path tempDir = getTempDirectory(cmd, tempRoot);
TKit.assertNotEquals(0, tempDir.toFile().list().length,
String.format(
"Check jpackage wrote some data in the supplied temporary directory [%s]",

View File

@ -22,18 +22,22 @@
*/
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import jdk.jpackage.test.TKit;
import jdk.jpackage.test.PackageTest;
import jdk.jpackage.test.PackageType;
import jdk.jpackage.test.Annotations.Test;
import jdk.jpackage.test.Annotations.Parameters;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import jdk.jpackage.test.Executor;
import static jdk.jpackage.test.WindowsHelper.getTempDirectory;
/*
* @test
* @summary Custom l10n of msi installers in jpackage
@ -51,48 +55,64 @@ import jdk.jpackage.test.Executor;
public class WinL10nTest {
public WinL10nTest(WixFileInitializer wxlFileInitializers[],
String expectedCulture, String expectedErrorMessage) {
String expectedCulture, String expectedErrorMessage,
String userLanguage, String userCountry,
boolean enableWixUIExtension) {
this.wxlFileInitializers = wxlFileInitializers;
this.expectedCulture = expectedCulture;
this.expectedErrorMessage = expectedErrorMessage;
this.userLanguage = userLanguage;
this.userCountry = userCountry;
this.enableWixUIExtension = enableWixUIExtension;
}
@Parameters
public static List<Object[]> data() {
return List.of(new Object[][]{
{null, "en-us", null},
{null, "en-us", null, null, null, false},
{null, "en-us", null, "en", "US", false},
{null, "en-us", null, "en", "US", true},
{null, "de-de", null, "de", "DE", false},
{null, "de-de", null, "de", "DE", true},
{null, "ja-jp", null, "ja", "JP", false},
{null, "ja-jp", null, "ja", "JP", true},
{null, "zh-cn", null, "zh", "CN", false},
{null, "zh-cn", null, "zh", "CN", true},
{new WixFileInitializer[] {
WixFileInitializer.create("a.wxl", "en-us")
}, "en-us", null},
}, "en-us", null, null, null, false},
{new WixFileInitializer[] {
WixFileInitializer.create("a.wxl", "fr")
}, "fr;en-us", null},
}, "fr;en-us", null, null, null, false},
{new WixFileInitializer[] {
WixFileInitializer.create("a.wxl", "fr"),
WixFileInitializer.create("b.wxl", "fr")
}, "fr;en-us", null},
}, "fr;en-us", null, null, null, false},
{new WixFileInitializer[] {
WixFileInitializer.create("a.wxl", "it"),
WixFileInitializer.create("b.wxl", "fr")
}, "it;fr;en-us", null},
}, "it;fr;en-us", null, null, null, false},
{new WixFileInitializer[] {
WixFileInitializer.create("c.wxl", "it"),
WixFileInitializer.create("b.wxl", "fr")
}, "fr;it;en-us", null},
}, "fr;it;en-us", null, null, null, false},
{new WixFileInitializer[] {
WixFileInitializer.create("a.wxl", "fr"),
WixFileInitializer.create("b.wxl", "it"),
WixFileInitializer.create("c.wxl", "fr"),
WixFileInitializer.create("d.wxl", "it")
}, "fr;it;en-us", null},
}, "fr;it;en-us", null, null, null, false},
{new WixFileInitializer[] {
WixFileInitializer.create("c.wxl", "it"),
WixFileInitializer.createMalformed("b.wxl")
}, null, null}
}, null, null, null, null, false},
{new WixFileInitializer[] {
WixFileInitializer.create("MsiInstallerStrings_de.wxl", "de")
}, "en-us", null, null, null, false}
});
}
private final static Stream<String> getLightCommandLine(
private static Stream<String> getLightCommandLine(
Executor.Result result) {
return result.getOutput().stream().filter(s -> {
s = s.trim();
@ -101,8 +121,16 @@ public class WinL10nTest {
});
}
private static List<TKit.TextStreamVerifier> createDefaultL10nFilesLocVerifiers(Path tempDir) {
return Arrays.stream(DEFAULT_L10N_FILES).map(loc ->
TKit.assertTextStream("-loc " + tempDir.resolve(
String.format("config/MsiInstallerStrings_%s.wxl", loc)).normalize()))
.toList();
}
@Test
public void test() throws IOException {
final Path tempRoot = TKit.createTempDirectory("tmp");
final boolean allWxlFilesValid;
if (wxlFileInitializers != null) {
@ -119,6 +147,26 @@ public class WinL10nTest {
// 1. Set fake run time to save time by skipping jlink step of jpackage.
// 2. Instruct test to save jpackage output.
cmd.setFakeRuntime().saveConsoleOutput(true);
// Set JVM default locale that is used to select primary l10n file.
if (userLanguage != null) {
cmd.addArguments("-J-Duser.language=" + userLanguage);
}
if (userCountry != null) {
cmd.addArguments("-J-Duser.country=" + userCountry);
}
// Cultures handling is affected by the WiX extensions used.
// By default only WixUtilExtension is used, this flag
// additionally enables WixUIExtension.
if (enableWixUIExtension) {
cmd.addArgument("--win-dir-chooser");
}
// Preserve config dir to check the set of copied l10n files.
Path tempDir = getTempDirectory(cmd, tempRoot);
Files.createDirectories(tempDir.getParent());
cmd.addArguments("--temp", tempDir.toString());
})
.addBundleVerifier((cmd, result) -> {
if (expectedCulture != null) {
@ -134,8 +182,14 @@ public class WinL10nTest {
if (wxlFileInitializers != null) {
if (allWxlFilesValid) {
for (var v : wxlFileInitializers) {
v.createCmdOutputVerifier(resourceDir).apply(
getLightCommandLine(result));
if (!v.name.startsWith("MsiInstallerStrings_")) {
v.createCmdOutputVerifier(resourceDir).apply(
getLightCommandLine(result));
}
}
Path tempDir = getTempDirectory(cmd, tempRoot).toAbsolutePath();
for (var v : createDefaultL10nFilesLocVerifiers(tempDir)) {
v.apply(getLightCommandLine(result));
}
} else {
Stream.of(wxlFileInitializers)
@ -168,9 +222,12 @@ public class WinL10nTest {
test.run();
}
final private WixFileInitializer wxlFileInitializers[];
final private WixFileInitializer[] wxlFileInitializers;
final private String expectedCulture;
final private String expectedErrorMessage;
final private String userLanguage;
final private String userCountry;
final private boolean enableWixUIExtension;
private Path resourceDir;
private static class WixFileInitializer {
@ -221,7 +278,7 @@ public class WinL10nTest {
TKit.TextStreamVerifier createCmdOutputVerifier(Path root) {
return TKit.assertTextStream(
root.resolve(name).toAbsolutePath().toString());
"-loc " + root.resolve(name).toAbsolutePath().normalize());
}
boolean isValid() {
@ -236,4 +293,6 @@ public class WinL10nTest {
private final String name;
private final String culture;
}
private static final String[] DEFAULT_L10N_FILES = { "de", "en", "ja", "zh_CN" };
}