aboutsummaryrefslogtreecommitdiff
path: root/src/wix/WixToolset.Core/Bind/FileResolver.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/wix/WixToolset.Core/Bind/FileResolver.cs')
-rw-r--r--src/wix/WixToolset.Core/Bind/FileResolver.cs207
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
3namespace 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}