@joebigelow / wix / commits / 08f53f40

Simplify reference resolution

WiX v3 extension loading had options that were rarely if ever used and library paths modeled after C++. Given the new Sdk-style model in WiX v4, we can simplify reference resolution. Fixes 6945, 6946

Rob Mensching committed Oct 14, 2022 at 09:34 UTC 08f53f409020b12dffaa2aeefa943b667a4b9328
8 files changed +173 -251
src/internal/WixToolset.BaseBuildTasks.Sources/FileSearchHelperMethods.cs deleted
-57
@@ -1,57 +0,0 @@
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 WixToolset.BaseBuildTasks
4 -{
5 - using System;
6 - using System.IO;
7 -
8 - /// <summary>
9 - /// Contains helper methods on searching for files
10 - /// </summary>
11 - public static class FileSearchHelperMethods
12 - {
13 - /// <summary>
14 - /// Searches for the existence of a file in multiple directories.
15 - /// Search is satisfied if default file path is valid and exists. If not,
16 - /// file name is extracted from default path and combined with each of the directories
17 - /// looking to see if it exists. If not found, input default path is returned.
18 - /// </summary>
19 - /// <param name="directories">Array of directories to look in, without filenames in them</param>
20 - /// <param name="defaultFullPath">Default path - to use if not found</param>
21 - /// <returns>File path if file found. Empty string if not found</returns>
22 - public static string SearchFilePaths(string[] directories, string defaultFullPath)
23 - {
24 - if (String.IsNullOrEmpty(defaultFullPath))
25 - {
26 - return String.Empty;
27 - }
28 -
29 - if (File.Exists(defaultFullPath))
30 - {
31 - return defaultFullPath;
32 - }
33 -
34 - if (directories == null)
35 - {
36 - return String.Empty;
37 - }
38 -
39 - var fileName = Path.GetFileName(defaultFullPath);
40 - foreach (var currentPath in directories)
41 - {
42 - if (String.IsNullOrWhiteSpace(currentPath))
43 - {
44 - continue;
45 - }
46 -
47 - var path = Path.Combine(currentPath, fileName);
48 - if (File.Exists(path))
49 - {
50 - return path;
51 - }
52 - }
53 -
54 - return String.Empty;
55 - }
56 - }
57 -}
src/internal/WixToolset.BaseBuildTasks.Sources/WixCommandLineBuilder.cs
-73
@@ -75,79 +75,6 @@ namespace WixToolset.BaseBuildTasks
75 }
76 }
77
78 - /// <summary>
79 - /// Build the extensions argument. Each extension is searched in the current folder, user defined search
80 - /// directories (ReferencePath), HintPath, and under Wix Extension Directory in that order.
81 - /// The order of precedence is based off of that described in Microsoft.Common.Targets's SearchPaths
82 - /// property for the ResolveAssemblyReferences task.
83 - /// </summary>
84 - /// <param name="extensions">The list of extensions to include.</param>
85 - /// <param name="wixExtensionDirectory">Evaluated default folder for Wix Extensions</param>
86 - /// <param name="referencePaths">User defined reference directories to search in</param>
87 - public void AppendExtensions(ITaskItem[] extensions, string wixExtensionDirectory, string [] referencePaths)
88 - {
89 - if (extensions == null)
90 - {
91 - return;
92 - }
93 -
94 - foreach (ITaskItem extension in extensions)
95 - {
96 - string className = extension.GetMetadata("Class");
97 -
98 - string fileName = Path.GetFileName(extension.ItemSpec);
99 -
100 - if (String.IsNullOrEmpty(Path.GetExtension(fileName)))
101 - {
102 - fileName += ".dll";
103 - }
104 -
105 - // First try reference paths
106 - var resolvedPath = FileSearchHelperMethods.SearchFilePaths(referencePaths, fileName);
107 -
108 - if (String.IsNullOrEmpty(resolvedPath))
109 - {
110 - // Now try HintPath
111 - resolvedPath = extension.GetMetadata("HintPath");
112 -
113 - if (!File.Exists(resolvedPath))
114 - {
115 - // Now try the item itself
116 - resolvedPath = extension.ItemSpec;
117 -
118 - if (String.IsNullOrEmpty(Path.GetExtension(resolvedPath)))
119 - {
120 - resolvedPath += ".dll";
121 - }
122 -
123 - if (!File.Exists(resolvedPath))
124 - {
125 - if (!String.IsNullOrEmpty(wixExtensionDirectory))
126 - {
127 - // Now try the extension directory
128 - resolvedPath = Path.Combine(wixExtensionDirectory, Path.GetFileName(resolvedPath));
129 - }
130 -
131 - if (!File.Exists(resolvedPath))
132 - {
133 - // Extension wasn't found, just set it to the extension name passed in
134 - resolvedPath = extension.ItemSpec;
135 - }
136 - }
137 - }
138 - }
139 -
140 - if (String.IsNullOrEmpty(className))
141 - {
142 - this.AppendSwitchIfNotNull("-ext ", resolvedPath);
143 - }
144 - else
145 - {
146 - this.AppendSwitchIfNotNull("-ext ", className + ", " + resolvedPath);
147 - }
148 - }
149 - }
150 -
78 /// <summary>
79 /// Append arbitrary text to the command-line if specified.
80 /// </summary>
src/wix/WixToolset.BuildTasks/ResolveWixReferences.cs
+77 -70
@@ -4,9 +4,10 @@ namespace WixToolset.BuildTasks
4 {
5 using System;
6 using System.Collections.Generic;
7 - using Microsoft.Build.Utilities;
8 - using Microsoft.Build.Framework;
7 using System.IO;
8 + using System.Linq;
9 + using Microsoft.Build.Framework;
10 + using Microsoft.Build.Utilities;
11
12 /// <summary>
13 /// This task searches for paths to references using the order specified in SearchPaths.
@@ -15,14 +16,14 @@ namespace WixToolset.BuildTasks
16 {
17 /// <summary>
18 /// Token value used in SearchPaths to indicate that the item's HintPath metadata should
18 - /// be searched as a full file path to resolve the reference.
19 + /// be searched as a full file path to resolve the reference.
20 /// Must match wix.targets, case sensitive.
21 /// </summary>
22 private const string HintPathToken = "{HintPathFromItem}";
23
24 /// <summary>
25 /// Token value used in SearchPaths to indicate that the item's Identity should
25 - /// be searched as a full file path to resolve the reference.
26 + /// be searched as a full file path to resolve the reference.
27 /// Must match wix.targets, case sensitive.
28 /// </summary>
29 private const string RawFileNameToken = "{RawFileName}";
@@ -34,24 +35,18 @@ namespace WixToolset.BuildTasks
35 public ITaskItem[] WixReferences { get; set; }
36
37 /// <summary>
37 - /// The directories or special locations that are searched to find the files
38 - /// on disk that represent the references. The order in which the search paths are listed
39 - /// is important. For each reference, the list of paths is searched from left to right.
40 - /// When a file that represents the reference is found, that search stops and the search
41 - /// for the next reference starts.
42 - ///
43 - /// This parameter accepts the following types of values:
44 - /// A directory path.
45 - /// {HintPathFromItem}: Specifies that the task will examine the HintPath metadata
46 - /// of the base item.
47 - /// TODO : {CandidateAssemblyFiles}: Specifies that the task will examine the files
48 - /// passed in through the CandidateAssemblyFiles parameter.
49 - /// TODO : {Registry:_AssemblyFoldersBase_, _RuntimeVersion_, _AssemblyFoldersSuffix_}:
50 - /// TODO : {AssemblyFolders}: Specifies the task will use the Visual Studio.NET 2003
51 - /// finding-assemblies-from-registry scheme.
52 - /// TODO : {GAC}: Specifies the task will search in the GAC.
53 - /// {RawFileName}: Specifies the task will consider the Include value of the item to be
54 - /// an exact path and file name.
38 + /// The directories or special locations that are searched to find the files
39 + /// on disk that represent the references. The order in which the search paths are listed
40 + /// is important. For each reference, the list of paths is searched from left to right.
41 + /// When a file that represents the reference is found, that search stops and the search
42 + /// for the next reference starts.
43 + ///
44 + /// This parameter accepts the following types of values:
45 + /// A directory path.
46 + /// {HintPathFromItem}: Specifies that the task will examine the HintPath metadata
47 + /// of the base item.
48 + /// {RawFileName}: Specifies the task will consider the Include value of the item to be
49 + /// an exact path and file name.
50 /// </summary>
51 public string[] SearchPaths { get; set; }
52
@@ -66,6 +61,12 @@ namespace WixToolset.BuildTasks
61 [Output]
62 public ITaskItem[] ResolvedWixReferences { get; private set; }
63
64 + /// <summary>
65 + /// Output items that contain the same metadata as input references and cannot be found.
66 + /// </summary>
67 + [Output]
68 + public ITaskItem[] UnresolvedWixReferences { get; private set; }
69 +
70 /// <summary>
71 /// Resolves reference paths by searching for referenced items using the specified SearchPaths.
72 /// </summary>
@@ -73,16 +74,25 @@ namespace WixToolset.BuildTasks
74 public override bool Execute()
75 {
76 var resolvedReferences = new List<ITaskItem>();
77 + var unresolvedReferences = new List<ITaskItem>();
78 var uniqueReferences = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
79
78 - foreach (var reference in this.WixReferences)
80 + foreach (var reference in this.WixReferences.Where(r => !String.IsNullOrWhiteSpace(r.ItemSpec)))
81 {
80 - var resolvedReference = ResolveWixReferences.ResolveReference(reference, this.SearchPaths, this.SearchFilenameExtensions, this.Log);
82 + (var resolvedReference, var found) = this.ResolveReference(reference, this.SearchPaths, this.SearchFilenameExtensions);
83
84 if (uniqueReferences.Add(resolvedReference.ItemSpec))
85 {
84 - this.Log.LogMessage(MessageImportance.Low, "Resolved path {0}", resolvedReference.ItemSpec);
85 - resolvedReferences.Add(resolvedReference);
86 + if (found)
87 + {
88 + this.Log.LogMessage(MessageImportance.Low, "Resolved path {0}", resolvedReference.ItemSpec);
89 + resolvedReferences.Add(resolvedReference);
90 + }
91 + else
92 + {
93 + this.Log.LogWarning(null, "WXE0001", null, null, 0, 0, 0, 0, "Unable to find extension {0}.", resolvedReference.ItemSpec);
94 + unresolvedReferences.Add(resolvedReference);
95 + }
96 }
97 else
98 {
@@ -91,6 +101,7 @@ namespace WixToolset.BuildTasks
101 }
102
103 this.ResolvedWixReferences = resolvedReferences.ToArray();
104 + this.UnresolvedWixReferences = unresolvedReferences.ToArray();
105 return true;
106 }
107
@@ -101,80 +112,76 @@ namespace WixToolset.BuildTasks
112 /// <param name="reference">The referenced item.</param>
113 /// <param name="searchPaths">The paths to search.</param>
114 /// <param name="searchFilenameExtensions">Filename extensions to check.</param>
104 - /// <param name="log">Logging helper.</param>
115 /// <returns>The resolved reference item, or the original reference if it could not be resolved.</returns>
106 - public static ITaskItem ResolveReference(ITaskItem reference, string[] searchPaths, string[] searchFilenameExtensions, TaskLoggingHelper log)
116 + public (ITaskItem, bool) ResolveReference(ITaskItem reference, string[] searchPaths, string[] searchFilenameExtensions)
117 {
108 - if (reference == null)
109 - {
110 - throw new ArgumentNullException("reference");
111 - }
118 + // Ensure we first check the reference without adding additional search filename extensions.
119 + searchFilenameExtensions = searchFilenameExtensions == null ? new[] { String.Empty } : searchFilenameExtensions.Prepend(String.Empty).ToArray();
120 +
121 + // Copy all the metadata from the source
122 + var resolvedReference = new TaskItem(reference);
123 + this.Log.LogMessage(MessageImportance.Low, "WixReference: {0}", reference.ItemSpec);
124 +
125 + var found = false;
126
127 + // Nothing to search, so just resolve the original reference item.
128 if (searchPaths == null)
129 {
115 - // Nothing to search, so just return the original reference item.
116 - return reference;
117 - }
130 + if (this.ResolveFilenameExtensions(resolvedReference, resolvedReference.ItemSpec, searchFilenameExtensions))
131 + {
132 + found = true;
133 + }
134
119 - if (searchFilenameExtensions == null)
120 - {
121 - searchFilenameExtensions = new string[] { };
135 + return (resolvedReference, found);
136 }
137
124 - // Copy all the metadata from the source
125 - var resolvedReference = new TaskItem(reference);
126 - log.LogMessage(MessageImportance.Low, "WixReference: {0}", reference.ItemSpec);
127 -
128 - // Now find the resolved path based on our order of precedence
138 + // Otherwise, now try to find the resolved path based on the order of precedence from search paths.
139 foreach (var searchPath in searchPaths)
140 {
131 - log.LogMessage(MessageImportance.Low, "Trying {0}", searchPath);
132 - if (searchPath.Equals(HintPathToken, StringComparison.Ordinal))
141 + this.Log.LogMessage(MessageImportance.Low, "Trying {0}", searchPath);
142 + if (HintPathToken.Equals(searchPath, StringComparison.Ordinal))
143 {
144 var path = reference.GetMetadata("HintPath");
135 - log.LogMessage(MessageImportance.Low, "Trying path {0}", path);
145 + if (String.IsNullOrWhiteSpace(path))
146 + {
147 + continue;
148 + }
149 +
150 + this.Log.LogMessage(MessageImportance.Low, "Trying path {0}", path);
151 if (File.Exists(path))
152 {
153 resolvedReference.ItemSpec = path;
154 + found = true;
155 break;
156 }
157 }
142 - else if (searchPath.Equals(RawFileNameToken, StringComparison.Ordinal))
158 + else if (RawFileNameToken.Equals(searchPath, StringComparison.Ordinal))
159 {
144 - log.LogMessage(MessageImportance.Low, "Trying path {0}", resolvedReference.ItemSpec);
145 - if (File.Exists(resolvedReference.ItemSpec))
146 - {
147 - break;
148 - }
149 -
150 - if (ResolveWixReferences.ResolveFilenameExtensions(resolvedReference,
151 - resolvedReference.ItemSpec, searchFilenameExtensions, log))
160 + if (this.ResolveFilenameExtensions(resolvedReference, resolvedReference.ItemSpec, searchFilenameExtensions))
161 {
162 + found = true;
163 break;
164 }
165 }
166 else
167 {
158 - var path = Path.Combine(searchPath, Path.GetFileName(reference.ItemSpec));
159 - log.LogMessage(MessageImportance.Low, "Trying path {0}", path);
160 - if (File.Exists(path))
161 - {
162 - resolvedReference.ItemSpec = path;
163 - break;
164 - }
168 + var path = Path.Combine(searchPath, reference.ItemSpec);
169
166 - if (ResolveWixReferences.ResolveFilenameExtensions(resolvedReference,
167 - path, searchFilenameExtensions, log))
170 + if (this.ResolveFilenameExtensions(resolvedReference, path, searchFilenameExtensions))
171 {
172 + found = true;
173 break;
174 }
175 }
176 }
177
174 - // Normalize the item path
175 - resolvedReference.ItemSpec = resolvedReference.GetMetadata("FullPath");
178 + if (found)
179 + {
180 + // Normalize the item spec to the full path.
181 + resolvedReference.ItemSpec = resolvedReference.GetMetadata("FullPath");
182 + }
183
177 - return resolvedReference;
184 + return (resolvedReference, found);
185 }
186
187 /// <summary>
@@ -183,14 +190,14 @@ namespace WixToolset.BuildTasks
190 /// <param name="reference">The reference being resolved.</param>
191 /// <param name="basePath">Full filename path without extension.</param>
192 /// <param name="filenameExtensions">Filename extensions to check.</param>
186 - /// <param name="log">Logging helper.</param>
193 /// <returns>True if the item was resolved, else false.</returns>
188 - private static bool ResolveFilenameExtensions(ITaskItem reference, string basePath, string[] filenameExtensions, TaskLoggingHelper log)
194 + private bool ResolveFilenameExtensions(ITaskItem reference, string basePath, string[] filenameExtensions)
195 {
196 foreach (var filenameExtension in filenameExtensions)
197 {
198 var path = basePath + filenameExtension;
193 - log.LogMessage(MessageImportance.Low, "Trying path {0}", path);
199 + this.Log.LogMessage(MessageImportance.Low, "Trying path {0}", path);
200 +
201 if (File.Exists(path))
202 {
203 reference.ItemSpec = path;
src/wix/WixToolset.BuildTasks/WixBuild.cs
+1 -5
@@ -18,8 +18,6 @@ namespace WixToolset.BuildTasks
18
19 public ITaskItem[] Extensions { get; set; }
20
21 - public string ExtensionDirectory { get; set; }
22 -
21 public string[] IncludeSearchPaths { get; set; }
22
23 public string InstallerPlatform { get; set; }
@@ -45,8 +43,6 @@ namespace WixToolset.BuildTasks
43 [Required]
44 public ITaskItem[] SourceFiles { get; set; }
45
48 - public string[] ReferencePaths { get; set; }
49 -
46 public ITaskItem[] BindInputPaths { get; set; }
47
48 public bool BindFiles { get; set; }
@@ -75,7 +71,7 @@ namespace WixToolset.BuildTasks
71 commandLineBuilder.AppendArrayIfNotNull("-culture ", this.Cultures);
72 commandLineBuilder.AppendArrayIfNotNull("-d ", this.DefineConstants);
73 commandLineBuilder.AppendArrayIfNotNull("-I ", this.IncludeSearchPaths);
78 - commandLineBuilder.AppendExtensions(this.Extensions, this.ExtensionDirectory, this.ReferencePaths);
74 + commandLineBuilder.AppendArrayIfNotNull("-ext ", this.Extensions);
75 commandLineBuilder.AppendSwitchIfNotNull("-cc ", this.CabinetCachePath);
76 commandLineBuilder.AppendSwitchIfNotNull("-intermediatefolder ", this.IntermediateDirectory);
77 commandLineBuilder.AppendSwitchIfNotNull("-trackingfile ", this.BindTrackingFile);
src/wix/WixToolset.Sdk/tools/wix.targets
+12 -29
@@ -128,10 +128,6 @@
128 <TargetPdbPath Condition=" '$(TargetPdbPath)' == '' ">$(TargetPdbDir)$(TargetPdbFileName)</TargetPdbPath>
129 </PropertyGroup>
130
131 - <PropertyGroup>
132 - <WixExtDir Condition=" '$(WixExtDir)' == ''">$(WixBinDir)</WixExtDir>
133 - </PropertyGroup>
134 -
131 <PropertyGroup>
132 <BindTrackingFilePrefix Condition=" '$(BindTrackingFilePrefix)' == '' ">$(MSBuildProjectFile).BindTracking</BindTrackingFilePrefix>
133 <BindTrackingFileExtension Condition=" '$(BindTrackingFileExtension)' == '' ">.txt</BindTrackingFileExtension>
@@ -362,10 +358,9 @@
358
359 By default the WixLibrarySearchPaths property is set to find libraries in the following order:
360
365 - (1) $(ReferencePaths) - the reference paths property, which comes from the .USER file.
361 + (1) $(ReferencePaths) - the reference paths property.
362 (2) The hintpath from the referenced item itself, indicated by {HintPathFromItem}.
363 (3) Treat the reference's Include as if it were a real file name.
368 - (4) Path specified by the WixExtDir property.
364
365 [IN]
366 @(WixLibrary) - the list of .wixlib files.
@@ -381,39 +376,33 @@
376 </PropertyGroup>
377 <Target
378 Name="ResolveWixLibraryReferences"
384 - DependsOnTargets="$(ResolveWixLibraryReferencesDependsOn)">
379 + DependsOnTargets="$(ResolveWixLibraryReferencesDependsOn)"
380 + Condition=" '@(WixLibrary)' != ''">
381
382 <PropertyGroup>
387 - <WixLibrarySearchPaths Condition=" '$(WixLibrarySearchPaths)' == '' ">$(ReferencePaths);{HintPathFromItem};{RawFileName};$(WixExtDir)</WixLibrarySearchPaths>
383 + <WixLibrarySearchPaths Condition=" '$(WixLibrarySearchPaths)' == '' ">$(ReferencePaths);{HintPathFromItem};{RawFileName}</WixLibrarySearchPaths>
384 </PropertyGroup>
385
386 <ResolveWixReferences
387 WixReferences="@(WixLibrary)"
388 SearchPaths="$(WixLibrarySearchPaths)"
393 - SearchFilenameExtensions=".wixlib"
394 - Condition=" '@(WixLibrary)' != ''">
395 - <Output TaskParameter="ResolvedWixReferences" ItemName="_AllResolvedWixLibraryPaths" />
389 + SearchFilenameExtensions=".wixlib">
390 + <Output TaskParameter="ResolvedWixReferences" ItemName="_ResolvedWixLibraryPaths" />
391 + <Output TaskParameter="UnresolvedWixReferences" ItemName="_UnresolvedWixLibraryPaths" />
392 </ResolveWixReferences>
397 -
398 - <RemoveDuplicates Inputs="@(_AllResolvedWixLibraryPaths)">
399 - <Output TaskParameter="Filtered" ItemName="_ResolvedWixLibraryPaths" />
400 - </RemoveDuplicates>
393 </Target>
394
395 <!--
396 ================================================================================================
397 ResolveWixExtensionReferences
398
407 - Resolves WiX extension references to full paths. Any properties you use
408 - to resolve paths to extensions must be defined before importing this
409 - file or the extensions will be automatically resolved to $(WixExtDir).
399 + Resolves WiX extension references to full paths.
400
401 By default the WixExtensionSearchPaths property is set to find extensions in the following order:
402
413 - (1) $(ReferencePaths) - the reference paths property, which comes from the .USER file.
403 + (1) $(ReferencePaths) - the reference paths property.
404 (2) The hintpath from the referenced item itself, indicated by {HintPathFromItem}.
405 (3) Treat the reference's Include as if it were a real file name.
416 - (4) Path specified by the WixExtDir property.
406
407 [IN]
408 @(WixExtension) - WixExtension item group
@@ -431,20 +420,16 @@
420 Condition=" '@(WixExtension)' != ''">
421
422 <PropertyGroup>
434 - <WixExtensionSearchPaths Condition=" '$(WixExtensionSearchPaths)' == '' ">$(ReferencePaths);{HintPathFromItem};{RawFileName};$(WixExtDir)</WixExtensionSearchPaths>
423 + <WixExtensionSearchPaths Condition=" '$(WixExtensionSearchPaths)' == '' ">$(ReferencePaths);{HintPathFromItem};{RawFileName}</WixExtensionSearchPaths>
424 </PropertyGroup>
425
426 <ResolveWixReferences
427 WixReferences="@(WixExtension)"
428 SearchPaths="$(WixExtensionSearchPaths)"
429 SearchFilenameExtensions=".wixext.dll">
441 - <Output TaskParameter="ResolvedWixReferences" ItemName="_AllResolvedWixExtensionPaths" />
430 + <Output TaskParameter="ResolvedWixReferences" ItemName="_ResolvedWixExtensionPaths" />
431 + <Output TaskParameter="UnresolvedWixReferences" ItemName="_UnresolvedWixExtensionPaths" />
432 </ResolveWixReferences>
443 -
444 - <!-- Remove duplicate extension items that would cause build errors -->
445 - <RemoveDuplicates Inputs="@(_AllResolvedWixExtensionPaths)">
446 - <Output TaskParameter="Filtered" ItemName="_ResolvedWixExtensionPaths" />
447 - </RemoveDuplicates>
433 </Target>
434
435 <!--
@@ -599,7 +584,6 @@
584
585 Cultures="%(CultureGroup.Identity)"
586
602 - ExtensionDirectory="$(WixExtDir)"
587 Extensions="@(_ResolvedWixExtensionPaths)"
588
589 IntermediateDirectory="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)"
@@ -615,7 +599,6 @@
599 InstallerPlatform="$(InstallerPlatform)"
600 NoLogo="true"
601 Pedantic="$(Pedantic)"
618 - ReferencePaths="$(ReferencePaths)"
602
603 BindInputPaths="@(LinkerBindInputPaths)"
604 BindFiles="$(BindFiles)"
src/wix/test/WixToolsetTest.Sdk/MsbuildFixture.cs
+58 -17
@@ -29,7 +29,7 @@ namespace WixToolsetTest.Sdk
29 var binFolder = Path.Combine(baseFolder, @"bin\");
30 var projectPath = Path.Combine(baseFolder, "SimpleBundle.wixproj");
31
32 - var result = MsbuildUtilities.BuildProject(buildSystem, projectPath, new[] {
32 + var result = MsbuildUtilities.BuildProject(buildSystem, projectPath, new[] {
33 MsbuildUtilities.GetQuotedPropertySwitch(buildSystem, "WixMSBuildProps", MsbuildFixture.WixPropsPath),
34 "-p:SignOutput=true",
35 });
@@ -162,7 +162,7 @@ namespace WixToolsetTest.Sdk
162 var platformSwitches = result.Output.Where(line => line.Contains("-platform x86"));
163 Assert.Single(platformSwitches);
164
165 - var warnings = result.Output.Where(line => line.Contains(": warning")).Select(ExtractWarningFromMessage).ToArray();
165 + var warnings = result.Output.Where(line => line.Contains(": warning")).Select(line => ExtractWarningFromMessage(line, baseFolder)).ToArray();
166 WixAssert.CompareLineByLine(new[]
167 {
168 @"WIX1118: The variable 'Variable' with value 'DifferentValue' was previously declared with value 'Value'.",
@@ -173,7 +173,7 @@ namespace WixToolsetTest.Sdk
173 @"WIX1122: The installer database '<basefolder>\obj\x86\Release\en-US\MsiPackage.msi' has external cabs, but at least one of them is not signed. Please ensure that all external cabs are signed, if you mean to sign them. If you don't mean to sign them, there is no need to inscribe the MSI as part of your build."
174 }, warnings);
175
176 - var testMessages = result.Output.Where(line => line.Contains("TEST:")).Select(ReplacePathsInMessage).ToArray();
176 + var testMessages = result.Output.Where(line => line.Contains("TEST:")).Select(line => ReplacePathsInMessage(line, baseFolder)).ToArray();
177 WixAssert.CompareLineByLine(new[]
178 {
179 @"TEST: SignCabs: <basefolder>\obj\x86\Release\en-US\cab1.cab",
@@ -191,20 +191,6 @@ namespace WixToolsetTest.Sdk
191 @"bin\x86\Release\en-US\MsiPackage.wixpdb",
192 }, paths);
193 }
194 -
195 - string ExtractWarningFromMessage(string message)
196 - {
197 - const string prefix = ": warning ";
198 -
199 - var start = message.IndexOf(prefix) + prefix.Length;
200 - var end = message.LastIndexOf("[");
201 - return ReplacePathsInMessage(message.Substring(start, end - start));
202 - }
203 -
204 - string ReplacePathsInMessage(string message)
205 - {
206 - return message.Replace(baseFolder, "<basefolder>").Trim();
207 - }
194 }
195
196 [Theory]
@@ -585,6 +571,46 @@ namespace WixToolsetTest.Sdk
571 }
572 }
573
574 +
575 + [Theory]
576 + [InlineData(BuildSystem.DotNetCoreSdk)]
577 + [InlineData(BuildSystem.MSBuild)]
578 + [InlineData(BuildSystem.MSBuild64)]
579 + public void CanBuildWithWarningWhenExtensionIsMissing(BuildSystem buildSystem)
580 + {
581 + var sourceFolder = TestData.Get(@"TestData", "WixlibMissingExtension");
582 +
583 + using (var fs = new TestDataFolderFileSystem())
584 + {
585 + fs.Initialize(sourceFolder);
586 + var baseFolder = fs.BaseFolder;
587 + var binFolder = Path.Combine(baseFolder, @"bin\");
588 + var projectPath = Path.Combine(baseFolder, "WixlibMissingExtension.wixproj");
589 +
590 + var result = MsbuildUtilities.BuildProject(buildSystem, projectPath, new[] {
591 + MsbuildUtilities.GetQuotedPropertySwitch(buildSystem, "WixMSBuildProps", MsbuildFixture.WixPropsPath),
592 + "-p:SignOutput=true",
593 + });
594 + result.AssertSuccess();
595 +
596 + var warnings = result.Output.Where(line => line.Contains(": warning")).Select(line => ExtractWarningFromMessage(line, baseFolder)).ToArray();
597 + WixAssert.CompareLineByLine(new[]
598 + {
599 + "WXE0001: Unable to find extension DoesNotExist.wixext.dll.",
600 + "WXE0001: Unable to find extension DoesNotExist.wixext.dll.",
601 + }, warnings);
602 +
603 + var paths = Directory.EnumerateFiles(binFolder, @"*.*", SearchOption.AllDirectories)
604 + .Select(s => s.Substring(baseFolder.Length + 1))
605 + .OrderBy(s => s)
606 + .ToArray();
607 + WixAssert.CompareLineByLine(new[]
608 + {
609 + @"bin\Release\WixlibMissingExtension.wixlib",
610 + }, paths);
611 + }
612 + }
613 +
614 [Theory(Skip = "Depends on creating broken publish which is not supported at this time")]
615 [InlineData(BuildSystem.DotNetCoreSdk)]
616 [InlineData(BuildSystem.MSBuild)]
@@ -610,5 +636,20 @@ namespace WixToolsetTest.Sdk
636 Assert.Contains(result.Output, m => m.Contains(expectedMessage));
637 }
638 }
639 +
640 + private static string ExtractWarningFromMessage(string message, string baseFolder)
641 + {
642 + const string prefix = ": warning ";
643 +
644 + var start = message.IndexOf(prefix) + prefix.Length;
645 + var end = message.LastIndexOf("[");
646 +
647 + return ReplacePathsInMessage(message.Substring(start, end - start), baseFolder);
648 + }
649 +
650 + private static string ReplacePathsInMessage(string message, string baseFolder)
651 + {
652 + return message.Replace(baseFolder, "<basefolder>").Trim();
653 + }
654 }
655 }
src/wix/test/WixToolsetTest.Sdk/TestData/WixlibMissingExtension/Library.wxs new
+7
@@ -0,0 +1,7 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Fragment>
3 + <StandardDirectory Id="ProgramFilesFolder">
4 + <Directory Id="WixLibFolder" />
5 + </StandardDirectory>
6 + </Fragment>
7 +</Wix>
src/wix/test/WixToolsetTest.Sdk/TestData/WixlibMissingExtension/WixlibMissingExtension.wixproj new
+18
@@ -0,0 +1,18 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<Project>
3 + <Import Project="$(WixMSBuildProps)" />
4 +
5 + <PropertyGroup>
6 + <OutputType>Library</OutputType>
7 + </PropertyGroup>
8 +
9 + <ItemGroup>
10 + <Compile Include="Library.wxs" />
11 + </ItemGroup>
12 +
13 + <ItemGroup>
14 + <WixExtension Include="DoesNotExist.wixext.dll" />
15 + </ItemGroup>
16 +
17 + <Import Project="$(WixTargetsPath)" />
18 +</Project>