diff options
Diffstat (limited to 'src/wix/WixToolset.Core/Bind/FileResolver.cs')
-rw-r--r-- | src/wix/WixToolset.Core/Bind/FileResolver.cs | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/src/wix/WixToolset.Core/Bind/FileResolver.cs b/src/wix/WixToolset.Core/Bind/FileResolver.cs new file mode 100644 index 00000000..eb878239 --- /dev/null +++ b/src/wix/WixToolset.Core/Bind/FileResolver.cs | |||
@@ -0,0 +1,207 @@ | |||
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.Core.Bind | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Extensibility; | ||
11 | using WixToolset.Extensibility.Data; | ||
12 | |||
13 | internal class FileResolver | ||
14 | { | ||
15 | private const string BindPathOpenString = "!(bindpath."; | ||
16 | |||
17 | private FileResolver(IEnumerable<IBindPath> bindPaths) | ||
18 | { | ||
19 | this.BindPaths = (bindPaths ?? Array.Empty<IBindPath>()).ToLookup(b => b.Stage); | ||
20 | this.RebaseTarget = this.BindPaths[BindStage.Target].Any(); | ||
21 | this.RebaseUpdated = this.BindPaths[BindStage.Updated].Any(); | ||
22 | } | ||
23 | |||
24 | public FileResolver(IEnumerable<IBindPath> bindPaths, IEnumerable<IResolverExtension> extensions) : this(bindPaths) | ||
25 | { | ||
26 | this.ResolverExtensions = extensions ?? Array.Empty<IResolverExtension>(); | ||
27 | } | ||
28 | |||
29 | public FileResolver(IEnumerable<IBindPath> bindPaths, IEnumerable<ILibrarianExtension> extensions) : this(bindPaths) | ||
30 | { | ||
31 | this.LibrarianExtensions = extensions ?? Array.Empty<ILibrarianExtension>(); | ||
32 | } | ||
33 | |||
34 | private ILookup<BindStage, IBindPath> BindPaths { get; } | ||
35 | |||
36 | public bool RebaseTarget { get; } | ||
37 | |||
38 | public bool RebaseUpdated { get; } | ||
39 | |||
40 | private IEnumerable<IResolverExtension> ResolverExtensions { get; } | ||
41 | |||
42 | private IEnumerable<ILibrarianExtension> LibrarianExtensions { get; } | ||
43 | |||
44 | public string Resolve(SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, string source) | ||
45 | { | ||
46 | var checkedPaths = new List<string>(); | ||
47 | |||
48 | foreach (var extension in this.LibrarianExtensions) | ||
49 | { | ||
50 | var resolved = extension.ResolveFile(sourceLineNumbers, symbolDefinition, source); | ||
51 | |||
52 | if (resolved?.CheckedPaths != null) | ||
53 | { | ||
54 | checkedPaths.AddRange(resolved.CheckedPaths); | ||
55 | } | ||
56 | |||
57 | if (!String.IsNullOrEmpty(resolved?.Path)) | ||
58 | { | ||
59 | return resolved?.Path; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, BindStage.Normal, checkedPaths); | ||
64 | } | ||
65 | |||
66 | /// <summary> | ||
67 | /// Resolves the source path of a file using binder extensions. | ||
68 | /// </summary> | ||
69 | /// <param name="source">Original source value.</param> | ||
70 | /// <param name="symbolDefinition">Optional type of source file being resolved.</param> | ||
71 | /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param> | ||
72 | /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param> | ||
73 | /// <param name="alreadyCheckedPaths">Optional collection of paths already checked.</param> | ||
74 | /// <returns>Should return a valid path for the stream to be imported.</returns> | ||
75 | public string ResolveFile(string source, IntermediateSymbolDefinition symbolDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage, IEnumerable<string> alreadyCheckedPaths = null) | ||
76 | { | ||
77 | var checkedPaths = new List<string>(); | ||
78 | |||
79 | if (alreadyCheckedPaths != null) | ||
80 | { | ||
81 | checkedPaths.AddRange(alreadyCheckedPaths); | ||
82 | } | ||
83 | |||
84 | foreach (var extension in this.ResolverExtensions) | ||
85 | { | ||
86 | var resolved = extension.ResolveFile(source, symbolDefinition, sourceLineNumbers, bindStage); | ||
87 | |||
88 | if (resolved?.CheckedPaths != null) | ||
89 | { | ||
90 | checkedPaths.AddRange(resolved.CheckedPaths); | ||
91 | } | ||
92 | |||
93 | if (!String.IsNullOrEmpty(resolved?.Path)) | ||
94 | { | ||
95 | return resolved?.Path; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, bindStage, checkedPaths); | ||
100 | } | ||
101 | |||
102 | private string MustResolveUsingBindPaths(string source, IntermediateSymbolDefinition symbolDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage, List<string> checkedPaths) | ||
103 | { | ||
104 | string resolved = null; | ||
105 | |||
106 | // If the file exists, we're good to go. | ||
107 | checkedPaths.Add(source); | ||
108 | if (CheckFileExists(source)) | ||
109 | { | ||
110 | resolved = source; | ||
111 | } | ||
112 | else if (Path.IsPathRooted(source)) // path is rooted so bindpaths won't help, bail since the file apparently doesn't exist. | ||
113 | { | ||
114 | resolved = null; | ||
115 | } | ||
116 | else // not a rooted path so let's try applying all the different source resolution options. | ||
117 | { | ||
118 | var bindName = String.Empty; | ||
119 | var path = source; | ||
120 | var pathWithoutSourceDir = String.Empty; | ||
121 | |||
122 | if (source.StartsWith(BindPathOpenString, StringComparison.Ordinal)) | ||
123 | { | ||
124 | var closeParen = source.IndexOf(')', BindPathOpenString.Length); | ||
125 | |||
126 | if (-1 != closeParen) | ||
127 | { | ||
128 | bindName = source.Substring(BindPathOpenString.Length, closeParen - BindPathOpenString.Length); | ||
129 | path = source.Substring(BindPathOpenString.Length + bindName.Length + 1); // +1 for the closing brace. | ||
130 | path = path.TrimStart('\\'); // remove starting '\\' char so the path doesn't look rooted. | ||
131 | } | ||
132 | } | ||
133 | else if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal)) | ||
134 | { | ||
135 | pathWithoutSourceDir = path.Substring(10); | ||
136 | } | ||
137 | |||
138 | var bindPaths = this.BindPaths[bindStage]; | ||
139 | |||
140 | foreach (var bindPath in bindPaths) | ||
141 | { | ||
142 | if (String.IsNullOrEmpty(bindName)) | ||
143 | { | ||
144 | if (String.IsNullOrEmpty(bindPath.Name)) | ||
145 | { | ||
146 | if (!String.IsNullOrEmpty(pathWithoutSourceDir)) | ||
147 | { | ||
148 | var filePath = Path.Combine(bindPath.Path, pathWithoutSourceDir); | ||
149 | |||
150 | checkedPaths.Add(filePath); | ||
151 | if (CheckFileExists(filePath)) | ||
152 | { | ||
153 | resolved = filePath; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | if (String.IsNullOrEmpty(resolved)) | ||
158 | { | ||
159 | var filePath = Path.Combine(bindPath.Path, path); | ||
160 | |||
161 | checkedPaths.Add(filePath); | ||
162 | if (CheckFileExists(filePath)) | ||
163 | { | ||
164 | resolved = filePath; | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | else if (bindName.Equals(bindPath.Name, StringComparison.OrdinalIgnoreCase)) | ||
170 | { | ||
171 | var filePath = Path.Combine(bindPath.Path, path); | ||
172 | |||
173 | checkedPaths.Add(filePath); | ||
174 | if (CheckFileExists(filePath)) | ||
175 | { | ||
176 | resolved = filePath; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | if (!String.IsNullOrEmpty(resolved)) | ||
181 | { | ||
182 | break; | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | |||
187 | if (null == resolved) | ||
188 | { | ||
189 | throw new WixException(ErrorMessages.FileNotFound(sourceLineNumbers, source, symbolDefinition.Name, checkedPaths)); | ||
190 | } | ||
191 | |||
192 | return resolved; | ||
193 | } | ||
194 | |||
195 | private static bool CheckFileExists(string path) | ||
196 | { | ||
197 | try | ||
198 | { | ||
199 | return File.Exists(path); | ||
200 | } | ||
201 | catch (ArgumentException) | ||
202 | { | ||
203 | throw new WixException(ErrorMessages.IllegalCharactersInPath(path)); | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | } | ||