@joebigelow / wix / commits / 651ad904

Support filtering localizations by culture

Rob Mensching committed Dec 31, 2017 at 02:17 UTC 651ad904724f32f5e993fa73aa11a611d95a1a10
4 files changed +95 -14
src/WixToolset.Core/CommandLine/BuildCommand.cs
+5 -4
@@ -13,20 +13,20 @@ namespace WixToolset.Core.CommandLine
13
14 internal class BuildCommand : ICommandLineCommand
15 {
16 - public BuildCommand(IServiceProvider serviceProvider, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, IEnumerable<string> libraryFiles, string outputPath, OutputType outputType, string cabCachePath, IEnumerable<string> cultures, bool bindFiles, IEnumerable<BindPath> bindPaths, IEnumerable<string> includeSearchPaths, string intermediateFolder, string contentsFile, string outputsFile, string builtOutputsFile)
16 + public BuildCommand(IServiceProvider serviceProvider, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, IEnumerable<string> libraryFiles, IEnumerable<string> filterCultures, string outputPath, OutputType outputType, string cabCachePath, bool bindFiles, IEnumerable<BindPath> bindPaths, IEnumerable<string> includeSearchPaths, string intermediateFolder, string contentsFile, string outputsFile, string builtOutputsFile)
17 {
18 this.ServiceProvider = serviceProvider;
19 this.Messaging = serviceProvider.GetService<IMessaging>();
20 this.ExtensionManager = serviceProvider.GetService<IExtensionManager>();
21 this.LocFiles = locFiles;
22 this.LibraryFiles = libraryFiles;
23 + this.FilterCultures = filterCultures;
24 this.PreprocessorVariables = preprocessorVariables;
25 this.SourceFiles = sources;
26 this.OutputPath = outputPath;
27 this.OutputType = outputType;
28
29 this.CabCachePath = cabCachePath;
29 - this.Cultures = cultures;
30 this.BindFiles = bindFiles;
31 this.BindPaths = bindPaths;
32 this.IncludeSearchPaths = includeSearchPaths;
@@ -43,6 +43,8 @@ namespace WixToolset.Core.CommandLine
43
44 public IExtensionManager ExtensionManager { get; }
45
46 + public IEnumerable<string> FilterCultures { get; }
47 +
48 public IEnumerable<string> IncludeSearchPaths { get; }
49
50 public IEnumerable<string> LocFiles { get; }
@@ -59,8 +61,6 @@ namespace WixToolset.Core.CommandLine
61
62 public string CabCachePath { get; }
63
62 - public IEnumerable<string> Cultures { get; }
63 -
64 public bool BindFiles { get; }
65
66 public IEnumerable<BindPath> BindPaths { get; }
@@ -205,6 +205,7 @@ namespace WixToolset.Core.CommandLine
205 {
206 var resolver = new Resolver(this.ServiceProvider);
207 resolver.BindPaths = this.BindPaths;
208 + resolver.FilterCultures = this.FilterCultures;
209 resolver.IntermediateFolder = this.IntermediateFolder;
210 resolver.IntermediateRepresentation = output;
211 resolver.Localizations = localizations;
src/WixToolset.Core/CommandLine/CommandLineParser.cs
+30 -2
@@ -114,7 +114,7 @@ namespace WixToolset.Core.CommandLine
114 cmdline.GetNextArgumentOrError(ref cabCachePath);
115 return true;
116
117 - case "cultures":
117 + case "culture":
118 cmdline.GetNextArgumentOrError(cultures);
119 return true;
120 case "contentsfile":
@@ -210,8 +210,9 @@ namespace WixToolset.Core.CommandLine
210 var sourceFiles = GatherSourceFiles(files, outputFolder);
211 var variables = this.GatherPreprocessorVariables(defines);
212 var bindPathList = this.GatherBindPaths(bindPaths);
213 + var filterCultures = CalculateFilterCultures(cultures);
214 var type = CalculateOutputType(outputType, outputFile);
214 - return new BuildCommand(this.ServiceProvider, sourceFiles, variables, locFiles, libraryFiles, outputFile, type, cabCachePath, cultures, bindFiles, bindPathList, includePaths, intermediateFolder, contentsFile, outputsFile, builtOutputsFile);
215 + return new BuildCommand(this.ServiceProvider, sourceFiles, variables, locFiles, libraryFiles, filterCultures, outputFile, type, cabCachePath, bindFiles, bindPathList, includePaths, intermediateFolder, contentsFile, outputsFile, builtOutputsFile);
216 }
217
218 case Commands.Compile:
@@ -225,6 +226,33 @@ namespace WixToolset.Core.CommandLine
226 return null;
227 }
228
229 + private static IEnumerable<string> CalculateFilterCultures(List<string> cultures)
230 + {
231 + var result = new List<string>();
232 +
233 + if (cultures == null)
234 + {
235 + }
236 + else if (cultures.Count == 1 && cultures[0].Equals("null", StringComparison.OrdinalIgnoreCase))
237 + {
238 + // When null is used treat it as if cultures wasn't specified. This is
239 + // needed for batching in the MSBuild task since MSBuild doesn't support
240 + // empty items.
241 + }
242 + else
243 + {
244 + foreach (var culture in cultures)
245 + {
246 + // Neutral is different from null. For neutral we still want to do culture filtering.
247 + // Set the culture to the empty string = identifier for the invariant culture.
248 + var filter = (culture.Equals("neutral", StringComparison.OrdinalIgnoreCase)) ? String.Empty : culture;
249 + result.Add(filter);
250 + }
251 + }
252 +
253 + return result;
254 + }
255 +
256 private static OutputType CalculateOutputType(string outputType, string outputFile)
257 {
258 if (String.IsNullOrEmpty(outputType))
src/WixToolset.Core/ResolveContext.cs
+2
@@ -25,6 +25,8 @@ namespace WixToolset.Core
25
26 public IEnumerable<IExtensionData> ExtensionData { get; set; }
27
28 + public IEnumerable<string> FilterCultures { get; set; }
29 +
30 public string IntermediateFolder { get; set; }
31
32 public Intermediate IntermediateRepresentation { get; set; }
src/WixToolset.Core/Resolver.cs
+58 -8
@@ -31,6 +31,8 @@ namespace WixToolset.Core
31
32 public IEnumerable<Localization> Localizations { get; set; }
33
34 + public IEnumerable<string> FilterCultures { get; set; }
35 +
36 public ResolveResult Execute()
37 {
38 var extensionManager = this.ServiceProvider.GetService<IExtensionManager>();
@@ -40,6 +42,7 @@ namespace WixToolset.Core
42 context.BindPaths = this.BindPaths;
43 context.Extensions = extensionManager.Create<IResolverExtension>();
44 context.ExtensionData = extensionManager.Create<IExtensionData>();
45 + context.FilterCultures = this.FilterCultures;
46 context.IntermediateFolder = this.IntermediateFolder;
47 context.IntermediateRepresentation = this.IntermediateRepresentation;
48 context.Localizations = this.Localizations;
@@ -207,29 +210,76 @@ namespace WixToolset.Core
210 {
211 var creator = context.ServiceProvider.GetService<ITupleDefinitionCreator>();
212
213 + var localizations = FilterLocalizations(context);
214 +
215 + foreach (var localization in localizations)
216 + {
217 + context.VariableResolver.AddLocalization(localization);
218 + }
219 +
220 + // Gather all the wix variables.
221 + var wixVariableTuples = context.IntermediateRepresentation.Sections.SelectMany(s => s.Tuples).OfType<WixVariableTuple>();
222 + foreach (var tuple in wixVariableTuples)
223 + {
224 + context.VariableResolver.AddVariable(tuple.SourceLineNumbers, tuple.WixVariable, tuple.Value, tuple.Overridable);
225 + }
226 + }
227 +
228 + private static IEnumerable<Localization> FilterLocalizations(IResolveContext context)
229 + {
230 + var result = new List<Localization>();
231 + var filter = CalculateCultureFilter(context);
232 +
233 var localizations = context.Localizations.Concat(context.IntermediateRepresentation.Localizations).ToList();
234
212 - // Add localizations from the extensions with data.
235 + // If there still is no filter, return all localizations.
236 + AddFilteredLocalizations(result, filter, localizations);
237 +
238 + // Filter localizations provided by extensions with data.
239 + var creator = context.ServiceProvider.GetService<ITupleDefinitionCreator>();
240 +
241 foreach (var data in context.ExtensionData)
242 {
243 var library = data.GetLibrary(creator);
244
245 if (library?.Localizations != null)
246 {
219 - localizations.AddRange(library.Localizations);
247 + var extensionFilter = (!filter.Any() && data.DefaultCulture != null) ? new[] { data.DefaultCulture } : filter;
248 +
249 + AddFilteredLocalizations(result, extensionFilter, library.Localizations);
250 }
251 }
252
223 - foreach (var localization in localizations)
253 + return result;
254 + }
255 +
256 + private static IEnumerable<string> CalculateCultureFilter(IResolveContext context)
257 + {
258 + var filter = context.FilterCultures ?? Array.Empty<string>();
259 +
260 + // If no filter was specified, look for a language neutral localization file specified
261 + // from the command-line (not embedded in the intermediate). If found, filter on language
262 + // neutral.
263 + if (!filter.Any() && context.Localizations.Any(l => String.IsNullOrEmpty(l.Culture)))
264 {
225 - context.VariableResolver.AddLocalization(localization);
265 + filter = new[] { String.Empty };
266 }
267
228 - // Gather all the wix variables.
229 - var wixVariableTuples = context.IntermediateRepresentation.Sections.SelectMany(s => s.Tuples).OfType<WixVariableTuple>();
230 - foreach (var tuple in wixVariableTuples)
268 + return filter;
269 + }
270 +
271 + private static void AddFilteredLocalizations(List<Localization> result, IEnumerable<string> filter, IEnumerable<Localization> localizations)
272 + {
273 + if (!filter.Any())
274 {
232 - context.VariableResolver.AddVariable(tuple.SourceLineNumbers, tuple.WixVariable, tuple.Value, tuple.Overridable);
275 + result.AddRange(localizations);
276 + }
277 + else // filter localizations in order specified by the filter
278 + {
279 + foreach (var culture in filter)
280 + {
281 + result.AddRange(localizations.Where(l => culture.Equals(l.Culture, StringComparison.OrdinalIgnoreCase)));
282 + }
283 }
284 }
285 }