aboutsummaryrefslogtreecommitdiff
path: root/src
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 /src
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
Diffstat (limited to 'src')
-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");