@joebigelow / wix-1 / commits / 39c7e2bb

Add support for BindPaths and building .wixlibs

Rob Mensching committed Oct 1, 2017 at 14:25 UTC 39c7e2bb0399802e65a3025c4a73db211e730479
8 files changed +421 -170
src/WixToolset.BuildTasks/DoIt.cs
+40 -11
@@ -2,6 +2,9 @@
2
3 namespace WixToolset.BuildTasks
4 {
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Runtime.InteropServices;
8 using Microsoft.Build.Framework;
9 using Microsoft.Build.Utilities;
10 using WixToolset.Core;
@@ -40,12 +43,14 @@ namespace WixToolset.BuildTasks
43
44 public bool NoLogo { get; set; }
45
43 - public ITaskItem[] ObjectFiles { get; set; }
46 + public ITaskItem[] LibraryFiles { get; set; }
47
48 [Output]
49 [Required]
50 public ITaskItem OutputFile { get; set; }
51
52 + public string OutputType { get; set; }
53 +
54 public string PdbOutputFile { get; set; }
55
56 public bool Pedantic { get; set; }
@@ -84,7 +89,7 @@ namespace WixToolset.BuildTasks
89
90 public ITaskItem[] BindInputPaths { get; set; }
91 public bool BindFiles { get; set; }
87 - public ITaskItem BindContentsFile{ get; set; }
92 + public ITaskItem BindContentsFile { get; set; }
93 public ITaskItem BindOutputsFile { get; set; }
94 public ITaskItem BindBuiltOutputsFile { get; set; }
95
@@ -102,21 +107,20 @@ namespace WixToolset.BuildTasks
107 public string[] SuppressIces { get; set; }
108 public string AdditionalCub { get; set; }
109
105 -
106 -
110 public override bool Execute()
111 {
112 try
113 {
114 this.ExecuteCore();
115 }
113 - catch (BuildException e)
114 - {
115 - this.Log.LogErrorFromException(e);
116 - }
117 - catch (WixException e)
116 + catch (Exception e)
117 {
118 this.Log.LogErrorFromException(e);
119 +
120 + if (e is NullReferenceException || e is SEHException)
121 + {
122 + throw;
123 + }
124 }
125
126 return !this.Log.HasLoggedErrors;
@@ -129,27 +133,31 @@ namespace WixToolset.BuildTasks
133 commandLineBuilder.AppendTextUnquoted("build");
134
135 commandLineBuilder.AppendSwitchIfNotNull("-out ", this.OutputFile);
136 + commandLineBuilder.AppendSwitchIfNotNull("-outputType ", this.OutputType);
137 + commandLineBuilder.AppendIfTrue("-nologo", this.NoLogo);
138 commandLineBuilder.AppendSwitchIfNotNull("-cultures ", this.Cultures);
139 commandLineBuilder.AppendArrayIfNotNull("-d ", this.DefineConstants);
140 commandLineBuilder.AppendArrayIfNotNull("-I ", this.IncludeSearchPaths);
141 commandLineBuilder.AppendExtensions(this.Extensions, this.ExtensionDirectory, this.ReferencePaths);
136 - commandLineBuilder.AppendIfTrue("-nologo", this.NoLogo);
142 commandLineBuilder.AppendIfTrue("-sval", this.SuppressValidation);
143 commandLineBuilder.AppendArrayIfNotNull("-sice ", this.SuppressIces);
144 commandLineBuilder.AppendSwitchIfNotNull("-usf ", this.UnreferencedSymbolsFile);
145 commandLineBuilder.AppendSwitchIfNotNull("-cc ", this.CabinetCachePath);
146 + commandLineBuilder.AppendSwitchIfNotNull("-intermediatefolder ", this.IntermediateDirectory);
147 commandLineBuilder.AppendSwitchIfNotNull("-contentsfile ", this.BindContentsFile);
148 commandLineBuilder.AppendSwitchIfNotNull("-outputsfile ", this.BindOutputsFile);
149 commandLineBuilder.AppendSwitchIfNotNull("-builtoutputsfile ", this.BindBuiltOutputsFile);
150 commandLineBuilder.AppendSwitchIfNotNull("-wixprojectfile ", this.WixProjectFile);
151 commandLineBuilder.AppendTextIfNotWhitespace(this.AdditionalOptions);
152
153 + commandLineBuilder.AppendArrayIfNotNull("-bindPath ", this.CalculateBindPathStrings());
154 commandLineBuilder.AppendArrayIfNotNull("-loc ", this.LocalizationFiles);
155 + commandLineBuilder.AppendArrayIfNotNull("-lib ", this.LibraryFiles);
156 commandLineBuilder.AppendFileNamesIfNotNull(this.SourceFiles, " ");
157
158 var commandLineString = commandLineBuilder.ToString();
159
152 - this.Log.LogMessage(MessageImportance.Normal, commandLineString);
160 + this.Log.LogMessage(MessageImportance.Normal, "wix.exe " + commandLineString);
161
162 var command = CommandLine.ParseStandardCommandLine(commandLineString);
163 command?.Execute();
@@ -160,6 +168,27 @@ namespace WixToolset.BuildTasks
168 this.Log.LogMessageFromText(e.Message, MessageImportance.Normal);
169 }
170
171 + private IEnumerable<string> CalculateBindPathStrings()
172 + {
173 + if (null != this.BindInputPaths)
174 + {
175 + foreach (var item in this.BindInputPaths)
176 + {
177 + var path = item.GetMetadata("FullPath");
178 +
179 + var bindName = item.GetMetadata("BindName");
180 + if (!String.IsNullOrEmpty(bindName))
181 + {
182 + yield return String.Concat(bindName, "=", path);
183 + }
184 + else
185 + {
186 + yield return path;
187 + }
188 + }
189 + }
190 + }
191 +
192 ///// <summary>
193 ///// Builds a command line from options in this task.
194 ///// </summary>
src/WixToolset.BuildTasks/WixCommandLineBuilder.cs
+7 -9
@@ -49,7 +49,7 @@ namespace WixToolset.BuildTasks
49 /// </summary>
50 /// <param name="switchName">Switch to append.</param>
51 /// <param name="values">Values specified by the user.</param>
52 - public void AppendArrayIfNotNull(string switchName, ITaskItem[] values)
52 + public void AppendArrayIfNotNull(string switchName, IEnumerable<ITaskItem> values)
53 {
54 if (values != null)
55 {
@@ -65,7 +65,7 @@ namespace WixToolset.BuildTasks
65 /// </summary>
66 /// <param name="switchName">Switch to append.</param>
67 /// <param name="values">Values specified by the user.</param>
68 - public void AppendArrayIfNotNull(string switchName, string[] values)
68 + public void AppendArrayIfNotNull(string switchName, IEnumerable<string> values)
69 {
70 if (values != null)
71 {
@@ -77,9 +77,9 @@ namespace WixToolset.BuildTasks
77 }
78
79 /// <summary>
80 - /// Build the extensions argument. Each extension is searched in the current folder, user defined search
80 + /// Build the extensions argument. Each extension is searched in the current folder, user defined search
81 /// directories (ReferencePath), HintPath, and under Wix Extension Directory in that order.
82 - /// The order of precednce is based off of that described in Microsoft.Common.Targets's SearchPaths
82 + /// The order of precedence is based off of that described in Microsoft.Common.Targets's SearchPaths
83 /// property for the ResolveAssemblyReferences task.
84 /// </summary>
85 /// <param name="extensions">The list of extensions to include.</param>
@@ -92,21 +92,19 @@ namespace WixToolset.BuildTasks
92 return;
93 }
94
95 - string resolvedPath;
96 -
95 foreach (ITaskItem extension in extensions)
96 {
97 string className = extension.GetMetadata("Class");
98
99 string fileName = Path.GetFileName(extension.ItemSpec);
100
103 - if (Path.GetExtension(fileName).Length == 0)
101 + if (String.IsNullOrEmpty(Path.GetExtension(fileName)))
102 {
103 fileName += ".dll";
104 }
105
106 // First try reference paths
109 - resolvedPath = FileSearchHelperMethods.SearchFilePaths(referencePaths, fileName);
107 + var resolvedPath = FileSearchHelperMethods.SearchFilePaths(referencePaths, fileName);
108
109 if (String.IsNullOrEmpty(resolvedPath))
110 {
@@ -118,7 +116,7 @@ namespace WixToolset.BuildTasks
116 // Now try the item itself
117 resolvedPath = extension.ItemSpec;
118
121 - if (Path.GetExtension(resolvedPath).Length == 0)
119 + if (String.IsNullOrEmpty(Path.GetExtension(resolvedPath)))
120 {
121 resolvedPath += ".dll";
122 }
src/WixToolset.BuildTasks/wix.targets
+11 -12
@@ -646,17 +646,17 @@
646 @(_BindInputs);
647 $(MSBuildAllProjects)"
648 Outputs="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindBuiltOutputsFile);@(_BindBuiltOutputs)"
649 - Condition=" '@(Compile)' != '' and ('$(OutputType)' == 'Bundle' or '$(OutputType)' == 'Package' or '$(OutputType)' == 'PatchCreation' or '$(OutputType)' == 'Module')">
650 -
649 + Condition=" '@(Compile)' != '' ">
650
651 <PropertyGroup>
652 + <OutputFile>$([System.IO.Path]::GetFullPath($(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(TargetName)$(TargetExt)))</OutputFile>
653 <PdbOutputFile>$(TargetPdbDir)%(CultureGroup.OutputFolder)$(TargetPdbName)</PdbOutputFile>
654 </PropertyGroup>
655
656 <DoIt
657 SourceFiles="@(_CompileWithObjectPath)"
658 + LibraryFiles="@(WixLibProjects);@(_ResolvedWixLibraryPaths)"
659 LocalizationFiles="@(EmbeddedResource)"
659 - ObjectFiles="@(CompileObjOutput);@(WixObject);@(WixLibProjects);@(_ResolvedWixLibraryPaths)"
660
661 Cultures="%(CultureGroup.Identity)"
662
@@ -665,7 +665,8 @@
665
666 IntermediateDirectory="$(IntermediateOutputPath)"
667
668 - OutputFile="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(TargetName)$(TargetExt)"
668 + OutputFile="$(OutputFile)"
669 + OutputType="$(OutputType)"
670 PdbOutputFile="$(PdbOutputFile)"
671
672 AdditionalOptions="$(CompilerAdditionalOptions) $(LinkerAdditionalOptions)"
@@ -1029,8 +1030,7 @@
1030 ================================================================================================
1031 -->
1032 <Target
1032 - Name="ReadPreviousBindInputsAndBuiltOutputs"
1033 - Condition=" '$(OutputType)' == 'Bundle' or '$(OutputType)' == 'Package' or '$(OutputType)' == 'PatchCreation' or '$(OutputType)' == 'Module' ">
1033 + Name="ReadPreviousBindInputsAndBuiltOutputs">
1034
1035 <ReadLinesFromFile File="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindContentsFile)">
1036 <Output TaskParameter="Lines" ItemName="_BindInputs" />
@@ -1169,17 +1169,16 @@
1169 ================================================================================================
1170 -->
1171 <Target
1172 - Name="UpdateLinkFileWrites"
1173 - Condition=" '$(OutputType)' == 'Bundle' or '$(OutputType)' == 'Package' or '$(OutputType)' == 'PatchCreation' or '$(OutputType)' == 'Module' ">
1172 + Name="UpdateLinkFileWrites">
1173
1174 <ReadLinesFromFile File="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindOutputsFile)">
1175 <Output TaskParameter="Lines" ItemName="FileWrites"/>
1176 </ReadLinesFromFile>
1177
1178 <ItemGroup>
1180 - <FileWrites Include="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindContentsFile)" />
1181 - <FileWrites Include="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindOutputsFile)" />
1182 - <FileWrites Include="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindBuiltOutputsFile)" />
1179 + <FileWrites Include="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindContentsFile)" Condition=" Exists('$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindContentsFile)') " />
1180 + <FileWrites Include="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindOutputsFile)" Condition=" Exists('$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindOutputsFile)') " />
1181 + <FileWrites Include="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindBuiltOutputsFile)" Condition=" Exists('$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindBuiltOutputsFile)') " />
1182 </ItemGroup>
1183
1184 <Message Importance="low" Text="Build files after link: @(FileWrites)" />
@@ -1302,7 +1301,7 @@
1301 </ReadLinesFromFile>
1302
1303 <ItemGroup>
1305 - <_FullPathToCopy Include="$(TargetPath)" Condition=" '@(_FullPathToCopy)'=='' " />
1304 + <_FullPathToCopy Include="$(OutputFile)" Condition=" '@(_FullPathToCopy)'=='' " />
1305 <_RelativePath Include="$([MSBuild]::MakeRelative($(FullIntermediateOutputPath), %(_FullPathToCopy.Identity)))" />
1306 </ItemGroup>
1307
src/WixToolset.Core/CommandLine/BuildCommand.cs
+171 -25
@@ -7,17 +7,24 @@ namespace WixToolset.Core
7 using System.IO;
8 using System.Linq;
9 using WixToolset.Data;
10 + using WixToolset.Extensibility;
11
12 internal class BuildCommand : ICommand
13 {
13 - public BuildCommand(IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, string outputPath, IEnumerable<string> cultures, string contentsFile, string outputsFile, string builtOutputsFile, string wixProjectFile)
14 + public BuildCommand(IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, IEnumerable<string> libraryFiles, string outputPath, OutputType outputType, IEnumerable<string> cultures, bool bindFiles, IEnumerable<BindPath> bindPaths, string intermediateFolder, string contentsFile, string outputsFile, string builtOutputsFile, string wixProjectFile)
15 {
16 this.LocFiles = locFiles;
17 + this.LibraryFiles = libraryFiles;
18 this.PreprocessorVariables = preprocessorVariables;
19 this.SourceFiles = sources;
20 this.OutputPath = outputPath;
21 + this.OutputType = outputType;
22
23 this.Cultures = cultures;
24 + this.BindFiles = bindFiles;
25 + this.BindPaths = bindPaths;
26 +
27 + this.IntermediateFolder = intermediateFolder ?? Path.GetTempPath();
28 this.ContentsFile = contentsFile;
29 this.OutputsFile = outputsFile;
30 this.BuiltOutputsFile = builtOutputsFile;
@@ -26,14 +33,24 @@ namespace WixToolset.Core
33
34 public IEnumerable<string> LocFiles { get; }
35
36 + public IEnumerable<string> LibraryFiles { get; }
37 +
38 private IEnumerable<SourceFile> SourceFiles { get; }
39
40 private IDictionary<string, string> PreprocessorVariables { get; }
41
42 private string OutputPath { get; }
43
44 + private OutputType OutputType { get; }
45 +
46 public IEnumerable<string> Cultures { get; }
47
48 + public bool BindFiles { get; }
49 +
50 + public IEnumerable<BindPath> BindPaths { get; }
51 +
52 + public string IntermediateFolder { get; }
53 +
54 public string ContentsFile { get; }
55
56 public string OutputsFile { get; }
@@ -44,21 +61,135 @@ namespace WixToolset.Core
61
62 public int Execute()
63 {
47 - var intermediates = CompilePhase();
64 + var intermediates = this.CompilePhase();
65 +
66 + var tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions());
67 +
68 + if (this.OutputType == OutputType.Library)
69 + {
70 + this.LibraryPhase(intermediates, tableDefinitions);
71 + }
72 + else
73 + {
74 + var output = this.LinkPhase(intermediates, tableDefinitions);
75 +
76 + if (!Messaging.Instance.EncounteredError)
77 + {
78 + this.BindPhase(output, tableDefinitions);
79 + }
80 + }
81 +
82 + return Messaging.Instance.LastErrorNumber;
83 + }
84 +
85 + private IEnumerable<Intermediate> CompilePhase()
86 + {
87 + var intermediates = new List<Intermediate>();
88 +
89 + var preprocessor = new Preprocessor();
90 +
91 + var compiler = new Compiler();
92 +
93 + foreach (var sourceFile in this.SourceFiles)
94 + {
95 + var document = preprocessor.Process(sourceFile.SourcePath, this.PreprocessorVariables);
96 +
97 + var intermediate = compiler.Compile(document);
98 +
99 + intermediates.Add(intermediate);
100 + }
101 +
102 + return intermediates;
103 + }
104 +
105 + private void LibraryPhase(IEnumerable<Intermediate> intermediates, TableDefinitionCollection tableDefinitions)
106 + {
107 + var localizations = this.LoadLocalizationFiles(tableDefinitions).ToList();
108 +
109 + // If there was an error adding localization files, then bail.
110 + if (Messaging.Instance.EncounteredError)
111 + {
112 + return;
113 + }
114
115 var sections = intermediates.SelectMany(i => i.Sections).ToList();
116
117 + LibraryBinaryFileResolver resolver = null;
118 +
119 + if (this.BindFiles)
120 + {
121 + resolver = new LibraryBinaryFileResolver();
122 + resolver.FileManagers = new List<IBinderFileManager> { new BinderFileManager() }; ;
123 + resolver.VariableResolver = new WixVariableResolver();
124 +
125 + BinderFileManagerCore core = new BinderFileManagerCore();
126 + core.AddBindPaths(this.BindPaths, BindStage.Normal);
127 +
128 + foreach (var fileManager in resolver.FileManagers)
129 + {
130 + fileManager.Core = core;
131 + }
132 + }
133 +
134 + var librarian = new Librarian();
135 +
136 + var library = librarian.Combine(sections, localizations, resolver);
137 +
138 + library?.Save(this.OutputPath);
139 + }
140 +
141 + private Output LinkPhase(IEnumerable<Intermediate> intermediates, TableDefinitionCollection tableDefinitions)
142 + {
143 + var sections = intermediates.SelectMany(i => i.Sections).ToList();
144 +
145 + sections.AddRange(SectionsFromLibraries(tableDefinitions));
146 +
147 var linker = new Linker();
148
53 - var output = linker.Link(sections, OutputType.Product);
149 + var output = linker.Link(sections, this.OutputType);
150 +
151 + return output;
152 + }
153 +
154 + private IEnumerable<Section> SectionsFromLibraries(TableDefinitionCollection tableDefinitions)
155 + {
156 + var sections = new List<Section>();
157 +
158 + if (this.LibraryFiles != null)
159 + {
160 + foreach (var libraryFile in this.LibraryFiles)
161 + {
162 + try
163 + {
164 + var library = Library.Load(libraryFile, tableDefinitions, false);
165 +
166 + sections.AddRange(library.Sections);
167 + }
168 + catch (WixCorruptFileException e)
169 + {
170 + Messaging.Instance.OnMessage(e.Error);
171 + }
172 + catch (WixUnexpectedFileFormatException e)
173 + {
174 + Messaging.Instance.OnMessage(e.Error);
175 + }
176 + }
177 + }
178
55 - var localizer = new Localizer();
179 + return sections;
180 + }
181 +
182 + private void BindPhase(Output output, TableDefinitionCollection tableDefinitions)
183 + {
184 + var localizations = this.LoadLocalizationFiles(tableDefinitions).ToList();
185 +
186 + var localizer = new Localizer(localizations);
187 +
188 + var resolver = new WixVariableResolver(localizer);
189
190 var binder = new Binder();
58 - binder.TempFilesLocation = Path.GetTempPath();
59 - binder.WixVariableResolver = new WixVariableResolver();
60 - binder.WixVariableResolver.Localizer = localizer;
61 - binder.AddExtension(new BinderFileManager());
191 + binder.TempFilesLocation = this.IntermediateFolder;
192 + binder.WixVariableResolver = resolver;
193 binder.SuppressValidation = true;
194
195 binder.ContentsFile = this.ContentsFile;
@@ -66,35 +197,50 @@ namespace WixToolset.Core
197 binder.BuiltOutputsFile = this.BuiltOutputsFile;
198 binder.WixprojectFile = this.WixProjectFile;
199
69 - foreach (var loc in this.LocFiles)
200 + if (this.BindPaths != null)
201 {
71 - var localization = Localizer.ParseLocalizationFile(loc, linker.TableDefinitions);
72 - binder.WixVariableResolver.Localizer.AddLocalization(localization);
202 + binder.BindPaths.AddRange(this.BindPaths);
203 }
204
75 - binder.Bind(output, this.OutputPath);
205 + binder.AddExtension(new BinderFileManager());
206
77 - return 0;
207 + binder.Bind(output, this.OutputPath);
208 }
209
80 - private IEnumerable<Intermediate> CompilePhase()
210 + private IEnumerable<Localization> LoadLocalizationFiles(TableDefinitionCollection tableDefinitions)
211 {
82 - var intermediates = new List<Intermediate>();
83 -
84 - var preprocessor = new Preprocessor();
212 + foreach (var loc in this.LocFiles)
213 + {
214 + var localization = Localizer.ParseLocalizationFile(loc, tableDefinitions);
215
86 - var compiler = new Compiler();
216 + yield return localization;
217 + }
218 + }
219
88 - foreach (var sourceFile in this.SourceFiles)
89 - {
90 - var document = preprocessor.Process(sourceFile.SourcePath, this.PreprocessorVariables);
220 + /// <summary>
221 + /// File resolution mechanism to create binary library.
222 + /// </summary>
223 + private class LibraryBinaryFileResolver : ILibraryBinaryFileResolver
224 + {
225 + public IEnumerable<IBinderFileManager> FileManagers { get; set; }
226
92 - var intermediate = compiler.Compile(document);
227 + public WixVariableResolver VariableResolver { get; set; }
228
94 - intermediates.Add(intermediate);
229 + public string Resolve(SourceLineNumber sourceLineNumber, string table, string path)
230 + {
231 + string resolvedPath = this.VariableResolver.ResolveVariables(sourceLineNumber, path, false);
232 +
233 + foreach (IBinderFileManager fileManager in this.FileManagers)
234 + {
235 + string finalPath = fileManager.ResolveFile(resolvedPath, table, sourceLineNumber, BindStage.Normal);
236 + if (!String.IsNullOrEmpty(finalPath))
237 + {
238 + return finalPath;
239 + }
240 + }
241 +
242 + return null;
243 }
96 -
97 - return intermediates;
244 }
245 }
246 }
src/WixToolset.Core/CommandLine/CommandLine.cs
+95 -20
@@ -57,14 +57,20 @@ namespace WixToolset.Core
57 var showVersion = false;
58 var outputFolder = String.Empty;
59 var outputFile = String.Empty;
60 - var sourceFile = String.Empty;
60 + var outputType = String.Empty;
61 var verbose = false;
62 var files = new List<string>();
63 var defines = new List<string>();
64 var includePaths = new List<string>();
65 var locFiles = new List<string>();
66 + var libraryFiles = new List<string>();
67 var suppressedWarnings = new List<int>();
68
69 + var bindFiles = false;
70 + var bindPaths = new List<string>();
71 +
72 + var intermediateFolder = String.Empty;
73 +
74 var cultures = new List<string>();
75 var contentsFile = String.Empty;
76 var outputsFile = String.Empty;
@@ -84,6 +90,14 @@ namespace WixToolset.Core
90 cmdline.ShowHelp = true;
91 return true;
92
93 + case "bindfiles":
94 + bindFiles = true;
95 + return true;
96 +
97 + case "bindpath":
98 + cmdline.GetNextArgumentOrError(bindPaths);
99 + return true;
100 +
101 case "cultures":
102 cmdline.GetNextArgumentOrError(cultures);
103 return true;
@@ -110,15 +124,27 @@ namespace WixToolset.Core
124 cmdline.GetNextArgumentOrError(includePaths);
125 return true;
126
127 + case "intermediatefolder":
128 + cmdline.GetNextArgumentOrError(ref intermediateFolder);
129 + return true;
130 +
131 case "loc":
132 cmdline.GetNextArgumentAsFilePathOrError(locFiles, "localization files");
133 return true;
134
135 + case "lib":
136 + cmdline.GetNextArgumentAsFilePathOrError(libraryFiles, "library files");
137 + return true;
138 +
139 case "o":
140 case "out":
141 cmdline.GetNextArgumentOrError(ref outputFile);
142 return true;
143
144 + case "outputtype":
145 + cmdline.GetNextArgumentOrError(ref outputType);
146 + return true;
147 +
148 case "nologo":
149 showLogo = false;
150 return true;
@@ -143,6 +169,8 @@ namespace WixToolset.Core
169 }
170 });
171
172 + Messaging.Instance.ShowVerboseMessages = verbose;
173 +
174 if (showVersion)
175 {
176 return new VersionCommand();
@@ -164,8 +192,10 @@ namespace WixToolset.Core
192 {
193 var sourceFiles = GatherSourceFiles(files, outputFolder);
194 var variables = GatherPreprocessorVariables(defines);
195 + var bindPathList = GatherBindPaths(bindPaths);
196 var extensions = cli.ExtensionManager;
168 - return new BuildCommand(sourceFiles, variables, locFiles, outputFile, cultures, contentsFile, outputsFile, builtOutputsFile, wixProjectFile);
197 + var type = CalculateOutputType(outputType, outputFile);
198 + return new BuildCommand(sourceFiles, variables, locFiles, libraryFiles, outputFile, type, cultures, bindFiles, bindPathList, intermediateFolder, contentsFile, outputsFile, builtOutputsFile, wixProjectFile);
199 }
200
201 case Commands.Compile:
@@ -179,6 +209,46 @@ namespace WixToolset.Core
209 return null;
210 }
211
212 + private static OutputType CalculateOutputType(string outputType, string outputFile)
213 + {
214 + if (String.IsNullOrEmpty(outputType))
215 + {
216 + outputType = Path.GetExtension(outputFile);
217 + }
218 +
219 + switch (outputType.ToLowerInvariant())
220 + {
221 + case "bundle":
222 + case ".exe":
223 + return OutputType.Bundle;
224 +
225 + case "library":
226 + case ".wixlib":
227 + return OutputType.Library;
228 +
229 + case "module":
230 + case ".msm":
231 + return OutputType.Module;
232 +
233 + case "patch":
234 + case ".msp":
235 + return OutputType.Patch;
236 +
237 + case ".pcp":
238 + return OutputType.PatchCreation;
239 +
240 + case "product":
241 + case ".msi":
242 + return OutputType.Product;
243 +
244 + case "transform":
245 + case ".mst":
246 + return OutputType.Transform;
247 + }
248 +
249 + return OutputType.Unknown;
250 + }
251 +
252 private static CommandLine Parse(string commandLineString, Func<CommandLine, string, bool> parseArgument)
253 {
254 var arguments = CommandLine.ParseArgumentsToArray(commandLineString).ToArray();
@@ -239,6 +309,26 @@ namespace WixToolset.Core
309 return variables;
310 }
311
312 + private static IEnumerable<BindPath> GatherBindPaths(IEnumerable<string> bindPaths)
313 + {
314 + var result = new List<BindPath>();
315 +
316 + foreach (var bindPath in bindPaths)
317 + {
318 + BindPath bp = BindPath.Parse(bindPath);
319 +
320 + if (Directory.Exists(bp.Path))
321 + {
322 + result.Add(bp);
323 + }
324 + else if (File.Exists(bp.Path))
325 + {
326 + Messaging.Instance.OnMessage(WixErrors.ExpectedDirectoryGotFile("-bindpath", bp.Path));
327 + }
328 + }
329 +
330 + return result;
331 + }
332
333 /// <summary>
334 /// Get a set of files that possibly have a search pattern in the path (such as '*').
@@ -361,7 +451,7 @@ namespace WixToolset.Core
451
452 private static bool TryDequeue(Queue<string> q, out string arg)
453 {
364 - if (q.Count> 0)
454 + if (q.Count > 0)
455 {
456 arg = q.Dequeue();
457 return true;
@@ -469,11 +559,6 @@ namespace WixToolset.Core
559 return false;
560 }
561
472 - /// <summary>
473 - /// Parses a response file.
474 - /// </summary>
475 - /// <param name="responseFile">The file to parse.</param>
476 - /// <returns>The array of arguments.</returns>
562 private static List<string> ParseResponseFile(string responseFile)
563 {
564 string arguments;
@@ -486,11 +571,6 @@ namespace WixToolset.Core
571 return CommandLine.ParseArgumentsToArray(arguments);
572 }
573
489 - /// <summary>
490 - /// Parses an argument string into an argument array based on whitespace and quoting.
491 - /// </summary>
492 - /// <param name="arguments">Argument string.</param>
493 - /// <returns>Argument array.</returns>
574 private static List<string> ParseArgumentsToArray(string arguments)
575 {
576 // Scan and parse the arguments string, dividing up the arguments based on whitespace.
@@ -526,7 +606,7 @@ namespace WixToolset.Core
606 // Add the argument to the list if it's not empty.
607 if (arg.Length > 0)
608 {
529 - argsList.Add(CommandLine.ExpandEnvVars(arg.ToString()));
609 + argsList.Add(CommandLine.ExpandEnvironmentVariables(arg.ToString()));
610 arg.Length = 0;
611 }
612 }
@@ -557,12 +637,7 @@ namespace WixToolset.Core
637 return argsList;
638 }
639
560 - /// <summary>
561 - /// Expand enxironment variables contained in the passed string
562 - /// </summary>
563 - /// <param name="arguments"></param>
564 - /// <returns></returns>
565 - private static string ExpandEnvVars(string arguments)
640 + private static string ExpandEnvironmentVariables(string arguments)
641 {
642 var id = Environment.GetEnvironmentVariables();
643
src/WixToolset.Core/Librarian.cs
+67 -50
@@ -4,8 +4,8 @@ namespace WixToolset
4 {
5 using System;
6 using System.Collections.Generic;
7 + using System.Linq;
8 using WixToolset.Data;
8 - using WixToolset.Extensibility;
9 using WixToolset.Link;
10
11 /// <summary>
@@ -13,70 +13,27 @@ namespace WixToolset
13 /// </summary>
14 public sealed class Librarian
15 {
16 - /// <summary>
17 - /// Instantiate a new Librarian class.
18 - /// </summary>
19 - public Librarian()
20 - {
21 - this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions());
22 - }
23 -
24 - /// <summary>
25 - /// Gets table definitions used by this librarian.
26 - /// </summary>
27 - /// <value>Table definitions.</value>
28 - public TableDefinitionCollection TableDefinitions { get; private set; }
29 -
30 - /// <summary>
31 - /// Adds an extension's data.
32 - /// </summary>
33 - /// <param name="extension">The extension data to add.</param>
34 - public void AddExtensionData(IExtensionData extension)
35 - {
36 - if (null != extension.TableDefinitions)
37 - {
38 - foreach (TableDefinition tableDefinition in extension.TableDefinitions)
39 - {
40 - try
41 - {
42 - this.TableDefinitions.Add(tableDefinition);
43 - }
44 - catch (ArgumentException)
45 - {
46 - Messaging.Instance.OnMessage(WixErrors.DuplicateExtensionTable(extension.GetType().ToString(), tableDefinition.Name));
47 - }
48 - }
49 - }
50 - }
51 -
16 /// <summary>
17 /// Create a library by combining several intermediates (objects).
18 /// </summary>
19 /// <param name="sections">The sections to combine into a library.</param>
20 /// <returns>Returns the new library.</returns>
57 - public Library Combine(IEnumerable<Section> sections)
21 + public Library Combine(IEnumerable<Section> sections, IEnumerable<Localization> localizations, ILibraryBinaryFileResolver resolver)
22 {
59 - Library library = new Library(sections);
23 + var localizationsByCulture = CollateLocalizations(localizations);
24
61 - this.Validate(library);
25 + var embedFilePaths = ResolveFilePathsToEmbed(sections, resolver);
26
63 - return (Messaging.Instance.EncounteredError ? null : library);
64 - }
27 + var library = new Library(sections, localizationsByCulture, embedFilePaths);
28
66 - /// <summary>
67 - /// Sends a message to the message delegate if there is one.
68 - /// </summary>
69 - /// <param name="mea">Message event arguments.</param>
70 - public void OnMessage(MessageEventArgs e)
71 - {
72 - Messaging.Instance.OnMessage(e);
29 + return this.Validate(library);
30 }
31
32 /// <summary>
33 /// Validate that a library contains one entry section and no duplicate symbols.
34 /// </summary>
35 /// <param name="library">Library to validate.</param>
79 - private void Validate(Library library)
36 + private Library Validate(Library library)
37 {
38 FindEntrySectionAndLoadSymbolsCommand find = new FindEntrySectionAndLoadSymbolsCommand(library.Sections);
39 find.Execute();
@@ -90,6 +47,66 @@ namespace WixToolset
47 // ReportDuplicateResolvedSymbolErrorsCommand reportDupes = new ReportDuplicateResolvedSymbolErrorsCommand(find.SymbolsWithDuplicates, resolve.ResolvedSections);
48 // reportDupes.Execute();
49 // }
50 +
51 + return (Messaging.Instance.EncounteredError ? null : library);
52 + }
53 +
54 + private static Dictionary<string, Localization> CollateLocalizations(IEnumerable<Localization> localizations)
55 + {
56 + var localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase);
57 +
58 + foreach (var localization in localizations)
59 + {
60 + if (localizationsByCulture.TryGetValue(localization.Culture, out var existingCulture))
61 + {
62 + existingCulture.Merge(localization);
63 + }
64 + else
65 + {
66 + localizationsByCulture.Add(localization.Culture, localization);
67 + }
68 + }
69 +
70 + return localizationsByCulture;
71 + }
72 +
73 + private static List<string> ResolveFilePathsToEmbed(IEnumerable<Section> sections, ILibraryBinaryFileResolver resolver)
74 + {
75 + var embedFilePaths = new List<string>();
76 +
77 + // Resolve paths to files that are to be embedded in the library.
78 + if (null != resolver)
79 + {
80 + foreach (Table table in sections.SelectMany(s => s.Tables))
81 + {
82 + foreach (Row row in table.Rows)
83 + {
84 + foreach (ObjectField objectField in row.Fields.OfType<ObjectField>())
85 + {
86 + if (null != objectField.Data)
87 + {
88 + string file = resolver.Resolve(row.SourceLineNumbers, table.Name, (string)objectField.Data);
89 + if (!String.IsNullOrEmpty(file))
90 + {
91 + // File was successfully resolved so track the embedded index as the embedded file index.
92 + objectField.EmbeddedFileIndex = embedFilePaths.Count;
93 + embedFilePaths.Add(file);
94 + }
95 + else
96 + {
97 + Messaging.Instance.OnMessage(WixDataErrors.FileNotFound(row.SourceLineNumbers, (string)objectField.Data, table.Name));
98 + }
99 + }
100 + else // clear out embedded file id in case there was one there before.
101 + {
102 + objectField.EmbeddedFileIndex = null;
103 + }
104 + }
105 + }
106 + }
107 + }
108 +
109 + return embedFilePaths;
110 }
111 }
112 }
src/WixToolset.Core/Localizer.cs
+24 -29
@@ -23,11 +23,32 @@ namespace WixToolset
23 /// <summary>
24 /// Instantiate a new Localizer.
25 /// </summary>
26 - public Localizer()
26 + public Localizer(IEnumerable<Localization> localizations)
27 {
28 this.Codepage = -1;
29 - this.variables = new Dictionary<string,WixVariableRow>();
29 + this.variables = new Dictionary<string, WixVariableRow>();
30 this.localizedControls = new Dictionary<string, LocalizedControl>();
31 +
32 + foreach (var localization in localizations)
33 + {
34 + if (-1 == this.Codepage)
35 + {
36 + this.Codepage = localization.Codepage;
37 + }
38 +
39 + foreach (WixVariableRow wixVariableRow in localization.Variables)
40 + {
41 + Localizer.AddWixVariable(this.variables, wixVariableRow);
42 + }
43 +
44 + foreach (KeyValuePair<string, LocalizedControl> localizedControl in localization.LocalizedControls)
45 + {
46 + if (!this.localizedControls.ContainsKey(localizedControl.Key))
47 + {
48 + this.localizedControls.Add(localizedControl.Key, localizedControl.Value);
49 + }
50 + }
51 + }
52 }
53
54 /// <summary>
@@ -75,31 +96,6 @@ namespace WixToolset
96 return localization;
97 }
98
78 - /// <summary>
79 - /// Add a localization file.
80 - /// </summary>
81 - /// <param name="localization">The localization file to add.</param>
82 - public void AddLocalization(Localization localization)
83 - {
84 - if (-1 == this.Codepage)
85 - {
86 - this.Codepage = localization.Codepage;
87 - }
88 -
89 - foreach (WixVariableRow wixVariableRow in localization.Variables)
90 - {
91 - Localizer.AddWixVariable(this.variables, wixVariableRow);
92 - }
93 -
94 - foreach (KeyValuePair<string, LocalizedControl> localizedControl in localization.LocalizedControls)
95 - {
96 - if (!this.localizedControls.ContainsKey(localizedControl.Key))
97 - {
98 - this.localizedControls.Add(localizedControl.Key, localizedControl.Value);
99 - }
100 - }
101 - }
102 -
99 /// <summary>
100 /// Get a localized data value.
101 /// </summary>
@@ -107,8 +103,7 @@ namespace WixToolset
103 /// <returns>The localized data value or null if it wasn't found.</returns>
104 public string GetLocalizedValue(string id)
105 {
110 - WixVariableRow wixVariableRow;
111 - return this.variables.TryGetValue(id, out wixVariableRow) ? wixVariableRow.Value : null;
106 + return this.variables.TryGetValue(id, out var wixVariableRow) ? wixVariableRow.Value : null;
107 }
108
109 /// <summary>
src/WixToolset.Core/WixVariableResolver.cs
+6 -14
@@ -3,7 +3,6 @@
3 namespace WixToolset
4 {
5 using System;
6 - using System.Collections;
6 using System.Collections.Generic;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Globalization;
@@ -17,34 +16,27 @@ namespace WixToolset
16 /// </summary>
17 public sealed class WixVariableResolver
18 {
20 - private Localizer localizer;
19 private Dictionary<string, string> wixVariables;
20
21 /// <summary>
22 /// Instantiate a new WixVariableResolver.
23 /// </summary>
26 - public WixVariableResolver()
24 + public WixVariableResolver(Localizer localizer = null)
25 {
26 this.wixVariables = new Dictionary<string, string>();
27 + this.Localizer = localizer;
28 }
29
30 /// <summary>
31 /// Gets or sets the localizer.
32 /// </summary>
33 /// <value>The localizer.</value>
35 - public Localizer Localizer
36 - {
37 - get { return this.localizer; }
38 - set { this.localizer = value; }
39 - }
34 + public Localizer Localizer { get; private set; }
35
36 /// <summary>
37 /// Gets the count of variables added to the resolver.
38 /// </summary>
44 - public int VariableCount
45 - {
46 - get { return this.wixVariables.Count; }
47 - }
39 + public int VariableCount => this.wixVariables.Count;
40
41 /// <summary>
42 /// Add a variable.
@@ -198,9 +190,9 @@ namespace WixToolset
190 Messaging.Instance.OnMessage(WixWarnings.DeprecatedLocalizationVariablePrefix(sourceLineNumbers, variableId));
191 }
192
201 - if (null != this.localizer)
193 + if (null != this.Localizer)
194 {
203 - resolvedValue = this.localizer.GetLocalizedValue(variableId);
195 + resolvedValue = this.Localizer.GetLocalizedValue(variableId);
196 }
197 }
198 else if (!localizationOnly && "wix" == variableNamespace)