main
cs 211 lines 9.08 KB
Raw
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.BuildTasks
4 {
5 using System;
6 using System.Collections.Generic;
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.
14 /// </summary>
15 public class ResolveWixReferences : Task
16 {
17 /// <summary>
18 /// Token value used in SearchPaths to indicate that the item's HintPath metadata should
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
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}";
30
31 /// <summary>
32 /// The list of references to resolve.
33 /// </summary>
34 [Required]
35 public ITaskItem[] WixReferences { get; set; }
36
37 /// <summary>
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
53 /// <summary>
54 /// The filename extension(s) to be checked when searching.
55 /// </summary>
56 public string[] SearchFilenameExtensions { get; set; }
57
58 /// <summary>
59 /// Output items that contain the same metadata as input references and have been resolved to full paths.
60 /// </summary>
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 /// <returns>True on success, or throws an exception on failure.</returns>
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
80 foreach (var reference in this.WixReferences.Where(r => !String.IsNullOrWhiteSpace(r.ItemSpec)))
81 {
82 (var resolvedReference, var found) = this.ResolveReference(reference, this.SearchPaths, this.SearchFilenameExtensions);
83
84 if (uniqueReferences.Add(resolvedReference.ItemSpec))
85 {
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 {
99 this.Log.LogMessage(MessageImportance.Low, "Resolved duplicate path {0}, discarding it", resolvedReference.ItemSpec);
100 }
101 }
102
103 this.ResolvedWixReferences = resolvedReferences.ToArray();
104 this.UnresolvedWixReferences = unresolvedReferences.ToArray();
105 return true;
106 }
107
108 /// <summary>
109 /// Resolves a single reference item by searcheing for referenced items using the specified SearchPaths.
110 /// This method is made public so the resolution logic can be reused by other tasks.
111 /// </summary>
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>
115 /// <returns>The resolved reference item, or the original reference if it could not be resolved.</returns>
116 public (ITaskItem, bool) ResolveReference(ITaskItem reference, string[] searchPaths, string[] searchFilenameExtensions)
117 {
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 {
130 if (this.ResolveFilenameExtensions(resolvedReference, resolvedReference.ItemSpec, searchFilenameExtensions))
131 {
132 found = true;
133 }
134
135 return (resolvedReference, found);
136 }
137
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 {
141 this.Log.LogMessage(MessageImportance.Low, "Trying {0}", searchPath);
142 if (HintPathToken.Equals(searchPath, StringComparison.Ordinal))
143 {
144 var path = reference.GetMetadata("HintPath");
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 }
158 else if (RawFileNameToken.Equals(searchPath, StringComparison.Ordinal))
159 {
160 if (this.ResolveFilenameExtensions(resolvedReference, resolvedReference.ItemSpec, searchFilenameExtensions))
161 {
162 found = true;
163 break;
164 }
165 }
166 else
167 {
168 var path = Path.Combine(searchPath, reference.ItemSpec);
169
170 if (this.ResolveFilenameExtensions(resolvedReference, path, searchFilenameExtensions))
171 {
172 found = true;
173 break;
174 }
175 }
176 }
177
178 if (found)
179 {
180 // Normalize the item spec to the full path.
181 resolvedReference.ItemSpec = resolvedReference.GetMetadata("FullPath");
182 }
183
184 return (resolvedReference, found);
185 }
186
187 /// <summary>
188 /// Helper method for checking filename extensions when resolving references.
189 /// </summary>
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>
193 /// <returns>True if the item was resolved, else false.</returns>
194 private bool ResolveFilenameExtensions(ITaskItem reference, string basePath, string[] filenameExtensions)
195 {
196 foreach (var filenameExtension in filenameExtensions)
197 {
198 var path = basePath + filenameExtension;
199 this.Log.LogMessage(MessageImportance.Low, "Trying path {0}", path);
200
201 if (File.Exists(path))
202 {
203 reference.ItemSpec = path;
204 return true;
205 }
206 }
207
208 return false;
209 }
210 }
211 }