From 39c7e2bb0399802e65a3025c4a73db211e730479 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 1 Oct 2017 14:25:33 -0700 Subject: Add support for BindPaths and building .wixlibs --- src/WixToolset.BuildTasks/DoIt.cs | 51 ++++-- src/WixToolset.BuildTasks/WixCommandLineBuilder.cs | 16 +- src/WixToolset.BuildTasks/wix.targets | 23 ++- src/WixToolset.Core/CommandLine/BuildCommand.cs | 196 ++++++++++++++++++--- src/WixToolset.Core/CommandLine/CommandLine.cs | 115 +++++++++--- src/WixToolset.Core/Librarian.cs | 117 ++++++------ src/WixToolset.Core/Localizer.cs | 53 +++--- src/WixToolset.Core/WixVariableResolver.cs | 20 +-- 8 files changed, 421 insertions(+), 170 deletions(-) diff --git a/src/WixToolset.BuildTasks/DoIt.cs b/src/WixToolset.BuildTasks/DoIt.cs index 7688342c..97554bc6 100644 --- a/src/WixToolset.BuildTasks/DoIt.cs +++ b/src/WixToolset.BuildTasks/DoIt.cs @@ -2,6 +2,9 @@ namespace WixToolset.BuildTasks { + using System; + using System.Collections.Generic; + using System.Runtime.InteropServices; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using WixToolset.Core; @@ -40,12 +43,14 @@ namespace WixToolset.BuildTasks public bool NoLogo { get; set; } - public ITaskItem[] ObjectFiles { get; set; } + public ITaskItem[] LibraryFiles { get; set; } [Output] [Required] public ITaskItem OutputFile { get; set; } + public string OutputType { get; set; } + public string PdbOutputFile { get; set; } public bool Pedantic { get; set; } @@ -84,7 +89,7 @@ namespace WixToolset.BuildTasks public ITaskItem[] BindInputPaths { get; set; } public bool BindFiles { get; set; } - public ITaskItem BindContentsFile{ get; set; } + public ITaskItem BindContentsFile { get; set; } public ITaskItem BindOutputsFile { get; set; } public ITaskItem BindBuiltOutputsFile { get; set; } @@ -102,21 +107,20 @@ namespace WixToolset.BuildTasks public string[] SuppressIces { get; set; } public string AdditionalCub { get; set; } - - public override bool Execute() { try { this.ExecuteCore(); } - catch (BuildException e) - { - this.Log.LogErrorFromException(e); - } - catch (WixException e) + catch (Exception e) { this.Log.LogErrorFromException(e); + + if (e is NullReferenceException || e is SEHException) + { + throw; + } } return !this.Log.HasLoggedErrors; @@ -129,27 +133,31 @@ namespace WixToolset.BuildTasks commandLineBuilder.AppendTextUnquoted("build"); commandLineBuilder.AppendSwitchIfNotNull("-out ", this.OutputFile); + commandLineBuilder.AppendSwitchIfNotNull("-outputType ", this.OutputType); + commandLineBuilder.AppendIfTrue("-nologo", this.NoLogo); commandLineBuilder.AppendSwitchIfNotNull("-cultures ", this.Cultures); commandLineBuilder.AppendArrayIfNotNull("-d ", this.DefineConstants); commandLineBuilder.AppendArrayIfNotNull("-I ", this.IncludeSearchPaths); commandLineBuilder.AppendExtensions(this.Extensions, this.ExtensionDirectory, this.ReferencePaths); - commandLineBuilder.AppendIfTrue("-nologo", this.NoLogo); commandLineBuilder.AppendIfTrue("-sval", this.SuppressValidation); commandLineBuilder.AppendArrayIfNotNull("-sice ", this.SuppressIces); commandLineBuilder.AppendSwitchIfNotNull("-usf ", this.UnreferencedSymbolsFile); commandLineBuilder.AppendSwitchIfNotNull("-cc ", this.CabinetCachePath); + commandLineBuilder.AppendSwitchIfNotNull("-intermediatefolder ", this.IntermediateDirectory); commandLineBuilder.AppendSwitchIfNotNull("-contentsfile ", this.BindContentsFile); commandLineBuilder.AppendSwitchIfNotNull("-outputsfile ", this.BindOutputsFile); commandLineBuilder.AppendSwitchIfNotNull("-builtoutputsfile ", this.BindBuiltOutputsFile); commandLineBuilder.AppendSwitchIfNotNull("-wixprojectfile ", this.WixProjectFile); commandLineBuilder.AppendTextIfNotWhitespace(this.AdditionalOptions); + commandLineBuilder.AppendArrayIfNotNull("-bindPath ", this.CalculateBindPathStrings()); commandLineBuilder.AppendArrayIfNotNull("-loc ", this.LocalizationFiles); + commandLineBuilder.AppendArrayIfNotNull("-lib ", this.LibraryFiles); commandLineBuilder.AppendFileNamesIfNotNull(this.SourceFiles, " "); var commandLineString = commandLineBuilder.ToString(); - this.Log.LogMessage(MessageImportance.Normal, commandLineString); + this.Log.LogMessage(MessageImportance.Normal, "wix.exe " + commandLineString); var command = CommandLine.ParseStandardCommandLine(commandLineString); command?.Execute(); @@ -160,6 +168,27 @@ namespace WixToolset.BuildTasks this.Log.LogMessageFromText(e.Message, MessageImportance.Normal); } + private IEnumerable CalculateBindPathStrings() + { + if (null != this.BindInputPaths) + { + foreach (var item in this.BindInputPaths) + { + var path = item.GetMetadata("FullPath"); + + var bindName = item.GetMetadata("BindName"); + if (!String.IsNullOrEmpty(bindName)) + { + yield return String.Concat(bindName, "=", path); + } + else + { + yield return path; + } + } + } + } + ///// ///// Builds a command line from options in this task. ///// diff --git a/src/WixToolset.BuildTasks/WixCommandLineBuilder.cs b/src/WixToolset.BuildTasks/WixCommandLineBuilder.cs index 9a6a005d..3f3084a3 100644 --- a/src/WixToolset.BuildTasks/WixCommandLineBuilder.cs +++ b/src/WixToolset.BuildTasks/WixCommandLineBuilder.cs @@ -49,7 +49,7 @@ namespace WixToolset.BuildTasks /// /// Switch to append. /// Values specified by the user. - public void AppendArrayIfNotNull(string switchName, ITaskItem[] values) + public void AppendArrayIfNotNull(string switchName, IEnumerable values) { if (values != null) { @@ -65,7 +65,7 @@ namespace WixToolset.BuildTasks /// /// Switch to append. /// Values specified by the user. - public void AppendArrayIfNotNull(string switchName, string[] values) + public void AppendArrayIfNotNull(string switchName, IEnumerable values) { if (values != null) { @@ -77,9 +77,9 @@ namespace WixToolset.BuildTasks } /// - /// Build the extensions argument. Each extension is searched in the current folder, user defined search + /// Build the extensions argument. Each extension is searched in the current folder, user defined search /// directories (ReferencePath), HintPath, and under Wix Extension Directory in that order. - /// The order of precednce is based off of that described in Microsoft.Common.Targets's SearchPaths + /// The order of precedence is based off of that described in Microsoft.Common.Targets's SearchPaths /// property for the ResolveAssemblyReferences task. /// /// The list of extensions to include. @@ -92,21 +92,19 @@ namespace WixToolset.BuildTasks return; } - string resolvedPath; - foreach (ITaskItem extension in extensions) { string className = extension.GetMetadata("Class"); string fileName = Path.GetFileName(extension.ItemSpec); - if (Path.GetExtension(fileName).Length == 0) + if (String.IsNullOrEmpty(Path.GetExtension(fileName))) { fileName += ".dll"; } // First try reference paths - resolvedPath = FileSearchHelperMethods.SearchFilePaths(referencePaths, fileName); + var resolvedPath = FileSearchHelperMethods.SearchFilePaths(referencePaths, fileName); if (String.IsNullOrEmpty(resolvedPath)) { @@ -118,7 +116,7 @@ namespace WixToolset.BuildTasks // Now try the item itself resolvedPath = extension.ItemSpec; - if (Path.GetExtension(resolvedPath).Length == 0) + if (String.IsNullOrEmpty(Path.GetExtension(resolvedPath))) { resolvedPath += ".dll"; } diff --git a/src/WixToolset.BuildTasks/wix.targets b/src/WixToolset.BuildTasks/wix.targets index eadd33ec..bcf0163b 100644 --- a/src/WixToolset.BuildTasks/wix.targets +++ b/src/WixToolset.BuildTasks/wix.targets @@ -646,17 +646,17 @@ @(_BindInputs); $(MSBuildAllProjects)" Outputs="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindBuiltOutputsFile);@(_BindBuiltOutputs)" - Condition=" '@(Compile)' != '' and ('$(OutputType)' == 'Bundle' or '$(OutputType)' == 'Package' or '$(OutputType)' == 'PatchCreation' or '$(OutputType)' == 'Module')"> - + Condition=" '@(Compile)' != '' "> + $([System.IO.Path]::GetFullPath($(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(TargetName)$(TargetExt))) $(TargetPdbDir)%(CultureGroup.OutputFolder)$(TargetPdbName) + Name="ReadPreviousBindInputsAndBuiltOutputs"> @@ -1169,17 +1169,16 @@ ================================================================================================ --> + Name="UpdateLinkFileWrites"> - - - + + + @@ -1302,7 +1301,7 @@ - <_FullPathToCopy Include="$(TargetPath)" Condition=" '@(_FullPathToCopy)'=='' " /> + <_FullPathToCopy Include="$(OutputFile)" Condition=" '@(_FullPathToCopy)'=='' " /> <_RelativePath Include="$([MSBuild]::MakeRelative($(FullIntermediateOutputPath), %(_FullPathToCopy.Identity)))" /> diff --git a/src/WixToolset.Core/CommandLine/BuildCommand.cs b/src/WixToolset.Core/CommandLine/BuildCommand.cs index ffb48305..d8954cb7 100644 --- a/src/WixToolset.Core/CommandLine/BuildCommand.cs +++ b/src/WixToolset.Core/CommandLine/BuildCommand.cs @@ -7,17 +7,24 @@ namespace WixToolset.Core using System.IO; using System.Linq; using WixToolset.Data; + using WixToolset.Extensibility; internal class BuildCommand : ICommand { - public BuildCommand(IEnumerable sources, IDictionary preprocessorVariables, IEnumerable locFiles, string outputPath, IEnumerable cultures, string contentsFile, string outputsFile, string builtOutputsFile, string wixProjectFile) + public BuildCommand(IEnumerable sources, IDictionary preprocessorVariables, IEnumerable locFiles, IEnumerable libraryFiles, string outputPath, OutputType outputType, IEnumerable cultures, bool bindFiles, IEnumerable bindPaths, string intermediateFolder, string contentsFile, string outputsFile, string builtOutputsFile, string wixProjectFile) { this.LocFiles = locFiles; + this.LibraryFiles = libraryFiles; this.PreprocessorVariables = preprocessorVariables; this.SourceFiles = sources; this.OutputPath = outputPath; + this.OutputType = outputType; this.Cultures = cultures; + this.BindFiles = bindFiles; + this.BindPaths = bindPaths; + + this.IntermediateFolder = intermediateFolder ?? Path.GetTempPath(); this.ContentsFile = contentsFile; this.OutputsFile = outputsFile; this.BuiltOutputsFile = builtOutputsFile; @@ -26,14 +33,24 @@ namespace WixToolset.Core public IEnumerable LocFiles { get; } + public IEnumerable LibraryFiles { get; } + private IEnumerable SourceFiles { get; } private IDictionary PreprocessorVariables { get; } private string OutputPath { get; } + private OutputType OutputType { get; } + public IEnumerable Cultures { get; } + public bool BindFiles { get; } + + public IEnumerable BindPaths { get; } + + public string IntermediateFolder { get; } + public string ContentsFile { get; } public string OutputsFile { get; } @@ -44,21 +61,135 @@ namespace WixToolset.Core public int Execute() { - var intermediates = CompilePhase(); + var intermediates = this.CompilePhase(); + + var tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions()); + + if (this.OutputType == OutputType.Library) + { + this.LibraryPhase(intermediates, tableDefinitions); + } + else + { + var output = this.LinkPhase(intermediates, tableDefinitions); + + if (!Messaging.Instance.EncounteredError) + { + this.BindPhase(output, tableDefinitions); + } + } + + return Messaging.Instance.LastErrorNumber; + } + + private IEnumerable CompilePhase() + { + var intermediates = new List(); + + var preprocessor = new Preprocessor(); + + var compiler = new Compiler(); + + foreach (var sourceFile in this.SourceFiles) + { + var document = preprocessor.Process(sourceFile.SourcePath, this.PreprocessorVariables); + + var intermediate = compiler.Compile(document); + + intermediates.Add(intermediate); + } + + return intermediates; + } + + private void LibraryPhase(IEnumerable intermediates, TableDefinitionCollection tableDefinitions) + { + var localizations = this.LoadLocalizationFiles(tableDefinitions).ToList(); + + // If there was an error adding localization files, then bail. + if (Messaging.Instance.EncounteredError) + { + return; + } var sections = intermediates.SelectMany(i => i.Sections).ToList(); + LibraryBinaryFileResolver resolver = null; + + if (this.BindFiles) + { + resolver = new LibraryBinaryFileResolver(); + resolver.FileManagers = new List { new BinderFileManager() }; ; + resolver.VariableResolver = new WixVariableResolver(); + + BinderFileManagerCore core = new BinderFileManagerCore(); + core.AddBindPaths(this.BindPaths, BindStage.Normal); + + foreach (var fileManager in resolver.FileManagers) + { + fileManager.Core = core; + } + } + + var librarian = new Librarian(); + + var library = librarian.Combine(sections, localizations, resolver); + + library?.Save(this.OutputPath); + } + + private Output LinkPhase(IEnumerable intermediates, TableDefinitionCollection tableDefinitions) + { + var sections = intermediates.SelectMany(i => i.Sections).ToList(); + + sections.AddRange(SectionsFromLibraries(tableDefinitions)); + var linker = new Linker(); - var output = linker.Link(sections, OutputType.Product); + var output = linker.Link(sections, this.OutputType); + + return output; + } + + private IEnumerable
SectionsFromLibraries(TableDefinitionCollection tableDefinitions) + { + var sections = new List
(); + + if (this.LibraryFiles != null) + { + foreach (var libraryFile in this.LibraryFiles) + { + try + { + var library = Library.Load(libraryFile, tableDefinitions, false); + + sections.AddRange(library.Sections); + } + catch (WixCorruptFileException e) + { + Messaging.Instance.OnMessage(e.Error); + } + catch (WixUnexpectedFileFormatException e) + { + Messaging.Instance.OnMessage(e.Error); + } + } + } - var localizer = new Localizer(); + return sections; + } + + private void BindPhase(Output output, TableDefinitionCollection tableDefinitions) + { + var localizations = this.LoadLocalizationFiles(tableDefinitions).ToList(); + + var localizer = new Localizer(localizations); + + var resolver = new WixVariableResolver(localizer); var binder = new Binder(); - binder.TempFilesLocation = Path.GetTempPath(); - binder.WixVariableResolver = new WixVariableResolver(); - binder.WixVariableResolver.Localizer = localizer; - binder.AddExtension(new BinderFileManager()); + binder.TempFilesLocation = this.IntermediateFolder; + binder.WixVariableResolver = resolver; binder.SuppressValidation = true; binder.ContentsFile = this.ContentsFile; @@ -66,35 +197,50 @@ namespace WixToolset.Core binder.BuiltOutputsFile = this.BuiltOutputsFile; binder.WixprojectFile = this.WixProjectFile; - foreach (var loc in this.LocFiles) + if (this.BindPaths != null) { - var localization = Localizer.ParseLocalizationFile(loc, linker.TableDefinitions); - binder.WixVariableResolver.Localizer.AddLocalization(localization); + binder.BindPaths.AddRange(this.BindPaths); } - binder.Bind(output, this.OutputPath); + binder.AddExtension(new BinderFileManager()); - return 0; + binder.Bind(output, this.OutputPath); } - private IEnumerable CompilePhase() + private IEnumerable LoadLocalizationFiles(TableDefinitionCollection tableDefinitions) { - var intermediates = new List(); - - var preprocessor = new Preprocessor(); + foreach (var loc in this.LocFiles) + { + var localization = Localizer.ParseLocalizationFile(loc, tableDefinitions); - var compiler = new Compiler(); + yield return localization; + } + } - foreach (var sourceFile in this.SourceFiles) - { - var document = preprocessor.Process(sourceFile.SourcePath, this.PreprocessorVariables); + /// + /// File resolution mechanism to create binary library. + /// + private class LibraryBinaryFileResolver : ILibraryBinaryFileResolver + { + public IEnumerable FileManagers { get; set; } - var intermediate = compiler.Compile(document); + public WixVariableResolver VariableResolver { get; set; } - intermediates.Add(intermediate); + public string Resolve(SourceLineNumber sourceLineNumber, string table, string path) + { + string resolvedPath = this.VariableResolver.ResolveVariables(sourceLineNumber, path, false); + + foreach (IBinderFileManager fileManager in this.FileManagers) + { + string finalPath = fileManager.ResolveFile(resolvedPath, table, sourceLineNumber, BindStage.Normal); + if (!String.IsNullOrEmpty(finalPath)) + { + return finalPath; + } + } + + return null; } - - return intermediates; } } } diff --git a/src/WixToolset.Core/CommandLine/CommandLine.cs b/src/WixToolset.Core/CommandLine/CommandLine.cs index 440ae9ef..cac54091 100644 --- a/src/WixToolset.Core/CommandLine/CommandLine.cs +++ b/src/WixToolset.Core/CommandLine/CommandLine.cs @@ -57,14 +57,20 @@ namespace WixToolset.Core var showVersion = false; var outputFolder = String.Empty; var outputFile = String.Empty; - var sourceFile = String.Empty; + var outputType = String.Empty; var verbose = false; var files = new List(); var defines = new List(); var includePaths = new List(); var locFiles = new List(); + var libraryFiles = new List(); var suppressedWarnings = new List(); + var bindFiles = false; + var bindPaths = new List(); + + var intermediateFolder = String.Empty; + var cultures = new List(); var contentsFile = String.Empty; var outputsFile = String.Empty; @@ -84,6 +90,14 @@ namespace WixToolset.Core cmdline.ShowHelp = true; return true; + case "bindfiles": + bindFiles = true; + return true; + + case "bindpath": + cmdline.GetNextArgumentOrError(bindPaths); + return true; + case "cultures": cmdline.GetNextArgumentOrError(cultures); return true; @@ -110,15 +124,27 @@ namespace WixToolset.Core cmdline.GetNextArgumentOrError(includePaths); return true; + case "intermediatefolder": + cmdline.GetNextArgumentOrError(ref intermediateFolder); + return true; + case "loc": cmdline.GetNextArgumentAsFilePathOrError(locFiles, "localization files"); return true; + case "lib": + cmdline.GetNextArgumentAsFilePathOrError(libraryFiles, "library files"); + return true; + case "o": case "out": cmdline.GetNextArgumentOrError(ref outputFile); return true; + case "outputtype": + cmdline.GetNextArgumentOrError(ref outputType); + return true; + case "nologo": showLogo = false; return true; @@ -143,6 +169,8 @@ namespace WixToolset.Core } }); + Messaging.Instance.ShowVerboseMessages = verbose; + if (showVersion) { return new VersionCommand(); @@ -164,8 +192,10 @@ namespace WixToolset.Core { var sourceFiles = GatherSourceFiles(files, outputFolder); var variables = GatherPreprocessorVariables(defines); + var bindPathList = GatherBindPaths(bindPaths); var extensions = cli.ExtensionManager; - return new BuildCommand(sourceFiles, variables, locFiles, outputFile, cultures, contentsFile, outputsFile, builtOutputsFile, wixProjectFile); + var type = CalculateOutputType(outputType, outputFile); + return new BuildCommand(sourceFiles, variables, locFiles, libraryFiles, outputFile, type, cultures, bindFiles, bindPathList, intermediateFolder, contentsFile, outputsFile, builtOutputsFile, wixProjectFile); } case Commands.Compile: @@ -179,6 +209,46 @@ namespace WixToolset.Core return null; } + private static OutputType CalculateOutputType(string outputType, string outputFile) + { + if (String.IsNullOrEmpty(outputType)) + { + outputType = Path.GetExtension(outputFile); + } + + switch (outputType.ToLowerInvariant()) + { + case "bundle": + case ".exe": + return OutputType.Bundle; + + case "library": + case ".wixlib": + return OutputType.Library; + + case "module": + case ".msm": + return OutputType.Module; + + case "patch": + case ".msp": + return OutputType.Patch; + + case ".pcp": + return OutputType.PatchCreation; + + case "product": + case ".msi": + return OutputType.Product; + + case "transform": + case ".mst": + return OutputType.Transform; + } + + return OutputType.Unknown; + } + private static CommandLine Parse(string commandLineString, Func parseArgument) { var arguments = CommandLine.ParseArgumentsToArray(commandLineString).ToArray(); @@ -239,6 +309,26 @@ namespace WixToolset.Core return variables; } + private static IEnumerable GatherBindPaths(IEnumerable bindPaths) + { + var result = new List(); + + foreach (var bindPath in bindPaths) + { + BindPath bp = BindPath.Parse(bindPath); + + if (Directory.Exists(bp.Path)) + { + result.Add(bp); + } + else if (File.Exists(bp.Path)) + { + Messaging.Instance.OnMessage(WixErrors.ExpectedDirectoryGotFile("-bindpath", bp.Path)); + } + } + + return result; + } /// /// Get a set of files that possibly have a search pattern in the path (such as '*'). @@ -361,7 +451,7 @@ namespace WixToolset.Core private static bool TryDequeue(Queue q, out string arg) { - if (q.Count> 0) + if (q.Count > 0) { arg = q.Dequeue(); return true; @@ -469,11 +559,6 @@ namespace WixToolset.Core return false; } - /// - /// Parses a response file. - /// - /// The file to parse. - /// The array of arguments. private static List ParseResponseFile(string responseFile) { string arguments; @@ -486,11 +571,6 @@ namespace WixToolset.Core return CommandLine.ParseArgumentsToArray(arguments); } - /// - /// Parses an argument string into an argument array based on whitespace and quoting. - /// - /// Argument string. - /// Argument array. private static List ParseArgumentsToArray(string arguments) { // Scan and parse the arguments string, dividing up the arguments based on whitespace. @@ -526,7 +606,7 @@ namespace WixToolset.Core // Add the argument to the list if it's not empty. if (arg.Length > 0) { - argsList.Add(CommandLine.ExpandEnvVars(arg.ToString())); + argsList.Add(CommandLine.ExpandEnvironmentVariables(arg.ToString())); arg.Length = 0; } } @@ -557,12 +637,7 @@ namespace WixToolset.Core return argsList; } - /// - /// Expand enxironment variables contained in the passed string - /// - /// - /// - private static string ExpandEnvVars(string arguments) + private static string ExpandEnvironmentVariables(string arguments) { var id = Environment.GetEnvironmentVariables(); diff --git a/src/WixToolset.Core/Librarian.cs b/src/WixToolset.Core/Librarian.cs index daf53478..66a8c32d 100644 --- a/src/WixToolset.Core/Librarian.cs +++ b/src/WixToolset.Core/Librarian.cs @@ -4,8 +4,8 @@ namespace WixToolset { using System; using System.Collections.Generic; + using System.Linq; using WixToolset.Data; - using WixToolset.Extensibility; using WixToolset.Link; /// @@ -13,70 +13,27 @@ namespace WixToolset /// public sealed class Librarian { - /// - /// Instantiate a new Librarian class. - /// - public Librarian() - { - this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions()); - } - - /// - /// Gets table definitions used by this librarian. - /// - /// Table definitions. - public TableDefinitionCollection TableDefinitions { get; private set; } - - /// - /// Adds an extension's data. - /// - /// The extension data to add. - public void AddExtensionData(IExtensionData extension) - { - if (null != extension.TableDefinitions) - { - foreach (TableDefinition tableDefinition in extension.TableDefinitions) - { - try - { - this.TableDefinitions.Add(tableDefinition); - } - catch (ArgumentException) - { - Messaging.Instance.OnMessage(WixErrors.DuplicateExtensionTable(extension.GetType().ToString(), tableDefinition.Name)); - } - } - } - } - /// /// Create a library by combining several intermediates (objects). /// /// The sections to combine into a library. /// Returns the new library. - public Library Combine(IEnumerable
sections) + public Library Combine(IEnumerable
sections, IEnumerable localizations, ILibraryBinaryFileResolver resolver) { - Library library = new Library(sections); + var localizationsByCulture = CollateLocalizations(localizations); - this.Validate(library); + var embedFilePaths = ResolveFilePathsToEmbed(sections, resolver); - return (Messaging.Instance.EncounteredError ? null : library); - } + var library = new Library(sections, localizationsByCulture, embedFilePaths); - /// - /// Sends a message to the message delegate if there is one. - /// - /// Message event arguments. - public void OnMessage(MessageEventArgs e) - { - Messaging.Instance.OnMessage(e); + return this.Validate(library); } /// /// Validate that a library contains one entry section and no duplicate symbols. /// /// Library to validate. - private void Validate(Library library) + private Library Validate(Library library) { FindEntrySectionAndLoadSymbolsCommand find = new FindEntrySectionAndLoadSymbolsCommand(library.Sections); find.Execute(); @@ -90,6 +47,66 @@ namespace WixToolset // ReportDuplicateResolvedSymbolErrorsCommand reportDupes = new ReportDuplicateResolvedSymbolErrorsCommand(find.SymbolsWithDuplicates, resolve.ResolvedSections); // reportDupes.Execute(); // } + + return (Messaging.Instance.EncounteredError ? null : library); + } + + private static Dictionary CollateLocalizations(IEnumerable localizations) + { + var localizationsByCulture = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var localization in localizations) + { + if (localizationsByCulture.TryGetValue(localization.Culture, out var existingCulture)) + { + existingCulture.Merge(localization); + } + else + { + localizationsByCulture.Add(localization.Culture, localization); + } + } + + return localizationsByCulture; + } + + private static List ResolveFilePathsToEmbed(IEnumerable
sections, ILibraryBinaryFileResolver resolver) + { + var embedFilePaths = new List(); + + // Resolve paths to files that are to be embedded in the library. + if (null != resolver) + { + foreach (Table table in sections.SelectMany(s => s.Tables)) + { + foreach (Row row in table.Rows) + { + foreach (ObjectField objectField in row.Fields.OfType()) + { + if (null != objectField.Data) + { + string file = resolver.Resolve(row.SourceLineNumbers, table.Name, (string)objectField.Data); + if (!String.IsNullOrEmpty(file)) + { + // File was successfully resolved so track the embedded index as the embedded file index. + objectField.EmbeddedFileIndex = embedFilePaths.Count; + embedFilePaths.Add(file); + } + else + { + Messaging.Instance.OnMessage(WixDataErrors.FileNotFound(row.SourceLineNumbers, (string)objectField.Data, table.Name)); + } + } + else // clear out embedded file id in case there was one there before. + { + objectField.EmbeddedFileIndex = null; + } + } + } + } + } + + return embedFilePaths; } } } diff --git a/src/WixToolset.Core/Localizer.cs b/src/WixToolset.Core/Localizer.cs index 3c299896..63ead24a 100644 --- a/src/WixToolset.Core/Localizer.cs +++ b/src/WixToolset.Core/Localizer.cs @@ -23,11 +23,32 @@ namespace WixToolset /// /// Instantiate a new Localizer. /// - public Localizer() + public Localizer(IEnumerable localizations) { this.Codepage = -1; - this.variables = new Dictionary(); + this.variables = new Dictionary(); this.localizedControls = new Dictionary(); + + foreach (var localization in localizations) + { + if (-1 == this.Codepage) + { + this.Codepage = localization.Codepage; + } + + foreach (WixVariableRow wixVariableRow in localization.Variables) + { + Localizer.AddWixVariable(this.variables, wixVariableRow); + } + + foreach (KeyValuePair localizedControl in localization.LocalizedControls) + { + if (!this.localizedControls.ContainsKey(localizedControl.Key)) + { + this.localizedControls.Add(localizedControl.Key, localizedControl.Value); + } + } + } } /// @@ -75,31 +96,6 @@ namespace WixToolset return localization; } - /// - /// Add a localization file. - /// - /// The localization file to add. - public void AddLocalization(Localization localization) - { - if (-1 == this.Codepage) - { - this.Codepage = localization.Codepage; - } - - foreach (WixVariableRow wixVariableRow in localization.Variables) - { - Localizer.AddWixVariable(this.variables, wixVariableRow); - } - - foreach (KeyValuePair localizedControl in localization.LocalizedControls) - { - if (!this.localizedControls.ContainsKey(localizedControl.Key)) - { - this.localizedControls.Add(localizedControl.Key, localizedControl.Value); - } - } - } - /// /// Get a localized data value. /// @@ -107,8 +103,7 @@ namespace WixToolset /// The localized data value or null if it wasn't found. public string GetLocalizedValue(string id) { - WixVariableRow wixVariableRow; - return this.variables.TryGetValue(id, out wixVariableRow) ? wixVariableRow.Value : null; + return this.variables.TryGetValue(id, out var wixVariableRow) ? wixVariableRow.Value : null; } /// diff --git a/src/WixToolset.Core/WixVariableResolver.cs b/src/WixToolset.Core/WixVariableResolver.cs index 921ff1e3..d437423c 100644 --- a/src/WixToolset.Core/WixVariableResolver.cs +++ b/src/WixToolset.Core/WixVariableResolver.cs @@ -3,7 +3,6 @@ namespace WixToolset { using System; - using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -17,34 +16,27 @@ namespace WixToolset /// public sealed class WixVariableResolver { - private Localizer localizer; private Dictionary wixVariables; /// /// Instantiate a new WixVariableResolver. /// - public WixVariableResolver() + public WixVariableResolver(Localizer localizer = null) { this.wixVariables = new Dictionary(); + this.Localizer = localizer; } /// /// Gets or sets the localizer. /// /// The localizer. - public Localizer Localizer - { - get { return this.localizer; } - set { this.localizer = value; } - } + public Localizer Localizer { get; private set; } /// /// Gets the count of variables added to the resolver. /// - public int VariableCount - { - get { return this.wixVariables.Count; } - } + public int VariableCount => this.wixVariables.Count; /// /// Add a variable. @@ -198,9 +190,9 @@ namespace WixToolset Messaging.Instance.OnMessage(WixWarnings.DeprecatedLocalizationVariablePrefix(sourceLineNumbers, variableId)); } - if (null != this.localizer) + if (null != this.Localizer) { - resolvedValue = this.localizer.GetLocalizedValue(variableId); + resolvedValue = this.Localizer.GetLocalizedValue(variableId); } } else if (!localizationOnly && "wix" == variableNamespace) -- cgit v1.2.3-55-g6feb