diff options
Diffstat (limited to 'src/WixToolset.Core/Bind/FileResolver.cs')
-rw-r--r-- | src/WixToolset.Core/Bind/FileResolver.cs | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Bind/FileResolver.cs b/src/WixToolset.Core/Bind/FileResolver.cs new file mode 100644 index 00000000..8d624e6f --- /dev/null +++ b/src/WixToolset.Core/Bind/FileResolver.cs | |||
@@ -0,0 +1,231 @@ | |||
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 System.Runtime.InteropServices; | ||
10 | using WixToolset.Data; | ||
11 | using WixToolset.Data.Bind; | ||
12 | using WixToolset.Extensibility; | ||
13 | |||
14 | internal class FileResolver | ||
15 | { | ||
16 | private const string BindPathOpenString = "!(bindpath."; | ||
17 | |||
18 | private FileResolver(IEnumerable<BindPath> bindPaths) | ||
19 | { | ||
20 | this.BindPaths = (bindPaths ?? Array.Empty<BindPath>()).ToLookup(b => b.Stage); | ||
21 | this.RebaseTarget = this.BindPaths[BindStage.Target].Any(); | ||
22 | this.RebaseUpdated = this.BindPaths[BindStage.Updated].Any(); | ||
23 | } | ||
24 | |||
25 | public FileResolver(IEnumerable<BindPath> bindPaths, IEnumerable<IBinderExtension> extensions) : this(bindPaths) | ||
26 | { | ||
27 | this.BinderExtensions = extensions ?? Array.Empty<IBinderExtension>(); | ||
28 | } | ||
29 | |||
30 | public FileResolver(IEnumerable<BindPath> bindPaths, IEnumerable<ILibrarianExtension> extensions) : this(bindPaths) | ||
31 | { | ||
32 | this.LibrarianExtensions = extensions ?? Array.Empty<ILibrarianExtension>(); | ||
33 | } | ||
34 | |||
35 | private ILookup<BindStage, BindPath> BindPaths { get; } | ||
36 | |||
37 | public bool RebaseTarget { get; } | ||
38 | |||
39 | public bool RebaseUpdated { get; } | ||
40 | |||
41 | private IEnumerable<IBinderExtension> BinderExtensions { get; } | ||
42 | |||
43 | private IEnumerable<ILibrarianExtension> LibrarianExtensions { get; } | ||
44 | |||
45 | /// <summary> | ||
46 | /// Copies a file. | ||
47 | /// </summary> | ||
48 | /// <param name="source">The file to copy.</param> | ||
49 | /// <param name="destination">The destination file.</param> | ||
50 | /// <param name="overwrite">true if the destination file can be overwritten; otherwise, false.</param> | ||
51 | public bool CopyFile(string source, string destination, bool overwrite) | ||
52 | { | ||
53 | foreach (var extension in this.BinderExtensions) | ||
54 | { | ||
55 | if (extension.CopyFile(source, destination, overwrite)) | ||
56 | { | ||
57 | return true; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | if (overwrite && File.Exists(destination)) | ||
62 | { | ||
63 | File.Delete(destination); | ||
64 | } | ||
65 | |||
66 | if (!CreateHardLink(destination, source, IntPtr.Zero)) | ||
67 | { | ||
68 | #if DEBUG | ||
69 | int er = Marshal.GetLastWin32Error(); | ||
70 | #endif | ||
71 | |||
72 | File.Copy(source, destination, overwrite); | ||
73 | } | ||
74 | |||
75 | return true; | ||
76 | } | ||
77 | |||
78 | /// <summary> | ||
79 | /// Moves a file. | ||
80 | /// </summary> | ||
81 | /// <param name="source">The file to move.</param> | ||
82 | /// <param name="destination">The destination file.</param> | ||
83 | public bool MoveFile(string source, string destination, bool overwrite) | ||
84 | { | ||
85 | foreach (var extension in this.BinderExtensions) | ||
86 | { | ||
87 | if (extension.MoveFile(source, destination, overwrite)) | ||
88 | { | ||
89 | return true; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | if (overwrite && File.Exists(destination)) | ||
94 | { | ||
95 | File.Delete(destination); | ||
96 | } | ||
97 | |||
98 | var directory = Path.GetDirectoryName(destination); | ||
99 | if (!String.IsNullOrEmpty(directory)) | ||
100 | { | ||
101 | Directory.CreateDirectory(directory); | ||
102 | } | ||
103 | |||
104 | File.Move(source, destination); | ||
105 | |||
106 | return true; | ||
107 | } | ||
108 | |||
109 | public string Resolve(SourceLineNumber sourceLineNumbers, string table, string path) | ||
110 | { | ||
111 | foreach (var extension in this.LibrarianExtensions) | ||
112 | { | ||
113 | var resolved = extension.Resolve(sourceLineNumbers, table, path); | ||
114 | |||
115 | if (null != resolved) | ||
116 | { | ||
117 | return resolved; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | return this.ResolveUsingBindPaths(path, table, sourceLineNumbers, BindStage.Normal); | ||
122 | } | ||
123 | |||
124 | /// <summary> | ||
125 | /// Resolves the source path of a file using binder extensions. | ||
126 | /// </summary> | ||
127 | /// <param name="source">Original source value.</param> | ||
128 | /// <param name="type">Optional type of source file being resolved.</param> | ||
129 | /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param> | ||
130 | /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param> | ||
131 | /// <returns>Should return a valid path for the stream to be imported.</returns> | ||
132 | public string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) | ||
133 | { | ||
134 | foreach (var extension in this.BinderExtensions) | ||
135 | { | ||
136 | var resolved = extension.ResolveFile(source, type, sourceLineNumbers, bindStage); | ||
137 | |||
138 | if (null != resolved) | ||
139 | { | ||
140 | return resolved; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | return this.ResolveUsingBindPaths(source, type, sourceLineNumbers, bindStage); | ||
145 | } | ||
146 | |||
147 | private string ResolveUsingBindPaths(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) | ||
148 | { | ||
149 | string resolved = null; | ||
150 | |||
151 | // If the file exists, we're good to go. | ||
152 | if (CheckFileExists(source)) | ||
153 | { | ||
154 | resolved = source; | ||
155 | } | ||
156 | else if (Path.IsPathRooted(source)) // path is rooted so bindpaths won't help, bail since the file apparently doesn't exist. | ||
157 | { | ||
158 | resolved = null; | ||
159 | } | ||
160 | else // not a rooted path so let's try applying all the different source resolution options. | ||
161 | { | ||
162 | string bindName = String.Empty; | ||
163 | var path = source; | ||
164 | string pathWithoutSourceDir = null; | ||
165 | |||
166 | if (source.StartsWith(BindPathOpenString, StringComparison.Ordinal)) | ||
167 | { | ||
168 | int closeParen = source.IndexOf(')', BindPathOpenString.Length); | ||
169 | if (-1 != closeParen) | ||
170 | { | ||
171 | bindName = source.Substring(BindPathOpenString.Length, closeParen - BindPathOpenString.Length); | ||
172 | path = source.Substring(BindPathOpenString.Length + bindName.Length + 1); // +1 for the closing brace. | ||
173 | path = path.TrimStart('\\'); // remove starting '\\' char so the path doesn't look rooted. | ||
174 | } | ||
175 | } | ||
176 | else if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal)) | ||
177 | { | ||
178 | pathWithoutSourceDir = path.Substring(10); | ||
179 | } | ||
180 | |||
181 | var bindPaths = this.BindPaths[bindStage]; | ||
182 | |||
183 | foreach (var bindPath in bindPaths) | ||
184 | { | ||
185 | if (!String.IsNullOrEmpty(pathWithoutSourceDir)) | ||
186 | { | ||
187 | var filePath = Path.Combine(bindPath.Path, pathWithoutSourceDir); | ||
188 | |||
189 | if (CheckFileExists(filePath)) | ||
190 | { | ||
191 | resolved = filePath; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | if (String.IsNullOrEmpty(resolved)) | ||
196 | { | ||
197 | var filePath = Path.Combine(bindPath.Path, path); | ||
198 | |||
199 | if (CheckFileExists(filePath)) | ||
200 | { | ||
201 | resolved = filePath; | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | |||
207 | if (null == resolved) | ||
208 | { | ||
209 | throw new WixFileNotFoundException(sourceLineNumbers, source, type); | ||
210 | } | ||
211 | |||
212 | // Didn't find the file. | ||
213 | return resolved; | ||
214 | } | ||
215 | |||
216 | private static bool CheckFileExists(string path) | ||
217 | { | ||
218 | try | ||
219 | { | ||
220 | return File.Exists(path); | ||
221 | } | ||
222 | catch (ArgumentException) | ||
223 | { | ||
224 | throw new WixException(WixErrors.IllegalCharactersInPath(path)); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] | ||
229 | private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes); | ||
230 | } | ||
231 | } | ||