@joebigelow / wix / commits / 9aa2910e

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

Rob Mensching committed Mar 15, 2022 at 14:01 UTC 9aa2910e79ce52144c5296077aa2444876419643
6 files changed +277 -125
src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
+153 -110
@@ -49,18 +49,8 @@ namespace WixToolset.Core.CommandLine
49
50 private string IntermediateFolder { get; set; }
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 -
52 private Platform Platform { get; set; }
53
62 - private string OutputFile { get; set; }
63 -
54 private CompressionLevel? DefaultCompressionLevel { get; set; }
55
56 private string TrackingFile { get; set; }
@@ -75,93 +65,70 @@ namespace WixToolset.Core.CommandLine
65
66 this.IntermediateFolder = this.commandLine.CalculateIntermedateFolder();
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 -
68 this.Platform = this.commandLine.Platform;
69
70 this.TrackingFile = this.commandLine.TrackingFile;
71
72 this.DefaultCompressionLevel = this.commandLine.DefaultCompressionLevel;
73
92 - var preprocessorVariables = this.commandLine.GatherPreprocessorVariables();
93 -
94 - var sourceFiles = this.commandLine.GatherSourceFiles(this.IntermediateFolder);
74 + var preprocessorVariables = this.commandLine.CalculatePreprocessorVariables();
75
76 var filterCultures = this.commandLine.CalculateFilterCultures();
77
78 var creator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>();
79
100 - this.EvaluateSourceFiles(sourceFiles, creator, out var codeFiles, out var wixipl);
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 - }
80 + var inputsOutputs = this.commandLine.CalculateInputsAndOutputs(creator);
81
82 if (this.Messaging.EncounteredError)
83 {
84 return Task.FromResult(this.Messaging.LastErrorNumber);
85 }
86
122 - var wixobjs = this.CompilePhase(preprocessorVariables, codeFiles, cancellationToken);
87 + var wixobjs = this.CompilePhase(preprocessorVariables, inputsOutputs.SourcePaths, this.commandLine.IncludeSearchPaths, cancellationToken);
88
124 - var wxls = this.LoadLocalizationFiles(this.commandLine.LocalizationFilePaths, preprocessorVariables, cancellationToken);
89 + var wxls = this.LoadLocalizationFiles(inputsOutputs.LocalizationPaths, preprocessorVariables, this.commandLine.IncludeSearchPaths, cancellationToken);
90
91 if (this.Messaging.EncounteredError)
92 {
93 return Task.FromResult(this.Messaging.LastErrorNumber);
94 }
95
131 - if (this.OutputType == OutputType.Library)
96 + if (inputsOutputs.OutputType == OutputType.Library)
97 {
98 using (new IntermediateFieldContext("wix.lib"))
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);
101 }
102 }
103 else
104 {
105 using (new IntermediateFieldContext("wix.link"))
106 {
107 + var wixipl = inputsOutputs.Wixipls.SingleOrDefault();
108 +
109 if (wixipl == null)
110 {
144 - wixipl = this.LinkPhase(wixobjs, this.commandLine.LibraryFilePaths, creator, cancellationToken);
111 + wixipl = this.LinkPhase(wixobjs, inputsOutputs, creator, cancellationToken);
112 }
113
114 if (!this.Messaging.EncounteredError)
115 {
149 - var outputExtension = Path.GetExtension(this.OutputFile);
116 + var outputExtension = Path.GetExtension(inputsOutputs.OutputPath);
117 if (String.IsNullOrEmpty(outputExtension) || ".wix" == outputExtension)
118 {
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));
121 }
122
156 - if (this.OutputType == OutputType.IntermediatePostLink)
123 + if (inputsOutputs.OutputType == OutputType.IntermediatePostLink)
124 {
158 - wixipl.Save(this.OutputFile);
125 + wixipl.Save(inputsOutputs.OutputPath);
126 }
127 else
128 {
129 using (new IntermediateFieldContext("wix.bind"))
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);
132 }
133 }
134 }
@@ -176,51 +143,13 @@ namespace WixToolset.Core.CommandLine
143 return this.commandLine.TryParseArgument(argument, parser);
144 }
145
179 - private void EvaluateSourceFiles(IEnumerable<SourceFile> sourceFiles, ISymbolDefinitionCreator creator, out List<SourceFile> codeFiles, out Intermediate wixipl)
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)
146 + private IReadOnlyList<Intermediate> CompilePhase(IDictionary<string, string> preprocessorVariables, IEnumerable<string> sourceFiles, IReadOnlyCollection<string> includeSearchPaths, CancellationToken cancellationToken)
147 {
148 var intermediates = new List<Intermediate>();
149
150 foreach (var sourceFile in sourceFiles)
151 {
223 - var document = this.Preprocess(preprocessorVariables, sourceFile.SourcePath, cancellationToken);
152 + var document = this.Preprocess(preprocessorVariables, sourceFile, includeSearchPaths, cancellationToken);
153
154 if (this.Messaging.EncounteredError)
155 {
@@ -255,14 +184,21 @@ namespace WixToolset.Core.CommandLine
184 return intermediates;
185 }
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)
188 {
189 + var libraries = this.LoadLibraries(libraryFiles, creator);
190 +
191 + if (this.Messaging.EncounteredError)
192 + {
193 + return;
194 + }
195 +
196 var context = this.ServiceProvider.GetService<ILibraryContext>();
197 context.BindFiles = bindFiles;
198 context.BindPaths = bindPaths;
199 context.Extensions = this.ExtensionManager.GetServices<ILibrarianExtension>();
200 context.Localizations = localizations;
265 - context.Intermediates = intermediates;
201 + context.Intermediates = intermediates.Concat(libraries).ToList();
202 context.CancellationToken = cancellationToken;
203
204 try
@@ -272,7 +208,7 @@ namespace WixToolset.Core.CommandLine
208
209 if (!this.Messaging.EncounteredError)
210 {
275 - result.Library.Save(this.OutputFile);
211 + result.Library.Save(outputPath);
212
213 this.LayoutFiles(this.IntermediateFolder, result.TrackedFiles, null, cancellationToken);
214 }
@@ -283,9 +219,9 @@ namespace WixToolset.Core.CommandLine
219 }
220 }
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)
223 {
288 - var libraries = this.LoadLibraries(libraryFiles, creator);
224 + var libraries = this.LoadLibraries(inputsOutputs.LibraryPaths, creator);
225
226 if (this.Messaging.EncounteredError)
227 {
@@ -295,7 +231,7 @@ namespace WixToolset.Core.CommandLine
231 var context = this.ServiceProvider.GetService<ILinkContext>();
232 context.Extensions = this.ExtensionManager.GetServices<ILinkerExtension>();
233 context.ExtensionData = this.ExtensionManager.GetServices<IExtensionData>();
298 - context.ExpectedOutputType = this.OutputType;
234 + context.ExpectedOutputType = inputsOutputs.OutputType;
235 context.Intermediates = intermediates.Concat(libraries).ToList();
236 context.SymbolDefinitionCreator = creator;
237 context.CancellationToken = cancellationToken;
@@ -304,7 +240,7 @@ namespace WixToolset.Core.CommandLine
240 return linker.Link(context);
241 }
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)
244 {
245 var intermediateFolder = this.IntermediateFolder;
246 if (String.IsNullOrEmpty(intermediateFolder))
@@ -350,9 +286,9 @@ namespace WixToolset.Core.CommandLine
286 context.FileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>();
287 context.IntermediateFolder = intermediateFolder;
288 context.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
353 - context.OutputPath = this.OutputFile;
354 - context.PdbType = this.PdbType;
355 - context.PdbPath = this.PdbType == PdbType.None ? null : this.PdbFile ?? Path.ChangeExtension(this.OutputFile, ".wixpdb");
289 + context.OutputPath = inputsOutputs.OutputPath;
290 + context.PdbType = inputsOutputs.PdbType;
291 + context.PdbPath = inputsOutputs.PdbPath;
292 context.CancellationToken = cancellationToken;
293
294 var binder = this.ServiceProvider.GetService<IBinder>();
@@ -405,14 +341,14 @@ namespace WixToolset.Core.CommandLine
341 return Array.Empty<Intermediate>();
342 }
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)
345 {
346 var localizations = new List<Localization>();
347 var parser = this.ServiceProvider.GetService<ILocalizationParser>();
348
349 foreach (var loc in locFiles)
350 {
415 - var document = this.Preprocess(preprocessorVariables, loc, cancellationToken);
351 + var document = this.Preprocess(preprocessorVariables, loc, includeSearchPaths, cancellationToken);
352
353 if (this.Messaging.EncounteredError)
354 {
@@ -426,12 +362,12 @@ namespace WixToolset.Core.CommandLine
362 return localizations;
363 }
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)
366 {
367 var context = this.ServiceProvider.GetService<IPreprocessContext>();
368 context.Extensions = this.ExtensionManager.GetServices<IPreprocessorExtension>();
369 context.Platform = this.Platform;
434 - context.IncludeSearchPaths = this.IncludeSearchPaths;
370 + context.IncludeSearchPaths = includeSearchPaths;
371 context.SourcePath = sourcePath;
372 context.Variables = preprocessorVariables;
373 context.CancellationToken = cancellationToken;
@@ -519,6 +455,8 @@ namespace WixToolset.Core.CommandLine
455
456 public List<string> SourceFilePaths { get; } = new List<string>();
457
458 + public List<string> UnevaluatedInputFilePaths { get; } = new List<string>();
459 +
460 public Platform Platform { get; private set; }
461
462 public string PdbFile { get; private set; }
@@ -632,6 +570,10 @@ namespace WixToolset.Core.CommandLine
570 parser.GetNextArgumentAsFilePathOrError(arg, "library files", this.LibraryFilePaths);
571 return true;
572
573 + case "src":
574 + parser.GetNextArgumentAsFilePathOrError(arg, "source code", this.SourceFilePaths);
575 + return true;
576 +
577 case "o":
578 case "out":
579 this.OutputFile = parser.GetNextArgumentAsFilePathOrError(arg);
@@ -665,7 +607,7 @@ namespace WixToolset.Core.CommandLine
607 }
608 else
609 {
668 - parser.GetArgumentAsFilePathOrError(arg, "source code", this.SourceFilePaths);
610 + parser.GetArgumentAsFilePathOrError(arg, "input file", this.UnevaluatedInputFilePaths);
611 return true;
612 }
613 }
@@ -747,7 +689,7 @@ namespace WixToolset.Core.CommandLine
689 return result;
690 }
691
750 - public IDictionary<string, string> GatherPreprocessorVariables()
692 + public IDictionary<string, string> CalculatePreprocessorVariables()
693 {
694 var variables = new Dictionary<string, string>();
695
@@ -767,19 +709,89 @@ namespace WixToolset.Core.CommandLine
709 return variables;
710 }
711
770 - public IEnumerable<SourceFile> GatherSourceFiles(string intermediateDirectory)
712 + public InputsAndOutputs CalculateInputsAndOutputs(ISymbolDefinitionCreator creator)
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;
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)
724 {
776 - var sourcePath = item;
777 - var outputPath = Path.Combine(intermediateDirectory, Path.GetFileNameWithoutExtension(sourcePath) + ".wir");
725 + var extension = Path.GetExtension(path);
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);
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 + }
763 }
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);
795 }
796
797 private bool TryParseBindPath(string bindPath, out IBindPath bp)
@@ -807,5 +819,36 @@ namespace WixToolset.Core.CommandLine
819 return true;
820 }
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 + }
853 }
854 }
src/wix/test/WixToolsetTest.CoreIntegration/BadInputFixture.cs
-15
@@ -6,25 +6,10 @@ namespace WixToolsetTest.CoreIntegration
6 using System.IO;
7 using WixBuildTools.TestSupport;
8 using WixToolset.Core.TestPackage;
9 - using WixToolset.Data;
9 using Xunit;
10
11 public class BadInputFixture
12 {
14 - [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 -
13 [Fact]
14 public void HandleInvalidIds()
15 {
src/wix/test/WixToolsetTest.CoreIntegration/CommandLineFixture.cs new
+59
@@ -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 +
3 +namespace 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 +}
src/wix/test/WixToolsetTest.CoreIntegration/TestData/SimplePackage/SimplePackage.wxs new
+11
@@ -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>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/WixlibMultiarch/MultiarchFile.wxs new
+11
@@ -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>
src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs
+43
@@ -52,6 +52,49 @@ namespace WixToolsetTest.CoreIntegration
52 }
53 }
54
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]
99 public void CanBuildWixlibWithBinariesFromNamedBindPaths()
100 {