From dbde9e7104b907bbbaea17e21247d8cafc8b3a4c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sat, 14 Oct 2017 16:12:07 -0700 Subject: Massive refactoring to introduce the concept of IBackend --- src/WixToolset.Core/Bind/FileResolver.cs | 231 +++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 src/WixToolset.Core/Bind/FileResolver.cs (limited to 'src/WixToolset.Core/Bind/FileResolver.cs') 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 @@ +// 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. + +namespace WixToolset.Core.Bind +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Runtime.InteropServices; + using WixToolset.Data; + using WixToolset.Data.Bind; + using WixToolset.Extensibility; + + internal class FileResolver + { + private const string BindPathOpenString = "!(bindpath."; + + private FileResolver(IEnumerable bindPaths) + { + this.BindPaths = (bindPaths ?? Array.Empty()).ToLookup(b => b.Stage); + this.RebaseTarget = this.BindPaths[BindStage.Target].Any(); + this.RebaseUpdated = this.BindPaths[BindStage.Updated].Any(); + } + + public FileResolver(IEnumerable bindPaths, IEnumerable extensions) : this(bindPaths) + { + this.BinderExtensions = extensions ?? Array.Empty(); + } + + public FileResolver(IEnumerable bindPaths, IEnumerable extensions) : this(bindPaths) + { + this.LibrarianExtensions = extensions ?? Array.Empty(); + } + + private ILookup BindPaths { get; } + + public bool RebaseTarget { get; } + + public bool RebaseUpdated { get; } + + private IEnumerable BinderExtensions { get; } + + private IEnumerable LibrarianExtensions { get; } + + /// + /// Copies a file. + /// + /// The file to copy. + /// The destination file. + /// true if the destination file can be overwritten; otherwise, false. + public bool CopyFile(string source, string destination, bool overwrite) + { + foreach (var extension in this.BinderExtensions) + { + if (extension.CopyFile(source, destination, overwrite)) + { + return true; + } + } + + if (overwrite && File.Exists(destination)) + { + File.Delete(destination); + } + + if (!CreateHardLink(destination, source, IntPtr.Zero)) + { +#if DEBUG + int er = Marshal.GetLastWin32Error(); +#endif + + File.Copy(source, destination, overwrite); + } + + return true; + } + + /// + /// Moves a file. + /// + /// The file to move. + /// The destination file. + public bool MoveFile(string source, string destination, bool overwrite) + { + foreach (var extension in this.BinderExtensions) + { + if (extension.MoveFile(source, destination, overwrite)) + { + return true; + } + } + + if (overwrite && File.Exists(destination)) + { + File.Delete(destination); + } + + var directory = Path.GetDirectoryName(destination); + if (!String.IsNullOrEmpty(directory)) + { + Directory.CreateDirectory(directory); + } + + File.Move(source, destination); + + return true; + } + + public string Resolve(SourceLineNumber sourceLineNumbers, string table, string path) + { + foreach (var extension in this.LibrarianExtensions) + { + var resolved = extension.Resolve(sourceLineNumbers, table, path); + + if (null != resolved) + { + return resolved; + } + } + + return this.ResolveUsingBindPaths(path, table, sourceLineNumbers, BindStage.Normal); + } + + /// + /// Resolves the source path of a file using binder extensions. + /// + /// Original source value. + /// Optional type of source file being resolved. + /// Optional source line of source file being resolved. + /// The binding stage used to determine what collection of bind paths will be used + /// Should return a valid path for the stream to be imported. + public string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) + { + foreach (var extension in this.BinderExtensions) + { + var resolved = extension.ResolveFile(source, type, sourceLineNumbers, bindStage); + + if (null != resolved) + { + return resolved; + } + } + + return this.ResolveUsingBindPaths(source, type, sourceLineNumbers, bindStage); + } + + private string ResolveUsingBindPaths(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) + { + string resolved = null; + + // If the file exists, we're good to go. + if (CheckFileExists(source)) + { + resolved = source; + } + else if (Path.IsPathRooted(source)) // path is rooted so bindpaths won't help, bail since the file apparently doesn't exist. + { + resolved = null; + } + else // not a rooted path so let's try applying all the different source resolution options. + { + string bindName = String.Empty; + var path = source; + string pathWithoutSourceDir = null; + + if (source.StartsWith(BindPathOpenString, StringComparison.Ordinal)) + { + int closeParen = source.IndexOf(')', BindPathOpenString.Length); + if (-1 != closeParen) + { + bindName = source.Substring(BindPathOpenString.Length, closeParen - BindPathOpenString.Length); + path = source.Substring(BindPathOpenString.Length + bindName.Length + 1); // +1 for the closing brace. + path = path.TrimStart('\\'); // remove starting '\\' char so the path doesn't look rooted. + } + } + else if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal)) + { + pathWithoutSourceDir = path.Substring(10); + } + + var bindPaths = this.BindPaths[bindStage]; + + foreach (var bindPath in bindPaths) + { + if (!String.IsNullOrEmpty(pathWithoutSourceDir)) + { + var filePath = Path.Combine(bindPath.Path, pathWithoutSourceDir); + + if (CheckFileExists(filePath)) + { + resolved = filePath; + } + } + + if (String.IsNullOrEmpty(resolved)) + { + var filePath = Path.Combine(bindPath.Path, path); + + if (CheckFileExists(filePath)) + { + resolved = filePath; + } + } + } + } + + if (null == resolved) + { + throw new WixFileNotFoundException(sourceLineNumbers, source, type); + } + + // Didn't find the file. + return resolved; + } + + private static bool CheckFileExists(string path) + { + try + { + return File.Exists(path); + } + catch (ArgumentException) + { + throw new WixException(WixErrors.IllegalCharactersInPath(path)); + } + } + + [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes); + } +} -- cgit v1.2.3-55-g6feb