aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-03-15 14:01:07 -0700
committerRob Mensching <rob@firegiant.com>2022-03-15 15:51:57 -0700
commit9aa2910e79ce52144c5296077aa2444876419643 (patch)
treec29eb047af78f2324c22573b45caa7e7bc690877
parent8ed8ca0721e08ea953f4751086c4845c98551c46 (diff)
downloadwix-9aa2910e79ce52144c5296077aa2444876419643.tar.gz
wix-9aa2910e79ce52144c5296077aa2444876419643.tar.bz2
wix-9aa2910e79ce52144c5296077aa2444876419643.zip
Rework build command line for combining wixlibs and default output
Changes the command line handling for the "build" command to allow creation of a .wixlib to accept compiled (but not linked) intermediates. This will allow .wixlibs to combine separate build commands (potentially with different "-arch" switches) into a single .wixlib. This change also fixes the issue where by default output was being written to the intermediate folder instead of the current directory. Fixes 6464 Fixes 6473
-rw-r--r--src/wix/WixToolset.Core/CommandLine/BuildCommand.cs263
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BadInputFixture.cs15
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs59
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/SimplePackage/SimplePackage.wxs11
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/WixlibMultiarch/MultiarchFile.wxs11
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs43
6 files changed, 277 insertions, 125 deletions
diff --git a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
index 67d2876d..f291fa8c 100644
--- a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
+++ b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
@@ -49,18 +49,8 @@ namespace WixToolset.Core.CommandLine
49 49
50 private string IntermediateFolder { get; set; } 50 private string IntermediateFolder { get; set; }
51 51
52 private OutputType OutputType { get; set; }
53
54 private List<string> IncludeSearchPaths { get; set; }
55
56 public string PdbFile { get; set; }
57
58 public PdbType PdbType { get; set; }
59
60 private Platform Platform { get; set; } 52 private Platform Platform { get; set; }
61 53
62 private string OutputFile { get; set; }
63
64 private CompressionLevel? DefaultCompressionLevel { get; set; } 54 private CompressionLevel? DefaultCompressionLevel { get; set; }
65 55
66 private string TrackingFile { get; set; } 56 private string TrackingFile { get; set; }
@@ -75,93 +65,70 @@ namespace WixToolset.Core.CommandLine
75 65
76 this.IntermediateFolder = this.commandLine.CalculateIntermedateFolder(); 66 this.IntermediateFolder = this.commandLine.CalculateIntermedateFolder();
77 67
78 this.OutputType = this.commandLine.CalculateOutputType();
79
80 this.IncludeSearchPaths = this.commandLine.IncludeSearchPaths;
81
82 this.PdbFile = this.commandLine.PdbFile;
83
84 this.PdbType = this.commandLine.PdbType;
85
86 this.Platform = this.commandLine.Platform; 68 this.Platform = this.commandLine.Platform;
87 69
88 this.TrackingFile = this.commandLine.TrackingFile; 70 this.TrackingFile = this.commandLine.TrackingFile;
89 71
90 this.DefaultCompressionLevel = this.commandLine.DefaultCompressionLevel; 72 this.DefaultCompressionLevel = this.commandLine.DefaultCompressionLevel;
91 73
92 var preprocessorVariables = this.commandLine.GatherPreprocessorVariables(); 74 var preprocessorVariables = this.commandLine.CalculatePreprocessorVariables();
93
94 var sourceFiles = this.commandLine.GatherSourceFiles(this.IntermediateFolder);
95 75
96 var filterCultures = this.commandLine.CalculateFilterCultures(); 76 var filterCultures = this.commandLine.CalculateFilterCultures();
97 77
98 var creator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>(); 78 var creator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>();
99 79
100 this.EvaluateSourceFiles(sourceFiles, creator, out var codeFiles, out var wixipl); 80 var inputsOutputs = this.commandLine.CalculateInputsAndOutputs(creator);
101
102 this.OutputFile = this.commandLine.OutputFile;
103
104 if (String.IsNullOrEmpty(this.OutputFile))
105 {
106 if (codeFiles.Count == 1)
107 {
108 // If output type is unknown, the extension will be replaced with the right default based on output type.
109 this.OutputFile = Path.ChangeExtension(codeFiles[0].OutputPath, DefaultExtensionForOutputType(this.OutputType));
110 }
111 else
112 {
113 this.Messaging.Write(ErrorMessages.MustSpecifyOutputWithMoreThanOneInput());
114 }
115 }
116 81
117 if (this.Messaging.EncounteredError) 82 if (this.Messaging.EncounteredError)
118 { 83 {
119 return Task.FromResult(this.Messaging.LastErrorNumber); 84 return Task.FromResult(this.Messaging.LastErrorNumber);
120 } 85 }
121 86
122 var wixobjs = this.CompilePhase(preprocessorVariables, codeFiles, cancellationToken); 87 var wixobjs = this.CompilePhase(preprocessorVariables, inputsOutputs.SourcePaths, this.commandLine.IncludeSearchPaths, cancellationToken);
123 88
124 var wxls = this.LoadLocalizationFiles(this.commandLine.LocalizationFilePaths, preprocessorVariables, cancellationToken); 89 var wxls = this.LoadLocalizationFiles(inputsOutputs.LocalizationPaths, preprocessorVariables, this.commandLine.IncludeSearchPaths, cancellationToken);
125 90
126 if (this.Messaging.EncounteredError) 91 if (this.Messaging.EncounteredError)
127 { 92 {
128 return Task.FromResult(this.Messaging.LastErrorNumber); 93 return Task.FromResult(this.Messaging.LastErrorNumber);
129 } 94 }
130 95
131 if (this.OutputType == OutputType.Library) 96 if (inputsOutputs.OutputType == OutputType.Library)
132 { 97 {
133 using (new IntermediateFieldContext("wix.lib")) 98 using (new IntermediateFieldContext("wix.lib"))
134 { 99 {
135 this.LibraryPhase(wixobjs, wxls, this.commandLine.BindFiles, this.commandLine.BindPaths, cancellationToken); 100 this.LibraryPhase(wixobjs, wxls, inputsOutputs.LibraryPaths, creator, this.commandLine.BindFiles, this.commandLine.BindPaths, inputsOutputs.OutputPath, cancellationToken);
136 } 101 }
137 } 102 }
138 else 103 else
139 { 104 {
140 using (new IntermediateFieldContext("wix.link")) 105 using (new IntermediateFieldContext("wix.link"))
141 { 106 {
107 var wixipl = inputsOutputs.Wixipls.SingleOrDefault();
108
142 if (wixipl == null) 109 if (wixipl == null)
143 { 110 {
144 wixipl = this.LinkPhase(wixobjs, this.commandLine.LibraryFilePaths, creator, cancellationToken); 111 wixipl = this.LinkPhase(wixobjs, inputsOutputs, creator, cancellationToken);
145 } 112 }
146 113
147 if (!this.Messaging.EncounteredError) 114 if (!this.Messaging.EncounteredError)
148 { 115 {
149 var outputExtension = Path.GetExtension(this.OutputFile); 116 var outputExtension = Path.GetExtension(inputsOutputs.OutputPath);
150 if (String.IsNullOrEmpty(outputExtension) || ".wix" == outputExtension) 117 if (String.IsNullOrEmpty(outputExtension) || ".wix" == outputExtension)
151 { 118 {
152 var entrySectionType = wixipl.Sections.Single().Type; 119 var entrySectionType = wixipl.Sections.Single().Type;
153 this.OutputFile = Path.ChangeExtension(this.OutputFile, DefaultExtensionForSectionType(entrySectionType)); 120 inputsOutputs.OutputPath = Path.ChangeExtension(inputsOutputs.OutputPath, DefaultExtensionForSectionType(entrySectionType));
154 } 121 }
155 122
156 if (this.OutputType == OutputType.IntermediatePostLink) 123 if (inputsOutputs.OutputType == OutputType.IntermediatePostLink)
157 { 124 {
158 wixipl.Save(this.OutputFile); 125 wixipl.Save(inputsOutputs.OutputPath);
159 } 126 }
160 else 127 else
161 { 128 {
162 using (new IntermediateFieldContext("wix.bind")) 129 using (new IntermediateFieldContext("wix.bind"))
163 { 130 {
164 this.BindPhase(wixipl, wxls, filterCultures, this.commandLine.CabCachePath, this.commandLine.BindPaths, cancellationToken); 131 this.BindPhase(wixipl, wxls, filterCultures, this.commandLine.CabCachePath, this.commandLine.BindPaths, inputsOutputs, cancellationToken);
165 } 132 }
166 } 133 }
167 } 134 }
@@ -176,51 +143,13 @@ namespace WixToolset.Core.CommandLine
176 return this.commandLine.TryParseArgument(argument, parser); 143 return this.commandLine.TryParseArgument(argument, parser);
177 } 144 }
178 145
179 private void EvaluateSourceFiles(IEnumerable<SourceFile> sourceFiles, ISymbolDefinitionCreator creator, out List<SourceFile> codeFiles, out Intermediate wixipl) 146 private IReadOnlyList<Intermediate> CompilePhase(IDictionary<string, string> preprocessorVariables, IEnumerable<string> sourceFiles, IReadOnlyCollection<string> includeSearchPaths, CancellationToken cancellationToken)
180 {
181 codeFiles = new List<SourceFile>();
182
183 wixipl = null;
184
185 foreach (var sourceFile in sourceFiles)
186 {
187 var extension = Path.GetExtension(sourceFile.SourcePath);
188
189 if (wixipl != null || ".wxs".Equals(extension, StringComparison.OrdinalIgnoreCase))
190 {
191 codeFiles.Add(sourceFile);
192 }
193 else
194 {
195 try
196 {
197 wixipl = Intermediate.Load(sourceFile.SourcePath, creator);
198 }
199 catch (WixException)
200 {
201 // We'll assume anything that isn't a valid intermediate is source code to compile.
202 codeFiles.Add(sourceFile);
203 }
204 }
205 }
206
207 if (wixipl == null && codeFiles.Count == 0)
208 {
209 this.Messaging.Write(ErrorMessages.NoSourceFiles());
210 }
211 else if (wixipl != null && codeFiles.Count != 0)
212 {
213 this.Messaging.Write(ErrorMessages.WixiplSourceFileIsExclusive());
214 }
215 }
216
217 private IReadOnlyList<Intermediate> CompilePhase(IDictionary<string, string> preprocessorVariables, IEnumerable<SourceFile> sourceFiles, CancellationToken cancellationToken)
218 { 147 {
219 var intermediates = new List<Intermediate>(); 148 var intermediates = new List<Intermediate>();
220 149
221 foreach (var sourceFile in sourceFiles) 150 foreach (var sourceFile in sourceFiles)
222 { 151 {
223 var document = this.Preprocess(preprocessorVariables, sourceFile.SourcePath, cancellationToken); 152 var document = this.Preprocess(preprocessorVariables, sourceFile, includeSearchPaths, cancellationToken);
224 153
225 if (this.Messaging.EncounteredError) 154 if (this.Messaging.EncounteredError)
226 { 155 {
@@ -255,14 +184,21 @@ namespace WixToolset.Core.CommandLine
255 return intermediates; 184 return intermediates;
256 } 185 }
257 186
258 private void LibraryPhase(IReadOnlyCollection<Intermediate> intermediates, IReadOnlyCollection<Localization> localizations, bool bindFiles, IReadOnlyCollection<IBindPath> bindPaths, CancellationToken cancellationToken) 187 private void LibraryPhase(IReadOnlyCollection<Intermediate> intermediates, IReadOnlyCollection<Localization> localizations, IEnumerable<string> libraryFiles, ISymbolDefinitionCreator creator, bool bindFiles, IReadOnlyCollection<IBindPath> bindPaths, string outputPath, CancellationToken cancellationToken)
259 { 188 {
189 var libraries = this.LoadLibraries(libraryFiles, creator);
190
191 if (this.Messaging.EncounteredError)
192 {
193 return;
194 }
195
260 var context = this.ServiceProvider.GetService<ILibraryContext>(); 196 var context = this.ServiceProvider.GetService<ILibraryContext>();
261 context.BindFiles = bindFiles; 197 context.BindFiles = bindFiles;
262 context.BindPaths = bindPaths; 198 context.BindPaths = bindPaths;
263 context.Extensions = this.ExtensionManager.GetServices<ILibrarianExtension>(); 199 context.Extensions = this.ExtensionManager.GetServices<ILibrarianExtension>();
264 context.Localizations = localizations; 200 context.Localizations = localizations;
265 context.Intermediates = intermediates; 201 context.Intermediates = intermediates.Concat(libraries).ToList();
266 context.CancellationToken = cancellationToken; 202 context.CancellationToken = cancellationToken;
267 203
268 try 204 try
@@ -272,7 +208,7 @@ namespace WixToolset.Core.CommandLine
272 208
273 if (!this.Messaging.EncounteredError) 209 if (!this.Messaging.EncounteredError)
274 { 210 {
275 result.Library.Save(this.OutputFile); 211 result.Library.Save(outputPath);
276 212
277 this.LayoutFiles(this.IntermediateFolder, result.TrackedFiles, null, cancellationToken); 213 this.LayoutFiles(this.IntermediateFolder, result.TrackedFiles, null, cancellationToken);
278 } 214 }
@@ -283,9 +219,9 @@ namespace WixToolset.Core.CommandLine
283 } 219 }
284 } 220 }
285 221
286 private Intermediate LinkPhase(IEnumerable<Intermediate> intermediates, IEnumerable<string> libraryFiles, ISymbolDefinitionCreator creator, CancellationToken cancellationToken) 222 private Intermediate LinkPhase(IEnumerable<Intermediate> intermediates, InputsAndOutputs inputsOutputs, ISymbolDefinitionCreator creator, CancellationToken cancellationToken)
287 { 223 {
288 var libraries = this.LoadLibraries(libraryFiles, creator); 224 var libraries = this.LoadLibraries(inputsOutputs.LibraryPaths, creator);
289 225
290 if (this.Messaging.EncounteredError) 226 if (this.Messaging.EncounteredError)
291 { 227 {
@@ -295,7 +231,7 @@ namespace WixToolset.Core.CommandLine
295 var context = this.ServiceProvider.GetService<ILinkContext>(); 231 var context = this.ServiceProvider.GetService<ILinkContext>();
296 context.Extensions = this.ExtensionManager.GetServices<ILinkerExtension>(); 232 context.Extensions = this.ExtensionManager.GetServices<ILinkerExtension>();
297 context.ExtensionData = this.ExtensionManager.GetServices<IExtensionData>(); 233 context.ExtensionData = this.ExtensionManager.GetServices<IExtensionData>();
298 context.ExpectedOutputType = this.OutputType; 234 context.ExpectedOutputType = inputsOutputs.OutputType;
299 context.Intermediates = intermediates.Concat(libraries).ToList(); 235 context.Intermediates = intermediates.Concat(libraries).ToList();
300 context.SymbolDefinitionCreator = creator; 236 context.SymbolDefinitionCreator = creator;
301 context.CancellationToken = cancellationToken; 237 context.CancellationToken = cancellationToken;
@@ -304,7 +240,7 @@ namespace WixToolset.Core.CommandLine
304 return linker.Link(context); 240 return linker.Link(context);
305 } 241 }
306 242
307 private void BindPhase(Intermediate output, IReadOnlyCollection<Localization> localizations, IReadOnlyCollection<string> filterCultures, string cabCachePath, IReadOnlyCollection<IBindPath> bindPaths, CancellationToken cancellationToken) 243 private void BindPhase(Intermediate output, IReadOnlyCollection<Localization> localizations, IReadOnlyCollection<string> filterCultures, string cabCachePath, IReadOnlyCollection<IBindPath> bindPaths, InputsAndOutputs inputsOutputs, CancellationToken cancellationToken)
308 { 244 {
309 var intermediateFolder = this.IntermediateFolder; 245 var intermediateFolder = this.IntermediateFolder;
310 if (String.IsNullOrEmpty(intermediateFolder)) 246 if (String.IsNullOrEmpty(intermediateFolder))
@@ -350,9 +286,9 @@ namespace WixToolset.Core.CommandLine
350 context.FileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>(); 286 context.FileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>();
351 context.IntermediateFolder = intermediateFolder; 287 context.IntermediateFolder = intermediateFolder;
352 context.IntermediateRepresentation = resolveResult.IntermediateRepresentation; 288 context.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
353 context.OutputPath = this.OutputFile; 289 context.OutputPath = inputsOutputs.OutputPath;
354 context.PdbType = this.PdbType; 290 context.PdbType = inputsOutputs.PdbType;
355 context.PdbPath = this.PdbType == PdbType.None ? null : this.PdbFile ?? Path.ChangeExtension(this.OutputFile, ".wixpdb"); 291 context.PdbPath = inputsOutputs.PdbPath;
356 context.CancellationToken = cancellationToken; 292 context.CancellationToken = cancellationToken;
357 293
358 var binder = this.ServiceProvider.GetService<IBinder>(); 294 var binder = this.ServiceProvider.GetService<IBinder>();
@@ -405,14 +341,14 @@ namespace WixToolset.Core.CommandLine
405 return Array.Empty<Intermediate>(); 341 return Array.Empty<Intermediate>();
406 } 342 }
407 343
408 private IReadOnlyList<Localization> LoadLocalizationFiles(IEnumerable<string> locFiles, IDictionary<string, string> preprocessorVariables, CancellationToken cancellationToken) 344 private IReadOnlyList<Localization> LoadLocalizationFiles(IEnumerable<string> locFiles, IDictionary<string, string> preprocessorVariables, IReadOnlyCollection<string> includeSearchPaths, CancellationToken cancellationToken)
409 { 345 {
410 var localizations = new List<Localization>(); 346 var localizations = new List<Localization>();
411 var parser = this.ServiceProvider.GetService<ILocalizationParser>(); 347 var parser = this.ServiceProvider.GetService<ILocalizationParser>();
412 348
413 foreach (var loc in locFiles) 349 foreach (var loc in locFiles)
414 { 350 {
415 var document = this.Preprocess(preprocessorVariables, loc, cancellationToken); 351 var document = this.Preprocess(preprocessorVariables, loc, includeSearchPaths, cancellationToken);
416 352
417 if (this.Messaging.EncounteredError) 353 if (this.Messaging.EncounteredError)
418 { 354 {
@@ -426,12 +362,12 @@ namespace WixToolset.Core.CommandLine
426 return localizations; 362 return localizations;
427 } 363 }
428 364
429 private XDocument Preprocess(IDictionary<string, string> preprocessorVariables, string sourcePath, CancellationToken cancellationToken) 365 private XDocument Preprocess(IDictionary<string, string> preprocessorVariables, string sourcePath, IReadOnlyCollection<string> includeSearchPaths, CancellationToken cancellationToken)
430 { 366 {
431 var context = this.ServiceProvider.GetService<IPreprocessContext>(); 367 var context = this.ServiceProvider.GetService<IPreprocessContext>();
432 context.Extensions = this.ExtensionManager.GetServices<IPreprocessorExtension>(); 368 context.Extensions = this.ExtensionManager.GetServices<IPreprocessorExtension>();
433 context.Platform = this.Platform; 369 context.Platform = this.Platform;
434 context.IncludeSearchPaths = this.IncludeSearchPaths; 370 context.IncludeSearchPaths = includeSearchPaths;
435 context.SourcePath = sourcePath; 371 context.SourcePath = sourcePath;
436 context.Variables = preprocessorVariables; 372 context.Variables = preprocessorVariables;
437 context.CancellationToken = cancellationToken; 373 context.CancellationToken = cancellationToken;
@@ -519,6 +455,8 @@ namespace WixToolset.Core.CommandLine
519 455
520 public List<string> SourceFilePaths { get; } = new List<string>(); 456 public List<string> SourceFilePaths { get; } = new List<string>();
521 457
458 public List<string> UnevaluatedInputFilePaths { get; } = new List<string>();
459
522 public Platform Platform { get; private set; } 460 public Platform Platform { get; private set; }
523 461
524 public string PdbFile { get; private set; } 462 public string PdbFile { get; private set; }
@@ -632,6 +570,10 @@ namespace WixToolset.Core.CommandLine
632 parser.GetNextArgumentAsFilePathOrError(arg, "library files", this.LibraryFilePaths); 570 parser.GetNextArgumentAsFilePathOrError(arg, "library files", this.LibraryFilePaths);
633 return true; 571 return true;
634 572
573 case "src":
574 parser.GetNextArgumentAsFilePathOrError(arg, "source code", this.SourceFilePaths);
575 return true;
576
635 case "o": 577 case "o":
636 case "out": 578 case "out":
637 this.OutputFile = parser.GetNextArgumentAsFilePathOrError(arg); 579 this.OutputFile = parser.GetNextArgumentAsFilePathOrError(arg);
@@ -665,7 +607,7 @@ namespace WixToolset.Core.CommandLine
665 } 607 }
666 else 608 else
667 { 609 {
668 parser.GetArgumentAsFilePathOrError(arg, "source code", this.SourceFilePaths); 610 parser.GetArgumentAsFilePathOrError(arg, "input file", this.UnevaluatedInputFilePaths);
669 return true; 611 return true;
670 } 612 }
671 } 613 }
@@ -747,7 +689,7 @@ namespace WixToolset.Core.CommandLine
747 return result; 689 return result;
748 } 690 }
749 691
750 public IDictionary<string, string> GatherPreprocessorVariables() 692 public IDictionary<string, string> CalculatePreprocessorVariables()
751 { 693 {
752 var variables = new Dictionary<string, string>(); 694 var variables = new Dictionary<string, string>();
753 695
@@ -767,19 +709,89 @@ namespace WixToolset.Core.CommandLine
767 return variables; 709 return variables;
768 } 710 }
769 711
770 public IEnumerable<SourceFile> GatherSourceFiles(string intermediateDirectory) 712 public InputsAndOutputs CalculateInputsAndOutputs(ISymbolDefinitionCreator creator)
771 { 713 {
772 var files = new List<SourceFile>(); 714 var codePaths = new List<string>(this.SourceFilePaths);
715 var localizationPaths = new List<string>(this.LocalizationFilePaths);
716 var libraryPaths = new List<string>(this.LibraryFilePaths);
717 var wixipls = new List<Intermediate>();
718 string lastWixiplPath = null;
773 719
774 foreach (var item in this.SourceFilePaths) 720 var outputPath = this.OutputFile;
721 var outputType = this.CalculateOutputType();
722
723 foreach (var path in this.UnevaluatedInputFilePaths)
775 { 724 {
776 var sourcePath = item; 725 var extension = Path.GetExtension(path);
777 var outputPath = Path.Combine(intermediateDirectory, Path.GetFileNameWithoutExtension(sourcePath) + ".wir"); 726
727 if (".wxs".Equals(extension, StringComparison.OrdinalIgnoreCase))
728 {
729 codePaths.Add(path);
730 }
731 else if (".wxl".Equals(extension, StringComparison.OrdinalIgnoreCase))
732 {
733 localizationPaths.Add(path);
734 }
735 else if (".wixlib".Equals(extension, StringComparison.OrdinalIgnoreCase))
736 {
737 libraryPaths.Add(path);
738 }
739 else
740 {
741 try
742 {
743 // Try to load the file as an intermediate to determine whether it is a
744 // .wixipl or a .wixlib.
745 var intermediate = Intermediate.Load(path, creator);
778 746
779 files.Add(new SourceFile(sourcePath, outputPath)); 747 if (intermediate.HasLevel(IntermediateLevels.Linked))
748 {
749 wixipls.Add(intermediate);
750 lastWixiplPath = path;
751 }
752 else
753 {
754 libraryPaths.Add(path);
755 }
756 }
757 catch (WixException)
758 {
759 // We'll assume anything that isn't a valid intermediate is source code to compile.
760 codePaths.Add(path);
761 }
762 }
780 } 763 }
781 764
782 return files; 765 if (wixipls.Count > 0)
766 {
767 if (wixipls.Count > 1 || codePaths.Count > 0 || libraryPaths.Count > 0)
768 {
769 this.Messaging.Write(ErrorMessages.WixiplSourceFileIsExclusive());
770 }
771 }
772 else if (codePaths.Count == 0 && libraryPaths.Count == 0)
773 {
774 this.Messaging.Write(ErrorMessages.NoSourceFiles());
775 }
776
777 if (!this.Messaging.EncounteredError && String.IsNullOrEmpty(outputPath))
778 {
779 var singleInputPath = codePaths.Count == 1 ? codePaths[0] : lastWixiplPath;
780
781 if (String.IsNullOrEmpty(singleInputPath))
782 {
783 this.Messaging.Write(ErrorMessages.MustSpecifyOutputWithMoreThanOneInput());
784 }
785 else
786 {
787 // If output type is unknown, the extension will be replaced with the right default based on output type.
788 outputPath = Path.ChangeExtension(singleInputPath, DefaultExtensionForOutputType(outputType));
789 }
790 }
791
792 var pdbPath = this.PdbType == PdbType.None ? null : this.PdbFile ?? Path.ChangeExtension(outputPath ?? "error.above", ".wixpdb");
793
794 return new InputsAndOutputs(codePaths, localizationPaths, libraryPaths, wixipls, outputPath, outputType, pdbPath, this.PdbType);
783 } 795 }
784 796
785 private bool TryParseBindPath(string bindPath, out IBindPath bp) 797 private bool TryParseBindPath(string bindPath, out IBindPath bp)
@@ -807,5 +819,36 @@ namespace WixToolset.Core.CommandLine
807 return true; 819 return true;
808 } 820 }
809 } 821 }
822
823 private class InputsAndOutputs
824 {
825 public InputsAndOutputs(IReadOnlyCollection<string> sourcePaths, IReadOnlyCollection<string> localizationPaths, IReadOnlyCollection<string> libraryPaths, IReadOnlyCollection<Intermediate> wixipls, string outputPath, OutputType outputType, string pdbPath, PdbType pdbType)
826 {
827 this.SourcePaths = sourcePaths;
828 this.LocalizationPaths = localizationPaths;
829 this.LibraryPaths = libraryPaths;
830 this.Wixipls = wixipls;
831 this.OutputPath = outputPath;
832 this.OutputType = outputType;
833 this.PdbPath = pdbPath;
834 this.PdbType = pdbType;
835 }
836
837 public IReadOnlyCollection<string> SourcePaths { get; }
838
839 public IReadOnlyCollection<string> LocalizationPaths { get; }
840
841 public IReadOnlyCollection<string> LibraryPaths { get; }
842
843 public IReadOnlyCollection<Intermediate> Wixipls { get; }
844
845 public string OutputPath { get; set; }
846
847 public OutputType OutputType { get; }
848
849 public string PdbPath { get; }
850
851 public PdbType PdbType { get; }
852 }
810 } 853 }
811} 854}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BadInputFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BadInputFixture.cs
index 62ffe1eb..f91c0ab0 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BadInputFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BadInputFixture.cs
@@ -6,26 +6,11 @@ namespace WixToolsetTest.CoreIntegration
6 using System.IO; 6 using System.IO;
7 using WixBuildTools.TestSupport; 7 using WixBuildTools.TestSupport;
8 using WixToolset.Core.TestPackage; 8 using WixToolset.Core.TestPackage;
9 using WixToolset.Data;
10 using Xunit; 9 using Xunit;
11 10
12 public class BadInputFixture 11 public class BadInputFixture
13 { 12 {
14 [Fact] 13 [Fact]
15 public void SwitchIsNotConsideredAnArgument()
16 {
17 var result = WixRunner.Execute(new[]
18 {
19 "build",
20 "-bindpath", "-thisisaswitchnotanarg",
21 });
22
23 Assert.Single(result.Messages, m => m.Id == (int)ErrorMessages.Ids.ExpectedArgument);
24 // TODO: when CantBuildSingleExeBundleWithInvalidArgument is fixed, uncomment:
25 //Assert.Equal((int)ErrorMessages.Ids.ExpectedArgument, result.ExitCode);
26 }
27
28 [Fact]
29 public void HandleInvalidIds() 14 public void HandleInvalidIds()
30 { 15 {
31 var folder = TestData.Get(@"TestData\BadInput"); 16 var folder = TestData.Get(@"TestData\BadInput");
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs
new file mode 100644
index 00000000..1b0f9633
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs
@@ -0,0 +1,59 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolsetTest.CoreIntegration
4{
5 using System.IO;
6 using System.Linq;
7 using WixBuildTools.TestSupport;
8 using WixToolset.Core.TestPackage;
9 using Xunit;
10
11 public class CommandLineFixture
12 {
13 [Fact]
14 public void SwitchIsNotConsideredAnArgument()
15 {
16 var result = WixRunner.Execute(new[]
17 {
18 "build",
19 "-bindpath", "-thisisaswitchnotanarg",
20 });
21
22 WixAssert.CompareLineByLine(new[]
23 {
24 "-bindpath is expected to be followed by a value. See -? for additional detail.",
25 "Additional argument '-bindpath' was unexpected. Remove the argument and add the '-?' switch for more information.",
26 "No source files specified."
27 }, result.Messages.Select(m => m.ToString()).ToArray());
28 Assert.Equal(391, result.ExitCode);
29 }
30
31 [Fact]
32 public void CanBuildWithNoOutputSpecified()
33 {
34 var folder = TestData.Get(@"TestData", "SimplePackage");
35 var bindFolder = TestData.Get(@"TestData", "SingleFile", "data");
36
37 using (var fs = new DisposableFileSystem())
38 {
39 var testFolder = fs.GetFolder(create: true);
40 var srcFile = Path.Combine(testFolder, "SimplePackage.wxs");
41 var intermediateFolder = Path.Combine(testFolder, "obj");
42 var expectedPath = Path.Combine(testFolder, "SimplePackage.msi");
43
44 // Copy the source folder into the test working folder so the output can be written to the same folder.
45 File.Copy(Path.Combine(folder, "SimplePackage.wxs"), srcFile);
46
47 var result = WixRunner.Execute(new[]
48 {
49 "build", srcFile,
50 "-bindpath", bindFolder,
51 "-intermediateFolder", intermediateFolder
52 });
53
54 result.AssertSuccess();
55 Assert.True(File.Exists(expectedPath), $"Expected to build MSI to: {expectedPath}");
56 }
57 }
58 }
59}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SimplePackage/SimplePackage.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SimplePackage/SimplePackage.wxs
new file mode 100644
index 00000000..0325867c
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SimplePackage/SimplePackage.wxs
@@ -0,0 +1,11 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="Downgrade error message." />
4
5 <Feature Id="ProductFeature">
6 <Component Directory="ProgramFilesFolder" Subdirectory="MsiPackage">
7 <File Source="test.txt" />
8 </Component>
9 </Feature>
10 </Package>
11</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/WixlibMultiarch/MultiarchFile.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/WixlibMultiarch/MultiarchFile.wxs
new file mode 100644
index 00000000..c74bf370
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/WixlibMultiarch/MultiarchFile.wxs
@@ -0,0 +1,11 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Fragment>
3 <Directory Id="ProgramFiles3264Folder">
4 <Directory Id="INSTALLFOLDER" Name="Foo">
5 <Component>
6 <File Source="test.txt" />
7 </Component>
8 </Directory>
9 </Directory>
10 </Fragment>
11</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs
index 00b83de8..01b82eb3 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs
@@ -53,6 +53,49 @@ namespace WixToolsetTest.CoreIntegration
53 } 53 }
54 54
55 [Fact] 55 [Fact]
56 public void CanBuildMultiarchWixlib()
57 {
58 var folder = TestData.Get(@"TestData", "WixlibMultiarch");
59
60 using (var fs = new DisposableFileSystem())
61 {
62 var baseFolder = fs.GetFolder();
63 var intermediateFolder = Path.Combine(baseFolder, "obj");
64 var wixlibPath = Path.Combine(intermediateFolder, @"test.wixlib");
65
66 var result = WixRunner.Execute(new[]
67 {
68 "build",
69 Path.Combine(folder, "MultiarchFile.wxs"),
70 "-intermediateFolder", intermediateFolder,
71 "-o", wixlibPath
72 });
73
74 result.AssertSuccess();
75
76 result = WixRunner.Execute(new[]
77 {
78 "build",
79 "-arch", "x64",
80 Path.Combine(folder, "MultiarchFile.wxs"),
81 wixlibPath,
82 "-intermediateFolder", intermediateFolder,
83 "-o", wixlibPath
84 });
85
86 result.AssertSuccess();
87
88 var wixlib = Intermediate.Load(wixlibPath);
89 var componentSymbols = wixlib.Sections.SelectMany(s => s.Symbols).OfType<ComponentSymbol>().ToList();
90 WixAssert.CompareLineByLine(new[]
91 {
92 "x64 filcV1yrx0x8wJWj4qMzcH21jwkPko",
93 "x86 filcV1yrx0x8wJWj4qMzcH21jwkPko",
94 }, componentSymbols.Select(c => (c.Win64 ? "x64 " : "x86 ") + c.Id.Id).OrderBy(s => s).ToArray());
95 }
96 }
97
98 [Fact]
56 public void CanBuildWixlibWithBinariesFromNamedBindPaths() 99 public void CanBuildWixlibWithBinariesFromNamedBindPaths()
57 { 100 {
58 var folder = TestData.Get(@"TestData\WixlibWithBinaries"); 101 var folder = TestData.Get(@"TestData\WixlibWithBinaries");