From b6fd14cbbd3ee9fbac04fc7a72b5c65fd53c140d Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Tue, 3 Aug 2021 16:40:27 +0200 Subject: [PATCH 01/23] Introduce IDirectoryCache & IDirectoryCacheFactory --- .../Evaluation/Context/EvaluationContext.cs | 24 +++++++++- src/Build/FileSystem/IDirectoryCache.cs | 44 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/Build/FileSystem/IDirectoryCache.cs diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 827d9465d75..8acccc64d13 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -50,7 +50,7 @@ public enum SharingPolicy /// private ConcurrentDictionary> FileEntryExpansionCache { get; } - private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem) + private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, IDirectoryCacheFactory directoryCacheFactory) { // Unsupported case: isolated context with non null file system. // Isolated means caches aren't reused, but the given file system might cache. @@ -60,6 +60,8 @@ private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem) Policy = policy; + // TODO: Use directoryCacheFactory. + SdkResolverService = new CachingSdkResolverService(); FileEntryExpansionCache = new ConcurrentDictionary>(); FileSystem = fileSystem ?? new CachingFileSystemWrapper(FileSystems.Default); @@ -91,7 +93,25 @@ public static EvaluationContext Create(SharingPolicy policy, MSBuildFileSystemBa { var context = new EvaluationContext( policy, - fileSystem); + fileSystem, + directoryCacheFactory: null); + + TestOnlyHookOnCreate?.Invoke(context); + + return context; + } + + /// + /// Factory for + /// + /// The to use. + /// The to use. + public static EvaluationContext Create(SharingPolicy policy, IDirectoryCacheFactory directoryCacheFactory) + { + var context = new EvaluationContext( + policy, + fileSystem: null, + directoryCacheFactory); TestOnlyHookOnCreate?.Invoke(context); diff --git a/src/Build/FileSystem/IDirectoryCache.cs b/src/Build/FileSystem/IDirectoryCache.cs new file mode 100644 index 00000000000..e74d7c71a2b --- /dev/null +++ b/src/Build/FileSystem/IDirectoryCache.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +#if NETCOREAPP +using System.IO.Enumeration; +#else +using Microsoft.IO.Enumeration; +#endif + +namespace Microsoft.Build.FileSystem +{ + public interface IDirectoryCacheFactory + { + IDirectoryCache GetDirectoryCacheForProject(string projectPath); + } + + public interface IDirectoryCache + { + bool FileExists(string path); + + bool DirectoryExists(string path); + + /// + /// Enumerates files in the given directory only (non-recursively). + /// + /// The desired return type. + /// The directory to enumerate. + /// A predicate to test whether a file should be included. + /// A transform from to . + /// + IEnumerable EnumerateFiles(string path, FileSystemEnumerable.FindPredicate predicate, FileSystemEnumerable.FindTransform transform); + + /// + /// Enumerates subdirectories in the given directory only (non-recursively). + /// + /// The desired return type. + /// The directory to enumerate. + /// A predicate to test whether a directory should be included. + /// A transform from to . + /// + IEnumerable EnumerateDirectories(string path, FileSystemEnumerable.FindPredicate predicate, FileSystemEnumerable.FindTransform transform); + } +} From 7dae6293fd507e1f39c92e2d82d5d91da17f4d50 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Wed, 4 Aug 2021 21:44:35 +0200 Subject: [PATCH 02/23] PR feedback: Pass Project instead of project path --- src/Build/FileSystem/IDirectoryCache.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Build/FileSystem/IDirectoryCache.cs b/src/Build/FileSystem/IDirectoryCache.cs index e74d7c71a2b..a9612ec6f61 100644 --- a/src/Build/FileSystem/IDirectoryCache.cs +++ b/src/Build/FileSystem/IDirectoryCache.cs @@ -8,11 +8,13 @@ using Microsoft.IO.Enumeration; #endif +using Microsoft.Build.Evaluation; + namespace Microsoft.Build.FileSystem { public interface IDirectoryCacheFactory { - IDirectoryCache GetDirectoryCacheForProject(string projectPath); + IDirectoryCache GetDirectoryCacheForProject(Project project); } public interface IDirectoryCache From 9dcb52ac42286eedc8f18b3dafd4aa056d579192 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Thu, 5 Aug 2021 15:42:39 +0200 Subject: [PATCH 03/23] Don't depend on System.IO.Enumeration --- ref/Microsoft.Build/net/Microsoft.Build.cs | 14 +++++ .../netstandard/Microsoft.Build.cs | 14 +++++ .../Evaluation/Context/EvaluationContext.cs | 25 ++++++++- src/Build/FileSystem/IDirectoryCache.cs | 51 +++++++++++++++---- src/Shared/FileMatcher.cs | 2 +- 5 files changed, 92 insertions(+), 14 deletions(-) diff --git a/ref/Microsoft.Build/net/Microsoft.Build.cs b/ref/Microsoft.Build/net/Microsoft.Build.cs index fb5b4b8da5e..c06bce83735 100644 --- a/ref/Microsoft.Build/net/Microsoft.Build.cs +++ b/ref/Microsoft.Build/net/Microsoft.Build.cs @@ -881,6 +881,7 @@ public partial class EvaluationContext { internal EvaluationContext() { } public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy) { throw null; } + public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy, Microsoft.Build.FileSystem.IDirectoryCacheFactory directoryCacheFactory) { throw null; } public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy, Microsoft.Build.FileSystem.MSBuildFileSystemBase fileSystem) { throw null; } public enum SharingPolicy { @@ -1504,6 +1505,19 @@ public ProxyTargets(System.Collections.Generic.IReadOnlyDictionary fileName); + public delegate TResult FindTransform(ref System.ReadOnlySpan fileName); + public partial interface IDirectoryCache + { + bool DirectoryExists(string path); + System.Collections.Generic.IEnumerable EnumerateDirectories(string path, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); + System.Collections.Generic.IEnumerable EnumerateFiles(string path, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); + bool FileExists(string path); + } + public partial interface IDirectoryCacheFactory + { + Microsoft.Build.FileSystem.IDirectoryCache GetDirectoryCacheForProject(Microsoft.Build.Evaluation.Project project); + } public abstract partial class MSBuildFileSystemBase { protected MSBuildFileSystemBase() { } diff --git a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs index b387429467c..7eccfc39bf4 100644 --- a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs +++ b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs @@ -881,6 +881,7 @@ public partial class EvaluationContext { internal EvaluationContext() { } public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy) { throw null; } + public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy, Microsoft.Build.FileSystem.IDirectoryCacheFactory directoryCacheFactory) { throw null; } public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy, Microsoft.Build.FileSystem.MSBuildFileSystemBase fileSystem) { throw null; } public enum SharingPolicy { @@ -1498,6 +1499,19 @@ public ProxyTargets(System.Collections.Generic.IReadOnlyDictionary fileName); + public delegate TResult FindTransform(ref System.ReadOnlySpan fileName); + public partial interface IDirectoryCache + { + bool DirectoryExists(string path); + System.Collections.Generic.IEnumerable EnumerateDirectories(string path, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); + System.Collections.Generic.IEnumerable EnumerateFiles(string path, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); + bool FileExists(string path); + } + public partial interface IDirectoryCacheFactory + { + Microsoft.Build.FileSystem.IDirectoryCache GetDirectoryCacheForProject(Microsoft.Build.Evaluation.Project project); + } public abstract partial class MSBuildFileSystemBase { protected MSBuildFileSystemBase() { } diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 8acccc64d13..1d98f307eb4 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.FileSystem; @@ -45,6 +46,9 @@ public enum SharingPolicy internal IFileSystem FileSystem { get; } internal EngineFileUtilities EngineFileUtilities { get; } + private IDirectoryCacheFactory _directoryCacheFactory; + private ConditionalWeakTable _directoryCachesPerProject; + /// /// Key to file entry list. Example usages: cache glob expansion and intermediary directory expansions during glob expansion. /// @@ -60,12 +64,16 @@ private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, IDirecto Policy = policy; - // TODO: Use directoryCacheFactory. - SdkResolverService = new CachingSdkResolverService(); FileEntryExpansionCache = new ConcurrentDictionary>(); FileSystem = fileSystem ?? new CachingFileSystemWrapper(FileSystems.Default); EngineFileUtilities = new EngineFileUtilities(new FileMatcher(FileSystem, FileEntryExpansionCache)); + + if (directoryCacheFactory != null) + { + _directoryCacheFactory = directoryCacheFactory; + _directoryCachesPerProject = new ConditionalWeakTable(); + } } /// @@ -144,5 +152,18 @@ internal EvaluationContext ContextForNewProject() return null; } } + + internal IDirectoryCache GetDirectoryCacheForProject(Project project) + { + IDirectoryCache directoryCache = _directoryCachesPerProject?.GetValue( + project, + project => _directoryCacheFactory.GetDirectoryCacheForProject(project)); + if (directoryCache == null) + { + return directoryCache; + } + // TODO + return null; + } } } diff --git a/src/Build/FileSystem/IDirectoryCache.cs b/src/Build/FileSystem/IDirectoryCache.cs index a9612ec6f61..aa44e7be235 100644 --- a/src/Build/FileSystem/IDirectoryCache.cs +++ b/src/Build/FileSystem/IDirectoryCache.cs @@ -1,26 +1,57 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; using System.Collections.Generic; -#if NETCOREAPP -using System.IO.Enumeration; -#else -using Microsoft.IO.Enumeration; -#endif using Microsoft.Build.Evaluation; namespace Microsoft.Build.FileSystem { + /// + /// A provider of instances. To be implemented by MSBuild hosts that wish to intercept + /// file existence checks and file enumerations performed during project evaluation. + /// + /// + /// Unlike , file enumeration returns file/directory names, not full paths. + /// public interface IDirectoryCacheFactory { + /// + /// Returns an to be used when evaluating the given . + /// + /// The project being evaluated. IDirectoryCache GetDirectoryCacheForProject(Project project); } + /// + /// A predicate taking file name. + /// + /// The file name to check. + public delegate bool FindPredicate(ref ReadOnlySpan fileName); + + /// + /// A function taking file name and returning an arbitrary result. + /// + /// The type of the result to return + /// The file name to transform. + public delegate TResult FindTransform(ref ReadOnlySpan fileName); + + /// + /// Allows the implementor to intercept file existence checks and file enumerations performed during project evaluation. + /// public interface IDirectoryCache { + /// + /// Returns true if the given path points to an existing file on disk. + /// + /// A normalized path. bool FileExists(string path); + /// + /// Returns true if the given path points to an existing directory on disk. + /// + /// A normalized path. bool DirectoryExists(string path); /// @@ -29,9 +60,8 @@ public interface IDirectoryCache /// The desired return type. /// The directory to enumerate. /// A predicate to test whether a file should be included. - /// A transform from to . - /// - IEnumerable EnumerateFiles(string path, FileSystemEnumerable.FindPredicate predicate, FileSystemEnumerable.FindTransform transform); + /// A transform from ReadOnlySpan<char> to . + IEnumerable EnumerateFiles(string path, FindPredicate predicate, FindTransform transform); /// /// Enumerates subdirectories in the given directory only (non-recursively). @@ -39,8 +69,7 @@ public interface IDirectoryCache /// The desired return type. /// The directory to enumerate. /// A predicate to test whether a directory should be included. - /// A transform from to . - /// - IEnumerable EnumerateDirectories(string path, FileSystemEnumerable.FindPredicate predicate, FileSystemEnumerable.FindTransform transform); + /// A transform from ReadOnlySpan<char> to . + IEnumerable EnumerateDirectories(string path, FindPredicate predicate, FindTransform transform); } } diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index 9af0619bab7..e2f846cffb8 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -244,7 +244,7 @@ private static IReadOnlyList GetAccessibleFileSystemEntries(IFileSystem { case FileSystemEntity.Files: return GetAccessibleFiles(fileSystem, path, pattern, projectDirectory, stripProjectDirectory); case FileSystemEntity.Directories: return GetAccessibleDirectories(fileSystem, path, pattern); - case FileSystemEntity.FilesAndDirectories: return GetAccessibleFilesAndDirectories(fileSystem,path, pattern); + case FileSystemEntity.FilesAndDirectories: return GetAccessibleFilesAndDirectories(fileSystem, path, pattern); default: ErrorUtilities.VerifyThrow(false, "Unexpected filesystem entity type."); break; From 521d0425776d25e19802540b2e499f012eb170b2 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Thu, 5 Aug 2021 16:44:14 +0200 Subject: [PATCH 04/23] Make EngineFileUtilities static --- .../IntrinsicTasks/ItemGroupIntrinsicTask.cs | 9 ++---- .../Evaluation/Context/EvaluationContext.cs | 5 ++- src/Build/Evaluation/Evaluator.cs | 5 ++- src/Build/Evaluation/Expander.cs | 2 +- .../LazyItemEvaluator.IncludeOperation.cs | 3 +- .../LazyItemEvaluator.LazyItemOperation.cs | 2 +- src/Build/Evaluation/LazyItemEvaluator.cs | 5 ++- src/Build/Utilities/EngineFileUtilities.cs | 31 ++++++++----------- 8 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs index 109cb49b9bf..f96e90a2822 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs @@ -28,8 +28,6 @@ internal class ItemGroupIntrinsicTask : IntrinsicTask /// private ProjectItemGroupTaskInstance _taskInstance; - private EngineFileUtilities _engineFileUtilities; - /// /// Instantiates an ItemGroup task /// @@ -41,7 +39,6 @@ public ItemGroupIntrinsicTask(ProjectItemGroupTaskInstance taskInstance, TargetL : base(loggingContext, projectInstance, logTaskInputs) { _taskInstance = taskInstance; - _engineFileUtilities = EngineFileUtilities.Default; } /// @@ -431,7 +428,7 @@ ISet removeMetadata // The expression is not of the form "@(X)". Treat as string // Pass the non wildcard expanded excludes here to fix https://github.com/Microsoft/msbuild/issues/2621 - string[] includeSplitFiles = _engineFileUtilities.GetFileListEscaped( + string[] includeSplitFiles = EngineFileUtilities.GetFileListEscaped( Project.Directory, includeSplit, excludes); @@ -455,7 +452,7 @@ ISet removeMetadata foreach (string excludeSplit in excludes) { - string[] excludeSplitFiles = _engineFileUtilities.GetFileListUnescaped(Project.Directory, excludeSplit); + string[] excludeSplitFiles = EngineFileUtilities.GetFileListUnescaped(Project.Directory, excludeSplit); foreach (string excludeSplitFile in excludeSplitFiles) { @@ -540,7 +537,7 @@ Expander expander // Don't unescape wildcards just yet - if there were any escaped, the caller wants to treat them // as literals. Everything else is safe to unescape at this point, since we're only matching // against the file system. - string[] fileList = _engineFileUtilities.GetFileListEscaped(Project.Directory, piece); + string[] fileList = EngineFileUtilities.GetFileListEscaped(Project.Directory, piece); foreach (string file in fileList) { diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 1d98f307eb4..94a37c525f4 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -8,7 +8,6 @@ using System.Threading; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.FileSystem; -using Microsoft.Build.Internal; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; @@ -44,7 +43,7 @@ public enum SharingPolicy internal ISdkResolverService SdkResolverService { get; } internal IFileSystem FileSystem { get; } - internal EngineFileUtilities EngineFileUtilities { get; } + internal FileMatcher FileMatcher { get; } private IDirectoryCacheFactory _directoryCacheFactory; private ConditionalWeakTable _directoryCachesPerProject; @@ -67,7 +66,7 @@ private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, IDirecto SdkResolverService = new CachingSdkResolverService(); FileEntryExpansionCache = new ConcurrentDictionary>(); FileSystem = fileSystem ?? new CachingFileSystemWrapper(FileSystems.Default); - EngineFileUtilities = new EngineFileUtilities(new FileMatcher(FileSystem, FileEntryExpansionCache)); + FileMatcher = new FileMatcher(FileSystem, FileEntryExpansionCache); if (directoryCacheFactory != null) { diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index 712307191f4..69f29d484fe 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -3,7 +3,6 @@ using System; using System.Collections; -using System.Collections.Concurrent; using System.Collections.Generic; using ObjectModel = System.Collections.ObjectModel; using System.Diagnostics; @@ -357,7 +356,7 @@ internal static List CreateItemsFromInclude(string rootDirectory, ProjectItem else { // The expression is not of the form "@(X)". Treat as string - string[] includeSplitFilesEscaped = EngineFileUtilities.Default.GetFileListEscaped(rootDirectory, includeSplitEscaped); + string[] includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(rootDirectory, includeSplitEscaped); if (includeSplitFilesEscaped.Length > 0) { @@ -2009,7 +2008,7 @@ private LoadImportsResult ExpandAndLoadImportsFromUnescapedImportExpression(stri } // Expand the wildcards and provide an alphabetical order list of import statements. - importFilesEscaped = _evaluationContext.EngineFileUtilities.GetFileListEscaped(directoryOfImportingFile, importExpressionEscapedItem, forceEvaluate: true); + importFilesEscaped = EngineFileUtilities.GetFileListEscaped(directoryOfImportingFile, importExpressionEscapedItem, forceEvaluate: true, fileMatcher: _evaluationContext.FileMatcher); } catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) { diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index fe397e4469a..79d3397e928 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -2216,7 +2216,7 @@ internal static IEnumerable> GetItemPairEnumerable(IEnumerable SelectItems(OrderedItemDataCollection.Builde includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped( _rootDirectory, glob, - excludePatternsForGlobs + excludePatternsForGlobs, + fileMatcher: FileMatcher ); } if (MSBuildEventSource.Log.IsEnabled()) diff --git a/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs b/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs index 288d11ce9b9..bdafd6087c7 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs @@ -49,7 +49,7 @@ protected LazyItemOperation(OperationBuilder builder, LazyItemEvaluator _lazyEvaluator.EngineFileUtilities; + protected FileMatcher FileMatcher => _lazyEvaluator.FileMatcher; public void Apply(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet globsToIgnore) { diff --git a/src/Build/Evaluation/LazyItemEvaluator.cs b/src/Build/Evaluation/LazyItemEvaluator.cs index 9fd3eec87e7..ad52c0b7837 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.cs @@ -6,7 +6,6 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation.Context; using Microsoft.Build.Eventing; -using Microsoft.Build.Internal; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Utilities; @@ -42,7 +41,7 @@ internal partial class LazyItemEvaluator protected IFileSystem FileSystem { get; } - protected EngineFileUtilities EngineFileUtilities { get; } + protected FileMatcher FileMatcher { get; } public LazyItemEvaluator(IEvaluatorData data, IItemFactory itemFactory, LoggingContext loggingContext, EvaluationProfiler evaluationProfiler, EvaluationContext evaluationContext) { @@ -55,7 +54,7 @@ public LazyItemEvaluator(IEvaluatorData data, IItemFactory ite _evaluationProfiler = evaluationProfiler; FileSystem = evaluationContext.FileSystem; - EngineFileUtilities = evaluationContext.EngineFileUtilities; + FileMatcher = evaluationContext.FileMatcher; } private ImmutableList GetItems(string itemType) diff --git a/src/Build/Utilities/EngineFileUtilities.cs b/src/Build/Utilities/EngineFileUtilities.cs index da8165d3369..242085521c9 100644 --- a/src/Build/Utilities/EngineFileUtilities.cs +++ b/src/Build/Utilities/EngineFileUtilities.cs @@ -12,10 +12,8 @@ namespace Microsoft.Build.Internal { - internal class EngineFileUtilities + internal static class EngineFileUtilities { - private readonly FileMatcher _fileMatcher; - // Regexes for wildcard filespecs that should not get expanded // By default all wildcards are expanded. private static List s_lazyWildCardExpansionRegexes; @@ -34,13 +32,6 @@ internal static void CaptureLazyWildcardRegexes() s_lazyWildCardExpansionRegexes = PopulateRegexFromEnvironment(); } - public static EngineFileUtilities Default = new EngineFileUtilities(FileMatcher.Default); - - public EngineFileUtilities(FileMatcher fileMatcher) - { - _fileMatcher = fileMatcher; - } - /// /// Used for the purposes of evaluating an item specification. Given a filespec that may include wildcard characters * and /// ?, we translate it into an actual list of files. If the input filespec doesn't contain any wildcard characters, and it @@ -54,14 +45,14 @@ public EngineFileUtilities(FileMatcher fileMatcher) /// The directory to evaluate, escaped. /// The filespec to evaluate, escaped. /// Array of file paths, unescaped. - internal string[] GetFileListUnescaped + internal static string[] GetFileListUnescaped ( string directoryEscaped, string filespecEscaped ) { - return GetFileList(directoryEscaped, filespecEscaped, returnEscaped: false, forceEvaluateWildCards: false); + return GetFileList(directoryEscaped, filespecEscaped, returnEscaped: false, forceEvaluateWildCards: false, excludeSpecsEscaped: null, fileMatcher: FileMatcher.Default); } /// @@ -78,16 +69,18 @@ string filespecEscaped /// The filespec to evaluate, escaped. /// Filespecs to exclude, escaped. /// Whether to force file glob expansion when eager expansion is turned off + /// /// Array of file paths, escaped. - internal string[] GetFileListEscaped + internal static string[] GetFileListEscaped ( string directoryEscaped, string filespecEscaped, IEnumerable excludeSpecsEscaped = null, - bool forceEvaluate = false + bool forceEvaluate = false, + FileMatcher fileMatcher = null ) { - return GetFileList(directoryEscaped, filespecEscaped, returnEscaped: true, forceEvaluate, excludeSpecsEscaped); + return GetFileList(directoryEscaped, filespecEscaped, returnEscaped: true, forceEvaluate, excludeSpecsEscaped, fileMatcher ?? FileMatcher.Default); } internal static bool FilespecHasWildcards(string filespecEscaped) @@ -119,14 +112,16 @@ internal static bool FilespecHasWildcards(string filespecEscaped) /// true to return escaped specs. /// Whether to force file glob expansion when eager expansion is turned off /// The exclude specification, escaped. + /// /// Array of file paths. - private string[] GetFileList + private static string[] GetFileList ( string directoryEscaped, string filespecEscaped, bool returnEscaped, bool forceEvaluateWildCards, - IEnumerable excludeSpecsEscaped = null + IEnumerable excludeSpecsEscaped, + FileMatcher fileMatcher ) { ErrorUtilities.VerifyThrowInternalLength(filespecEscaped, nameof(filespecEscaped)); @@ -156,7 +151,7 @@ private string[] GetFileList // as a relative path, we will get back a bunch of relative paths. // If the filespec started out as an absolute path, we will get // back a bunch of absolute paths. - fileList = _fileMatcher.GetFiles(directoryUnescaped, filespecUnescaped, excludeSpecsUnescaped); + fileList = fileMatcher.GetFiles(directoryUnescaped, filespecUnescaped, excludeSpecsUnescaped); ErrorUtilities.VerifyThrow(fileList != null, "We must have a list of files here, even if it's empty."); From e235b15577855ae6cf649056e54441fbd209976a Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Mon, 9 Aug 2021 16:08:00 +0200 Subject: [PATCH 05/23] Implement DirectoryCacheOverFileSystem --- .../Evaluation/Context/EvaluationContext.cs | 16 +++-- .../DirectoryCacheOverFileSystem.cs | 58 +++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 src/Build/FileSystem/DirectoryCacheOverFileSystem.cs diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 94a37c525f4..7dcb253fd40 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -48,10 +48,12 @@ public enum SharingPolicy private IDirectoryCacheFactory _directoryCacheFactory; private ConditionalWeakTable _directoryCachesPerProject; + private IDirectoryCache _defaultDirectoryCache; + /// /// Key to file entry list. Example usages: cache glob expansion and intermediary directory expansions during glob expansion. /// - private ConcurrentDictionary> FileEntryExpansionCache { get; } + internal ConcurrentDictionary> FileEntryExpansionCache { get; } private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, IDirectoryCacheFactory directoryCacheFactory) { @@ -157,12 +159,18 @@ internal IDirectoryCache GetDirectoryCacheForProject(Project project) IDirectoryCache directoryCache = _directoryCachesPerProject?.GetValue( project, project => _directoryCacheFactory.GetDirectoryCacheForProject(project)); + + // If we don't have a non-null directory cache factory or it returned null, lazily create a wrapper over IFileSystem. if (directoryCache == null) { - return directoryCache; + directoryCache = Volatile.Read(ref _defaultDirectoryCache); + if (directoryCache == null) + { + directoryCache = new DirectoryCacheOverFileSystem(FileSystem); + Volatile.Write(ref _defaultDirectoryCache, directoryCache); + } } - // TODO - return null; + return directoryCache; } } } diff --git a/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs b/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs new file mode 100644 index 00000000000..c6556409572 --- /dev/null +++ b/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; +using System; +using System.Collections.Generic; + +namespace Microsoft.Build.FileSystem +{ + /// + /// Implements on top of . + /// + internal sealed class DirectoryCacheOverFileSystem : IDirectoryCache + { + private IFileSystem _fileSystem; + + public DirectoryCacheOverFileSystem(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + public bool FileExists(string path) + { + return _fileSystem.FileExists(path); + } + + public bool DirectoryExists(string path) + { + return _fileSystem.DirectoryExists(path); + } + + public IEnumerable EnumerateDirectories(string path, FindPredicate predicate, FindTransform transform) + { + return EnumerateAndTransformFullPaths(_fileSystem.EnumerateDirectories(path), predicate, transform); + } + + public IEnumerable EnumerateFiles(string path, FindPredicate predicate, FindTransform transform) + { + return EnumerateAndTransformFullPaths(_fileSystem.EnumerateFiles(path), predicate, transform); + } + + private IEnumerable EnumerateAndTransformFullPaths(IEnumerable fullPaths, FindPredicate predicate, FindTransform transform) + { + foreach (string fullPath in fullPaths) + { + // TODO: Call Path.GetFileName() from Microsoft.IO. + int lastSlashPos = fullPath.LastIndexOfAny(FileUtilities.Slashes); + ReadOnlySpan fileName = fullPath.AsSpan(lastSlashPos + 1, fullPath.Length - lastSlashPos - 1); + + if (predicate(ref fileName)) + { + yield return transform(ref fileName); + } + } + } + } +} From 62737b672cd70301bd2bb5072e6d89846a092744 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Mon, 9 Aug 2021 16:08:12 +0200 Subject: [PATCH 06/23] Basic wiring of EvaluationContext-specific FileMatcher --- src/Build/Definition/Project.cs | 1 + src/Build/Evaluation/Evaluator.cs | 19 ++++++++++++++++--- src/Build/Evaluation/LazyItemEvaluator.cs | 7 ++++--- src/Build/Instance/ProjectInstance.cs | 1 + src/Shared/FileMatcher.cs | 10 +++++----- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/Build/Definition/Project.cs b/src/Build/Definition/Project.cs index ff8abfdce9b..a0c03f26877 100644 --- a/src/Build/Definition/Project.cs +++ b/src/Build/Definition/Project.cs @@ -3623,6 +3623,7 @@ private void Reevaluate( Evaluator.Evaluate( _data, + Owner, Xml, loadSettings, ProjectCollection.MaxNodeCount, diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index 69f29d484fe..1355f91e9f1 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -32,6 +32,7 @@ using EngineFileUtilities = Microsoft.Build.Internal.EngineFileUtilities; using ReservedPropertyNames = Microsoft.Build.Internal.ReservedPropertyNames; using SdkReferencePropertyExpansionMode = Microsoft.Build.Utilities.EscapeHatches.SdkReferencePropertyExpansionMode; +using Microsoft.Build.FileSystem; namespace Microsoft.Build.Evaluation { @@ -179,6 +180,11 @@ internal class Evaluator /// private List _streamImports; + /// + /// The to use for expanding globs. + /// + private FileMatcher _fileMatcher; + private readonly bool _interactive; private readonly bool _isRunningInVisualStudio; @@ -188,6 +194,7 @@ internal class Evaluator /// private Evaluator( IEvaluatorData data, + Project project, ProjectRootElement projectRootElement, ProjectLoadSettings loadSettings, int maxNodeCount, @@ -259,6 +266,10 @@ private Evaluator( _streamImports = new List(); // When the imports are concatenated with a semicolon, this automatically prepends a semicolon if and only if another element is later added. _streamImports.Add(string.Empty); + + // Create a FileMatcher for the given combination of EvaluationContext and the project being evaluated. + IDirectoryCache directoryCache = _evaluationContext.GetDirectoryCacheForProject(project); + _fileMatcher = new FileMatcher(evaluationContext.FileSystem, evaluationContext.FileEntryExpansionCache); } /// @@ -283,6 +294,7 @@ private Evaluator( /// internal static void Evaluate( IEvaluatorData data, + Project project, ProjectRootElement root, ProjectLoadSettings loadSettings, int maxNodeCount, @@ -301,6 +313,7 @@ internal static void Evaluate( var profileEvaluation = (loadSettings & ProjectLoadSettings.ProfileEvaluation) != 0 || loggingService.IncludeEvaluationProfile; var evaluator = new Evaluator( data, + project, root, loadSettings, maxNodeCount, @@ -356,7 +369,7 @@ internal static List CreateItemsFromInclude(string rootDirectory, ProjectItem else { // The expression is not of the form "@(X)". Treat as string - string[] includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(rootDirectory, includeSplitEscaped); + string[] includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(rootDirectory, includeSplitEscaped, excludeSpecsEscaped: null, forceEvaluate: false, null /* TODO */); if (includeSplitFilesEscaped.Length > 0) { @@ -645,7 +658,7 @@ private void Evaluate() using (_evaluationProfiler.TrackPass(EvaluationPass.Items)) { // comment next line to turn off lazy Evaluation - lazyEvaluator = new LazyItemEvaluator(_data, _itemFactory, _evaluationLoggingContext, _evaluationProfiler, _evaluationContext); + lazyEvaluator = new LazyItemEvaluator(_data, _itemFactory, _evaluationLoggingContext, _evaluationProfiler, _evaluationContext, _fileMatcher); // Pass3: evaluate project items MSBuildEventSource.Log.EvaluatePass3Start(projectFile); @@ -2008,7 +2021,7 @@ private LoadImportsResult ExpandAndLoadImportsFromUnescapedImportExpression(stri } // Expand the wildcards and provide an alphabetical order list of import statements. - importFilesEscaped = EngineFileUtilities.GetFileListEscaped(directoryOfImportingFile, importExpressionEscapedItem, forceEvaluate: true, fileMatcher: _evaluationContext.FileMatcher); + importFilesEscaped = EngineFileUtilities.GetFileListEscaped(directoryOfImportingFile, importExpressionEscapedItem, forceEvaluate: true, fileMatcher: _fileMatcher); } catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) { diff --git a/src/Build/Evaluation/LazyItemEvaluator.cs b/src/Build/Evaluation/LazyItemEvaluator.cs index ad52c0b7837..d85ad8c8982 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.cs @@ -29,7 +29,7 @@ internal partial class LazyItemEvaluator private readonly Expander _outerExpander; private readonly IEvaluatorData _evaluatorData; private readonly Expander _expander; - private readonly IItemFactory _itemFactory; + protected readonly IItemFactory _itemFactory; private readonly LoggingContext _loggingContext; private readonly EvaluationProfiler _evaluationProfiler; @@ -43,7 +43,8 @@ internal partial class LazyItemEvaluator protected FileMatcher FileMatcher { get; } - public LazyItemEvaluator(IEvaluatorData data, IItemFactory itemFactory, LoggingContext loggingContext, EvaluationProfiler evaluationProfiler, EvaluationContext evaluationContext) + public LazyItemEvaluator(IEvaluatorData data, IItemFactory itemFactory, LoggingContext loggingContext, EvaluationProfiler evaluationProfiler, EvaluationContext evaluationContext, + FileMatcher fileMatcher) { _outerEvaluatorData = data; _outerExpander = new Expander(_outerEvaluatorData, _outerEvaluatorData, evaluationContext.FileSystem); @@ -54,7 +55,7 @@ public LazyItemEvaluator(IEvaluatorData data, IItemFactory ite _evaluationProfiler = evaluationProfiler; FileSystem = evaluationContext.FileSystem; - FileMatcher = evaluationContext.FileMatcher; + FileMatcher = fileMatcher; } private ImmutableList GetItems(string itemType) diff --git a/src/Build/Instance/ProjectInstance.cs b/src/Build/Instance/ProjectInstance.cs index b67aba3de21..3a8eba5b4a5 100644 --- a/src/Build/Instance/ProjectInstance.cs +++ b/src/Build/Instance/ProjectInstance.cs @@ -2761,6 +2761,7 @@ out var usingDifferentToolsVersionFromProjectFile Evaluator.Evaluate( this, + null, // TODO? xml, projectLoadSettings ?? buildParameters.ProjectLoadSettings, /* Use override ProjectLoadSettings if specified */ buildParameters.MaxNodeCount, diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index e2f846cffb8..a73958d2c83 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -94,7 +94,7 @@ public FileMatcher(IFileSystem fileSystem, ConcurrentDictionary> getFileSystemDirectoryEntriesCache = null) + internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemEntries, ConcurrentDictionary> fileEntryExpansionCache = null) { if (Traits.Instance.MSBuildCacheFileEnumerations) { @@ -103,12 +103,12 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE } else { - _cachedGlobExpansions = getFileSystemDirectoryEntriesCache; + _cachedGlobExpansions = fileEntryExpansionCache; } _fileSystem = fileSystem; - _getFileSystemEntries = getFileSystemDirectoryEntriesCache == null + _getFileSystemEntries = fileEntryExpansionCache == null ? getFileSystemEntries : (type, path, pattern, directory, stripProjectDirectory) => { @@ -123,7 +123,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE FileSystemEntity.FilesAndDirectories => "A", _ => throw new NotImplementedException() } + ";" + path; - IReadOnlyList allEntriesForPath = getFileSystemDirectoryEntriesCache.GetOrAdd( + IReadOnlyList allEntriesForPath = fileEntryExpansionCache.GetOrAdd( cacheKey, s => getFileSystemEntries( type, @@ -144,7 +144,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE // Cache only directories, for files we won't hit the cache because the file name patterns tend to be unique if (type == FileSystemEntity.Directories) { - return getFileSystemDirectoryEntriesCache.GetOrAdd( + return fileEntryExpansionCache.GetOrAdd( $"D;{path};{pattern ?? "*"}", s => getFileSystemEntries( type, From 10fff0f4ae992d6c368319379542c4f0c7008728 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Tue, 10 Aug 2021 11:38:37 +0200 Subject: [PATCH 07/23] Make FileMatcher consume IDirectoryCache instead of IFileSystem --- .../Evaluation/Context/EvaluationContext.cs | 2 - src/Build/Evaluation/Evaluator.cs | 2 +- .../FileSystem/IDirectoryCacheFactory.cs | 23 ++++++ src/Shared/FileMatcher.cs | 75 ++++++++++++------- .../DirectoryCacheOverFileSystem.cs | 7 +- src/Shared/FileSystem/FileSystems.cs | 8 ++ .../FileSystem/IDirectoryCache.cs | 37 ++++----- src/Shared/UnitTests/FileMatcher_Tests.cs | 6 +- 8 files changed, 104 insertions(+), 56 deletions(-) create mode 100644 src/Build/FileSystem/IDirectoryCacheFactory.cs rename src/{Build => Shared}/FileSystem/DirectoryCacheOverFileSystem.cs (94%) rename src/{Build => Shared}/FileSystem/IDirectoryCache.cs (72%) diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 7dcb253fd40..c2b5b5d4ddc 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -43,7 +43,6 @@ public enum SharingPolicy internal ISdkResolverService SdkResolverService { get; } internal IFileSystem FileSystem { get; } - internal FileMatcher FileMatcher { get; } private IDirectoryCacheFactory _directoryCacheFactory; private ConditionalWeakTable _directoryCachesPerProject; @@ -68,7 +67,6 @@ private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, IDirecto SdkResolverService = new CachingSdkResolverService(); FileEntryExpansionCache = new ConcurrentDictionary>(); FileSystem = fileSystem ?? new CachingFileSystemWrapper(FileSystems.Default); - FileMatcher = new FileMatcher(FileSystem, FileEntryExpansionCache); if (directoryCacheFactory != null) { diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index 1355f91e9f1..af54e25b678 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -269,7 +269,7 @@ private Evaluator( // Create a FileMatcher for the given combination of EvaluationContext and the project being evaluated. IDirectoryCache directoryCache = _evaluationContext.GetDirectoryCacheForProject(project); - _fileMatcher = new FileMatcher(evaluationContext.FileSystem, evaluationContext.FileEntryExpansionCache); + _fileMatcher = new FileMatcher(directoryCache, evaluationContext.FileEntryExpansionCache); } /// diff --git a/src/Build/FileSystem/IDirectoryCacheFactory.cs b/src/Build/FileSystem/IDirectoryCacheFactory.cs new file mode 100644 index 00000000000..27acee7b415 --- /dev/null +++ b/src/Build/FileSystem/IDirectoryCacheFactory.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Evaluation; + +namespace Microsoft.Build.FileSystem +{ + /// + /// A provider of instances. To be implemented by MSBuild hosts that wish to intercept + /// file existence checks and file enumerations performed during project evaluation. + /// + /// + /// Unlike , file enumeration returns file/directory names, not full paths. + /// + public interface IDirectoryCacheFactory + { + /// + /// Returns an to be used when evaluating the given . + /// + /// The project being evaluated. + IDirectoryCache GetDirectoryCacheForProject(Project project); + } +} diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index a73958d2c83..4f1b8fc15b2 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -10,6 +10,9 @@ using System.Text.RegularExpressions; using System.Collections.Generic; using System.Threading.Tasks; +#if BUILD_ENGINE +using Microsoft.Build.FileSystem; +#endif using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Utilities; @@ -20,7 +23,7 @@ namespace Microsoft.Build.Shared /// internal class FileMatcher { - private readonly IFileSystem _fileSystem; + private readonly IDirectoryCache _directoryCache; private const string recursiveDirectoryMatch = "**"; private static readonly string s_directorySeparator = new string(Path.DirectorySeparatorChar, 1); @@ -79,12 +82,12 @@ private static class FileSpecRegexParts /// /// The Default FileMatcher does not cache directory enumeration. /// - public static FileMatcher Default = new FileMatcher(FileSystems.Default, null); + public static FileMatcher Default = new FileMatcher(FileSystems.DefaultDirectoryCache, null); - public FileMatcher(IFileSystem fileSystem, ConcurrentDictionary> fileEntryExpansionCache = null) : this( - fileSystem, + public FileMatcher(IDirectoryCache directoryCache, ConcurrentDictionary> fileEntryExpansionCache = null) : this( + directoryCache, (entityType, path, pattern, projectDirectory, stripProjectDirectory) => GetAccessibleFileSystemEntries( - fileSystem, + directoryCache, entityType, path, pattern, @@ -94,7 +97,7 @@ public FileMatcher(IFileSystem fileSystem, ConcurrentDictionary> fileEntryExpansionCache = null) + internal FileMatcher(IDirectoryCache directoryCache, GetFileSystemEntries getFileSystemEntries, ConcurrentDictionary> fileEntryExpansionCache = null) { if (Traits.Instance.MSBuildCacheFileEnumerations) { @@ -106,7 +109,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE _cachedGlobExpansions = fileEntryExpansionCache; } - _fileSystem = fileSystem; + _directoryCache = directoryCache; _getFileSystemEntries = fileEntryExpansionCache == null ? getFileSystemEntries @@ -235,16 +238,16 @@ internal static bool HasPropertyOrItemReferences(string filespec) /// The pattern to search. /// The directory for the project within which the call is made /// If true the project directory should be stripped - /// The file system abstraction to use that implements file system operations + /// The file system abstraction to use that implements file system operations /// - private static IReadOnlyList GetAccessibleFileSystemEntries(IFileSystem fileSystem, FileSystemEntity entityType, string path, string pattern, string projectDirectory, bool stripProjectDirectory) + private static IReadOnlyList GetAccessibleFileSystemEntries(IDirectoryCache directoryCache, FileSystemEntity entityType, string path, string pattern, string projectDirectory, bool stripProjectDirectory) { path = FileUtilities.FixFilePath(path); switch (entityType) { - case FileSystemEntity.Files: return GetAccessibleFiles(fileSystem, path, pattern, projectDirectory, stripProjectDirectory); - case FileSystemEntity.Directories: return GetAccessibleDirectories(fileSystem, path, pattern); - case FileSystemEntity.FilesAndDirectories: return GetAccessibleFilesAndDirectories(fileSystem, path, pattern); + case FileSystemEntity.Files: return GetAccessibleFiles(directoryCache, path, pattern, projectDirectory, stripProjectDirectory); + case FileSystemEntity.Directories: return GetAccessibleDirectories(directoryCache, path, pattern); + case FileSystemEntity.FilesAndDirectories: return GetAccessibleFilesAndDirectories(directoryCache, path, pattern); default: ErrorUtilities.VerifyThrow(false, "Unexpected filesystem entity type."); break; @@ -258,18 +261,18 @@ private static IReadOnlyList GetAccessibleFileSystemEntries(IFileSystem /// /// /// - /// The file system abstraction to use that implements file system operations + /// The file system abstraction to use that implements file system operations /// An enumerable of matching file system entries (can be empty). - private static IReadOnlyList GetAccessibleFilesAndDirectories(IFileSystem fileSystem, string path, string pattern) + private static IReadOnlyList GetAccessibleFilesAndDirectories(IDirectoryCache directoryCache, string path, string pattern) { - if (fileSystem.DirectoryExists(path)) + if (directoryCache.DirectoryExists(path)) { try { return (ShouldEnforceMatching(pattern) - ? fileSystem.EnumerateFileSystemEntries(path, pattern) + ? EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.FilesAndDirectories, path, pattern) .Where(o => IsMatch(Path.GetFileName(o), pattern)) - : fileSystem.EnumerateFileSystemEntries(path, pattern) + : EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.FilesAndDirectories, path, pattern) ).ToArray(); } // for OS security @@ -324,11 +327,11 @@ private static bool ShouldEnforceMatching(string searchPattern) /// The pattern. /// The project directory /// - /// The file system abstraction to use that implements file system operations + /// The file system abstraction to use that implements file system operations /// Files that can be accessed. private static IReadOnlyList GetAccessibleFiles ( - IFileSystem fileSystem, + IDirectoryCache directoryCache, string path, string filespec, // can be null string projectDirectory, @@ -344,11 +347,11 @@ bool stripProjectDirectory IEnumerable files; if (filespec == null) { - files = fileSystem.EnumerateFiles(dir); + files = EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.Files, dir, "*"); } else { - files = fileSystem.EnumerateFiles(dir, filespec); + files = EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.Files, dir, filespec); if (ShouldEnforceMatching(filespec)) { files = files.Where(o => IsMatch(Path.GetFileName(o), filespec)); @@ -392,11 +395,11 @@ bool stripProjectDirectory /// /// The path. /// Pattern to match - /// The file system abstraction to use that implements file system operations + /// The file system abstraction to use that implements file system operations /// Accessible directories. private static IReadOnlyList GetAccessibleDirectories ( - IFileSystem fileSystem, + IDirectoryCache directoryCache, string path, string pattern ) @@ -407,11 +410,11 @@ string pattern if (pattern == null) { - directories = fileSystem.EnumerateDirectories((path.Length == 0) ? s_thisDirectory : path); + directories = EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.Directories, (path.Length == 0) ? s_thisDirectory : path, "*"); } else { - directories = fileSystem.EnumerateDirectories((path.Length == 0) ? s_thisDirectory : path, pattern); + directories = EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.Directories, (path.Length == 0) ? s_thisDirectory : path, pattern); if (ShouldEnforceMatching(pattern)) { directories = directories.Where(o => IsMatch(Path.GetFileName(o), pattern)); @@ -442,6 +445,26 @@ string pattern } } + // TODO: Temporary until #6075 is implemented. + private static IEnumerable EnumerateFullFileSystemPaths(IDirectoryCache directoryCache, FileSystemEntity entityType, string path, string pattern) + { + FindPredicate predicate = (ref ReadOnlySpan fileName) => + { + string fileNameString = fileName.ToString(); + return IsAllFilesWildcard(pattern) || IsMatch(fileNameString, pattern); + }; + FindTransform transform = (ref ReadOnlySpan fileName) => Path.Combine(path, fileName.ToString()); + + IEnumerable directories = (entityType == FileSystemEntity.Directories || entityType == FileSystemEntity.FilesAndDirectories) + ? directoryCache.EnumerateDirectories(path, predicate, transform) + : Enumerable.Empty(); + IEnumerable files = (entityType == FileSystemEntity.Files || entityType == FileSystemEntity.FilesAndDirectories) + ? directoryCache.EnumerateFiles(path, predicate, transform) + : Enumerable.Empty(); + + return Enumerable.Concat(directories, files); + } + /// /// Given a path name, get its long version. /// @@ -2098,7 +2121,7 @@ out bool isLegalFileSpec * If the fixed directory part doesn't exist, then this means no files should be * returned. */ - if (fixedDirectoryPart.Length > 0 && !_fileSystem.DirectoryExists(fixedDirectoryPart)) + if (fixedDirectoryPart.Length > 0 && !_directoryCache.DirectoryExists(fixedDirectoryPart)) { return SearchAction.ReturnEmptyList; } diff --git a/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs b/src/Shared/FileSystem/DirectoryCacheOverFileSystem.cs similarity index 94% rename from src/Build/FileSystem/DirectoryCacheOverFileSystem.cs rename to src/Shared/FileSystem/DirectoryCacheOverFileSystem.cs index c6556409572..de4398c9018 100644 --- a/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs +++ b/src/Shared/FileSystem/DirectoryCacheOverFileSystem.cs @@ -1,12 +1,13 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Build.Shared; -using Microsoft.Build.Shared.FileSystem; +#if BUILD_ENGINE +using Microsoft.Build.FileSystem; +#endif using System; using System.Collections.Generic; -namespace Microsoft.Build.FileSystem +namespace Microsoft.Build.Shared.FileSystem { /// /// Implements on top of . diff --git a/src/Shared/FileSystem/FileSystems.cs b/src/Shared/FileSystem/FileSystems.cs index 15b6b43bb2b..d0bbe8f87fe 100644 --- a/src/Shared/FileSystem/FileSystems.cs +++ b/src/Shared/FileSystem/FileSystems.cs @@ -1,6 +1,10 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#if BUILD_ENGINE +using Microsoft.Build.FileSystem; +#endif + namespace Microsoft.Build.Shared.FileSystem { /// @@ -10,6 +14,10 @@ internal static class FileSystems { public static IFileSystem Default = GetFileSystem(); +#if !CLR2COMPATIBILITY + public static IDirectoryCache DefaultDirectoryCache = new DirectoryCacheOverFileSystem(Default); +#endif + private static IFileSystem GetFileSystem() { #if CLR2COMPATIBILITY diff --git a/src/Build/FileSystem/IDirectoryCache.cs b/src/Shared/FileSystem/IDirectoryCache.cs similarity index 72% rename from src/Build/FileSystem/IDirectoryCache.cs rename to src/Shared/FileSystem/IDirectoryCache.cs index aa44e7be235..8fe4e48cd27 100644 --- a/src/Build/FileSystem/IDirectoryCache.cs +++ b/src/Shared/FileSystem/IDirectoryCache.cs @@ -4,43 +4,38 @@ using System; using System.Collections.Generic; -using Microsoft.Build.Evaluation; - +#if BUILD_ENGINE namespace Microsoft.Build.FileSystem +#else +namespace Microsoft.Build.Shared.FileSystem +#endif { - /// - /// A provider of instances. To be implemented by MSBuild hosts that wish to intercept - /// file existence checks and file enumerations performed during project evaluation. - /// - /// - /// Unlike , file enumeration returns file/directory names, not full paths. - /// - public interface IDirectoryCacheFactory - { - /// - /// Returns an to be used when evaluating the given . - /// - /// The project being evaluated. - IDirectoryCache GetDirectoryCacheForProject(Project project); - } - /// /// A predicate taking file name. /// /// The file name to check. - public delegate bool FindPredicate(ref ReadOnlySpan fileName); +#if BUILD_ENGINE + public +#endif + delegate bool FindPredicate(ref ReadOnlySpan fileName); /// /// A function taking file name and returning an arbitrary result. /// /// The type of the result to return /// The file name to transform. - public delegate TResult FindTransform(ref ReadOnlySpan fileName); +#if BUILD_ENGINE + public +#endif + delegate TResult FindTransform(ref ReadOnlySpan fileName); /// /// Allows the implementor to intercept file existence checks and file enumerations performed during project evaluation. /// - public interface IDirectoryCache +#if BUILD_ENGINE + public +#endif + interface IDirectoryCache { /// /// Returns true if the given path points to an existing file on disk. diff --git a/src/Shared/UnitTests/FileMatcher_Tests.cs b/src/Shared/UnitTests/FileMatcher_Tests.cs index 471e1536b63..bce902916ac 100644 --- a/src/Shared/UnitTests/FileMatcher_Tests.cs +++ b/src/Shared/UnitTests/FileMatcher_Tests.cs @@ -101,7 +101,7 @@ void VerifyImpl(FileMatcher fileMatcher, string include, string[] excludes, bool } } - var fileMatcherWithCache = new FileMatcher(FileSystems.Default, new ConcurrentDictionary>()); + var fileMatcherWithCache = new FileMatcher(FileSystems.DefaultDirectoryCache, new ConcurrentDictionary>()); void Verify(string include, string[] excludes, bool shouldHaveNoMatches = false, string customMessage = null) { @@ -2376,7 +2376,7 @@ private static void MatchDriver(string filespec, string[] excludeFilespecs, stri { MockFileSystem mockFileSystem = new MockFileSystem(matchingFiles, nonmatchingFiles, untouchableFiles); - var fileMatcher = new FileMatcher(new FileSystemAdapter(mockFileSystem), mockFileSystem.GetAccessibleFileSystemEntries); + var fileMatcher = new FileMatcher(new DirectoryCacheOverFileSystem(new FileSystemAdapter(mockFileSystem)), mockFileSystem.GetAccessibleFileSystemEntries); string[] files = fileMatcher.GetFiles ( @@ -2447,7 +2447,7 @@ private static IReadOnlyList GetFileSystemEntriesLoopBack(FileMatcher.Fi * Validate that SplitFileSpec(...) is returning the expected constituent values. *************************************************************************************/ - private static FileMatcher loopBackFileMatcher = new FileMatcher(FileSystems.Default, GetFileSystemEntriesLoopBack); + private static FileMatcher loopBackFileMatcher = new FileMatcher(FileSystems.DefaultDirectoryCache, GetFileSystemEntriesLoopBack); private static void ValidateSplitFileSpec ( From 200e03746296e223e32c4248930e7374db8af14a Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Tue, 17 Aug 2021 14:50:38 +0200 Subject: [PATCH 08/23] Revert "Make FileMatcher consume IDirectoryCache instead of IFileSystem" This reverts commit fde5ed67ba18f3f41b272b4549f296209e56592a. --- .../Evaluation/Context/EvaluationContext.cs | 2 + src/Build/Evaluation/Evaluator.cs | 2 +- .../DirectoryCacheOverFileSystem.cs | 7 +- .../FileSystem/IDirectoryCache.cs | 37 +++++---- .../FileSystem/IDirectoryCacheFactory.cs | 23 ------ src/Shared/FileMatcher.cs | 75 +++++++------------ src/Shared/FileSystem/FileSystems.cs | 8 -- src/Shared/UnitTests/FileMatcher_Tests.cs | 6 +- 8 files changed, 56 insertions(+), 104 deletions(-) rename src/{Shared => Build}/FileSystem/DirectoryCacheOverFileSystem.cs (94%) rename src/{Shared => Build}/FileSystem/IDirectoryCache.cs (72%) delete mode 100644 src/Build/FileSystem/IDirectoryCacheFactory.cs diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index c2b5b5d4ddc..7dcb253fd40 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -43,6 +43,7 @@ public enum SharingPolicy internal ISdkResolverService SdkResolverService { get; } internal IFileSystem FileSystem { get; } + internal FileMatcher FileMatcher { get; } private IDirectoryCacheFactory _directoryCacheFactory; private ConditionalWeakTable _directoryCachesPerProject; @@ -67,6 +68,7 @@ private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, IDirecto SdkResolverService = new CachingSdkResolverService(); FileEntryExpansionCache = new ConcurrentDictionary>(); FileSystem = fileSystem ?? new CachingFileSystemWrapper(FileSystems.Default); + FileMatcher = new FileMatcher(FileSystem, FileEntryExpansionCache); if (directoryCacheFactory != null) { diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index af54e25b678..1355f91e9f1 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -269,7 +269,7 @@ private Evaluator( // Create a FileMatcher for the given combination of EvaluationContext and the project being evaluated. IDirectoryCache directoryCache = _evaluationContext.GetDirectoryCacheForProject(project); - _fileMatcher = new FileMatcher(directoryCache, evaluationContext.FileEntryExpansionCache); + _fileMatcher = new FileMatcher(evaluationContext.FileSystem, evaluationContext.FileEntryExpansionCache); } /// diff --git a/src/Shared/FileSystem/DirectoryCacheOverFileSystem.cs b/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs similarity index 94% rename from src/Shared/FileSystem/DirectoryCacheOverFileSystem.cs rename to src/Build/FileSystem/DirectoryCacheOverFileSystem.cs index de4398c9018..c6556409572 100644 --- a/src/Shared/FileSystem/DirectoryCacheOverFileSystem.cs +++ b/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs @@ -1,13 +1,12 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if BUILD_ENGINE -using Microsoft.Build.FileSystem; -#endif +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; using System; using System.Collections.Generic; -namespace Microsoft.Build.Shared.FileSystem +namespace Microsoft.Build.FileSystem { /// /// Implements on top of . diff --git a/src/Shared/FileSystem/IDirectoryCache.cs b/src/Build/FileSystem/IDirectoryCache.cs similarity index 72% rename from src/Shared/FileSystem/IDirectoryCache.cs rename to src/Build/FileSystem/IDirectoryCache.cs index 8fe4e48cd27..aa44e7be235 100644 --- a/src/Shared/FileSystem/IDirectoryCache.cs +++ b/src/Build/FileSystem/IDirectoryCache.cs @@ -4,38 +4,43 @@ using System; using System.Collections.Generic; -#if BUILD_ENGINE +using Microsoft.Build.Evaluation; + namespace Microsoft.Build.FileSystem -#else -namespace Microsoft.Build.Shared.FileSystem -#endif { + /// + /// A provider of instances. To be implemented by MSBuild hosts that wish to intercept + /// file existence checks and file enumerations performed during project evaluation. + /// + /// + /// Unlike , file enumeration returns file/directory names, not full paths. + /// + public interface IDirectoryCacheFactory + { + /// + /// Returns an to be used when evaluating the given . + /// + /// The project being evaluated. + IDirectoryCache GetDirectoryCacheForProject(Project project); + } + /// /// A predicate taking file name. /// /// The file name to check. -#if BUILD_ENGINE - public -#endif - delegate bool FindPredicate(ref ReadOnlySpan fileName); + public delegate bool FindPredicate(ref ReadOnlySpan fileName); /// /// A function taking file name and returning an arbitrary result. /// /// The type of the result to return /// The file name to transform. -#if BUILD_ENGINE - public -#endif - delegate TResult FindTransform(ref ReadOnlySpan fileName); + public delegate TResult FindTransform(ref ReadOnlySpan fileName); /// /// Allows the implementor to intercept file existence checks and file enumerations performed during project evaluation. /// -#if BUILD_ENGINE - public -#endif - interface IDirectoryCache + public interface IDirectoryCache { /// /// Returns true if the given path points to an existing file on disk. diff --git a/src/Build/FileSystem/IDirectoryCacheFactory.cs b/src/Build/FileSystem/IDirectoryCacheFactory.cs deleted file mode 100644 index 27acee7b415..00000000000 --- a/src/Build/FileSystem/IDirectoryCacheFactory.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Build.Evaluation; - -namespace Microsoft.Build.FileSystem -{ - /// - /// A provider of instances. To be implemented by MSBuild hosts that wish to intercept - /// file existence checks and file enumerations performed during project evaluation. - /// - /// - /// Unlike , file enumeration returns file/directory names, not full paths. - /// - public interface IDirectoryCacheFactory - { - /// - /// Returns an to be used when evaluating the given . - /// - /// The project being evaluated. - IDirectoryCache GetDirectoryCacheForProject(Project project); - } -} diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index 4f1b8fc15b2..a73958d2c83 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -10,9 +10,6 @@ using System.Text.RegularExpressions; using System.Collections.Generic; using System.Threading.Tasks; -#if BUILD_ENGINE -using Microsoft.Build.FileSystem; -#endif using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Utilities; @@ -23,7 +20,7 @@ namespace Microsoft.Build.Shared /// internal class FileMatcher { - private readonly IDirectoryCache _directoryCache; + private readonly IFileSystem _fileSystem; private const string recursiveDirectoryMatch = "**"; private static readonly string s_directorySeparator = new string(Path.DirectorySeparatorChar, 1); @@ -82,12 +79,12 @@ private static class FileSpecRegexParts /// /// The Default FileMatcher does not cache directory enumeration. /// - public static FileMatcher Default = new FileMatcher(FileSystems.DefaultDirectoryCache, null); + public static FileMatcher Default = new FileMatcher(FileSystems.Default, null); - public FileMatcher(IDirectoryCache directoryCache, ConcurrentDictionary> fileEntryExpansionCache = null) : this( - directoryCache, + public FileMatcher(IFileSystem fileSystem, ConcurrentDictionary> fileEntryExpansionCache = null) : this( + fileSystem, (entityType, path, pattern, projectDirectory, stripProjectDirectory) => GetAccessibleFileSystemEntries( - directoryCache, + fileSystem, entityType, path, pattern, @@ -97,7 +94,7 @@ public FileMatcher(IDirectoryCache directoryCache, ConcurrentDictionary> fileEntryExpansionCache = null) + internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemEntries, ConcurrentDictionary> fileEntryExpansionCache = null) { if (Traits.Instance.MSBuildCacheFileEnumerations) { @@ -109,7 +106,7 @@ internal FileMatcher(IDirectoryCache directoryCache, GetFileSystemEntries getFil _cachedGlobExpansions = fileEntryExpansionCache; } - _directoryCache = directoryCache; + _fileSystem = fileSystem; _getFileSystemEntries = fileEntryExpansionCache == null ? getFileSystemEntries @@ -238,16 +235,16 @@ internal static bool HasPropertyOrItemReferences(string filespec) /// The pattern to search. /// The directory for the project within which the call is made /// If true the project directory should be stripped - /// The file system abstraction to use that implements file system operations + /// The file system abstraction to use that implements file system operations /// - private static IReadOnlyList GetAccessibleFileSystemEntries(IDirectoryCache directoryCache, FileSystemEntity entityType, string path, string pattern, string projectDirectory, bool stripProjectDirectory) + private static IReadOnlyList GetAccessibleFileSystemEntries(IFileSystem fileSystem, FileSystemEntity entityType, string path, string pattern, string projectDirectory, bool stripProjectDirectory) { path = FileUtilities.FixFilePath(path); switch (entityType) { - case FileSystemEntity.Files: return GetAccessibleFiles(directoryCache, path, pattern, projectDirectory, stripProjectDirectory); - case FileSystemEntity.Directories: return GetAccessibleDirectories(directoryCache, path, pattern); - case FileSystemEntity.FilesAndDirectories: return GetAccessibleFilesAndDirectories(directoryCache, path, pattern); + case FileSystemEntity.Files: return GetAccessibleFiles(fileSystem, path, pattern, projectDirectory, stripProjectDirectory); + case FileSystemEntity.Directories: return GetAccessibleDirectories(fileSystem, path, pattern); + case FileSystemEntity.FilesAndDirectories: return GetAccessibleFilesAndDirectories(fileSystem, path, pattern); default: ErrorUtilities.VerifyThrow(false, "Unexpected filesystem entity type."); break; @@ -261,18 +258,18 @@ private static IReadOnlyList GetAccessibleFileSystemEntries(IDirectoryCa /// /// /// - /// The file system abstraction to use that implements file system operations + /// The file system abstraction to use that implements file system operations /// An enumerable of matching file system entries (can be empty). - private static IReadOnlyList GetAccessibleFilesAndDirectories(IDirectoryCache directoryCache, string path, string pattern) + private static IReadOnlyList GetAccessibleFilesAndDirectories(IFileSystem fileSystem, string path, string pattern) { - if (directoryCache.DirectoryExists(path)) + if (fileSystem.DirectoryExists(path)) { try { return (ShouldEnforceMatching(pattern) - ? EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.FilesAndDirectories, path, pattern) + ? fileSystem.EnumerateFileSystemEntries(path, pattern) .Where(o => IsMatch(Path.GetFileName(o), pattern)) - : EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.FilesAndDirectories, path, pattern) + : fileSystem.EnumerateFileSystemEntries(path, pattern) ).ToArray(); } // for OS security @@ -327,11 +324,11 @@ private static bool ShouldEnforceMatching(string searchPattern) /// The pattern. /// The project directory /// - /// The file system abstraction to use that implements file system operations + /// The file system abstraction to use that implements file system operations /// Files that can be accessed. private static IReadOnlyList GetAccessibleFiles ( - IDirectoryCache directoryCache, + IFileSystem fileSystem, string path, string filespec, // can be null string projectDirectory, @@ -347,11 +344,11 @@ bool stripProjectDirectory IEnumerable files; if (filespec == null) { - files = EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.Files, dir, "*"); + files = fileSystem.EnumerateFiles(dir); } else { - files = EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.Files, dir, filespec); + files = fileSystem.EnumerateFiles(dir, filespec); if (ShouldEnforceMatching(filespec)) { files = files.Where(o => IsMatch(Path.GetFileName(o), filespec)); @@ -395,11 +392,11 @@ bool stripProjectDirectory /// /// The path. /// Pattern to match - /// The file system abstraction to use that implements file system operations + /// The file system abstraction to use that implements file system operations /// Accessible directories. private static IReadOnlyList GetAccessibleDirectories ( - IDirectoryCache directoryCache, + IFileSystem fileSystem, string path, string pattern ) @@ -410,11 +407,11 @@ string pattern if (pattern == null) { - directories = EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.Directories, (path.Length == 0) ? s_thisDirectory : path, "*"); + directories = fileSystem.EnumerateDirectories((path.Length == 0) ? s_thisDirectory : path); } else { - directories = EnumerateFullFileSystemPaths(directoryCache, FileSystemEntity.Directories, (path.Length == 0) ? s_thisDirectory : path, pattern); + directories = fileSystem.EnumerateDirectories((path.Length == 0) ? s_thisDirectory : path, pattern); if (ShouldEnforceMatching(pattern)) { directories = directories.Where(o => IsMatch(Path.GetFileName(o), pattern)); @@ -445,26 +442,6 @@ string pattern } } - // TODO: Temporary until #6075 is implemented. - private static IEnumerable EnumerateFullFileSystemPaths(IDirectoryCache directoryCache, FileSystemEntity entityType, string path, string pattern) - { - FindPredicate predicate = (ref ReadOnlySpan fileName) => - { - string fileNameString = fileName.ToString(); - return IsAllFilesWildcard(pattern) || IsMatch(fileNameString, pattern); - }; - FindTransform transform = (ref ReadOnlySpan fileName) => Path.Combine(path, fileName.ToString()); - - IEnumerable directories = (entityType == FileSystemEntity.Directories || entityType == FileSystemEntity.FilesAndDirectories) - ? directoryCache.EnumerateDirectories(path, predicate, transform) - : Enumerable.Empty(); - IEnumerable files = (entityType == FileSystemEntity.Files || entityType == FileSystemEntity.FilesAndDirectories) - ? directoryCache.EnumerateFiles(path, predicate, transform) - : Enumerable.Empty(); - - return Enumerable.Concat(directories, files); - } - /// /// Given a path name, get its long version. /// @@ -2121,7 +2098,7 @@ out bool isLegalFileSpec * If the fixed directory part doesn't exist, then this means no files should be * returned. */ - if (fixedDirectoryPart.Length > 0 && !_directoryCache.DirectoryExists(fixedDirectoryPart)) + if (fixedDirectoryPart.Length > 0 && !_fileSystem.DirectoryExists(fixedDirectoryPart)) { return SearchAction.ReturnEmptyList; } diff --git a/src/Shared/FileSystem/FileSystems.cs b/src/Shared/FileSystem/FileSystems.cs index d0bbe8f87fe..15b6b43bb2b 100644 --- a/src/Shared/FileSystem/FileSystems.cs +++ b/src/Shared/FileSystem/FileSystems.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if BUILD_ENGINE -using Microsoft.Build.FileSystem; -#endif - namespace Microsoft.Build.Shared.FileSystem { /// @@ -14,10 +10,6 @@ internal static class FileSystems { public static IFileSystem Default = GetFileSystem(); -#if !CLR2COMPATIBILITY - public static IDirectoryCache DefaultDirectoryCache = new DirectoryCacheOverFileSystem(Default); -#endif - private static IFileSystem GetFileSystem() { #if CLR2COMPATIBILITY diff --git a/src/Shared/UnitTests/FileMatcher_Tests.cs b/src/Shared/UnitTests/FileMatcher_Tests.cs index bce902916ac..471e1536b63 100644 --- a/src/Shared/UnitTests/FileMatcher_Tests.cs +++ b/src/Shared/UnitTests/FileMatcher_Tests.cs @@ -101,7 +101,7 @@ void VerifyImpl(FileMatcher fileMatcher, string include, string[] excludes, bool } } - var fileMatcherWithCache = new FileMatcher(FileSystems.DefaultDirectoryCache, new ConcurrentDictionary>()); + var fileMatcherWithCache = new FileMatcher(FileSystems.Default, new ConcurrentDictionary>()); void Verify(string include, string[] excludes, bool shouldHaveNoMatches = false, string customMessage = null) { @@ -2376,7 +2376,7 @@ private static void MatchDriver(string filespec, string[] excludeFilespecs, stri { MockFileSystem mockFileSystem = new MockFileSystem(matchingFiles, nonmatchingFiles, untouchableFiles); - var fileMatcher = new FileMatcher(new DirectoryCacheOverFileSystem(new FileSystemAdapter(mockFileSystem)), mockFileSystem.GetAccessibleFileSystemEntries); + var fileMatcher = new FileMatcher(new FileSystemAdapter(mockFileSystem), mockFileSystem.GetAccessibleFileSystemEntries); string[] files = fileMatcher.GetFiles ( @@ -2447,7 +2447,7 @@ private static IReadOnlyList GetFileSystemEntriesLoopBack(FileMatcher.Fi * Validate that SplitFileSpec(...) is returning the expected constituent values. *************************************************************************************/ - private static FileMatcher loopBackFileMatcher = new FileMatcher(FileSystems.DefaultDirectoryCache, GetFileSystemEntriesLoopBack); + private static FileMatcher loopBackFileMatcher = new FileMatcher(FileSystems.Default, GetFileSystemEntriesLoopBack); private static void ValidateSplitFileSpec ( From 1004b54eb2874388b4cb82c070a53f4fa3c4e331 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Tue, 17 Aug 2021 15:39:41 +0200 Subject: [PATCH 09/23] Introduce DirectoryCacheFileSystemWrapper --- .../Evaluation/Context/EvaluationContext.cs | 33 ++--- src/Build/Evaluation/Evaluator.cs | 4 +- .../DirectoryCacheFileSystemWrapper.cs | 133 ++++++++++++++++++ .../DirectoryCacheOverFileSystem.cs | 58 -------- src/Shared/FileMatcher.cs | 2 +- 5 files changed, 151 insertions(+), 79 deletions(-) create mode 100644 src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs delete mode 100644 src/Build/FileSystem/DirectoryCacheOverFileSystem.cs diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 7dcb253fd40..b227f388adb 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -46,9 +46,7 @@ public enum SharingPolicy internal FileMatcher FileMatcher { get; } private IDirectoryCacheFactory _directoryCacheFactory; - private ConditionalWeakTable _directoryCachesPerProject; - - private IDirectoryCache _defaultDirectoryCache; + private ConditionalWeakTable _fileSystemsPerProject; /// /// Key to file entry list. Example usages: cache glob expansion and intermediary directory expansions during glob expansion. @@ -73,7 +71,7 @@ private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, IDirecto if (directoryCacheFactory != null) { _directoryCacheFactory = directoryCacheFactory; - _directoryCachesPerProject = new ConditionalWeakTable(); + _fileSystemsPerProject = new ConditionalWeakTable(); } } @@ -154,23 +152,22 @@ internal EvaluationContext ContextForNewProject() } } - internal IDirectoryCache GetDirectoryCacheForProject(Project project) + internal IFileSystem GetFileSystemForProject(Project project) { - IDirectoryCache directoryCache = _directoryCachesPerProject?.GetValue( + IFileSystem fileSystemForProject = _fileSystemsPerProject?.GetValue( project, - project => _directoryCacheFactory.GetDirectoryCacheForProject(project)); - - // If we don't have a non-null directory cache factory or it returned null, lazily create a wrapper over IFileSystem. - if (directoryCache == null) - { - directoryCache = Volatile.Read(ref _defaultDirectoryCache); - if (directoryCache == null) + project => { - directoryCache = new DirectoryCacheOverFileSystem(FileSystem); - Volatile.Write(ref _defaultDirectoryCache, directoryCache); - } - } - return directoryCache; + IDirectoryCache directoryCache = _directoryCacheFactory.GetDirectoryCacheForProject(project); + if(directoryCache != null) + { + return new DirectoryCacheFileSystemWrapper(FileSystem, directoryCache); + } + return null; + }); + + // If we don't have a non-null directory cache factory or it returned null, fall back to returning the shared FileSystem. + return fileSystemForProject ?? FileSystem; } } } diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index 1355f91e9f1..ede85ad1e73 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -268,8 +268,8 @@ private Evaluator( _streamImports.Add(string.Empty); // Create a FileMatcher for the given combination of EvaluationContext and the project being evaluated. - IDirectoryCache directoryCache = _evaluationContext.GetDirectoryCacheForProject(project); - _fileMatcher = new FileMatcher(evaluationContext.FileSystem, evaluationContext.FileEntryExpansionCache); + IFileSystem fileSystem = _evaluationContext.GetFileSystemForProject(project); + _fileMatcher = new FileMatcher(fileSystem, evaluationContext.FileEntryExpansionCache); } /// diff --git a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs new file mode 100644 index 00000000000..af5958c7924 --- /dev/null +++ b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.Build.FileSystem +{ + internal class DirectoryCacheFileSystemWrapper : IFileSystem + { + /// + /// The base to fall back to for functionality not provided by . + /// + private readonly IFileSystem _fileSystem; + + /// + /// A host-provided cache used for file existence and directory enumeration. + /// + private readonly IDirectoryCache _directoryCache; + + public DirectoryCacheFileSystemWrapper(IFileSystem fileSystem, IDirectoryCache directoryCache) + { + _fileSystem = fileSystem; + _directoryCache = directoryCache; + } + + #region IFileSystem implementation based on IDirectoryCache + + public bool FileOrDirectoryExists(string path) + { + return _directoryCache.FileExists(path) || _directoryCache.DirectoryExists(path); + } + + public bool DirectoryExists(string path) + { + return _directoryCache.DirectoryExists(path); + } + + public bool FileExists(string path) + { + return _directoryCache.FileExists(path); + } + + public IEnumerable EnumerateDirectories(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) + { + if (searchOption != SearchOption.TopDirectoryOnly) + { + // Recursive enumeration is not used during evaluation, pass it through. + return _fileSystem.EnumerateDirectories(path, searchPattern, searchOption); + } + return EnumerateFullFileSystemPaths(path, searchPattern, includeFiles: false, includeDirectories: true); + } + + public IEnumerable EnumerateFiles(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) + { + if (searchOption != SearchOption.TopDirectoryOnly) + { + // Recursive enumeration is not used during evaluation, pass it through. + return _fileSystem.EnumerateFiles(path, searchPattern, searchOption); + } + return EnumerateFullFileSystemPaths(path, searchPattern, includeFiles: true, includeDirectories: false); + } + + public IEnumerable EnumerateFileSystemEntries(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) + { + if (searchOption != SearchOption.TopDirectoryOnly) + { + // Recursive enumeration is not used during evaluation, pass it through. + return _fileSystem.EnumerateFileSystemEntries(path, searchPattern, searchOption); + } + return EnumerateFullFileSystemPaths(path, searchPattern, includeFiles: true, includeDirectories: true); + } + + private IEnumerable EnumerateFullFileSystemPaths(string path, string searchPattern, bool includeFiles, bool includeDirectories) + { + FindPredicate predicate = (ref ReadOnlySpan fileName) => + { + string fileNameString = fileName.ToString(); + return FileMatcher.IsAllFilesWildcard(searchPattern) || FileMatcher.IsMatch(fileNameString, searchPattern); + }; + FindTransform transform = (ref ReadOnlySpan fileName) => Path.Combine(path, fileName.ToString()); + + IEnumerable directories = includeDirectories + ? _directoryCache.EnumerateDirectories(path, predicate, transform) + : Enumerable.Empty(); + IEnumerable files = includeFiles + ? _directoryCache.EnumerateFiles(path, predicate, transform) + : Enumerable.Empty(); + + return Enumerable.Concat(directories, files); + } + + #endregion + + #region IFileSystem pass-through implementation + + public FileAttributes GetAttributes(string path) + { + return _fileSystem.GetAttributes(path); + } + + public DateTime GetLastWriteTimeUtc(string path) + { + return _fileSystem.GetLastWriteTimeUtc(path); + } + + public TextReader ReadFile(string path) + { + return _fileSystem.ReadFile(path); + } + + public Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share) + { + return _fileSystem.GetFileStream(path, mode, access, share); + } + + public string ReadFileAllText(string path) + { + return _fileSystem.ReadFileAllText(path); + } + + public byte[] ReadFileAllBytes(string path) + { + return _fileSystem.ReadFileAllBytes(path); + } + + #endregion + } +} diff --git a/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs b/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs deleted file mode 100644 index c6556409572..00000000000 --- a/src/Build/FileSystem/DirectoryCacheOverFileSystem.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Build.Shared; -using Microsoft.Build.Shared.FileSystem; -using System; -using System.Collections.Generic; - -namespace Microsoft.Build.FileSystem -{ - /// - /// Implements on top of . - /// - internal sealed class DirectoryCacheOverFileSystem : IDirectoryCache - { - private IFileSystem _fileSystem; - - public DirectoryCacheOverFileSystem(IFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - public bool FileExists(string path) - { - return _fileSystem.FileExists(path); - } - - public bool DirectoryExists(string path) - { - return _fileSystem.DirectoryExists(path); - } - - public IEnumerable EnumerateDirectories(string path, FindPredicate predicate, FindTransform transform) - { - return EnumerateAndTransformFullPaths(_fileSystem.EnumerateDirectories(path), predicate, transform); - } - - public IEnumerable EnumerateFiles(string path, FindPredicate predicate, FindTransform transform) - { - return EnumerateAndTransformFullPaths(_fileSystem.EnumerateFiles(path), predicate, transform); - } - - private IEnumerable EnumerateAndTransformFullPaths(IEnumerable fullPaths, FindPredicate predicate, FindTransform transform) - { - foreach (string fullPath in fullPaths) - { - // TODO: Call Path.GetFileName() from Microsoft.IO. - int lastSlashPos = fullPath.LastIndexOfAny(FileUtilities.Slashes); - ReadOnlySpan fileName = fullPath.AsSpan(lastSlashPos + 1, fullPath.Length - lastSlashPos - 1); - - if (predicate(ref fileName)) - { - yield return transform(ref fileName); - } - } - } - } -} diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index a73958d2c83..9d2b990b0b4 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -2564,7 +2564,7 @@ private static bool DirectoryEndsWithPattern(string directoryPath, string patter /// Returns true if is * or *.*. /// /// The filename pattern to check. - private static bool IsAllFilesWildcard(string pattern) => pattern?.Length switch + internal static bool IsAllFilesWildcard(string pattern) => pattern?.Length switch { 1 => pattern[0] == '*', 3 => pattern[0] == '*' && pattern[1] == '.' && pattern[2] == '*', From faeda3e4705370d3ce568bbc3e53f08b7d1fd7c7 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Tue, 17 Aug 2021 16:01:41 +0200 Subject: [PATCH 10/23] Avoid string allocations when calling IsMatch --- .../DirectoryCacheFileSystemWrapper.cs | 4 ++-- src/Shared/FileMatcher.cs | 23 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs index af5958c7924..45f0056e2b2 100644 --- a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs +++ b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs @@ -79,9 +79,9 @@ private IEnumerable EnumerateFullFileSystemPaths(string path, string sea { FindPredicate predicate = (ref ReadOnlySpan fileName) => { - string fileNameString = fileName.ToString(); - return FileMatcher.IsAllFilesWildcard(searchPattern) || FileMatcher.IsMatch(fileNameString, searchPattern); + return FileMatcher.IsAllFilesWildcard(searchPattern) || FileMatcher.IsMatch(fileName, searchPattern); }; + // TODO: Don't create a new string and use Path.Join when it becomes available. FindTransform transform = (ref ReadOnlySpan fileName) => Path.Combine(path, fileName.ToString()); IEnumerable directories = includeDirectories diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index 9d2b990b0b4..57f7ef82d9d 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -1664,12 +1664,18 @@ internal Result() internal string wildcardDirectoryPart = string.Empty; } + // TODO: Remove this overload and fix callers to pass ReadOnlySpan. + internal static bool IsMatch(string input, string pattern) + { + return IsMatch(input.AsSpan(), pattern); + } + /// /// A wildcard (* and ?) matching algorithm that tests whether the input string matches against the pattern. /// /// String which is matched against the pattern. /// Pattern against which string is matched. - internal static bool IsMatch(string input, string pattern) + internal static bool IsMatch(ReadOnlySpan input, string pattern) { if (input == null) { @@ -1705,9 +1711,12 @@ internal static bool IsMatch(string input, string pattern) // to using the string indexer. The iIndex and pIndex parameters are only used // when we have to compare two non ASCII characters. Using just string.Compare for // character comparison, would reduce the speed by approx. 5 times. - bool CompareIgnoreCase(char inputChar, char patternChar, int iIndex, int pIndex) + bool CompareIgnoreCase(ref ReadOnlySpan input, int iIndex, int pIndex) #endif { + char inputChar = input[iIndex]; + char patternChar = pattern[iIndex]; + // We will mostly be comparing ASCII characters, check English letters first. char inputCharLower = (char)(inputChar | 0x20); if (inputCharLower >= 'a' && inputCharLower <= 'z') @@ -1721,7 +1730,7 @@ bool CompareIgnoreCase(char inputChar, char patternChar, int iIndex, int pIndex) // and a non ASCII character cannot have its lowercase/uppercase inside the ASCII table return inputChar == patternChar; } - return string.Compare(input, iIndex, pattern, pIndex, 1, StringComparison.OrdinalIgnoreCase) == 0; + return MemoryExtensions.Equals(input.Slice(iIndex, 1), pattern.AsSpan(pIndex, 1), StringComparison.OrdinalIgnoreCase); } #if MONO ; // The end of the CompareIgnoreCase anonymous function @@ -1761,7 +1770,7 @@ bool CompareIgnoreCase(char inputChar, char patternChar, int iIndex, int pIndex) break; } // If the tail doesn't match, we can safely return e.g. ("aaa", "*b") - if (!CompareIgnoreCase(input[inputTailIndex], pattern[patternTailIndex], patternTailIndex, inputTailIndex) && + if (!CompareIgnoreCase(ref input, patternTailIndex, inputTailIndex) && pattern[patternTailIndex] != '?') { return false; @@ -1781,7 +1790,7 @@ bool CompareIgnoreCase(char inputChar, char patternChar, int iIndex, int pIndex) // The ? wildcard cannot be skipped as we will have a wrong result for e.g. ("aab" "*?b") if (pattern[patternIndex] != '?') { - while (!CompareIgnoreCase(input[inputIndex], pattern[patternIndex], inputIndex, patternIndex)) + while (!CompareIgnoreCase(ref input, inputIndex, patternIndex)) { // Return if there is no character that match e.g. ("aa", "*b") if (++inputIndex >= inputLength) @@ -1796,7 +1805,7 @@ bool CompareIgnoreCase(char inputChar, char patternChar, int iIndex, int pIndex) } // If we have a match, step to the next character - if (CompareIgnoreCase(input[inputIndex], pattern[patternIndex], inputIndex, patternIndex) || + if (CompareIgnoreCase(ref input, inputIndex, patternIndex) || pattern[patternIndex] == '?') { patternIndex++; @@ -2557,7 +2566,7 @@ private static bool IsSubdirectoryOf(string possibleChild, string possibleParent private static bool DirectoryEndsWithPattern(string directoryPath, string pattern) { int index = directoryPath.LastIndexOfAny(FileUtilities.Slashes); - return (index != -1 && IsMatch(directoryPath.Substring(index + 1), pattern)); + return (index != -1 && IsMatch(directoryPath.AsSpan(index + 1), pattern)); } /// From 1d0a741a130eb779c764da18660d400512a7d681 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Wed, 18 Aug 2021 22:35:04 +0200 Subject: [PATCH 11/23] Pass IDirectoryCacheFactory to Project ctor (reverts most changes to EvaluationContext) --- ref/Microsoft.Build/net/Microsoft.Build.cs | 4 +- .../netstandard/Microsoft.Build.cs | 4 +- src/Build/Definition/Project.cs | 45 +++++++++++++---- src/Build/Definition/ProjectOptions.cs | 6 +++ .../Evaluation/Context/EvaluationContext.cs | 49 +------------------ src/Build/Evaluation/Evaluator.cs | 7 ++- src/Build/FileSystem/IDirectoryCache.cs | 6 +-- 7 files changed, 56 insertions(+), 65 deletions(-) diff --git a/ref/Microsoft.Build/net/Microsoft.Build.cs b/ref/Microsoft.Build/net/Microsoft.Build.cs index c06bce83735..b9fb1dc0f08 100644 --- a/ref/Microsoft.Build/net/Microsoft.Build.cs +++ b/ref/Microsoft.Build/net/Microsoft.Build.cs @@ -498,6 +498,7 @@ namespace Microsoft.Build.Definition public partial class ProjectOptions { public ProjectOptions() { } + public Microsoft.Build.FileSystem.IDirectoryCacheFactory DirectoryCacheFactory { get { throw null; } set { } } public Microsoft.Build.Evaluation.Context.EvaluationContext EvaluationContext { get { throw null; } set { } } public System.Collections.Generic.IDictionary GlobalProperties { get { throw null; } set { } } public Microsoft.Build.Evaluation.ProjectLoadSettings LoadSettings { get { throw null; } set { } } @@ -881,7 +882,6 @@ public partial class EvaluationContext { internal EvaluationContext() { } public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy) { throw null; } - public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy, Microsoft.Build.FileSystem.IDirectoryCacheFactory directoryCacheFactory) { throw null; } public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy, Microsoft.Build.FileSystem.MSBuildFileSystemBase fileSystem) { throw null; } public enum SharingPolicy { @@ -1516,7 +1516,7 @@ public partial interface IDirectoryCache } public partial interface IDirectoryCacheFactory { - Microsoft.Build.FileSystem.IDirectoryCache GetDirectoryCacheForProject(Microsoft.Build.Evaluation.Project project); + Microsoft.Build.FileSystem.IDirectoryCache GetDirectoryCacheForEvaluation(int evaluationId); } public abstract partial class MSBuildFileSystemBase { diff --git a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs index 7eccfc39bf4..635bc7d11e2 100644 --- a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs +++ b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs @@ -498,6 +498,7 @@ namespace Microsoft.Build.Definition public partial class ProjectOptions { public ProjectOptions() { } + public Microsoft.Build.FileSystem.IDirectoryCacheFactory DirectoryCacheFactory { get { throw null; } set { } } public Microsoft.Build.Evaluation.Context.EvaluationContext EvaluationContext { get { throw null; } set { } } public System.Collections.Generic.IDictionary GlobalProperties { get { throw null; } set { } } public Microsoft.Build.Evaluation.ProjectLoadSettings LoadSettings { get { throw null; } set { } } @@ -881,7 +882,6 @@ public partial class EvaluationContext { internal EvaluationContext() { } public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy) { throw null; } - public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy, Microsoft.Build.FileSystem.IDirectoryCacheFactory directoryCacheFactory) { throw null; } public static Microsoft.Build.Evaluation.Context.EvaluationContext Create(Microsoft.Build.Evaluation.Context.EvaluationContext.SharingPolicy policy, Microsoft.Build.FileSystem.MSBuildFileSystemBase fileSystem) { throw null; } public enum SharingPolicy { @@ -1510,7 +1510,7 @@ public partial interface IDirectoryCache } public partial interface IDirectoryCacheFactory { - Microsoft.Build.FileSystem.IDirectoryCache GetDirectoryCacheForProject(Microsoft.Build.Evaluation.Project project); + Microsoft.Build.FileSystem.IDirectoryCache GetDirectoryCacheForEvaluation(int evaluationId); } public abstract partial class MSBuildFileSystemBase { diff --git a/src/Build/Definition/Project.cs b/src/Build/Definition/Project.cs index a0c03f26877..cbd3232a1be 100644 --- a/src/Build/Definition/Project.cs +++ b/src/Build/Definition/Project.cs @@ -32,6 +32,7 @@ using EvaluationItemSpec = Microsoft.Build.Evaluation.ItemSpec; using EvaluationItemExpressionFragment = Microsoft.Build.Evaluation.ItemSpec.ItemExpressionFragment; using SdkResult = Microsoft.Build.BackEnd.SdkResolution.SdkResult; +using Microsoft.Build.FileSystem; namespace Microsoft.Build.Evaluation { @@ -68,6 +69,11 @@ public class Project : ILinkableObject internal ProjectLink Link => implementation; object ILinkableObject.Link => IsLinked ? Link : null; + /// + /// Host-provided factory for interfaces to be used during evaluation. + /// + private readonly IDirectoryCacheFactory _directoryCacheFactory; + /// /// Default project template options (include all features). /// @@ -250,11 +256,12 @@ public Project(ProjectRootElement xml, IDictionary globalPropert /// The the project is added to. /// The to use for evaluation. public Project(ProjectRootElement xml, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings) - : this(xml, globalProperties, toolsVersion, subToolsetVersion, projectCollection, loadSettings, null) + : this(xml, globalProperties, toolsVersion, subToolsetVersion, projectCollection, loadSettings, null, null) { } - private Project(ProjectRootElement xml, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext) + private Project(ProjectRootElement xml, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, + EvaluationContext evaluationContext, IDirectoryCacheFactory directoryCacheFactory) { ErrorUtilities.VerifyThrowArgumentNull(xml, nameof(xml)); ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(toolsVersion, nameof(toolsVersion)); @@ -264,6 +271,7 @@ private Project(ProjectRootElement xml, IDictionary globalProper implementationInternal = (IProjectLinkInternal)defaultImplementation; implementation = defaultImplementation; + _directoryCacheFactory = directoryCacheFactory; defaultImplementation.Initialize(globalProperties, toolsVersion, subToolsetVersion, loadSettings, evaluationContext); } @@ -342,11 +350,12 @@ public Project(XmlReader xmlReader, IDictionary globalProperties /// The collection with which this project should be associated. May not be null. /// The load settings for this project. public Project(XmlReader xmlReader, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings) - : this(xmlReader, globalProperties, toolsVersion, subToolsetVersion, projectCollection, loadSettings, null) + : this(xmlReader, globalProperties, toolsVersion, subToolsetVersion, projectCollection, loadSettings, null, null) { } - private Project(XmlReader xmlReader, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext) + private Project(XmlReader xmlReader, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, + EvaluationContext evaluationContext, IDirectoryCacheFactory directoryCacheFactory) { ErrorUtilities.VerifyThrowArgumentNull(xmlReader, nameof(xmlReader)); ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(toolsVersion, nameof(toolsVersion)); @@ -356,6 +365,7 @@ private Project(XmlReader xmlReader, IDictionary globalPropertie implementationInternal = (IProjectLinkInternal)defaultImplementation; implementation = defaultImplementation; + _directoryCacheFactory = directoryCacheFactory; defaultImplementation.Initialize(globalProperties, toolsVersion, subToolsetVersion, loadSettings, evaluationContext); } @@ -436,11 +446,12 @@ public Project(string projectFile, IDictionary globalProperties, /// The collection with which this project should be associated. May not be null. /// The load settings for this project. public Project(string projectFile, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings) - : this(projectFile, globalProperties, toolsVersion, subToolsetVersion, projectCollection, loadSettings, null) + : this(projectFile, globalProperties, toolsVersion, subToolsetVersion, projectCollection, loadSettings, null, null) { } - private Project(string projectFile, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext) + private Project(string projectFile, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, + EvaluationContext evaluationContext, IDirectoryCacheFactory directoryCacheFactory) { ErrorUtilities.VerifyThrowArgumentNull(projectFile, nameof(projectFile)); ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(toolsVersion, nameof(toolsVersion)); @@ -451,6 +462,8 @@ private Project(string projectFile, IDictionary globalProperties implementationInternal = (IProjectLinkInternal)defaultImplementation; implementation = defaultImplementation; + _directoryCacheFactory = directoryCacheFactory; + // Note: not sure why only this ctor flavor do TryUnloadProject // seems the XmlReader based one should also clean the same way. try @@ -488,7 +501,8 @@ public static Project FromFile(string file, ProjectOptions options) options.SubToolsetVersion, options.ProjectCollection ?? ProjectCollection.GlobalProjectCollection, options.LoadSettings, - options.EvaluationContext); + options.EvaluationContext, + options.DirectoryCacheFactory); } /// @@ -505,7 +519,8 @@ public static Project FromProjectRootElement(ProjectRootElement rootElement, Pro options.SubToolsetVersion, options.ProjectCollection ?? ProjectCollection.GlobalProjectCollection, options.LoadSettings, - options.EvaluationContext); + options.EvaluationContext, + options.DirectoryCacheFactory); } /// @@ -522,7 +537,8 @@ public static Project FromXmlReader(XmlReader reader, ProjectOptions options) options.SubToolsetVersion, options.ProjectCollection ?? ProjectCollection.GlobalProjectCollection, options.LoadSettings, - options.EvaluationContext); + options.EvaluationContext, + options.DirectoryCacheFactory); } /// @@ -1767,6 +1783,17 @@ internal void VerifyThrowInvalidOperationNotImported(ProjectRootElement otherXml ErrorUtilities.VerifyThrowInvalidOperation(ReferenceEquals(Xml, otherXml), "OM_CannotModifyEvaluatedObjectInImportedFile", otherXml.Location.File); } + /// + /// Returns as provided by the passed when creating the + /// project, specific for a given evaluation ID. + /// + /// The evaluation ID for which the cache is requested. + /// + internal IDirectoryCache GetDirectoryCacheForEvaluation(int evaluationId) + { + return _directoryCacheFactory?.GetDirectoryCacheForEvaluation(evaluationId); + } + /// /// Internal project evaluation implementation. /// diff --git a/src/Build/Definition/ProjectOptions.cs b/src/Build/Definition/ProjectOptions.cs index 44d2ecccfd6..fadfe73a6e3 100644 --- a/src/Build/Definition/ProjectOptions.cs +++ b/src/Build/Definition/ProjectOptions.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Microsoft.Build.Evaluation; using Microsoft.Build.Evaluation.Context; +using Microsoft.Build.FileSystem; namespace Microsoft.Build.Definition { @@ -38,5 +39,10 @@ public class ProjectOptions /// The to use for evaluation. /// public EvaluationContext EvaluationContext { get; set; } + + /// + /// Provides to be used for evaluation. + /// + public IDirectoryCacheFactory DirectoryCacheFactory { get; set; } } } diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index b227f388adb..13ceeed1234 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -45,15 +45,12 @@ public enum SharingPolicy internal IFileSystem FileSystem { get; } internal FileMatcher FileMatcher { get; } - private IDirectoryCacheFactory _directoryCacheFactory; - private ConditionalWeakTable _fileSystemsPerProject; - /// /// Key to file entry list. Example usages: cache glob expansion and intermediary directory expansions during glob expansion. /// internal ConcurrentDictionary> FileEntryExpansionCache { get; } - private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, IDirectoryCacheFactory directoryCacheFactory) + private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem) { // Unsupported case: isolated context with non null file system. // Isolated means caches aren't reused, but the given file system might cache. @@ -67,12 +64,6 @@ private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, IDirecto FileEntryExpansionCache = new ConcurrentDictionary>(); FileSystem = fileSystem ?? new CachingFileSystemWrapper(FileSystems.Default); FileMatcher = new FileMatcher(FileSystem, FileEntryExpansionCache); - - if (directoryCacheFactory != null) - { - _directoryCacheFactory = directoryCacheFactory; - _fileSystemsPerProject = new ConditionalWeakTable(); - } } /// @@ -100,25 +91,7 @@ public static EvaluationContext Create(SharingPolicy policy, MSBuildFileSystemBa { var context = new EvaluationContext( policy, - fileSystem, - directoryCacheFactory: null); - - TestOnlyHookOnCreate?.Invoke(context); - - return context; - } - - /// - /// Factory for - /// - /// The to use. - /// The to use. - public static EvaluationContext Create(SharingPolicy policy, IDirectoryCacheFactory directoryCacheFactory) - { - var context = new EvaluationContext( - policy, - fileSystem: null, - directoryCacheFactory); + fileSystem); TestOnlyHookOnCreate?.Invoke(context); @@ -151,23 +124,5 @@ internal EvaluationContext ContextForNewProject() return null; } } - - internal IFileSystem GetFileSystemForProject(Project project) - { - IFileSystem fileSystemForProject = _fileSystemsPerProject?.GetValue( - project, - project => - { - IDirectoryCache directoryCache = _directoryCacheFactory.GetDirectoryCacheForProject(project); - if(directoryCache != null) - { - return new DirectoryCacheFileSystemWrapper(FileSystem, directoryCache); - } - return null; - }); - - // If we don't have a non-null directory cache factory or it returned null, fall back to returning the shared FileSystem. - return fileSystemForProject ?? FileSystem; - } } } diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index ede85ad1e73..2ffe263f1e7 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -267,8 +267,11 @@ private Evaluator( // When the imports are concatenated with a semicolon, this automatically prepends a semicolon if and only if another element is later added. _streamImports.Add(string.Empty); - // Create a FileMatcher for the given combination of EvaluationContext and the project being evaluated. - IFileSystem fileSystem = _evaluationContext.GetFileSystemForProject(project); + // Create a FileMatcher for the given project being evaluated, evaluation context, and evaluation ID. + IDirectoryCache directoryCache = project.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId); + IFileSystem fileSystem = directoryCache is not null + ? new DirectoryCacheFileSystemWrapper(evaluationContext.FileSystem, directoryCache) + : evaluationContext.FileSystem; _fileMatcher = new FileMatcher(fileSystem, evaluationContext.FileEntryExpansionCache); } diff --git a/src/Build/FileSystem/IDirectoryCache.cs b/src/Build/FileSystem/IDirectoryCache.cs index aa44e7be235..6c42b990bff 100644 --- a/src/Build/FileSystem/IDirectoryCache.cs +++ b/src/Build/FileSystem/IDirectoryCache.cs @@ -18,10 +18,10 @@ namespace Microsoft.Build.FileSystem public interface IDirectoryCacheFactory { /// - /// Returns an to be used when evaluating the given . + /// Returns an to be used when evaluating the project associated with this . /// - /// The project being evaluated. - IDirectoryCache GetDirectoryCacheForProject(Project project); + /// The ID of the evaluation for which the interface is requested. + IDirectoryCache GetDirectoryCacheForEvaluation(int evaluationId); } /// From 65c4942543440f7f5da90dcce7d3bd2131c93aed Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Wed, 18 Aug 2021 23:04:47 +0200 Subject: [PATCH 12/23] Fix bugs --- src/Build/Evaluation/Evaluator.cs | 2 +- src/Shared/FileMatcher.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index 2ffe263f1e7..5d006a452e2 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -268,7 +268,7 @@ private Evaluator( _streamImports.Add(string.Empty); // Create a FileMatcher for the given project being evaluated, evaluation context, and evaluation ID. - IDirectoryCache directoryCache = project.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId); + IDirectoryCache directoryCache = project?.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId); IFileSystem fileSystem = directoryCache is not null ? new DirectoryCacheFileSystemWrapper(evaluationContext.FileSystem, directoryCache) : evaluationContext.FileSystem; diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index 57f7ef82d9d..466ece84f00 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -1715,7 +1715,7 @@ bool CompareIgnoreCase(ref ReadOnlySpan input, int iIndex, int pIndex) #endif { char inputChar = input[iIndex]; - char patternChar = pattern[iIndex]; + char patternChar = pattern[pIndex]; // We will mostly be comparing ASCII characters, check English letters first. char inputCharLower = (char)(inputChar | 0x20); @@ -1770,7 +1770,7 @@ bool CompareIgnoreCase(ref ReadOnlySpan input, int iIndex, int pIndex) break; } // If the tail doesn't match, we can safely return e.g. ("aaa", "*b") - if (!CompareIgnoreCase(ref input, patternTailIndex, inputTailIndex) && + if (!CompareIgnoreCase(ref input, inputTailIndex, patternTailIndex) && pattern[patternTailIndex] != '?') { return false; From e0340b7697f28f1d133cf32913c2fa8c9be85682 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Thu, 19 Aug 2021 13:06:50 +0200 Subject: [PATCH 13/23] Use directory cache in all of evaluation --- .../Evaluation/Context/EvaluationContext.cs | 26 ++++++++++----- src/Build/Evaluation/Evaluator.cs | 33 +++++++++---------- src/Build/Evaluation/Expander.cs | 28 ++++++++++++++++ src/Build/Evaluation/LazyItemEvaluator.cs | 5 ++- 4 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 13ceeed1234..d0a12e16409 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -50,18 +50,13 @@ public enum SharingPolicy /// internal ConcurrentDictionary> FileEntryExpansionCache { get; } - private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem) + private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, ISdkResolverService sdkResolverService = null, + ConcurrentDictionary> fileEntryExpansionCache = null) { - // Unsupported case: isolated context with non null file system. - // Isolated means caches aren't reused, but the given file system might cache. - ErrorUtilities.VerifyThrowArgument( - policy == SharingPolicy.Shared || fileSystem == null, - "IsolatedContextDoesNotSupportFileSystem"); - Policy = policy; - SdkResolverService = new CachingSdkResolverService(); - FileEntryExpansionCache = new ConcurrentDictionary>(); + SdkResolverService = sdkResolverService ?? new CachingSdkResolverService(); + FileEntryExpansionCache = fileEntryExpansionCache ?? new ConcurrentDictionary>(); FileSystem = fileSystem ?? new CachingFileSystemWrapper(FileSystems.Default); FileMatcher = new FileMatcher(FileSystem, FileEntryExpansionCache); } @@ -89,6 +84,12 @@ public static EvaluationContext Create(SharingPolicy policy) /// public static EvaluationContext Create(SharingPolicy policy, MSBuildFileSystemBase fileSystem) { + // Unsupported case: isolated context with non null file system. + // Isolated means caches aren't reused, but the given file system might cache. + ErrorUtilities.VerifyThrowArgument( + policy == SharingPolicy.Shared || fileSystem == null, + "IsolatedContextDoesNotSupportFileSystem"); + var context = new EvaluationContext( policy, fileSystem); @@ -124,5 +125,12 @@ internal EvaluationContext ContextForNewProject() return null; } } + + internal EvaluationContext ContextWithFileSystem(IFileSystem fileSystem) + { + var newContext = new EvaluationContext(this.Policy, fileSystem, this.SdkResolverService, this.FileEntryExpansionCache); + newContext._used = 1; + return newContext; + } } } diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index 5d006a452e2..d35d10df3ae 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -180,11 +180,6 @@ internal class Evaluator /// private List _streamImports; - /// - /// The to use for expanding globs. - /// - private FileMatcher _fileMatcher; - private readonly bool _interactive; private readonly bool _isRunningInVisualStudio; @@ -212,6 +207,7 @@ private Evaluator( { ErrorUtilities.VerifyThrowInternalNull(data, nameof(data)); ErrorUtilities.VerifyThrowInternalNull(projectRootElementCache, nameof(projectRootElementCache)); + ErrorUtilities.VerifyThrowInternalNull(evaluationContext, nameof(evaluationContext)); ErrorUtilities.VerifyThrowInternalNull(loggingService, nameof(loggingService)); ErrorUtilities.VerifyThrowInternalNull(buildEventContext, nameof(buildEventContext)); @@ -226,12 +222,20 @@ private Evaluator( // Wrap the IEvaluatorData<> object passed in. data = new PropertyTrackingEvaluatorDataWrapper(data, _evaluationLoggingContext, Traits.Instance.LogPropertyTracking); } - _evaluationContext = evaluationContext ?? EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated); + + // If the host wishes to provide a directory cache for this evaluation, wrap the EvaluationContext to use the right file system. + _evaluationContext = evaluationContext; + IDirectoryCache directoryCache = project?.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId); + if (directoryCache is not null) + { + IFileSystem fileSystem = new DirectoryCacheFileSystemWrapper(evaluationContext.FileSystem, directoryCache); + _evaluationContext = evaluationContext.ContextWithFileSystem(fileSystem); + } // Create containers for the evaluation results data.InitializeForEvaluation(toolsetProvider, _evaluationContext.FileSystem); - _expander = new Expander(data, data, _evaluationContext.FileSystem); + _expander = new Expander(data, data, _evaluationContext); // This setting may change after the build has started, therefore if the user has not set the property to true on the build parameters we need to check to see if it is set to true on the environment variable. _expander.WarnForUninitializedProperties = BuildParameters.WarnOnUninitializedProperty || Traits.Instance.EscapeHatches.WarnOnUninitializedProperty; @@ -266,13 +270,6 @@ private Evaluator( _streamImports = new List(); // When the imports are concatenated with a semicolon, this automatically prepends a semicolon if and only if another element is later added. _streamImports.Add(string.Empty); - - // Create a FileMatcher for the given project being evaluated, evaluation context, and evaluation ID. - IDirectoryCache directoryCache = project?.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId); - IFileSystem fileSystem = directoryCache is not null - ? new DirectoryCacheFileSystemWrapper(evaluationContext.FileSystem, directoryCache) - : evaluationContext.FileSystem; - _fileMatcher = new FileMatcher(fileSystem, evaluationContext.FileEntryExpansionCache); } /// @@ -309,7 +306,7 @@ internal static void Evaluate( BuildEventContext buildEventContext, ISdkResolverService sdkResolverService, int submissionId, - EvaluationContext evaluationContext = null, + EvaluationContext evaluationContext, bool interactive = false) { MSBuildEventSource.Log.EvaluateStart(root.ProjectFileLocation.File); @@ -372,7 +369,7 @@ internal static List CreateItemsFromInclude(string rootDirectory, ProjectItem else { // The expression is not of the form "@(X)". Treat as string - string[] includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(rootDirectory, includeSplitEscaped, excludeSpecsEscaped: null, forceEvaluate: false, null /* TODO */); + string[] includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(rootDirectory, includeSplitEscaped, excludeSpecsEscaped: null, forceEvaluate: false, expander.EvaluationContext.FileMatcher); if (includeSplitFilesEscaped.Length > 0) { @@ -661,7 +658,7 @@ private void Evaluate() using (_evaluationProfiler.TrackPass(EvaluationPass.Items)) { // comment next line to turn off lazy Evaluation - lazyEvaluator = new LazyItemEvaluator(_data, _itemFactory, _evaluationLoggingContext, _evaluationProfiler, _evaluationContext, _fileMatcher); + lazyEvaluator = new LazyItemEvaluator(_data, _itemFactory, _evaluationLoggingContext, _evaluationProfiler, _evaluationContext); // Pass3: evaluate project items MSBuildEventSource.Log.EvaluatePass3Start(projectFile); @@ -2024,7 +2021,7 @@ private LoadImportsResult ExpandAndLoadImportsFromUnescapedImportExpression(stri } // Expand the wildcards and provide an alphabetical order list of import statements. - importFilesEscaped = EngineFileUtilities.GetFileListEscaped(directoryOfImportingFile, importExpressionEscapedItem, forceEvaluate: true, fileMatcher: _fileMatcher); + importFilesEscaped = EngineFileUtilities.GetFileListEscaped(directoryOfImportingFile, importExpressionEscapedItem, forceEvaluate: true, fileMatcher: _evaluationContext.FileMatcher); } catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) { diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index 79d3397e928..569b0555c25 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -25,6 +25,7 @@ using TaskItemFactory = Microsoft.Build.Execution.ProjectItemInstance.TaskItem.TaskItemFactory; using Microsoft.NET.StringTools; +using Microsoft.Build.Evaluation.Context; namespace Microsoft.Build.Evaluation { @@ -301,6 +302,11 @@ private void FlushFirstValueIfNeeded() private readonly IFileSystem _fileSystem; + /// + /// Non-null if the expander was constructed for evaluation. + /// + internal EvaluationContext EvaluationContext { get; } + /// /// Creates an expander passing it some properties to use. /// Properties may be null. @@ -312,6 +318,18 @@ internal Expander(IPropertyProvider

properties, IFileSystem fileSystem) _fileSystem = fileSystem; } + ///

+ /// Creates an expander passing it some properties to use and the evaluation context. + /// Properties may be null. + /// + internal Expander(IPropertyProvider

properties, EvaluationContext evaluationContext) + { + _properties = properties; + _usedUninitializedProperties = new UsedUninitializedProperties(); + _fileSystem = evaluationContext.FileSystem; + EvaluationContext = evaluationContext; + } + ///

/// Creates an expander passing it some properties and items to use. /// Either or both may be null. @@ -322,6 +340,16 @@ internal Expander(IPropertyProvider

properties, IItemProvider items, IFile _items = items; } + ///

+ /// Creates an expander passing it some properties and items to use, and the evaluation context. + /// Either or both may be null. + /// + internal Expander(IPropertyProvider

properties, IItemProvider items, EvaluationContext evaluationContext) + : this(properties, evaluationContext) + { + _items = items; + } + ///

/// Creates an expander passing it some properties, items, and/or metadata to use. /// Any or all may be null. diff --git a/src/Build/Evaluation/LazyItemEvaluator.cs b/src/Build/Evaluation/LazyItemEvaluator.cs index d85ad8c8982..53a92503e47 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.cs @@ -43,8 +43,7 @@ internal partial class LazyItemEvaluator protected FileMatcher FileMatcher { get; } - public LazyItemEvaluator(IEvaluatorData data, IItemFactory itemFactory, LoggingContext loggingContext, EvaluationProfiler evaluationProfiler, EvaluationContext evaluationContext, - FileMatcher fileMatcher) + public LazyItemEvaluator(IEvaluatorData data, IItemFactory itemFactory, LoggingContext loggingContext, EvaluationProfiler evaluationProfiler, EvaluationContext evaluationContext) { _outerEvaluatorData = data; _outerExpander = new Expander(_outerEvaluatorData, _outerEvaluatorData, evaluationContext.FileSystem); @@ -55,7 +54,7 @@ public LazyItemEvaluator(IEvaluatorData data, IItemFactory ite _evaluationProfiler = evaluationProfiler; FileSystem = evaluationContext.FileSystem; - FileMatcher = fileMatcher; + FileMatcher = evaluationContext.FileMatcher; } private ImmutableList GetItems(string itemType) From 77ed3ffcb2c57331fa9d6946548f6636f30dab1c Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Thu, 19 Aug 2021 13:07:01 +0200 Subject: [PATCH 14/23] Add unit test --- .../ProjectEvaluationContext_Tests.cs | 35 +++++++++++ src/Shared/UnitTests/ObjectModelHelpers.cs | 59 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs b/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs index 232d22c62dd..49497a0e870 100644 --- a/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs +++ b/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs @@ -129,6 +129,41 @@ public void IsolatedContextShouldNotSupportBeingPassedAFileSystem() Should.Throw(() => EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated, fileSystem)); } + [Fact] + public void EvaluationShouldUseDirectoryCache() + { + var projectFile = _env.CreateFile("1.proj", @" ".Cleanup()).Path; + + var projectCollection = _env.CreateProjectCollection().Collection; + var directoryCacheFactory = new Helpers.LoggingDirectoryCacheFactory(); + + var project = Project.FromFile( + projectFile, + new ProjectOptions + { + ProjectCollection = projectCollection, + DirectoryCacheFactory = directoryCacheFactory, + } + ); + + directoryCacheFactory.DirectoryCaches.Count.ShouldBe(1); + var directoryCache = directoryCacheFactory.DirectoryCaches[0]; + + directoryCache.EvaluationId.ShouldBe(project.LastEvaluationId); + + directoryCache.ExistenceChecks.OrderBy(kvp => kvp.Key).ShouldBe( + new Dictionary + { + { _env.DefaultTestDirectory.Path, 1}, + { Path.Combine(_env.DefaultTestDirectory.Path, "1.file"), 2 } + }.OrderBy(kvp => kvp.Key)); + directoryCache.Enumerations.ShouldBe( + new Dictionary + { + { _env.DefaultTestDirectory.Path, 1 } + }); + } + [Theory] [InlineData(EvaluationContext.SharingPolicy.Shared)] [InlineData(EvaluationContext.SharingPolicy.Isolated)] diff --git a/src/Shared/UnitTests/ObjectModelHelpers.cs b/src/Shared/UnitTests/ObjectModelHelpers.cs index 9261b45785c..e3a2fa45482 100644 --- a/src/Shared/UnitTests/ObjectModelHelpers.cs +++ b/src/Shared/UnitTests/ObjectModelHelpers.cs @@ -2019,6 +2019,65 @@ public void Dispose() } } + internal sealed class LoggingDirectoryCacheFactory : IDirectoryCacheFactory + { + public List DirectoryCaches { get; } = new(); + + public IDirectoryCache GetDirectoryCacheForEvaluation(int evaluationId) + { + var directoryCache = new LoggingDirectoryCache(evaluationId); + DirectoryCaches.Add(directoryCache); + return directoryCache; + } + } + + internal sealed class LoggingDirectoryCache : IDirectoryCache + { + internal int EvaluationId { get; } + + public ConcurrentDictionary ExistenceChecks { get; } = new(); + public ConcurrentDictionary Enumerations { get; } = new(); + + public LoggingDirectoryCache(int evaluationId) + { + EvaluationId = evaluationId; + } + + public bool DirectoryExists(string path) + { + IncrementExistenceChecks(path); + return Directory.Exists(path); + } + + public bool FileExists(string path) + { + IncrementExistenceChecks(path); + return File.Exists(path); + } + + public IEnumerable EnumerateDirectories(string path, FindPredicate predicate, FindTransform transform) + { + IncrementEnumerations(path); + return Enumerable.Empty(); + } + + public IEnumerable EnumerateFiles(string path, FindPredicate predicate, FindTransform transform) + { + IncrementEnumerations(path); + return Enumerable.Empty(); + } + + private void IncrementExistenceChecks(string path) + { + ExistenceChecks.AddOrUpdate(path, p => 1, (p, c) => c + 1); + } + + private void IncrementEnumerations(string path) + { + Enumerations.AddOrUpdate(path, p => 1, (p, c) => c + 1); + } + } + internal class LoggingFileSystem : MSBuildFileSystemBase { private int _fileSystemCalls; From 90a6bde08e1bf31dc66186642e8fb7532996b4ec Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Thu, 19 Aug 2021 13:43:25 +0200 Subject: [PATCH 15/23] Tweaks and bug fixes --- src/Build/Definition/Project.cs | 4 ++-- src/Build/Evaluation/Context/EvaluationContext.cs | 13 ++++++++++--- src/Build/Evaluation/Evaluator.cs | 8 ++++---- src/Build/Evaluation/Expander.cs | 2 +- src/Build/Evaluation/IEvaluatorData.cs | 4 ++-- .../Evaluation/LazyItemEvaluator.EvaluatorData.cs | 6 +++--- .../LazyItemEvaluator.LazyItemOperation.cs | 2 +- src/Build/Evaluation/LazyItemEvaluator.cs | 12 ++++++------ .../PropertyTrackingEvaluatorDataWrapper.cs | 4 ++-- src/Build/Instance/ProjectInstance.cs | 2 +- 10 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/Build/Definition/Project.cs b/src/Build/Definition/Project.cs index cbd3232a1be..8ba6bbbfc8b 100644 --- a/src/Build/Definition/Project.cs +++ b/src/Build/Definition/Project.cs @@ -4192,7 +4192,7 @@ internal Data(Project project, PropertyDictionary globa /// /// Prepares the data object for evaluation. /// - public void InitializeForEvaluation(IToolsetProvider toolsetProvider, IFileSystem fileSystem) + public void InitializeForEvaluation(IToolsetProvider toolsetProvider, EvaluationContext evaluationContext) { DefaultTargets = null; Properties = new PropertyDictionary(); @@ -4200,7 +4200,7 @@ public void InitializeForEvaluation(IToolsetProvider toolsetProvider, IFileSyste Items = new ItemDictionary(); ItemsIgnoringCondition = new ItemDictionary(); ItemsByEvaluatedIncludeCache = new MultiDictionary(StringComparer.OrdinalIgnoreCase); - Expander = new Expander(Properties, Items, fileSystem); + Expander = new Expander(Properties, Items, evaluationContext); ItemDefinitions = new RetrievableEntryHashSet(MSBuildNameIgnoreCaseComparer.Default); Targets = new RetrievableEntryHashSet(StringComparer.OrdinalIgnoreCase); ImportClosure = new List(); diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index d0a12e16409..5bc1e2ec3df 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -48,7 +48,7 @@ public enum SharingPolicy /// /// Key to file entry list. Example usages: cache glob expansion and intermediary directory expansions during glob expansion. /// - internal ConcurrentDictionary> FileEntryExpansionCache { get; } + private ConcurrentDictionary> FileEntryExpansionCache { get; } private EvaluationContext(SharingPolicy policy, IFileSystem fileSystem, ISdkResolverService sdkResolverService = null, ConcurrentDictionary> fileEntryExpansionCache = null) @@ -126,10 +126,17 @@ internal EvaluationContext ContextForNewProject() } } + /// + /// Creates a copy of this with a given swapped in. + /// + /// The file system to use by the new evaluation context. + /// The new evaluation context. internal EvaluationContext ContextWithFileSystem(IFileSystem fileSystem) { - var newContext = new EvaluationContext(this.Policy, fileSystem, this.SdkResolverService, this.FileEntryExpansionCache); - newContext._used = 1; + var newContext = new EvaluationContext(this.Policy, fileSystem, this.SdkResolverService, this.FileEntryExpansionCache) + { + _used = 1 + }; return newContext; } } diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index d35d10df3ae..db93150bdf1 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -19,6 +19,7 @@ using Microsoft.Build.Eventing; using Microsoft.Build.Execution; using Microsoft.Build.Experimental.ProjectCache; +using Microsoft.Build.FileSystem; using Microsoft.Build.Framework; using Microsoft.Build.Framework.Profiler; using Microsoft.Build.Internal; @@ -32,7 +33,6 @@ using EngineFileUtilities = Microsoft.Build.Internal.EngineFileUtilities; using ReservedPropertyNames = Microsoft.Build.Internal.ReservedPropertyNames; using SdkReferencePropertyExpansionMode = Microsoft.Build.Utilities.EscapeHatches.SdkReferencePropertyExpansionMode; -using Microsoft.Build.FileSystem; namespace Microsoft.Build.Evaluation { @@ -223,7 +223,7 @@ private Evaluator( data = new PropertyTrackingEvaluatorDataWrapper(data, _evaluationLoggingContext, Traits.Instance.LogPropertyTracking); } - // If the host wishes to provide a directory cache for this evaluation, wrap the EvaluationContext to use the right file system. + // If the host wishes to provide a directory cache for this evaluation, create a new EvaluationContext with the right file system. _evaluationContext = evaluationContext; IDirectoryCache directoryCache = project?.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId); if (directoryCache is not null) @@ -233,7 +233,7 @@ private Evaluator( } // Create containers for the evaluation results - data.InitializeForEvaluation(toolsetProvider, _evaluationContext.FileSystem); + data.InitializeForEvaluation(toolsetProvider, _evaluationContext); _expander = new Expander(data, data, _evaluationContext); @@ -369,7 +369,7 @@ internal static List CreateItemsFromInclude(string rootDirectory, ProjectItem else { // The expression is not of the form "@(X)". Treat as string - string[] includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(rootDirectory, includeSplitEscaped, excludeSpecsEscaped: null, forceEvaluate: false, expander.EvaluationContext.FileMatcher); + string[] includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped(rootDirectory, includeSplitEscaped, excludeSpecsEscaped: null, forceEvaluate: false, fileMatcher: expander.EvaluationContext?.FileMatcher); if (includeSplitFilesEscaped.Length > 0) { diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index 569b0555c25..1fe132a8d21 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -13,6 +13,7 @@ using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Microsoft.Build.Collections; +using Microsoft.Build.Evaluation.Context; using Microsoft.Build.Execution; using Microsoft.Build.Internal; using Microsoft.Build.Shared; @@ -25,7 +26,6 @@ using TaskItemFactory = Microsoft.Build.Execution.ProjectItemInstance.TaskItem.TaskItemFactory; using Microsoft.NET.StringTools; -using Microsoft.Build.Evaluation.Context; namespace Microsoft.Build.Evaluation { diff --git a/src/Build/Evaluation/IEvaluatorData.cs b/src/Build/Evaluation/IEvaluatorData.cs index af0265b01d3..7e2c9c5c85c 100644 --- a/src/Build/Evaluation/IEvaluatorData.cs +++ b/src/Build/Evaluation/IEvaluatorData.cs @@ -8,7 +8,7 @@ using Microsoft.Build.Collections; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.SdkResolution; -using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Evaluation.Context; namespace Microsoft.Build.Evaluation { @@ -210,7 +210,7 @@ List EvaluatedItemElements /// /// Prepares the data block for a new evaluation pass /// - void InitializeForEvaluation(IToolsetProvider toolsetProvider, IFileSystem fileSystem); + void InitializeForEvaluation(IToolsetProvider toolsetProvider, EvaluationContext evaluationContext); /// /// Indicates to the data block that evaluation has completed, diff --git a/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs b/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs index 4c88a6d976f..fc3996350a3 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs @@ -8,7 +8,7 @@ using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.Construction; using Microsoft.Build.Execution; -using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Evaluation.Context; namespace Microsoft.Build.Evaluation { @@ -285,9 +285,9 @@ public ProjectTargetInstance GetTarget(string targetName) return _wrappedData.GetTarget(targetName); } - public void InitializeForEvaluation(IToolsetProvider toolsetProvider, IFileSystem fileSystem) + public void InitializeForEvaluation(IToolsetProvider toolsetProvider, EvaluationContext evaluationContext) { - _wrappedData.InitializeForEvaluation(toolsetProvider, fileSystem); + _wrappedData.InitializeForEvaluation(toolsetProvider, evaluationContext); } public void RecordImport(ProjectImportElement importElement, ProjectRootElement import, int versionEvaluated, SdkResult sdkResult) diff --git a/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs b/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs index bdafd6087c7..20706932be6 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs @@ -44,7 +44,7 @@ protected LazyItemOperation(OperationBuilder builder, LazyItemEvaluator GetReferencedItems(itemType, ImmutableHashSet.Empty)); _itemFactory = new ItemFactoryWrapper(_itemElement, _lazyEvaluator._itemFactory); - _expander = new Expander(_evaluatorData, _evaluatorData, _lazyEvaluator.FileSystem); + _expander = new Expander(_evaluatorData, _evaluatorData, _lazyEvaluator.EvaluationContext); _itemSpec.Expander = _expander; } diff --git a/src/Build/Evaluation/LazyItemEvaluator.cs b/src/Build/Evaluation/LazyItemEvaluator.cs index 53a92503e47..f5d52eed4ee 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.cs @@ -39,22 +39,22 @@ internal partial class LazyItemEvaluator new Dictionary() : new Dictionary(StringComparer.OrdinalIgnoreCase); - protected IFileSystem FileSystem { get; } + protected EvaluationContext EvaluationContext { get; } - protected FileMatcher FileMatcher { get; } + protected IFileSystem FileSystem => EvaluationContext.FileSystem; + protected FileMatcher FileMatcher => EvaluationContext.FileMatcher; public LazyItemEvaluator(IEvaluatorData data, IItemFactory itemFactory, LoggingContext loggingContext, EvaluationProfiler evaluationProfiler, EvaluationContext evaluationContext) { _outerEvaluatorData = data; - _outerExpander = new Expander(_outerEvaluatorData, _outerEvaluatorData, evaluationContext.FileSystem); + _outerExpander = new Expander(_outerEvaluatorData, _outerEvaluatorData, evaluationContext); _evaluatorData = new EvaluatorData(_outerEvaluatorData, itemType => GetItems(itemType)); - _expander = new Expander(_evaluatorData, _evaluatorData, evaluationContext.FileSystem); + _expander = new Expander(_evaluatorData, _evaluatorData, evaluationContext); _itemFactory = itemFactory; _loggingContext = loggingContext; _evaluationProfiler = evaluationProfiler; - FileSystem = evaluationContext.FileSystem; - FileMatcher = evaluationContext.FileMatcher; + EvaluationContext = evaluationContext; } private ImmutableList GetItems(string itemType) diff --git a/src/Build/Evaluation/PropertyTrackingEvaluatorDataWrapper.cs b/src/Build/Evaluation/PropertyTrackingEvaluatorDataWrapper.cs index 6e3dc3a3ec7..54effc239c5 100644 --- a/src/Build/Evaluation/PropertyTrackingEvaluatorDataWrapper.cs +++ b/src/Build/Evaluation/PropertyTrackingEvaluatorDataWrapper.cs @@ -5,10 +5,10 @@ using Microsoft.Build.Collections; using Microsoft.Build.Construction; using Microsoft.Build.Execution; -using Microsoft.Build.Shared.FileSystem; using System; using System.Collections.Generic; using Microsoft.Build.BackEnd.Components.Logging; +using Microsoft.Build.Evaluation.Context; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using SdkResult = Microsoft.Build.BackEnd.SdkResolution.SdkResult; @@ -135,7 +135,7 @@ public P SetProperty(ProjectPropertyElement propertyElement, string evaluatedVal public ItemDictionary Items => _wrapped.Items; public List EvaluatedItemElements => _wrapped.EvaluatedItemElements; public PropertyDictionary EnvironmentVariablePropertiesDictionary => _wrapped.EnvironmentVariablePropertiesDictionary; - public void InitializeForEvaluation(IToolsetProvider toolsetProvider, IFileSystem fileSystem) => _wrapped.InitializeForEvaluation(toolsetProvider, fileSystem); + public void InitializeForEvaluation(IToolsetProvider toolsetProvider, EvaluationContext evaluationContext) => _wrapped.InitializeForEvaluation(toolsetProvider, evaluationContext); public void FinishEvaluation() => _wrapped.FinishEvaluation(); public void AddItem(I item) => _wrapped.AddItem(item); public void AddItemIgnoringCondition(I item) => _wrapped.AddItemIgnoringCondition(item); diff --git a/src/Build/Instance/ProjectInstance.cs b/src/Build/Instance/ProjectInstance.cs index 3a8eba5b4a5..502370f337a 100644 --- a/src/Build/Instance/ProjectInstance.cs +++ b/src/Build/Instance/ProjectInstance.cs @@ -1347,7 +1347,7 @@ ICollection IItemProvider.GetItems(str /// Only called during evaluation, so does not check for immutability. /// void IEvaluatorData. - InitializeForEvaluation(IToolsetProvider toolsetProvider, IFileSystem fileSystem) + InitializeForEvaluation(IToolsetProvider toolsetProvider, EvaluationContext evaluationContext) { // All been done in the constructor. We don't allow re-evaluation of project instances. } From 73d7636a5cb09eb114cac7398e15e05f9d0e4a73 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Thu, 19 Aug 2021 22:43:22 +0200 Subject: [PATCH 16/23] More internal IFileSystem deprecation and tweaks --- .../BackEnd/TaskRegistry_Tests.cs | 2 +- .../Evaluation/Evaluator_Tests.cs | 2 +- .../Evaluation/Expander_Tests.cs | 230 +++++++++--------- .../Evaluation/ItemSpec_Tests.cs | 2 +- .../ExpressionTreeExpression_Tests.cs | 2 +- src/Build.UnitTests/ExpressionTree_Tests.cs | 28 +-- .../Components/RequestBuilder/ItemBucket.cs | 2 +- .../Components/RequestBuilder/TargetEntry.cs | 2 +- src/Build/Definition/ProjectItem.cs | 2 +- src/Build/Definition/Toolset.cs | 2 +- src/Build/Definition/ToolsetReader.cs | 4 +- .../Evaluation/Context/EvaluationContext.cs | 1 - src/Build/Evaluation/Expander.cs | 12 +- .../LazyItemEvaluator.EvaluatorData.cs | 2 +- src/Build/Evaluation/LazyItemEvaluator.cs | 2 +- src/Build/Instance/ProjectInstance.cs | 6 +- src/Build/Instance/ProjectItemInstance.cs | 2 +- src/Shared/FileMatcher.cs | 10 +- 18 files changed, 156 insertions(+), 157 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs b/src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs index ecdfc421d3c..16bcba2d9d4 100644 --- a/src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs @@ -2255,7 +2255,7 @@ internal static Expander GetExpand secondaryItemsByName.ImportItems(thirdItemGroup); secondaryItemsByName.ImportItems(trueItemGroup); - Expander expander = new Expander(pg, secondaryItemsByName, FileSystems.Default); + Expander expander = new Expander(pg, secondaryItemsByName); return expander; } diff --git a/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs b/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs index 8ac6ba52f14..1e23a6c7bc5 100644 --- a/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs @@ -4298,7 +4298,7 @@ public void VerifyDTDProcessingIsDisabled2() public void VerifyConditionEvaluatorResetStateOnFailure() { PropertyDictionary propertyBag = new PropertyDictionary(); - Expander expander = new Expander(propertyBag, FileSystems.Default); + Expander expander = new Expander(propertyBag); string condition = " '$(TargetOSFamily)' >= '3' "; // Give an incorrect value for the property "TargetOSFamily", and then the evaluation should throw an exception. diff --git a/src/Build.UnitTests/Evaluation/Expander_Tests.cs b/src/Build.UnitTests/Evaluation/Expander_Tests.cs index b58a72d0843..8b2fcb45c18 100644 --- a/src/Build.UnitTests/Evaluation/Expander_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Expander_Tests.cs @@ -43,7 +43,7 @@ public class Expander_Tests public void ExpandAllIntoTaskItems0() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("", ExpanderOptions.ExpandProperties, null); @@ -54,7 +54,7 @@ public void ExpandAllIntoTaskItems0() public void ExpandAllIntoTaskItems1() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("foo", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -65,7 +65,7 @@ public void ExpandAllIntoTaskItems1() public void ExpandAllIntoTaskItems2() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("foo;bar", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -92,7 +92,7 @@ public void ExpandAllIntoTaskItems3() itemsByType.ImportItems(ig); itemsByType.ImportItems(ig2); - Expander expander = new Expander(pg, itemsByType, FileSystems.Default); + Expander expander = new Expander(pg, itemsByType); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("foo;bar;@(compile);@(resource)", ExpanderOptions.ExpandPropertiesAndItems, MockElementLocation.Instance); @@ -113,7 +113,7 @@ public void ExpandAllIntoTaskItems4() pg.Set(ProjectPropertyInstance.Create("b", "bbb")); pg.Set(ProjectPropertyInstance.Create("c", "cc;dd")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("foo$(a);$(b);$(c)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -137,7 +137,7 @@ public void ExpandPropertiesIntoProjectPropertyInstances() pg.Set(ProjectPropertyInstance.Create("b", "bbb")); pg.Set(ProjectPropertyInstance.Create("c", "cc;dd")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); ProjectItemInstanceFactory itemFactory = new ProjectItemInstanceFactory(project, "i"); IList itemsOut = expander.ExpandIntoItemsLeaveEscaped("foo$(a);$(b);$(c);$(d", itemFactory, ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -154,7 +154,7 @@ public void ExpandEmptyPropertyExpressionToEmpty() ProjectHelpers.CreateEmptyProjectInstance(); PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$()", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); Assert.Equal(String.Empty, result); @@ -778,7 +778,7 @@ private Expander CreateItemFunctio itemMetadataTable["Language"] = "english"; IMetadataTable itemMetadata = new StringMetadataTable(itemMetadataTable); - Expander expander = new Expander(pg, ig, itemMetadata, FileSystems.Default); + Expander expander = new Expander(pg, ig, itemMetadata); return expander; } @@ -800,7 +800,7 @@ private Expander CreateExpander() ig.Add(i0); ig.Add(i1); - Expander expander = new Expander(pg, ig, FileSystems.Default); + Expander expander = new Expander(pg, ig); return expander; } @@ -1344,7 +1344,7 @@ public void ExpandAllIntoTaskItemsComplex() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); + Expander expander = new Expander(lookup, lookup, itemMetadata); IList taskItems = expander.ExpandIntoTaskItemsLeaveEscaped( "@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; " + @@ -1376,7 +1376,7 @@ public void ExpandAllIntoStringComplexPiecemeal() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); + Expander expander = new Expander(lookup, lookup, itemMetadata); string stringToExpand = "@(Resource->'%(Filename)') ;"; Assert.Equal( @@ -1429,7 +1429,7 @@ public void ExpandAllIntoStringEmpty() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); + Expander expander = new Expander(lookup, lookup, itemMetadata); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); xmlattribute.Value = "@(IntermediateAssembly->'')"; @@ -1455,7 +1455,7 @@ public void ExpandAllIntoStringComplex() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); + Expander expander = new Expander(lookup, lookup, itemMetadata); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); xmlattribute.Value = "@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; " + @@ -1477,7 +1477,7 @@ public void ExpandAllIntoStringLeaveEscapedComplex() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); + Expander expander = new Expander(lookup, lookup, itemMetadata); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); xmlattribute.Value = "@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; " + @@ -1526,7 +1526,7 @@ public void ExpandAllIntoStringTruncated() }); lookup.PopulateWithItems("ManyItems", itemGroup); - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); + Expander expander = new Expander(lookup, lookup, itemMetadata); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); xmlattribute.Value = "'%(ManySpacesMetadata)' != '' and '$(ManySpacesProperty)' != '' and '@(ManySpacesItem)' != '' and '@(Exactly1024)' != '' and '@(ManyItems)' != '' and '@(ManyItems->'%(Foo)')' != '' and '@(ManyItems->'%(Nonexistent)')' != ''"; @@ -1558,7 +1558,7 @@ public void ExpandAllIntoStringExpectIdenticalReference() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); + Expander expander = new Expander(lookup, lookup, itemMetadata); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); @@ -1591,7 +1591,7 @@ public void ExpandAllIntoStringExpanderOptions() string value = @"@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; $(OutputPath) ; $(TargetPath) ; %(Language)_%(Culture)"; - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); + Expander expander = new Expander(lookup, lookup, itemMetadata); Assert.Equal(@"@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; ; %(NonExistent) ; \jk ; l\mno%3bpqr\stu ; @(IntermediateAssembly->'%(RelativeDir)') ; %(Language)_%(Culture)", expander.ExpandIntoStringAndUnescape(value, ExpanderOptions.ExpandProperties, MockElementLocation.Instance)); @@ -1612,7 +1612,7 @@ public void ExpandAllIntoStringListLeaveEscapedComplex() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); + Expander expander = new Expander(lookup, lookup, itemMetadata); string value = "@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; " + "$(OutputPath) ; $(TargetPath) ; %(Language)_%(Culture)"; @@ -1654,7 +1654,7 @@ public void RegistryPropertyInvalidPrefixSpecialCase() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\VSTSDB@VSTSDBDirectory)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1670,7 +1670,7 @@ public void Regress692569() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$(Solutions.VSVersion)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1688,7 +1688,7 @@ public void RegistryPropertyInvalidPrefixError() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped(@"$(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\VSTSDB@XXXXDBDirectory)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -1706,7 +1706,7 @@ public void RegistryPropertyInvalidPrefixError2() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped(@"$(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\VSTSDB@VSTSDBDirectoryX)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -1720,7 +1720,7 @@ public void RegistryPropertyString() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", "String", RegistryValueKind.String); @@ -1741,7 +1741,7 @@ public void RegistryPropertyBinary() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); UTF8Encoding enc = new UTF8Encoding(); @@ -1765,7 +1765,7 @@ public void RegistryPropertyDWord() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", 123456, RegistryValueKind.DWord); @@ -1787,7 +1787,7 @@ public void RegistryPropertyExpandString() string envVar = NativeMethodsShared.IsWindows ? "TEMP" : "USER"; PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -1808,7 +1808,7 @@ public void RegistryPropertyQWord() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", (long)123456789123456789, RegistryValueKind.QWord); @@ -1829,7 +1829,7 @@ public void RegistryPropertyMultiString() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", new string[] { "A", "B", "C", "D" }, RegistryValueKind.MultiString); @@ -1926,7 +1926,7 @@ public void PropertyFunctionNullArgument() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$([System.Convert]::ChangeType('null',$(SomeStuff.GetTypeCode())))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1941,7 +1941,7 @@ public void PropertyFunctionNullReturn() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); // The null-returning function is the only thing in the expression. string result = expander.ExpandIntoStringLeaveEscaped("$([System.Environment]::GetEnvironmentVariable(`_NonExistentVar`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1961,7 +1961,7 @@ public void PropertyFunctionNoArguments() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToUpperInvariant())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1977,7 +1977,7 @@ public void PropertyFunctionNoArgumentsTrim() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("FileName", " foo.ext ")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(FileName.Trim())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1993,7 +1993,7 @@ public void PropertyFunctionPropertyGet() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.Length)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2009,7 +2009,7 @@ public void PropertyFunctionPropertyManualGet() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.get_Length())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2025,7 +2025,7 @@ public void PropertyFunctionPropertyNoArgumentsConcat() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToLowerInvariant())_goop", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2041,7 +2041,7 @@ public void PropertyFunctionPropertyWithArgument() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString(13))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2057,7 +2057,7 @@ public void PropertyFunctionPropertyWithArgumentWithSpaces() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString(8))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2074,7 +2074,7 @@ public void PropertyFunctionPropertyPathRootSubtraction() pg.Set(ProjectPropertyInstance.Create("RootPath", Path.Combine(s_rootPathPrefix, "this", "is", "the", "root"))); pg.Set(ProjectPropertyInstance.Create("MyPath", Path.Combine(s_rootPathPrefix, "this", "is", "the", "root", "my", "project", "is", "here.proj"))); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(MyPath.SubString($(RootPath.Length)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2091,7 +2091,7 @@ public void PropertyFunctionPropertyWithArgumentExpandedProperty() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString(1$(Value)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2108,7 +2108,7 @@ public void PropertyFunctionPropertyWithArgumentBooleanReturn() pg.Set(ProjectPropertyInstance.Create("PathRoot", Path.Combine(s_rootPathPrefix, "goo"))); pg.Set(ProjectPropertyInstance.Create("PathRoot2", Path.Combine(s_rootPathPrefix, "goop") + Path.DirectorySeparatorChar)); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$(PathRoot2.Endswith(" + Path.DirectorySeparatorChar + "))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); Assert.Equal("True", result); @@ -2126,7 +2126,7 @@ public void PropertyFunctionPropertyWithArgumentNestedAndChainedFunction() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString(1$(Value)).ToLowerInvariant().SubString($(Value)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2144,7 +2144,7 @@ public void PropertyFunctionPropertyWithArgumentChained() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToUpperInvariant().ToLowerInvariant())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); Assert.Equal("this is some stuff", result); @@ -2160,7 +2160,7 @@ public void PropertyFunctionPropertyWithArgumentNested() pg.Set(ProjectPropertyInstance.Create("Value", "12345")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "1234567890")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString($(Value.get_Length())))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2175,7 +2175,7 @@ public void PropertyFunctionGenericListReturn() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$([MSBuild]::__GetListTest())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2191,7 +2191,7 @@ public void PropertyFunctionArrayReturn() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("List", "A-B-C-D")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(List.Split(-))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2209,7 +2209,7 @@ public void PropertyFunctionDictionaryReturn() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$([System.Environment]::GetEnvironmentVariables())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ToUpperInvariant(); string expected = ("OS=" + Environment.GetEnvironmentVariable("OS")).ToUpperInvariant(); @@ -2227,7 +2227,7 @@ public void PropertyFunctionArrayReturnManualSplitter() pg.Set(ProjectPropertyInstance.Create("List", "A-B-C-D")); pg.Set(ProjectPropertyInstance.Create("Splitter", "-")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(List.Split($(Splitter.ToCharArray())))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2244,7 +2244,7 @@ public void PropertyFunctionInCondition() pg.Set(ProjectPropertyInstance.Create("PathRoot", Path.Combine(s_rootPathPrefix, "goo"))); pg.Set(ProjectPropertyInstance.Create("PathRoot2", Path.Combine(s_rootPathPrefix, "goop") + Path.DirectorySeparatorChar)); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); Assert.True( ConditionEvaluator.EvaluateCondition( @@ -2282,7 +2282,7 @@ public void PropertyFunctionInvalid1() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff($(Value)))]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2301,7 +2301,7 @@ public void PropertyFunctionInvalid2() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff.Lgg)]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2319,7 +2319,7 @@ public void PropertyFunctionInvalid3() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToUpperInvariant().Foo)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2337,7 +2337,7 @@ public void PropertyFunctionInvalid4() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff($(System.DateTime.Now)))]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2355,7 +2355,7 @@ public void PropertyFunctionInvalid5() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToLowerInvariant()_goop)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2372,7 +2372,7 @@ public void PropertyFunctionInvalid6() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff.Substring(HELLO!))]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2389,7 +2389,7 @@ public void PropertyFunctionInvalid7() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff.Substring(-10))]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2405,7 +2405,7 @@ public void PropertyFunctionInvalid8() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped("$(([System.DateTime]::Now).ToString(\"MM.dd.yyyy\"))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2419,7 +2419,7 @@ public void PropertyFunctionInvalidNoMetadataFunctions() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("[%(LowerLetterList.Identity.ToUpper())]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2435,7 +2435,7 @@ public void PropertyFunctionNoCollisionsOnType() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("System", "The System Namespace")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$(System)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2454,7 +2454,7 @@ public void PropertyFunctionStaticMethodMakeRelative() pg.Set(ProjectPropertyInstance.Create("ParentPath", Path.Combine(s_rootPathPrefix, "abc", "def"))); pg.Set(ProjectPropertyInstance.Create("FilePath", Path.Combine(s_rootPathPrefix, "abc", "def", "foo.cpp"))); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::MakeRelative($(ParentPath), `$(FilePath)`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2471,7 +2471,7 @@ public void PropertyFunctionStaticMethod1() pg.Set(ProjectPropertyInstance.Create("Drive", s_rootPathPrefix)); pg.Set(ProjectPropertyInstance.Create("File", Path.Combine("foo", "file.txt"))); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine($(Drive), `$(File)`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2487,7 +2487,7 @@ public void PropertyFunctionConstructor1() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("ver1", @"1.2.3.4")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); object result = expander.ExpandPropertiesLeaveTypedAndEscaped(@"$([System.Version]::new($(ver1)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2511,7 +2511,7 @@ public void PropertyFunctionConstructor2() pg.Set(ProjectPropertyInstance.Create("ver1", @"1.2.3.4")); pg.Set(ProjectPropertyInstance.Create("ver2", @"2.2.3.4")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.Version]::new($(ver1)).CompareTo($([System.Version]::new($(ver2)))))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2531,7 +2531,7 @@ public void PropertyStaticFunctionAllEnabled() PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); try { @@ -2554,7 +2554,7 @@ public void PropertyStaticFunctionLocatedFromAssemblyWithNamespaceName() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string env = Environment.GetEnvironmentVariable("MSBUILDENABLEALLPROPERTYFUNCTIONS"); @@ -2583,7 +2583,7 @@ public void PropertyStaticFunctionUsingNamespaceNotFound() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string env = Environment.GetEnvironmentVariable("MSBUILDENABLEALLPROPERTYFUNCTIONS"); @@ -2617,7 +2617,7 @@ public void PropertyFunctionStaticMethodQuoted1() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", Path.Combine("foo", "file.txt"))); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + s_rootPathPrefix + "`, `$(File)`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2633,7 +2633,7 @@ public void PropertyFunctionStaticMethodQuoted1Spaces() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", "foo goo" + Path.DirectorySeparatorChar + "file.txt")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + Path.Combine(s_rootPathPrefix, "foo goo") + "`, `$(File)`))", @@ -2651,7 +2651,7 @@ public void PropertyFunctionStaticMethodQuoted1Spaces2() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", Path.Combine("foo bar", "baz.txt"))); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + Path.Combine(s_rootPathPrefix, "foo baz") + @"`, `$(File)`))", @@ -2669,7 +2669,7 @@ public void PropertyFunctionStaticMethodQuoted1Spaces3() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", Path.Combine("foo bar", "baz.txt"))); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + Path.Combine(s_rootPathPrefix, "foo baz") + @" `, `$(File)`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2685,7 +2685,7 @@ public void PropertyFunctionStaticMethodQuoted2() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string dateTime = "'" + _dateToParse + "'"; string result = expander.ExpandIntoStringLeaveEscaped("$([System.DateTime]::Parse(" + dateTime + ").ToString(\"yyyy/MM/dd HH:mm:ss\"))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2701,7 +2701,7 @@ public void PropertyFunctionStaticMethodQuoted3() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string dateTime = "'" + _dateToParse + "'"; string result = expander.ExpandIntoStringLeaveEscaped("$([System.DateTime]::Parse(" + dateTime + ").ToString(\"MM.dd.yyyy\"))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2716,7 +2716,7 @@ public void PropertyFunctionStaticMethodQuoted4() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$([System.DateTime]::Now.ToString(\"MM.dd.yyyy\"))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2732,7 +2732,7 @@ public void PropertyFunctionStaticMethodNested() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", "foo" + Path.DirectorySeparatorChar + "file.txt")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + s_rootPathPrefix + @@ -2750,7 +2750,7 @@ public void PropertyFunctionStaticMethodRegex1() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", "foo" + Path.DirectorySeparatorChar + "file.txt")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); // Support enum combines as Enum.Parse expects them string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.Text.RegularExpressions.Regex]::IsMatch(`-42`, `^-?\d+(\.\d{2})?$`, `RegexOptions.IgnoreCase,RegexOptions.Singleline`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2775,7 +2775,7 @@ public void PropertyFunctionStaticMethodChained() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string dateTime = "'" + _dateToParse + "'"; string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.DateTime]::Parse(" + dateTime + ").ToString(`yyyy/MM/dd HH:mm:ss`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2790,7 +2790,7 @@ public void PropertyFunctionGetFolderPath() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.Environment]::GetFolderPath(SpecialFolder.System))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2824,7 +2824,7 @@ public void PropertyFunctionRuntimeInformation(string propertyFunction, string e .Replace("$$architecture$$", architecture); var pg = new PropertyDictionary(); - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); string currentPlatformString = Helpers.GetOSPlatformAsString(); @@ -2854,7 +2854,7 @@ public void StringIndexOfTests(string propertyName, string properyValue, string var pg = new PropertyDictionary {[propertyName] = ProjectPropertyInstance.Create(propertyName, properyValue)}; - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped(propertyFunction, ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ShouldBe(expectedExpansion); } @@ -2863,7 +2863,7 @@ public void StringIndexOfTests(string propertyName, string properyValue, string public void IsOsPlatformShouldBeCaseInsensitiveToParameter() { var pg = new PropertyDictionary(); - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); var osPlatformLowerCase = Helpers.GetOSPlatformAsString().ToLower(); @@ -2879,7 +2879,7 @@ public void IsOsPlatformShouldBeCaseInsensitiveToParameter() public void PropertyFunctionVersionComparisonsFailsWithInvalidArguments(string badVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); string expectedMessage = ResourceUtilities.GetResourceString("InvalidVersionFormat"); AssertThrows(expander, $"$([MSBuild]::VersionGreaterThan('{badVersion}', '1.0.0'))", expectedMessage); @@ -2912,7 +2912,7 @@ public void PropertyFunctionVersionComparisonsFailsWithInvalidArguments(string b public void PropertyFunctionVersionComparisons(string a, string b, int expectedSign) { var pg = new PropertyDictionary(); - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); AssertSuccess(expander, expectedSign > 0, $"$([MSBuild]::VersionGreaterThan('{a}', '{b}'))"); AssertSuccess(expander, expectedSign >= 0, $"$([MSBuild]::VersionGreaterThanOrEquals('{a}', '{b}'))"); @@ -2931,7 +2931,7 @@ public void PropertyFunctionVersionComparisons(string a, string b, int expectedS public void PropertyFunctionTargetFrameworkParsing(string tfm, string expectedIdentifier, string expectedVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); AssertSuccess(expander, expectedIdentifier, $"$([MSBuild]::GetTargetFrameworkIdentifier('{tfm}'))"); AssertSuccess(expander, expectedVersion, $"$([MSBuild]::GetTargetFrameworkVersion('{tfm}'))"); @@ -2945,7 +2945,7 @@ public void PropertyFunctionTargetFrameworkParsing(string tfm, string expectedId public void PropertyFunctionTargetFrameworkVersionMultipartParsing(string tfm, int versionPartCount, string expectedVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); AssertSuccess(expander, expectedVersion, $"$([MSBuild]::GetTargetFrameworkVersion('{tfm}', {versionPartCount}))"); } @@ -2958,7 +2958,7 @@ public void PropertyFunctionTargetFrameworkVersionMultipartParsing(string tfm, i public void PropertyFunctionTargetPlatformVersionMultipartParsing(string tfm, int versionPartCount, string expectedVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); AssertSuccess(expander, expectedVersion, $"$([MSBuild]::GetTargetPlatformVersion('{tfm}', {versionPartCount}))"); } @@ -2972,7 +2972,7 @@ public void PropertyFunctionTargetPlatformVersionMultipartParsing(string tfm, in public void PropertyFunctionTargetPlatformParsing(string tfm, string expectedIdentifier, string expectedVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); AssertSuccess(expander, expectedIdentifier, $"$([MSBuild]::GetTargetPlatformIdentifier('{tfm}'))"); AssertSuccess(expander, expectedVersion, $"$([MSBuild]::GetTargetPlatformVersion('{tfm}'))"); @@ -2994,7 +2994,7 @@ public void PropertyFunctionTargetPlatformParsing(string tfm, string expectedIde public void PropertyFunctionTargetFrameworkComparisons(string tfm1, string tfm2, bool expectedFrameworkCompatible) { var pg = new PropertyDictionary(); - var expander = new Expander(pg, FileSystems.Default); + var expander = new Expander(pg); AssertSuccess(expander, expectedFrameworkCompatible, $"$([MSBuild]::IsTargetFrameworkCompatible('{tfm1}', '{tfm2}'))"); } @@ -3028,7 +3028,7 @@ public void PropertyFunctionStaticMethodEnumArgument() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped("$([System.String]::Equals(`a`, `A`, StringComparison.OrdinalIgnoreCase))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); Assert.Equal(true.ToString(), result); @@ -3051,7 +3051,7 @@ public void PropertyFunctionStaticMethodDirectoryNameOfFileAbove() pg.Set(ProjectPropertyInstance.Create("StartingDirectory", directoryStart)); pg.Set(ProjectPropertyInstance.Create("FileToFind", tempFile)); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringAndUnescape(@"$([MSBuild]::GetDirectoryNameOfFileAbove($(StartingDirectory), $(FileToFind)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3085,7 +3085,7 @@ public void PropertyFunctionStaticMethodGetPathOfFileAbove() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("FileToFind", Path.GetFileName(fileToFind))); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringAndUnescape(@"$([MSBuild]::GetPathOfFileAbove($(FileToFind)))", ExpanderOptions.ExpandProperties, mockElementLocation); @@ -3128,7 +3128,7 @@ public void PropertyFunctionStaticMethodGetPathOfFileAboveFileNameOnly() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("FileWithPath", fileWithPath)); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::GetPathOfFileAbove($(FileWithPath)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); }); @@ -3144,7 +3144,7 @@ public void PropertyFunctionStaticMethodGetCultureInfo() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); #if FEATURE_CULTUREINFO_GETCULTURES string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.Globalization.CultureInfo]::GetCultureInfo(`en-US`).ToString())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3163,7 +3163,7 @@ public void PropertyFunctionStaticMethodArithmeticAddInt32() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::Add(40, 2))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3178,7 +3178,7 @@ public void PropertyFunctionStaticMethodArithmeticAddDouble() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::Add(39.9, 2.1))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3193,7 +3193,7 @@ public void PropertyFunctionValueOrDefault() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::ValueOrDefault('', '42'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3214,7 +3214,7 @@ public void PropertyFunctionValueOrDefaultFromEnvironment() pg["BonkersTargetsPath"] = ProjectPropertyInstance.Create("BonkersTargetsPath", "Bonkers"); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::ValueOrDefault('$(BonkersTargetsPath)', '42'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3237,7 +3237,7 @@ public void PropertyFunctionDoesTaskHostExist() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist('CurrentRuntime', 'CurrentArchitecture'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3254,7 +3254,7 @@ public void PropertyFunctionDoesTaskHostExist_Whitespace() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist(' CurrentRuntime ', 'CurrentArchitecture'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3270,7 +3270,7 @@ public void PropertyFunctionNormalizeDirectory() { ProjectPropertyInstance.Create("MyPath", "one"), ProjectPropertyInstance.Create("MySecondPath", "two"), - }), FileSystems.Default); + })); Assert.Equal( $"{Path.GetFullPath("one")}{Path.DirectorySeparatorChar}", @@ -3291,7 +3291,7 @@ public void PropertyFunctionDoesTaskHostExist_Error() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist('ASDF', 'CurrentArchitecture'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3314,7 +3314,7 @@ public void PropertyFunctionDoesTaskHostExist_Evaluated() pg["Runtime"] = ProjectPropertyInstance.Create("Runtime", "CurrentRuntime"); pg["Architecture"] = ProjectPropertyInstance.Create("Architecture", "CurrentArchitecture"); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist('$(Runtime)', '$(Architecture)'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3338,7 +3338,7 @@ public void PropertyFunctionDoesTaskHostExist_NonexistentTaskHost() PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist('CLR2', 'CurrentArchitecture'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3364,7 +3364,7 @@ public void PropertyFunctionStaticMethodFileAttributes() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string tempFile = FileUtilities.GetTemporaryFile(); try @@ -3390,7 +3390,7 @@ public void PropertyFunctionStaticMethodIntrinsicMaths() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::Add(39.9, 2.1))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3464,7 +3464,7 @@ public void PropertySimpleSpaced() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(@"$( SomeStuff )", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3481,7 +3481,7 @@ public void PropertyFunctionGetRegitryValue() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeProperty", "Value")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -3504,7 +3504,7 @@ public void PropertyFunctionGetRegitryValueDefault() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeProperty", "Value")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue(String.Empty, "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -3527,7 +3527,7 @@ public void PropertyFunctionGetRegistryValueFromView1() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeProperty", "Value")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue(String.Empty, "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -3550,7 +3550,7 @@ public void PropertyFunctionGetRegistryValueFromView2() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeProperty", "Value")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue(String.Empty, "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -3584,7 +3584,7 @@ public void PropertyFunctionConsumingItemMetadata() ItemDictionary itemsByType = new ItemDictionary(); itemsByType.ImportItems(ig); - Expander expander = new Expander(pg, itemsByType, itemMetadata, FileSystems.Default); + Expander expander = new Expander(pg, itemsByType, itemMetadata); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine($(SomePath),%(Compile.Identity)))", ExpanderOptions.ExpandAll, MockElementLocation.Instance); @@ -3615,7 +3615,7 @@ public void PropertyStringConstructorConsumingItemMetadata(string metadatumName, public void PropertyFunctionHashCodeSameOnlyIfStringSame() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string[] stringsToHash = { "cat1s", "cat1z", @@ -3677,7 +3677,7 @@ public void Medley() pg.Set(ProjectPropertyInstance.Create("input", @"EXPORT a")); pg.Set(ProjectPropertyInstance.Create("propertycontainingnullasastring", @"null")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); var validTests = new List { new string[] {"$(input.ToString()[1])", "X"}, @@ -3932,7 +3932,7 @@ public void PropertyFunctionEnsureTrailingSlash() pg.Set(ProjectPropertyInstance.Create("SomeProperty", path)); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); // Verify a constant expands properly string result = expander.ExpandIntoStringLeaveEscaped($"$([MSBuild]::EnsureTrailingSlash('{path}'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3958,7 +3958,7 @@ public void PropertyFunctionWithNewLines() pg.Set(ProjectPropertyInstance.Create("SomeProperty", "6C8546D5297C424F962201B0E0E9F142")); - Expander expander = new Expander(pg, FileSystems.Default); + Expander expander = new Expander(pg); string result = expander.ExpandIntoStringLeaveEscaped(propertyFunction, ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3998,7 +3998,7 @@ public void PropertyFunctionVersionParse() [Fact] public void PropertyFunctionGuidNewGuid() { - var expander = new Expander(new PropertyDictionary(), FileSystems.Default); + var expander = new Expander(new PropertyDictionary()); string result = expander.ExpandIntoStringLeaveEscaped("$([System.Guid]::NewGuid())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -4230,7 +4230,7 @@ private void TestPropertyFunction(string expression, string propertyName, string { var properties = new PropertyDictionary(); properties.Set(ProjectPropertyInstance.Create(propertyName, propertyValue)); - var expander = new Expander(properties, FileSystems.Default); + var expander = new Expander(properties); string result = expander.ExpandIntoStringLeaveEscaped(expression, ExpanderOptions.ExpandProperties, MockElementLocation.Instance); result.ShouldBe(expected); } diff --git a/src/Build.UnitTests/Evaluation/ItemSpec_Tests.cs b/src/Build.UnitTests/Evaluation/ItemSpec_Tests.cs index f0564b0dcba..74349ef360d 100644 --- a/src/Build.UnitTests/Evaluation/ItemSpec_Tests.cs +++ b/src/Build.UnitTests/Evaluation/ItemSpec_Tests.cs @@ -85,7 +85,7 @@ private ProjectInstanceExpander CreateExpander(Dictionary item { var itemDictionary = ToItemDictionary(items); - return new ProjectInstanceExpander(new PropertyDictionary(), itemDictionary, (IFileSystem) FileSystems.Default); + return new ProjectInstanceExpander(new PropertyDictionary(), itemDictionary); } private static ItemDictionary ToItemDictionary(Dictionary itemTypes) diff --git a/src/Build.UnitTests/ExpressionTreeExpression_Tests.cs b/src/Build.UnitTests/ExpressionTreeExpression_Tests.cs index 6c0dd4d9db9..ce400029e7c 100644 --- a/src/Build.UnitTests/ExpressionTreeExpression_Tests.cs +++ b/src/Build.UnitTests/ExpressionTreeExpression_Tests.cs @@ -396,7 +396,7 @@ public ExpressionTest(ITestOutputHelper output) metadataDictionary["Culture"] = "french"; StringMetadataTable itemMetadata = new StringMetadataTable(metadataDictionary); - _expander = new Expander(propertyBag, itemBag, itemMetadata, FileSystems.Default); + _expander = new Expander(propertyBag, itemBag, itemMetadata); foreach (string file in FilesWithExistenceChecks) { diff --git a/src/Build.UnitTests/ExpressionTree_Tests.cs b/src/Build.UnitTests/ExpressionTree_Tests.cs index 5124da86a6a..833f6f5b5ed 100644 --- a/src/Build.UnitTests/ExpressionTree_Tests.cs +++ b/src/Build.UnitTests/ExpressionTree_Tests.cs @@ -25,7 +25,7 @@ public class ExpressionTreeTest public void SimpleEvaluationTests() { Parser p = new Parser(); - Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); + Expander expander = new Expander(new PropertyDictionary()); AssertParseEvaluate(p, "true", expander, true); AssertParseEvaluate(p, "on", expander, true); @@ -41,7 +41,7 @@ public void SimpleEvaluationTests() public void EqualityTests() { Parser p = new Parser(); - Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); + Expander expander = new Expander(new PropertyDictionary()); AssertParseEvaluate(p, "true == on", expander, true); AssertParseEvaluate(p, "TrUe == On", expander, true); @@ -66,7 +66,7 @@ public void EqualityTests() public void RelationalTests() { Parser p = new Parser(); - Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); + Expander expander = new Expander(new PropertyDictionary()); AssertParseEvaluate(p, "1234 < 1235", expander, true); AssertParseEvaluate(p, "1234 <= 1235", expander, true); @@ -85,7 +85,7 @@ public void RelationalTests() public void AndandOrTests() { Parser p = new Parser(); - Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); + Expander expander = new Expander(new PropertyDictionary()); AssertParseEvaluate(p, "true == on and 1234 < 1235", expander, true); } @@ -97,7 +97,7 @@ public void FunctionTests() { Parser p = new Parser(); GenericExpressionNode tree; - Expander expander = new Expander(new PropertyDictionary(), new ItemDictionary(), FileSystems.Default); + Expander expander = new Expander(new PropertyDictionary(), new ItemDictionary()); expander.Metadata = new StringMetadataTable(null); bool value; @@ -149,7 +149,7 @@ public void PropertyTests() propertyBag.Set(ProjectPropertyInstance.Create("x86", "x86")); propertyBag.Set(ProjectPropertyInstance.Create("no", "no")); - Expander expander = new Expander(propertyBag, new ItemDictionary(), FileSystems.Default); + Expander expander = new Expander(propertyBag, new ItemDictionary()); AssertParseEvaluate(p, "$(foo)", expander, true); AssertParseEvaluate(p, "!$(foo)", expander, false); // Test properties with strings @@ -187,7 +187,7 @@ public void ItemListTests() itemBag.Add(new ProjectItemInstance(parentProject, "Compile", "baz.cs", parentProject.FullPath)); itemBag.Add(new ProjectItemInstance(parentProject, "Boolean", "true", parentProject.FullPath)); - Expander expander = new Expander(new PropertyDictionary(), itemBag, FileSystems.Default); + Expander expander = new Expander(new PropertyDictionary(), itemBag); AssertParseEvaluate(p, "@(Compile) == 'foo.cs;bar.cs;baz.cs'", expander, true); AssertParseEvaluate(p, "@(Compile,' ') == 'foo.cs bar.cs baz.cs'", expander, true); @@ -230,7 +230,7 @@ public void StringExpansionTests() propertyBag.Set(ProjectPropertyInstance.Create("AnotherTestQuote", "Here's Johnny!")); propertyBag.Set(ProjectPropertyInstance.Create("Atsign", "Test the @ replacement")); - Expander expander = new Expander(propertyBag, itemBag, FileSystems.Default); + Expander expander = new Expander(propertyBag, itemBag); AssertParseEvaluate(p, "'simplestring: true foo.cs;bar.cs;baz.cs' == '$(simple): $(foo) @(compile)'", expander, true); AssertParseEvaluate(p, "'$(c1) $(c2)' == 'Another (complex) one. Another (complex) one.'", expander, true); @@ -262,7 +262,7 @@ public void ComplexTests() propertyBag.Set(ProjectPropertyInstance.Create("c1", "Another (complex) one.")); propertyBag.Set(ProjectPropertyInstance.Create("c2", "Another (complex) one.")); - Expander expander = new Expander(propertyBag, itemBag, FileSystems.Default); + Expander expander = new Expander(propertyBag, itemBag); AssertParseEvaluate(p, "(($(foo) != 'two' and $(bar)) and 5 >= 1) or $(one) == 1", expander, true); AssertParseEvaluate(p, "(($(foo) != 'twoo' or !$(bar)) and 5 >= 1) or $(two) == 1", expander, true); @@ -283,7 +283,7 @@ public void InvalidItemInConditionEvaluation() PropertyDictionary propertyBag = new PropertyDictionary(); - Expander expander = new Expander(propertyBag, itemBag, FileSystems.Default); + Expander expander = new Expander(propertyBag, itemBag); AssertParseEvaluateThrow(p, "@(Compile) > 0", expander, null); } @@ -312,7 +312,7 @@ public void OldSyntaxTests() propertyBag.Set(ProjectPropertyInstance.Create("c1", "Another (complex) one.")); propertyBag.Set(ProjectPropertyInstance.Create("c2", "Another (complex) one.")); - Expander expander = new Expander(propertyBag, itemBag, FileSystems.Default); + Expander expander = new Expander(propertyBag, itemBag); AssertParseEvaluate(p, "(($(foo) != 'two' and $(bar)) and 5 >= 1) or $(one) == 1", expander, true); } @@ -329,7 +329,7 @@ public void ConditionedPropertyUpdateTests() itemBag.Add(new ProjectItemInstance(parentProject, "Compile", "bar.cs", parentProject.FullPath)); itemBag.Add(new ProjectItemInstance(parentProject, "Compile", "baz.cs", parentProject.FullPath)); - Expander expander = new Expander(new PropertyDictionary(), itemBag, FileSystems.Default); + Expander expander = new Expander(new PropertyDictionary(), itemBag); Dictionary> conditionedProperties = new Dictionary>(); ConditionEvaluator.IConditionEvaluationState state = new ConditionEvaluator.ConditionEvaluationState @@ -418,7 +418,7 @@ public void NotTests() propertyBag.Set(ProjectPropertyInstance.Create("foo", "4")); propertyBag.Set(ProjectPropertyInstance.Create("bar", "32")); - Expander expander = new Expander(propertyBag, new ItemDictionary(), FileSystems.Default); + Expander expander = new Expander(propertyBag, new ItemDictionary()); AssertParseEvaluate(p, "!true", expander, false); AssertParseEvaluate(p, "!(true)", expander, false); @@ -509,7 +509,7 @@ private void AssertParseEvaluateThrow(Parser p, string expression, Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); + Expander expander = new Expander(new PropertyDictionary()); AssertParseEvaluateThrow(p, "foo", expander); AssertParseEvaluateThrow(p, "0", expander); diff --git a/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs b/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs index 1779d6ba70f..faceb4c727b 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs @@ -96,7 +96,7 @@ int bucketSequenceNumber } _metadata = metadata; - _expander = new Expander(_lookup, _lookup, new StringMetadataTable(metadata), FileSystems.Default); + _expander = new Expander(_lookup, _lookup, new StringMetadataTable(metadata)); _bucketSequenceNumber = bucketSequenceNumber; } diff --git a/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs b/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs index d3a925b34a8..0c71bf57487 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs @@ -173,7 +173,7 @@ internal TargetEntry(BuildRequestEntry requestEntry, ITargetBuilderCallback targ _targetSpecification = targetSpecification; _parentTarget = parentTarget; _buildReason = buildReason; - _expander = new Expander(baseLookup, baseLookup, FileSystems.Default); + _expander = new Expander(baseLookup, baseLookup); _state = TargetEntryState.Dependencies; _baseLookup = baseLookup; _host = host; diff --git a/src/Build/Definition/ProjectItem.cs b/src/Build/Definition/ProjectItem.cs index 0b3c28e1562..bb1aa48eaa2 100644 --- a/src/Build/Definition/ProjectItem.cs +++ b/src/Build/Definition/ProjectItem.cs @@ -490,7 +490,7 @@ string IItem.GetMetadataValueEscaped(string name) if (metadatum != null && Expander.ExpressionMayContainExpandableExpressions(metadatum.EvaluatedValueEscaped)) { - Expander expander = new Expander(null, null, new BuiltInMetadataTable(this), FileSystems.Default); + Expander expander = new Expander(null, null, new BuiltInMetadataTable(this)); value = expander.ExpandIntoStringLeaveEscaped(metadatum.EvaluatedValueEscaped, ExpanderOptions.ExpandBuiltInMetadata, metadatum.Location); } diff --git a/src/Build/Definition/Toolset.cs b/src/Build/Definition/Toolset.cs index 77d32963bf6..ca57f96dd15 100644 --- a/src/Build/Definition/Toolset.cs +++ b/src/Build/Definition/Toolset.cs @@ -967,7 +967,7 @@ private void InitializeProperties(ILoggingService loggingServices, BuildEventCon if (_expander == null) { - _expander = new Expander(_propertyBag, FileSystems.Default); + _expander = new Expander(_propertyBag); } } catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) diff --git a/src/Build/Definition/ToolsetReader.cs b/src/Build/Definition/ToolsetReader.cs index 869b3318ea4..3b241b8b1a7 100644 --- a/src/Build/Definition/ToolsetReader.cs +++ b/src/Build/Definition/ToolsetReader.cs @@ -451,7 +451,7 @@ bool accumulateProperties IEnumerable rawProperties = GetPropertyDefinitions(toolsVersion.Name); - Expander expander = new Expander(initialProperties, FileSystems.Default); + Expander expander = new Expander(initialProperties); foreach (ToolsetPropertyDefinition property in rawProperties) { @@ -668,7 +668,7 @@ private void EvaluateAndSetProperty(ToolsetPropertyDefinition property, Property if (accumulateProperties) { - expander = new Expander(initialProperties, FileSystems.Default); + expander = new Expander(initialProperties); } } diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 5bc1e2ec3df..19510f6d663 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.FileSystem; diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index 1fe132a8d21..cc629ee8939 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -311,11 +311,11 @@ private void FlushFirstValueIfNeeded() /// Creates an expander passing it some properties to use. /// Properties may be null. /// - internal Expander(IPropertyProvider

properties, IFileSystem fileSystem) + internal Expander(IPropertyProvider

properties) { _properties = properties; _usedUninitializedProperties = new UsedUninitializedProperties(); - _fileSystem = fileSystem; + _fileSystem = FileSystems.Default; } ///

@@ -334,8 +334,8 @@ internal Expander(IPropertyProvider

properties, EvaluationContext evaluationC /// Creates an expander passing it some properties and items to use. /// Either or both may be null. ///

- internal Expander(IPropertyProvider

properties, IItemProvider items, IFileSystem fileSystem) - : this(properties, fileSystem) + internal Expander(IPropertyProvider

properties, IItemProvider items) + : this(properties) { _items = items; } @@ -354,8 +354,8 @@ internal Expander(IPropertyProvider

properties, IItemProvider items, Evalu /// Creates an expander passing it some properties, items, and/or metadata to use. /// Any or all may be null. ///

- internal Expander(IPropertyProvider

properties, IItemProvider items, IMetadataTable metadata, IFileSystem fileSystem) - : this(properties, items, fileSystem) + internal Expander(IPropertyProvider

properties, IItemProvider items, IMetadataTable metadata) + : this(properties, items) { _metadata = metadata; } diff --git a/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs b/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs index fc3996350a3..c493969feb5 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs @@ -7,8 +7,8 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.Construction; -using Microsoft.Build.Execution; using Microsoft.Build.Evaluation.Context; +using Microsoft.Build.Execution; namespace Microsoft.Build.Evaluation { diff --git a/src/Build/Evaluation/LazyItemEvaluator.cs b/src/Build/Evaluation/LazyItemEvaluator.cs index f5d52eed4ee..ada3da6a27f 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.cs @@ -29,7 +29,7 @@ internal partial class LazyItemEvaluator private readonly Expander _outerExpander; private readonly IEvaluatorData _evaluatorData; private readonly Expander _expander; - protected readonly IItemFactory _itemFactory; + private readonly IItemFactory _itemFactory; private readonly LoggingContext _loggingContext; private readonly EvaluationProfiler _evaluationProfiler; diff --git a/src/Build/Instance/ProjectInstance.cs b/src/Build/Instance/ProjectInstance.cs index 502370f337a..6d084b857a6 100644 --- a/src/Build/Instance/ProjectInstance.cs +++ b/src/Build/Instance/ProjectInstance.cs @@ -1876,7 +1876,7 @@ public bool Build(string[] targets, IEnumerable loggers, IEnumerable public string ExpandString(string unexpandedValue) { - Expander expander = new Expander(this, this, FileSystems.Default); + Expander expander = new Expander(this, this); string result = expander.ExpandIntoStringAndUnescape(unexpandedValue, ExpanderOptions.ExpandPropertiesAndItems, ProjectFileLocation); @@ -1894,7 +1894,7 @@ public string ExpandString(string unexpandedValue) /// public bool EvaluateCondition(string condition) { - Expander expander = new Expander(this, this, FileSystems.Default); + Expander expander = new Expander(this, this); bool result = ConditionEvaluator.EvaluateCondition( condition, @@ -2761,7 +2761,7 @@ out var usingDifferentToolsVersionFromProjectFile Evaluator.Evaluate( this, - null, // TODO? + null, xml, projectLoadSettings ?? buildParameters.ProjectLoadSettings, /* Use override ProjectLoadSettings if specified */ buildParameters.MaxNodeCount, diff --git a/src/Build/Instance/ProjectItemInstance.cs b/src/Build/Instance/ProjectItemInstance.cs index 2a34f84a2d9..94e9d311be8 100644 --- a/src/Build/Instance/ProjectItemInstance.cs +++ b/src/Build/Instance/ProjectItemInstance.cs @@ -1330,7 +1330,7 @@ public string GetMetadataEscaped(string metadataName) if (metadatum != null && Expander.ExpressionMayContainExpandableExpressions(metadatum.EvaluatedValueEscaped)) { - Expander expander = new Expander(null, null, new BuiltInMetadataTable(null, this), FileSystems.Default); + Expander expander = new Expander(null, null, new BuiltInMetadataTable(null, this)); // We don't have a location to use, but this is very unlikely to error return expander.ExpandIntoStringLeaveEscaped(metadatum.EvaluatedValueEscaped, ExpanderOptions.ExpandBuiltInMetadata, ElementLocation.EmptyLocation); diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index 466ece84f00..b1f8aa45166 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -94,7 +94,7 @@ public FileMatcher(IFileSystem fileSystem, ConcurrentDictionary> fileEntryExpansionCache = null) + internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemEntries, ConcurrentDictionary> getFileSystemDirectoryEntriesCache = null) { if (Traits.Instance.MSBuildCacheFileEnumerations) { @@ -103,12 +103,12 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE } else { - _cachedGlobExpansions = fileEntryExpansionCache; + _cachedGlobExpansions = getFileSystemDirectoryEntriesCache; } _fileSystem = fileSystem; - _getFileSystemEntries = fileEntryExpansionCache == null + _getFileSystemEntries = getFileSystemDirectoryEntriesCache == null ? getFileSystemEntries : (type, path, pattern, directory, stripProjectDirectory) => { @@ -123,7 +123,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE FileSystemEntity.FilesAndDirectories => "A", _ => throw new NotImplementedException() } + ";" + path; - IReadOnlyList allEntriesForPath = fileEntryExpansionCache.GetOrAdd( + IReadOnlyList allEntriesForPath = getFileSystemDirectoryEntriesCache.GetOrAdd( cacheKey, s => getFileSystemEntries( type, @@ -144,7 +144,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE // Cache only directories, for files we won't hit the cache because the file name patterns tend to be unique if (type == FileSystemEntity.Directories) { - return fileEntryExpansionCache.GetOrAdd( + return getFileSystemDirectoryEntriesCache.GetOrAdd( $"D;{path};{pattern ?? "*"}", s => getFileSystemEntries( type, From f91659262773f64947a1a94b13e33ced425303c6 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Wed, 1 Sep 2021 23:54:42 +0200 Subject: [PATCH 17/23] Add pattern parameter to EnumerateFiles and EnumerateDirectories --- ref/Microsoft.Build/net/Microsoft.Build.cs | 4 ++-- .../netstandard/Microsoft.Build.cs | 4 ++-- .../DirectoryCacheFileSystemWrapper.cs | 4 ++-- src/Build/FileSystem/IDirectoryCache.cs | 18 ++++++++++++++++-- src/Shared/UnitTests/ObjectModelHelpers.cs | 4 ++-- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/ref/Microsoft.Build/net/Microsoft.Build.cs b/ref/Microsoft.Build/net/Microsoft.Build.cs index b9fb1dc0f08..9f42d2be544 100644 --- a/ref/Microsoft.Build/net/Microsoft.Build.cs +++ b/ref/Microsoft.Build/net/Microsoft.Build.cs @@ -1510,8 +1510,8 @@ namespace Microsoft.Build.FileSystem public partial interface IDirectoryCache { bool DirectoryExists(string path); - System.Collections.Generic.IEnumerable EnumerateDirectories(string path, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); - System.Collections.Generic.IEnumerable EnumerateFiles(string path, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); + System.Collections.Generic.IEnumerable EnumerateDirectories(string path, string pattern, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); + System.Collections.Generic.IEnumerable EnumerateFiles(string path, string pattern, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); bool FileExists(string path); } public partial interface IDirectoryCacheFactory diff --git a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs index 635bc7d11e2..c12fdafce68 100644 --- a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs +++ b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs @@ -1504,8 +1504,8 @@ namespace Microsoft.Build.FileSystem public partial interface IDirectoryCache { bool DirectoryExists(string path); - System.Collections.Generic.IEnumerable EnumerateDirectories(string path, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); - System.Collections.Generic.IEnumerable EnumerateFiles(string path, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); + System.Collections.Generic.IEnumerable EnumerateDirectories(string path, string pattern, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); + System.Collections.Generic.IEnumerable EnumerateFiles(string path, string pattern, Microsoft.Build.FileSystem.FindPredicate predicate, Microsoft.Build.FileSystem.FindTransform transform); bool FileExists(string path); } public partial interface IDirectoryCacheFactory diff --git a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs index 45f0056e2b2..d28d105ca3c 100644 --- a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs +++ b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs @@ -85,10 +85,10 @@ private IEnumerable EnumerateFullFileSystemPaths(string path, string sea FindTransform transform = (ref ReadOnlySpan fileName) => Path.Combine(path, fileName.ToString()); IEnumerable directories = includeDirectories - ? _directoryCache.EnumerateDirectories(path, predicate, transform) + ? _directoryCache.EnumerateDirectories(path, searchPattern, predicate, transform) : Enumerable.Empty(); IEnumerable files = includeFiles - ? _directoryCache.EnumerateFiles(path, predicate, transform) + ? _directoryCache.EnumerateFiles(path, searchPattern, predicate, transform) : Enumerable.Empty(); return Enumerable.Concat(directories, files); diff --git a/src/Build/FileSystem/IDirectoryCache.cs b/src/Build/FileSystem/IDirectoryCache.cs index 6c42b990bff..6567bf8a65d 100644 --- a/src/Build/FileSystem/IDirectoryCache.cs +++ b/src/Build/FileSystem/IDirectoryCache.cs @@ -59,17 +59,31 @@ public interface IDirectoryCache ///

/// The desired return type. /// The directory to enumerate. + /// A search pattern supported by the platform which is guaranteed to return a superset of relevant files. /// A predicate to test whether a file should be included. /// A transform from ReadOnlySpan<char> to . - IEnumerable EnumerateFiles(string path, FindPredicate predicate, FindTransform transform); + /// + /// The parameter may match more files than what the caller is interested in. In other words, + /// can return false even if the implementation enumerates only files whose names + /// match the pattern. The implementation is free to ignore the pattern and call the predicate for all files on the given + /// . + /// + IEnumerable EnumerateFiles(string path, string pattern, FindPredicate predicate, FindTransform transform); /// /// Enumerates subdirectories in the given directory only (non-recursively). /// /// The desired return type. /// The directory to enumerate. + /// A search pattern supported by the platform which is guaranteed to return a superset of relevant directories. /// A predicate to test whether a directory should be included. /// A transform from ReadOnlySpan<char> to . - IEnumerable EnumerateDirectories(string path, FindPredicate predicate, FindTransform transform); + /// + /// The parameter may match more direcories than what the caller is interested in. In other words, + /// can return false even if the implementation enumerates only directories whose names + /// match the pattern. The implementation is free to ignore the pattern and call the predicate for all directories on the given + /// . + /// + IEnumerable EnumerateDirectories(string path, string pattern, FindPredicate predicate, FindTransform transform); } } diff --git a/src/Shared/UnitTests/ObjectModelHelpers.cs b/src/Shared/UnitTests/ObjectModelHelpers.cs index e3a2fa45482..e647fd709c0 100644 --- a/src/Shared/UnitTests/ObjectModelHelpers.cs +++ b/src/Shared/UnitTests/ObjectModelHelpers.cs @@ -2055,13 +2055,13 @@ public bool FileExists(string path) return File.Exists(path); } - public IEnumerable EnumerateDirectories(string path, FindPredicate predicate, FindTransform transform) + public IEnumerable EnumerateDirectories(string path, string pattern, FindPredicate predicate, FindTransform transform) { IncrementEnumerations(path); return Enumerable.Empty(); } - public IEnumerable EnumerateFiles(string path, FindPredicate predicate, FindTransform transform) + public IEnumerable EnumerateFiles(string path, string pattern, FindPredicate predicate, FindTransform transform) { IncrementEnumerations(path); return Enumerable.Empty(); From 43f1323387da8a8649470c93126f5539eb42f80a Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Fri, 15 Oct 2021 12:16:38 +0200 Subject: [PATCH 18/23] Revert "More internal IFileSystem deprecation and tweaks" This reverts commit 180f7a83ff3cf43ed8370e5dcd67fa5dbb77b818. --- .../BackEnd/TaskRegistry_Tests.cs | 2 +- .../Evaluation/Evaluator_Tests.cs | 2 +- .../Evaluation/Expander_Tests.cs | 230 +++++++++--------- .../Evaluation/ItemSpec_Tests.cs | 2 +- .../ExpressionTreeExpression_Tests.cs | 2 +- src/Build.UnitTests/ExpressionTree_Tests.cs | 28 +-- .../Components/RequestBuilder/ItemBucket.cs | 2 +- .../Components/RequestBuilder/TargetEntry.cs | 2 +- src/Build/Definition/ProjectItem.cs | 2 +- src/Build/Definition/Toolset.cs | 2 +- src/Build/Definition/ToolsetReader.cs | 4 +- .../Evaluation/Context/EvaluationContext.cs | 1 + src/Build/Evaluation/Expander.cs | 12 +- .../LazyItemEvaluator.EvaluatorData.cs | 2 +- src/Build/Evaluation/LazyItemEvaluator.cs | 2 +- src/Build/Instance/ProjectInstance.cs | 6 +- src/Build/Instance/ProjectItemInstance.cs | 2 +- src/Shared/FileMatcher.cs | 10 +- 18 files changed, 157 insertions(+), 156 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs b/src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs index 16bcba2d9d4..ecdfc421d3c 100644 --- a/src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs @@ -2255,7 +2255,7 @@ internal static Expander GetExpand secondaryItemsByName.ImportItems(thirdItemGroup); secondaryItemsByName.ImportItems(trueItemGroup); - Expander expander = new Expander(pg, secondaryItemsByName); + Expander expander = new Expander(pg, secondaryItemsByName, FileSystems.Default); return expander; } diff --git a/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs b/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs index 1e23a6c7bc5..8ac6ba52f14 100644 --- a/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs @@ -4298,7 +4298,7 @@ public void VerifyDTDProcessingIsDisabled2() public void VerifyConditionEvaluatorResetStateOnFailure() { PropertyDictionary propertyBag = new PropertyDictionary(); - Expander expander = new Expander(propertyBag); + Expander expander = new Expander(propertyBag, FileSystems.Default); string condition = " '$(TargetOSFamily)' >= '3' "; // Give an incorrect value for the property "TargetOSFamily", and then the evaluation should throw an exception. diff --git a/src/Build.UnitTests/Evaluation/Expander_Tests.cs b/src/Build.UnitTests/Evaluation/Expander_Tests.cs index 8b2fcb45c18..b58a72d0843 100644 --- a/src/Build.UnitTests/Evaluation/Expander_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Expander_Tests.cs @@ -43,7 +43,7 @@ public class Expander_Tests public void ExpandAllIntoTaskItems0() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("", ExpanderOptions.ExpandProperties, null); @@ -54,7 +54,7 @@ public void ExpandAllIntoTaskItems0() public void ExpandAllIntoTaskItems1() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("foo", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -65,7 +65,7 @@ public void ExpandAllIntoTaskItems1() public void ExpandAllIntoTaskItems2() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("foo;bar", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -92,7 +92,7 @@ public void ExpandAllIntoTaskItems3() itemsByType.ImportItems(ig); itemsByType.ImportItems(ig2); - Expander expander = new Expander(pg, itemsByType); + Expander expander = new Expander(pg, itemsByType, FileSystems.Default); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("foo;bar;@(compile);@(resource)", ExpanderOptions.ExpandPropertiesAndItems, MockElementLocation.Instance); @@ -113,7 +113,7 @@ public void ExpandAllIntoTaskItems4() pg.Set(ProjectPropertyInstance.Create("b", "bbb")); pg.Set(ProjectPropertyInstance.Create("c", "cc;dd")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); IList itemsOut = expander.ExpandIntoTaskItemsLeaveEscaped("foo$(a);$(b);$(c)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -137,7 +137,7 @@ public void ExpandPropertiesIntoProjectPropertyInstances() pg.Set(ProjectPropertyInstance.Create("b", "bbb")); pg.Set(ProjectPropertyInstance.Create("c", "cc;dd")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); ProjectItemInstanceFactory itemFactory = new ProjectItemInstanceFactory(project, "i"); IList itemsOut = expander.ExpandIntoItemsLeaveEscaped("foo$(a);$(b);$(c);$(d", itemFactory, ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -154,7 +154,7 @@ public void ExpandEmptyPropertyExpressionToEmpty() ProjectHelpers.CreateEmptyProjectInstance(); PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$()", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); Assert.Equal(String.Empty, result); @@ -778,7 +778,7 @@ private Expander CreateItemFunctio itemMetadataTable["Language"] = "english"; IMetadataTable itemMetadata = new StringMetadataTable(itemMetadataTable); - Expander expander = new Expander(pg, ig, itemMetadata); + Expander expander = new Expander(pg, ig, itemMetadata, FileSystems.Default); return expander; } @@ -800,7 +800,7 @@ private Expander CreateExpander() ig.Add(i0); ig.Add(i1); - Expander expander = new Expander(pg, ig); + Expander expander = new Expander(pg, ig, FileSystems.Default); return expander; } @@ -1344,7 +1344,7 @@ public void ExpandAllIntoTaskItemsComplex() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata); + Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); IList taskItems = expander.ExpandIntoTaskItemsLeaveEscaped( "@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; " + @@ -1376,7 +1376,7 @@ public void ExpandAllIntoStringComplexPiecemeal() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata); + Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); string stringToExpand = "@(Resource->'%(Filename)') ;"; Assert.Equal( @@ -1429,7 +1429,7 @@ public void ExpandAllIntoStringEmpty() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata); + Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); xmlattribute.Value = "@(IntermediateAssembly->'')"; @@ -1455,7 +1455,7 @@ public void ExpandAllIntoStringComplex() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata); + Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); xmlattribute.Value = "@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; " + @@ -1477,7 +1477,7 @@ public void ExpandAllIntoStringLeaveEscapedComplex() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata); + Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); xmlattribute.Value = "@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; " + @@ -1526,7 +1526,7 @@ public void ExpandAllIntoStringTruncated() }); lookup.PopulateWithItems("ManyItems", itemGroup); - Expander expander = new Expander(lookup, lookup, itemMetadata); + Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); xmlattribute.Value = "'%(ManySpacesMetadata)' != '' and '$(ManySpacesProperty)' != '' and '@(ManySpacesItem)' != '' and '@(Exactly1024)' != '' and '@(ManyItems)' != '' and '@(ManyItems->'%(Foo)')' != '' and '@(ManyItems->'%(Nonexistent)')' != ''"; @@ -1558,7 +1558,7 @@ public void ExpandAllIntoStringExpectIdenticalReference() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata); + Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); @@ -1591,7 +1591,7 @@ public void ExpandAllIntoStringExpanderOptions() string value = @"@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; $(OutputPath) ; $(TargetPath) ; %(Language)_%(Culture)"; - Expander expander = new Expander(lookup, lookup, itemMetadata); + Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); Assert.Equal(@"@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; ; %(NonExistent) ; \jk ; l\mno%3bpqr\stu ; @(IntermediateAssembly->'%(RelativeDir)') ; %(Language)_%(Culture)", expander.ExpandIntoStringAndUnescape(value, ExpanderOptions.ExpandProperties, MockElementLocation.Instance)); @@ -1612,7 +1612,7 @@ public void ExpandAllIntoStringListLeaveEscapedComplex() StringMetadataTable itemMetadata; CreateComplexPropertiesItemsMetadata(out lookup, out itemMetadata); - Expander expander = new Expander(lookup, lookup, itemMetadata); + Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); string value = "@(Resource->'%(Filename)') ; @(Content) ; @(NonExistent) ; $(NonExistent) ; %(NonExistent) ; " + "$(OutputPath) ; $(TargetPath) ; %(Language)_%(Culture)"; @@ -1654,7 +1654,7 @@ public void RegistryPropertyInvalidPrefixSpecialCase() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\VSTSDB@VSTSDBDirectory)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1670,7 +1670,7 @@ public void Regress692569() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$(Solutions.VSVersion)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1688,7 +1688,7 @@ public void RegistryPropertyInvalidPrefixError() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped(@"$(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\VSTSDB@XXXXDBDirectory)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -1706,7 +1706,7 @@ public void RegistryPropertyInvalidPrefixError2() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped(@"$(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\VSTSDB@VSTSDBDirectoryX)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -1720,7 +1720,7 @@ public void RegistryPropertyString() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", "String", RegistryValueKind.String); @@ -1741,7 +1741,7 @@ public void RegistryPropertyBinary() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); UTF8Encoding enc = new UTF8Encoding(); @@ -1765,7 +1765,7 @@ public void RegistryPropertyDWord() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", 123456, RegistryValueKind.DWord); @@ -1787,7 +1787,7 @@ public void RegistryPropertyExpandString() string envVar = NativeMethodsShared.IsWindows ? "TEMP" : "USER"; PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -1808,7 +1808,7 @@ public void RegistryPropertyQWord() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", (long)123456789123456789, RegistryValueKind.QWord); @@ -1829,7 +1829,7 @@ public void RegistryPropertyMultiString() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", new string[] { "A", "B", "C", "D" }, RegistryValueKind.MultiString); @@ -1926,7 +1926,7 @@ public void PropertyFunctionNullArgument() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$([System.Convert]::ChangeType('null',$(SomeStuff.GetTypeCode())))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1941,7 +1941,7 @@ public void PropertyFunctionNullReturn() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); // The null-returning function is the only thing in the expression. string result = expander.ExpandIntoStringLeaveEscaped("$([System.Environment]::GetEnvironmentVariable(`_NonExistentVar`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1961,7 +1961,7 @@ public void PropertyFunctionNoArguments() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToUpperInvariant())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1977,7 +1977,7 @@ public void PropertyFunctionNoArgumentsTrim() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("FileName", " foo.ext ")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(FileName.Trim())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -1993,7 +1993,7 @@ public void PropertyFunctionPropertyGet() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.Length)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2009,7 +2009,7 @@ public void PropertyFunctionPropertyManualGet() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.get_Length())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2025,7 +2025,7 @@ public void PropertyFunctionPropertyNoArgumentsConcat() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToLowerInvariant())_goop", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2041,7 +2041,7 @@ public void PropertyFunctionPropertyWithArgument() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString(13))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2057,7 +2057,7 @@ public void PropertyFunctionPropertyWithArgumentWithSpaces() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString(8))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2074,7 +2074,7 @@ public void PropertyFunctionPropertyPathRootSubtraction() pg.Set(ProjectPropertyInstance.Create("RootPath", Path.Combine(s_rootPathPrefix, "this", "is", "the", "root"))); pg.Set(ProjectPropertyInstance.Create("MyPath", Path.Combine(s_rootPathPrefix, "this", "is", "the", "root", "my", "project", "is", "here.proj"))); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(MyPath.SubString($(RootPath.Length)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2091,7 +2091,7 @@ public void PropertyFunctionPropertyWithArgumentExpandedProperty() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString(1$(Value)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2108,7 +2108,7 @@ public void PropertyFunctionPropertyWithArgumentBooleanReturn() pg.Set(ProjectPropertyInstance.Create("PathRoot", Path.Combine(s_rootPathPrefix, "goo"))); pg.Set(ProjectPropertyInstance.Create("PathRoot2", Path.Combine(s_rootPathPrefix, "goop") + Path.DirectorySeparatorChar)); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$(PathRoot2.Endswith(" + Path.DirectorySeparatorChar + "))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); Assert.Equal("True", result); @@ -2126,7 +2126,7 @@ public void PropertyFunctionPropertyWithArgumentNestedAndChainedFunction() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString(1$(Value)).ToLowerInvariant().SubString($(Value)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2144,7 +2144,7 @@ public void PropertyFunctionPropertyWithArgumentChained() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToUpperInvariant().ToLowerInvariant())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); Assert.Equal("this is some stuff", result); @@ -2160,7 +2160,7 @@ public void PropertyFunctionPropertyWithArgumentNested() pg.Set(ProjectPropertyInstance.Create("Value", "12345")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "1234567890")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.SubString($(Value.get_Length())))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2175,7 +2175,7 @@ public void PropertyFunctionGenericListReturn() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$([MSBuild]::__GetListTest())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2191,7 +2191,7 @@ public void PropertyFunctionArrayReturn() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("List", "A-B-C-D")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(List.Split(-))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2209,7 +2209,7 @@ public void PropertyFunctionDictionaryReturn() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$([System.Environment]::GetEnvironmentVariables())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ToUpperInvariant(); string expected = ("OS=" + Environment.GetEnvironmentVariable("OS")).ToUpperInvariant(); @@ -2227,7 +2227,7 @@ public void PropertyFunctionArrayReturnManualSplitter() pg.Set(ProjectPropertyInstance.Create("List", "A-B-C-D")); pg.Set(ProjectPropertyInstance.Create("Splitter", "-")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(List.Split($(Splitter.ToCharArray())))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2244,7 +2244,7 @@ public void PropertyFunctionInCondition() pg.Set(ProjectPropertyInstance.Create("PathRoot", Path.Combine(s_rootPathPrefix, "goo"))); pg.Set(ProjectPropertyInstance.Create("PathRoot2", Path.Combine(s_rootPathPrefix, "goop") + Path.DirectorySeparatorChar)); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); Assert.True( ConditionEvaluator.EvaluateCondition( @@ -2282,7 +2282,7 @@ public void PropertyFunctionInvalid1() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff($(Value)))]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2301,7 +2301,7 @@ public void PropertyFunctionInvalid2() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff.Lgg)]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2319,7 +2319,7 @@ public void PropertyFunctionInvalid3() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToUpperInvariant().Foo)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2337,7 +2337,7 @@ public void PropertyFunctionInvalid4() pg.Set(ProjectPropertyInstance.Create("Value", "3")); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff($(System.DateTime.Now)))]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2355,7 +2355,7 @@ public void PropertyFunctionInvalid5() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped("$(SomeStuff.ToLowerInvariant()_goop)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2372,7 +2372,7 @@ public void PropertyFunctionInvalid6() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff.Substring(HELLO!))]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2389,7 +2389,7 @@ public void PropertyFunctionInvalid7() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped("[$(SomeStuff.Substring(-10))]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2405,7 +2405,7 @@ public void PropertyFunctionInvalid8() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped("$(([System.DateTime]::Now).ToString(\"MM.dd.yyyy\"))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); } @@ -2419,7 +2419,7 @@ public void PropertyFunctionInvalidNoMetadataFunctions() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("[%(LowerLetterList.Identity.ToUpper())]", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2435,7 +2435,7 @@ public void PropertyFunctionNoCollisionsOnType() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("System", "The System Namespace")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$(System)", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2454,7 +2454,7 @@ public void PropertyFunctionStaticMethodMakeRelative() pg.Set(ProjectPropertyInstance.Create("ParentPath", Path.Combine(s_rootPathPrefix, "abc", "def"))); pg.Set(ProjectPropertyInstance.Create("FilePath", Path.Combine(s_rootPathPrefix, "abc", "def", "foo.cpp"))); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::MakeRelative($(ParentPath), `$(FilePath)`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2471,7 +2471,7 @@ public void PropertyFunctionStaticMethod1() pg.Set(ProjectPropertyInstance.Create("Drive", s_rootPathPrefix)); pg.Set(ProjectPropertyInstance.Create("File", Path.Combine("foo", "file.txt"))); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine($(Drive), `$(File)`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2487,7 +2487,7 @@ public void PropertyFunctionConstructor1() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("ver1", @"1.2.3.4")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); object result = expander.ExpandPropertiesLeaveTypedAndEscaped(@"$([System.Version]::new($(ver1)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2511,7 +2511,7 @@ public void PropertyFunctionConstructor2() pg.Set(ProjectPropertyInstance.Create("ver1", @"1.2.3.4")); pg.Set(ProjectPropertyInstance.Create("ver2", @"2.2.3.4")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.Version]::new($(ver1)).CompareTo($([System.Version]::new($(ver2)))))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2531,7 +2531,7 @@ public void PropertyStaticFunctionAllEnabled() PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); try { @@ -2554,7 +2554,7 @@ public void PropertyStaticFunctionLocatedFromAssemblyWithNamespaceName() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string env = Environment.GetEnvironmentVariable("MSBUILDENABLEALLPROPERTYFUNCTIONS"); @@ -2583,7 +2583,7 @@ public void PropertyStaticFunctionUsingNamespaceNotFound() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string env = Environment.GetEnvironmentVariable("MSBUILDENABLEALLPROPERTYFUNCTIONS"); @@ -2617,7 +2617,7 @@ public void PropertyFunctionStaticMethodQuoted1() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", Path.Combine("foo", "file.txt"))); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + s_rootPathPrefix + "`, `$(File)`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2633,7 +2633,7 @@ public void PropertyFunctionStaticMethodQuoted1Spaces() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", "foo goo" + Path.DirectorySeparatorChar + "file.txt")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + Path.Combine(s_rootPathPrefix, "foo goo") + "`, `$(File)`))", @@ -2651,7 +2651,7 @@ public void PropertyFunctionStaticMethodQuoted1Spaces2() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", Path.Combine("foo bar", "baz.txt"))); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + Path.Combine(s_rootPathPrefix, "foo baz") + @"`, `$(File)`))", @@ -2669,7 +2669,7 @@ public void PropertyFunctionStaticMethodQuoted1Spaces3() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", Path.Combine("foo bar", "baz.txt"))); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + Path.Combine(s_rootPathPrefix, "foo baz") + @" `, `$(File)`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2685,7 +2685,7 @@ public void PropertyFunctionStaticMethodQuoted2() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string dateTime = "'" + _dateToParse + "'"; string result = expander.ExpandIntoStringLeaveEscaped("$([System.DateTime]::Parse(" + dateTime + ").ToString(\"yyyy/MM/dd HH:mm:ss\"))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2701,7 +2701,7 @@ public void PropertyFunctionStaticMethodQuoted3() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string dateTime = "'" + _dateToParse + "'"; string result = expander.ExpandIntoStringLeaveEscaped("$([System.DateTime]::Parse(" + dateTime + ").ToString(\"MM.dd.yyyy\"))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2716,7 +2716,7 @@ public void PropertyFunctionStaticMethodQuoted4() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$([System.DateTime]::Now.ToString(\"MM.dd.yyyy\"))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2732,7 +2732,7 @@ public void PropertyFunctionStaticMethodNested() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", "foo" + Path.DirectorySeparatorChar + "file.txt")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine(`" + s_rootPathPrefix + @@ -2750,7 +2750,7 @@ public void PropertyFunctionStaticMethodRegex1() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("File", "foo" + Path.DirectorySeparatorChar + "file.txt")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); // Support enum combines as Enum.Parse expects them string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.Text.RegularExpressions.Regex]::IsMatch(`-42`, `^-?\d+(\.\d{2})?$`, `RegexOptions.IgnoreCase,RegexOptions.Singleline`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2775,7 +2775,7 @@ public void PropertyFunctionStaticMethodChained() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string dateTime = "'" + _dateToParse + "'"; string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.DateTime]::Parse(" + dateTime + ").ToString(`yyyy/MM/dd HH:mm:ss`))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2790,7 +2790,7 @@ public void PropertyFunctionGetFolderPath() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.Environment]::GetFolderPath(SpecialFolder.System))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -2824,7 +2824,7 @@ public void PropertyFunctionRuntimeInformation(string propertyFunction, string e .Replace("$$architecture$$", architecture); var pg = new PropertyDictionary(); - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); string currentPlatformString = Helpers.GetOSPlatformAsString(); @@ -2854,7 +2854,7 @@ public void StringIndexOfTests(string propertyName, string properyValue, string var pg = new PropertyDictionary {[propertyName] = ProjectPropertyInstance.Create(propertyName, properyValue)}; - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped(propertyFunction, ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ShouldBe(expectedExpansion); } @@ -2863,7 +2863,7 @@ public void StringIndexOfTests(string propertyName, string properyValue, string public void IsOsPlatformShouldBeCaseInsensitiveToParameter() { var pg = new PropertyDictionary(); - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); var osPlatformLowerCase = Helpers.GetOSPlatformAsString().ToLower(); @@ -2879,7 +2879,7 @@ public void IsOsPlatformShouldBeCaseInsensitiveToParameter() public void PropertyFunctionVersionComparisonsFailsWithInvalidArguments(string badVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); string expectedMessage = ResourceUtilities.GetResourceString("InvalidVersionFormat"); AssertThrows(expander, $"$([MSBuild]::VersionGreaterThan('{badVersion}', '1.0.0'))", expectedMessage); @@ -2912,7 +2912,7 @@ public void PropertyFunctionVersionComparisonsFailsWithInvalidArguments(string b public void PropertyFunctionVersionComparisons(string a, string b, int expectedSign) { var pg = new PropertyDictionary(); - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); AssertSuccess(expander, expectedSign > 0, $"$([MSBuild]::VersionGreaterThan('{a}', '{b}'))"); AssertSuccess(expander, expectedSign >= 0, $"$([MSBuild]::VersionGreaterThanOrEquals('{a}', '{b}'))"); @@ -2931,7 +2931,7 @@ public void PropertyFunctionVersionComparisons(string a, string b, int expectedS public void PropertyFunctionTargetFrameworkParsing(string tfm, string expectedIdentifier, string expectedVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); AssertSuccess(expander, expectedIdentifier, $"$([MSBuild]::GetTargetFrameworkIdentifier('{tfm}'))"); AssertSuccess(expander, expectedVersion, $"$([MSBuild]::GetTargetFrameworkVersion('{tfm}'))"); @@ -2945,7 +2945,7 @@ public void PropertyFunctionTargetFrameworkParsing(string tfm, string expectedId public void PropertyFunctionTargetFrameworkVersionMultipartParsing(string tfm, int versionPartCount, string expectedVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); AssertSuccess(expander, expectedVersion, $"$([MSBuild]::GetTargetFrameworkVersion('{tfm}', {versionPartCount}))"); } @@ -2958,7 +2958,7 @@ public void PropertyFunctionTargetFrameworkVersionMultipartParsing(string tfm, i public void PropertyFunctionTargetPlatformVersionMultipartParsing(string tfm, int versionPartCount, string expectedVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); AssertSuccess(expander, expectedVersion, $"$([MSBuild]::GetTargetPlatformVersion('{tfm}', {versionPartCount}))"); } @@ -2972,7 +2972,7 @@ public void PropertyFunctionTargetPlatformVersionMultipartParsing(string tfm, in public void PropertyFunctionTargetPlatformParsing(string tfm, string expectedIdentifier, string expectedVersion) { var pg = new PropertyDictionary(); - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); AssertSuccess(expander, expectedIdentifier, $"$([MSBuild]::GetTargetPlatformIdentifier('{tfm}'))"); AssertSuccess(expander, expectedVersion, $"$([MSBuild]::GetTargetPlatformVersion('{tfm}'))"); @@ -2994,7 +2994,7 @@ public void PropertyFunctionTargetPlatformParsing(string tfm, string expectedIde public void PropertyFunctionTargetFrameworkComparisons(string tfm1, string tfm2, bool expectedFrameworkCompatible) { var pg = new PropertyDictionary(); - var expander = new Expander(pg); + var expander = new Expander(pg, FileSystems.Default); AssertSuccess(expander, expectedFrameworkCompatible, $"$([MSBuild]::IsTargetFrameworkCompatible('{tfm1}', '{tfm2}'))"); } @@ -3028,7 +3028,7 @@ public void PropertyFunctionStaticMethodEnumArgument() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$([System.String]::Equals(`a`, `A`, StringComparison.OrdinalIgnoreCase))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); Assert.Equal(true.ToString(), result); @@ -3051,7 +3051,7 @@ public void PropertyFunctionStaticMethodDirectoryNameOfFileAbove() pg.Set(ProjectPropertyInstance.Create("StartingDirectory", directoryStart)); pg.Set(ProjectPropertyInstance.Create("FileToFind", tempFile)); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringAndUnescape(@"$([MSBuild]::GetDirectoryNameOfFileAbove($(StartingDirectory), $(FileToFind)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3085,7 +3085,7 @@ public void PropertyFunctionStaticMethodGetPathOfFileAbove() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("FileToFind", Path.GetFileName(fileToFind))); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringAndUnescape(@"$([MSBuild]::GetPathOfFileAbove($(FileToFind)))", ExpanderOptions.ExpandProperties, mockElementLocation); @@ -3128,7 +3128,7 @@ public void PropertyFunctionStaticMethodGetPathOfFileAboveFileNameOnly() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("FileWithPath", fileWithPath)); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::GetPathOfFileAbove($(FileWithPath)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); }); @@ -3144,7 +3144,7 @@ public void PropertyFunctionStaticMethodGetCultureInfo() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); #if FEATURE_CULTUREINFO_GETCULTURES string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.Globalization.CultureInfo]::GetCultureInfo(`en-US`).ToString())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3163,7 +3163,7 @@ public void PropertyFunctionStaticMethodArithmeticAddInt32() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::Add(40, 2))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3178,7 +3178,7 @@ public void PropertyFunctionStaticMethodArithmeticAddDouble() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::Add(39.9, 2.1))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3193,7 +3193,7 @@ public void PropertyFunctionValueOrDefault() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::ValueOrDefault('', '42'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3214,7 +3214,7 @@ public void PropertyFunctionValueOrDefaultFromEnvironment() pg["BonkersTargetsPath"] = ProjectPropertyInstance.Create("BonkersTargetsPath", "Bonkers"); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::ValueOrDefault('$(BonkersTargetsPath)', '42'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3237,7 +3237,7 @@ public void PropertyFunctionDoesTaskHostExist() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist('CurrentRuntime', 'CurrentArchitecture'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3254,7 +3254,7 @@ public void PropertyFunctionDoesTaskHostExist_Whitespace() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist(' CurrentRuntime ', 'CurrentArchitecture'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3270,7 +3270,7 @@ public void PropertyFunctionNormalizeDirectory() { ProjectPropertyInstance.Create("MyPath", "one"), ProjectPropertyInstance.Create("MySecondPath", "two"), - })); + }), FileSystems.Default); Assert.Equal( $"{Path.GetFullPath("one")}{Path.DirectorySeparatorChar}", @@ -3291,7 +3291,7 @@ public void PropertyFunctionDoesTaskHostExist_Error() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist('ASDF', 'CurrentArchitecture'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3314,7 +3314,7 @@ public void PropertyFunctionDoesTaskHostExist_Evaluated() pg["Runtime"] = ProjectPropertyInstance.Create("Runtime", "CurrentRuntime"); pg["Architecture"] = ProjectPropertyInstance.Create("Architecture", "CurrentArchitecture"); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist('$(Runtime)', '$(Architecture)'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3338,7 +3338,7 @@ public void PropertyFunctionDoesTaskHostExist_NonexistentTaskHost() PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::DoesTaskHostExist('CLR2', 'CurrentArchitecture'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3364,7 +3364,7 @@ public void PropertyFunctionStaticMethodFileAttributes() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string tempFile = FileUtilities.GetTemporaryFile(); try @@ -3390,7 +3390,7 @@ public void PropertyFunctionStaticMethodIntrinsicMaths() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::Add(39.9, 2.1))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3464,7 +3464,7 @@ public void PropertySimpleSpaced() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeStuff", "This IS SOME STUff")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$( SomeStuff )", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3481,7 +3481,7 @@ public void PropertyFunctionGetRegitryValue() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeProperty", "Value")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue("Value", "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -3504,7 +3504,7 @@ public void PropertyFunctionGetRegitryValueDefault() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeProperty", "Value")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue(String.Empty, "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -3527,7 +3527,7 @@ public void PropertyFunctionGetRegistryValueFromView1() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeProperty", "Value")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue(String.Empty, "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -3550,7 +3550,7 @@ public void PropertyFunctionGetRegistryValueFromView2() PropertyDictionary pg = new PropertyDictionary(); pg.Set(ProjectPropertyInstance.Create("SomeProperty", "Value")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\MSBuild_test"); key.SetValue(String.Empty, "%" + envVar + "%", RegistryValueKind.ExpandString); @@ -3584,7 +3584,7 @@ public void PropertyFunctionConsumingItemMetadata() ItemDictionary itemsByType = new ItemDictionary(); itemsByType.ImportItems(ig); - Expander expander = new Expander(pg, itemsByType, itemMetadata); + Expander expander = new Expander(pg, itemsByType, itemMetadata, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(@"$([System.IO.Path]::Combine($(SomePath),%(Compile.Identity)))", ExpanderOptions.ExpandAll, MockElementLocation.Instance); @@ -3615,7 +3615,7 @@ public void PropertyStringConstructorConsumingItemMetadata(string metadatumName, public void PropertyFunctionHashCodeSameOnlyIfStringSame() { PropertyDictionary pg = new PropertyDictionary(); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string[] stringsToHash = { "cat1s", "cat1z", @@ -3677,7 +3677,7 @@ public void Medley() pg.Set(ProjectPropertyInstance.Create("input", @"EXPORT a")); pg.Set(ProjectPropertyInstance.Create("propertycontainingnullasastring", @"null")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); var validTests = new List { new string[] {"$(input.ToString()[1])", "X"}, @@ -3932,7 +3932,7 @@ public void PropertyFunctionEnsureTrailingSlash() pg.Set(ProjectPropertyInstance.Create("SomeProperty", path)); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); // Verify a constant expands properly string result = expander.ExpandIntoStringLeaveEscaped($"$([MSBuild]::EnsureTrailingSlash('{path}'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3958,7 +3958,7 @@ public void PropertyFunctionWithNewLines() pg.Set(ProjectPropertyInstance.Create("SomeProperty", "6C8546D5297C424F962201B0E0E9F142")); - Expander expander = new Expander(pg); + Expander expander = new Expander(pg, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(propertyFunction, ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -3998,7 +3998,7 @@ public void PropertyFunctionVersionParse() [Fact] public void PropertyFunctionGuidNewGuid() { - var expander = new Expander(new PropertyDictionary()); + var expander = new Expander(new PropertyDictionary(), FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped("$([System.Guid]::NewGuid())", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); @@ -4230,7 +4230,7 @@ private void TestPropertyFunction(string expression, string propertyName, string { var properties = new PropertyDictionary(); properties.Set(ProjectPropertyInstance.Create(propertyName, propertyValue)); - var expander = new Expander(properties); + var expander = new Expander(properties, FileSystems.Default); string result = expander.ExpandIntoStringLeaveEscaped(expression, ExpanderOptions.ExpandProperties, MockElementLocation.Instance); result.ShouldBe(expected); } diff --git a/src/Build.UnitTests/Evaluation/ItemSpec_Tests.cs b/src/Build.UnitTests/Evaluation/ItemSpec_Tests.cs index 74349ef360d..f0564b0dcba 100644 --- a/src/Build.UnitTests/Evaluation/ItemSpec_Tests.cs +++ b/src/Build.UnitTests/Evaluation/ItemSpec_Tests.cs @@ -85,7 +85,7 @@ private ProjectInstanceExpander CreateExpander(Dictionary item { var itemDictionary = ToItemDictionary(items); - return new ProjectInstanceExpander(new PropertyDictionary(), itemDictionary); + return new ProjectInstanceExpander(new PropertyDictionary(), itemDictionary, (IFileSystem) FileSystems.Default); } private static ItemDictionary ToItemDictionary(Dictionary itemTypes) diff --git a/src/Build.UnitTests/ExpressionTreeExpression_Tests.cs b/src/Build.UnitTests/ExpressionTreeExpression_Tests.cs index ce400029e7c..6c0dd4d9db9 100644 --- a/src/Build.UnitTests/ExpressionTreeExpression_Tests.cs +++ b/src/Build.UnitTests/ExpressionTreeExpression_Tests.cs @@ -396,7 +396,7 @@ public ExpressionTest(ITestOutputHelper output) metadataDictionary["Culture"] = "french"; StringMetadataTable itemMetadata = new StringMetadataTable(metadataDictionary); - _expander = new Expander(propertyBag, itemBag, itemMetadata); + _expander = new Expander(propertyBag, itemBag, itemMetadata, FileSystems.Default); foreach (string file in FilesWithExistenceChecks) { diff --git a/src/Build.UnitTests/ExpressionTree_Tests.cs b/src/Build.UnitTests/ExpressionTree_Tests.cs index 833f6f5b5ed..5124da86a6a 100644 --- a/src/Build.UnitTests/ExpressionTree_Tests.cs +++ b/src/Build.UnitTests/ExpressionTree_Tests.cs @@ -25,7 +25,7 @@ public class ExpressionTreeTest public void SimpleEvaluationTests() { Parser p = new Parser(); - Expander expander = new Expander(new PropertyDictionary()); + Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); AssertParseEvaluate(p, "true", expander, true); AssertParseEvaluate(p, "on", expander, true); @@ -41,7 +41,7 @@ public void SimpleEvaluationTests() public void EqualityTests() { Parser p = new Parser(); - Expander expander = new Expander(new PropertyDictionary()); + Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); AssertParseEvaluate(p, "true == on", expander, true); AssertParseEvaluate(p, "TrUe == On", expander, true); @@ -66,7 +66,7 @@ public void EqualityTests() public void RelationalTests() { Parser p = new Parser(); - Expander expander = new Expander(new PropertyDictionary()); + Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); AssertParseEvaluate(p, "1234 < 1235", expander, true); AssertParseEvaluate(p, "1234 <= 1235", expander, true); @@ -85,7 +85,7 @@ public void RelationalTests() public void AndandOrTests() { Parser p = new Parser(); - Expander expander = new Expander(new PropertyDictionary()); + Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); AssertParseEvaluate(p, "true == on and 1234 < 1235", expander, true); } @@ -97,7 +97,7 @@ public void FunctionTests() { Parser p = new Parser(); GenericExpressionNode tree; - Expander expander = new Expander(new PropertyDictionary(), new ItemDictionary()); + Expander expander = new Expander(new PropertyDictionary(), new ItemDictionary(), FileSystems.Default); expander.Metadata = new StringMetadataTable(null); bool value; @@ -149,7 +149,7 @@ public void PropertyTests() propertyBag.Set(ProjectPropertyInstance.Create("x86", "x86")); propertyBag.Set(ProjectPropertyInstance.Create("no", "no")); - Expander expander = new Expander(propertyBag, new ItemDictionary()); + Expander expander = new Expander(propertyBag, new ItemDictionary(), FileSystems.Default); AssertParseEvaluate(p, "$(foo)", expander, true); AssertParseEvaluate(p, "!$(foo)", expander, false); // Test properties with strings @@ -187,7 +187,7 @@ public void ItemListTests() itemBag.Add(new ProjectItemInstance(parentProject, "Compile", "baz.cs", parentProject.FullPath)); itemBag.Add(new ProjectItemInstance(parentProject, "Boolean", "true", parentProject.FullPath)); - Expander expander = new Expander(new PropertyDictionary(), itemBag); + Expander expander = new Expander(new PropertyDictionary(), itemBag, FileSystems.Default); AssertParseEvaluate(p, "@(Compile) == 'foo.cs;bar.cs;baz.cs'", expander, true); AssertParseEvaluate(p, "@(Compile,' ') == 'foo.cs bar.cs baz.cs'", expander, true); @@ -230,7 +230,7 @@ public void StringExpansionTests() propertyBag.Set(ProjectPropertyInstance.Create("AnotherTestQuote", "Here's Johnny!")); propertyBag.Set(ProjectPropertyInstance.Create("Atsign", "Test the @ replacement")); - Expander expander = new Expander(propertyBag, itemBag); + Expander expander = new Expander(propertyBag, itemBag, FileSystems.Default); AssertParseEvaluate(p, "'simplestring: true foo.cs;bar.cs;baz.cs' == '$(simple): $(foo) @(compile)'", expander, true); AssertParseEvaluate(p, "'$(c1) $(c2)' == 'Another (complex) one. Another (complex) one.'", expander, true); @@ -262,7 +262,7 @@ public void ComplexTests() propertyBag.Set(ProjectPropertyInstance.Create("c1", "Another (complex) one.")); propertyBag.Set(ProjectPropertyInstance.Create("c2", "Another (complex) one.")); - Expander expander = new Expander(propertyBag, itemBag); + Expander expander = new Expander(propertyBag, itemBag, FileSystems.Default); AssertParseEvaluate(p, "(($(foo) != 'two' and $(bar)) and 5 >= 1) or $(one) == 1", expander, true); AssertParseEvaluate(p, "(($(foo) != 'twoo' or !$(bar)) and 5 >= 1) or $(two) == 1", expander, true); @@ -283,7 +283,7 @@ public void InvalidItemInConditionEvaluation() PropertyDictionary propertyBag = new PropertyDictionary(); - Expander expander = new Expander(propertyBag, itemBag); + Expander expander = new Expander(propertyBag, itemBag, FileSystems.Default); AssertParseEvaluateThrow(p, "@(Compile) > 0", expander, null); } @@ -312,7 +312,7 @@ public void OldSyntaxTests() propertyBag.Set(ProjectPropertyInstance.Create("c1", "Another (complex) one.")); propertyBag.Set(ProjectPropertyInstance.Create("c2", "Another (complex) one.")); - Expander expander = new Expander(propertyBag, itemBag); + Expander expander = new Expander(propertyBag, itemBag, FileSystems.Default); AssertParseEvaluate(p, "(($(foo) != 'two' and $(bar)) and 5 >= 1) or $(one) == 1", expander, true); } @@ -329,7 +329,7 @@ public void ConditionedPropertyUpdateTests() itemBag.Add(new ProjectItemInstance(parentProject, "Compile", "bar.cs", parentProject.FullPath)); itemBag.Add(new ProjectItemInstance(parentProject, "Compile", "baz.cs", parentProject.FullPath)); - Expander expander = new Expander(new PropertyDictionary(), itemBag); + Expander expander = new Expander(new PropertyDictionary(), itemBag, FileSystems.Default); Dictionary> conditionedProperties = new Dictionary>(); ConditionEvaluator.IConditionEvaluationState state = new ConditionEvaluator.ConditionEvaluationState @@ -418,7 +418,7 @@ public void NotTests() propertyBag.Set(ProjectPropertyInstance.Create("foo", "4")); propertyBag.Set(ProjectPropertyInstance.Create("bar", "32")); - Expander expander = new Expander(propertyBag, new ItemDictionary()); + Expander expander = new Expander(propertyBag, new ItemDictionary(), FileSystems.Default); AssertParseEvaluate(p, "!true", expander, false); AssertParseEvaluate(p, "!(true)", expander, false); @@ -509,7 +509,7 @@ private void AssertParseEvaluateThrow(Parser p, string expression, Expander expander = new Expander(new PropertyDictionary()); + Expander expander = new Expander(new PropertyDictionary(), FileSystems.Default); AssertParseEvaluateThrow(p, "foo", expander); AssertParseEvaluateThrow(p, "0", expander); diff --git a/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs b/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs index faceb4c727b..1779d6ba70f 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs @@ -96,7 +96,7 @@ int bucketSequenceNumber } _metadata = metadata; - _expander = new Expander(_lookup, _lookup, new StringMetadataTable(metadata)); + _expander = new Expander(_lookup, _lookup, new StringMetadataTable(metadata), FileSystems.Default); _bucketSequenceNumber = bucketSequenceNumber; } diff --git a/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs b/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs index 0c71bf57487..d3a925b34a8 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs @@ -173,7 +173,7 @@ internal TargetEntry(BuildRequestEntry requestEntry, ITargetBuilderCallback targ _targetSpecification = targetSpecification; _parentTarget = parentTarget; _buildReason = buildReason; - _expander = new Expander(baseLookup, baseLookup); + _expander = new Expander(baseLookup, baseLookup, FileSystems.Default); _state = TargetEntryState.Dependencies; _baseLookup = baseLookup; _host = host; diff --git a/src/Build/Definition/ProjectItem.cs b/src/Build/Definition/ProjectItem.cs index bb1aa48eaa2..0b3c28e1562 100644 --- a/src/Build/Definition/ProjectItem.cs +++ b/src/Build/Definition/ProjectItem.cs @@ -490,7 +490,7 @@ string IItem.GetMetadataValueEscaped(string name) if (metadatum != null && Expander.ExpressionMayContainExpandableExpressions(metadatum.EvaluatedValueEscaped)) { - Expander expander = new Expander(null, null, new BuiltInMetadataTable(this)); + Expander expander = new Expander(null, null, new BuiltInMetadataTable(this), FileSystems.Default); value = expander.ExpandIntoStringLeaveEscaped(metadatum.EvaluatedValueEscaped, ExpanderOptions.ExpandBuiltInMetadata, metadatum.Location); } diff --git a/src/Build/Definition/Toolset.cs b/src/Build/Definition/Toolset.cs index ca57f96dd15..77d32963bf6 100644 --- a/src/Build/Definition/Toolset.cs +++ b/src/Build/Definition/Toolset.cs @@ -967,7 +967,7 @@ private void InitializeProperties(ILoggingService loggingServices, BuildEventCon if (_expander == null) { - _expander = new Expander(_propertyBag); + _expander = new Expander(_propertyBag, FileSystems.Default); } } catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) diff --git a/src/Build/Definition/ToolsetReader.cs b/src/Build/Definition/ToolsetReader.cs index 3b241b8b1a7..869b3318ea4 100644 --- a/src/Build/Definition/ToolsetReader.cs +++ b/src/Build/Definition/ToolsetReader.cs @@ -451,7 +451,7 @@ bool accumulateProperties IEnumerable rawProperties = GetPropertyDefinitions(toolsVersion.Name); - Expander expander = new Expander(initialProperties); + Expander expander = new Expander(initialProperties, FileSystems.Default); foreach (ToolsetPropertyDefinition property in rawProperties) { @@ -668,7 +668,7 @@ private void EvaluateAndSetProperty(ToolsetPropertyDefinition property, Property if (accumulateProperties) { - expander = new Expander(initialProperties); + expander = new Expander(initialProperties, FileSystems.Default); } } diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 19510f6d663..5bc1e2ec3df 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.FileSystem; diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index cc629ee8939..1fe132a8d21 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -311,11 +311,11 @@ private void FlushFirstValueIfNeeded() /// Creates an expander passing it some properties to use. /// Properties may be null. ///
- internal Expander(IPropertyProvider

properties) + internal Expander(IPropertyProvider

properties, IFileSystem fileSystem) { _properties = properties; _usedUninitializedProperties = new UsedUninitializedProperties(); - _fileSystem = FileSystems.Default; + _fileSystem = fileSystem; } ///

@@ -334,8 +334,8 @@ internal Expander(IPropertyProvider

properties, EvaluationContext evaluationC /// Creates an expander passing it some properties and items to use. /// Either or both may be null. ///

- internal Expander(IPropertyProvider

properties, IItemProvider items) - : this(properties) + internal Expander(IPropertyProvider

properties, IItemProvider items, IFileSystem fileSystem) + : this(properties, fileSystem) { _items = items; } @@ -354,8 +354,8 @@ internal Expander(IPropertyProvider

properties, IItemProvider items, Evalu /// Creates an expander passing it some properties, items, and/or metadata to use. /// Any or all may be null. ///

- internal Expander(IPropertyProvider

properties, IItemProvider items, IMetadataTable metadata) - : this(properties, items) + internal Expander(IPropertyProvider

properties, IItemProvider items, IMetadataTable metadata, IFileSystem fileSystem) + : this(properties, items, fileSystem) { _metadata = metadata; } diff --git a/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs b/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs index c493969feb5..fc3996350a3 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs @@ -7,8 +7,8 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.Construction; -using Microsoft.Build.Evaluation.Context; using Microsoft.Build.Execution; +using Microsoft.Build.Evaluation.Context; namespace Microsoft.Build.Evaluation { diff --git a/src/Build/Evaluation/LazyItemEvaluator.cs b/src/Build/Evaluation/LazyItemEvaluator.cs index ada3da6a27f..f5d52eed4ee 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.cs @@ -29,7 +29,7 @@ internal partial class LazyItemEvaluator private readonly Expander _outerExpander; private readonly IEvaluatorData _evaluatorData; private readonly Expander _expander; - private readonly IItemFactory _itemFactory; + protected readonly IItemFactory _itemFactory; private readonly LoggingContext _loggingContext; private readonly EvaluationProfiler _evaluationProfiler; diff --git a/src/Build/Instance/ProjectInstance.cs b/src/Build/Instance/ProjectInstance.cs index 6d084b857a6..502370f337a 100644 --- a/src/Build/Instance/ProjectInstance.cs +++ b/src/Build/Instance/ProjectInstance.cs @@ -1876,7 +1876,7 @@ public bool Build(string[] targets, IEnumerable loggers, IEnumerable public string ExpandString(string unexpandedValue) { - Expander expander = new Expander(this, this); + Expander expander = new Expander(this, this, FileSystems.Default); string result = expander.ExpandIntoStringAndUnescape(unexpandedValue, ExpanderOptions.ExpandPropertiesAndItems, ProjectFileLocation); @@ -1894,7 +1894,7 @@ public string ExpandString(string unexpandedValue) /// public bool EvaluateCondition(string condition) { - Expander expander = new Expander(this, this); + Expander expander = new Expander(this, this, FileSystems.Default); bool result = ConditionEvaluator.EvaluateCondition( condition, @@ -2761,7 +2761,7 @@ out var usingDifferentToolsVersionFromProjectFile Evaluator.Evaluate( this, - null, + null, // TODO? xml, projectLoadSettings ?? buildParameters.ProjectLoadSettings, /* Use override ProjectLoadSettings if specified */ buildParameters.MaxNodeCount, diff --git a/src/Build/Instance/ProjectItemInstance.cs b/src/Build/Instance/ProjectItemInstance.cs index 94e9d311be8..2a34f84a2d9 100644 --- a/src/Build/Instance/ProjectItemInstance.cs +++ b/src/Build/Instance/ProjectItemInstance.cs @@ -1330,7 +1330,7 @@ public string GetMetadataEscaped(string metadataName) if (metadatum != null && Expander.ExpressionMayContainExpandableExpressions(metadatum.EvaluatedValueEscaped)) { - Expander expander = new Expander(null, null, new BuiltInMetadataTable(null, this)); + Expander expander = new Expander(null, null, new BuiltInMetadataTable(null, this), FileSystems.Default); // We don't have a location to use, but this is very unlikely to error return expander.ExpandIntoStringLeaveEscaped(metadatum.EvaluatedValueEscaped, ExpanderOptions.ExpandBuiltInMetadata, ElementLocation.EmptyLocation); diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index b1f8aa45166..466ece84f00 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -94,7 +94,7 @@ public FileMatcher(IFileSystem fileSystem, ConcurrentDictionary> getFileSystemDirectoryEntriesCache = null) + internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemEntries, ConcurrentDictionary> fileEntryExpansionCache = null) { if (Traits.Instance.MSBuildCacheFileEnumerations) { @@ -103,12 +103,12 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE } else { - _cachedGlobExpansions = getFileSystemDirectoryEntriesCache; + _cachedGlobExpansions = fileEntryExpansionCache; } _fileSystem = fileSystem; - _getFileSystemEntries = getFileSystemDirectoryEntriesCache == null + _getFileSystemEntries = fileEntryExpansionCache == null ? getFileSystemEntries : (type, path, pattern, directory, stripProjectDirectory) => { @@ -123,7 +123,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE FileSystemEntity.FilesAndDirectories => "A", _ => throw new NotImplementedException() } + ";" + path; - IReadOnlyList allEntriesForPath = getFileSystemDirectoryEntriesCache.GetOrAdd( + IReadOnlyList allEntriesForPath = fileEntryExpansionCache.GetOrAdd( cacheKey, s => getFileSystemEntries( type, @@ -144,7 +144,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE // Cache only directories, for files we won't hit the cache because the file name patterns tend to be unique if (type == FileSystemEntity.Directories) { - return getFileSystemDirectoryEntriesCache.GetOrAdd( + return fileEntryExpansionCache.GetOrAdd( $"D;{path};{pattern ?? "*"}", s => getFileSystemEntries( type, From 89f7cc6af405419aee344b3afa4cb5bdb04dd501 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Fri, 15 Oct 2021 12:22:42 +0200 Subject: [PATCH 19/23] Redo some of the minor tweaks reverted in previous commit --- src/Build/Evaluation/Context/EvaluationContext.cs | 1 - src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs | 2 +- src/Build/Instance/ProjectInstance.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 5bc1e2ec3df..19510f6d663 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.FileSystem; diff --git a/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs b/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs index fc3996350a3..c493969feb5 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.EvaluatorData.cs @@ -7,8 +7,8 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.Construction; -using Microsoft.Build.Execution; using Microsoft.Build.Evaluation.Context; +using Microsoft.Build.Execution; namespace Microsoft.Build.Evaluation { diff --git a/src/Build/Instance/ProjectInstance.cs b/src/Build/Instance/ProjectInstance.cs index 502370f337a..35795e9d705 100644 --- a/src/Build/Instance/ProjectInstance.cs +++ b/src/Build/Instance/ProjectInstance.cs @@ -2761,7 +2761,7 @@ out var usingDifferentToolsVersionFromProjectFile Evaluator.Evaluate( this, - null, // TODO? + null, xml, projectLoadSettings ?? buildParameters.ProjectLoadSettings, /* Use override ProjectLoadSettings if specified */ buildParameters.MaxNodeCount, From e800fa86e2601b3abc30912e33f0e4e5228231d5 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Fri, 15 Oct 2021 14:17:25 +0200 Subject: [PATCH 20/23] Optimize path manipulations (Microsoft.IO.Redist) --- .../DirectoryCacheFileSystemWrapper.cs | 7 ++-- src/Shared/FileMatcher.cs | 35 +++++++++++++++---- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs index d28d105ca3c..34e35a566be 100644 --- a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs +++ b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs @@ -8,6 +8,10 @@ using System.IO; using System.Linq; +#if FEATURE_MSIOREDIST +using Path = Microsoft.IO.Path; +#endif + namespace Microsoft.Build.FileSystem { internal class DirectoryCacheFileSystemWrapper : IFileSystem @@ -81,8 +85,7 @@ private IEnumerable EnumerateFullFileSystemPaths(string path, string sea { return FileMatcher.IsAllFilesWildcard(searchPattern) || FileMatcher.IsMatch(fileName, searchPattern); }; - // TODO: Don't create a new string and use Path.Join when it becomes available. - FindTransform transform = (ref ReadOnlySpan fileName) => Path.Combine(path, fileName.ToString()); + FindTransform transform = (ref ReadOnlySpan fileName) => Path.Join(path.AsSpan(), fileName); IEnumerable directories = includeDirectories ? _directoryCache.EnumerateDirectories(path, searchPattern, predicate, transform) diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index 466ece84f00..7eb3ca6700a 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -132,7 +132,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE directory, false)); IEnumerable filteredEntriesForPath = (pattern != null && !IsAllFilesWildcard(pattern)) - ? allEntriesForPath.Where(o => IsMatch(Path.GetFileName(o), pattern)) + ? allEntriesForPath.Where(o => IsFileNameMatch(o, pattern)) : allEntriesForPath; return stripProjectDirectory ? RemoveProjectDirectory(filteredEntriesForPath, directory).ToArray() @@ -268,7 +268,7 @@ private static IReadOnlyList GetAccessibleFilesAndDirectories(IFileSyste { return (ShouldEnforceMatching(pattern) ? fileSystem.EnumerateFileSystemEntries(path, pattern) - .Where(o => IsMatch(Path.GetFileName(o), pattern)) + .Where(o => IsFileNameMatch(o, pattern)) : fileSystem.EnumerateFileSystemEntries(path, pattern) ).ToArray(); } @@ -351,7 +351,7 @@ bool stripProjectDirectory files = fileSystem.EnumerateFiles(dir, filespec); if (ShouldEnforceMatching(filespec)) { - files = files.Where(o => IsMatch(Path.GetFileName(o), filespec)); + files = files.Where(o => IsFileNameMatch(o, filespec)); } } // If the Item is based on a relative path we need to strip @@ -414,7 +414,7 @@ string pattern directories = fileSystem.EnumerateDirectories((path.Length == 0) ? s_thisDirectory : path, pattern); if (ShouldEnforceMatching(pattern)) { - directories = directories.Where(o => IsMatch(Path.GetFileName(o), pattern)); + directories = directories.Where(o => IsFileNameMatch(o, pattern)); } } @@ -956,7 +956,7 @@ private void GetFilesRecursive( for (int i = 0; i < excludeNextSteps.Length; i++) { if (excludeNextSteps[i].NeedsDirectoryRecursion && - (excludeNextSteps[i].DirectoryPattern == null || IsMatch(Path.GetFileName(subdir), excludeNextSteps[i].DirectoryPattern))) + (excludeNextSteps[i].DirectoryPattern == null || IsFileNameMatch(subdir, excludeNextSteps[i].DirectoryPattern))) { RecursionState thisExcludeStep = searchesToExclude[i]; thisExcludeStep.BaseDirectory = subdir; @@ -1097,7 +1097,7 @@ private static bool MatchFileRecursionStep(RecursionState recursionState, string } else if (recursionState.SearchData.Filespec != null) { - return IsMatch(Path.GetFileName(file), recursionState.SearchData.Filespec); + return IsFileNameMatch(file, recursionState.SearchData.Filespec); } // if no file-spec provided, match the file to the regular expression @@ -1664,7 +1664,28 @@ internal Result() internal string wildcardDirectoryPart = string.Empty; } - // TODO: Remove this overload and fix callers to pass ReadOnlySpan. + ///

+ /// A wildcard (* and ?) matching algorithm that tests whether the input path file name matches against the pattern. + /// + /// The path whose file name is matched against the pattern. + /// The pattern. + internal static bool IsFileNameMatch(string path, string pattern) + { + // Use a span-based Path.GetFileName if it is available. +#if FEATURE_MSIOREDIST + return IsMatch(Microsoft.IO.Path.GetFileName(path.AsSpan()), pattern); +#elif NETSTANDARD2_0 + return IsMatch(Path.GetFileName(path), pattern); +#else + return IsMatch(Path.GetFileName(path.AsSpan()), pattern); +#endif + } + + /// + /// A wildcard (* and ?) matching algorithm that tests whether the input string matches against the pattern. + /// + /// String which is matched against the pattern. + /// Pattern against which string is matched. internal static bool IsMatch(string input, string pattern) { return IsMatch(input.AsSpan(), pattern); From 231a8693f21e4bf57352b8665d1f45e402d7c9db Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Fri, 15 Oct 2021 15:25:00 +0200 Subject: [PATCH 21/23] Tweaks and comments --- src/Build/Definition/Project.cs | 3 +- src/Build/Evaluation/LazyItemEvaluator.cs | 2 +- .../DirectoryCacheFileSystemWrapper.cs | 30 ++++--------------- src/Build/FileSystem/IDirectoryCache.cs | 2 -- src/Shared/FileMatcher.cs | 10 +++---- 5 files changed, 14 insertions(+), 33 deletions(-) diff --git a/src/Build/Definition/Project.cs b/src/Build/Definition/Project.cs index 8ba6bbbfc8b..731950b2405 100644 --- a/src/Build/Definition/Project.cs +++ b/src/Build/Definition/Project.cs @@ -1788,7 +1788,8 @@ internal void VerifyThrowInvalidOperationNotImported(ProjectRootElement otherXml /// project, specific for a given evaluation ID. ///
/// The evaluation ID for which the cache is requested. - /// + /// An implementation, or null if this project has no + /// associated with it or it returned null. internal IDirectoryCache GetDirectoryCacheForEvaluation(int evaluationId) { return _directoryCacheFactory?.GetDirectoryCacheForEvaluation(evaluationId); diff --git a/src/Build/Evaluation/LazyItemEvaluator.cs b/src/Build/Evaluation/LazyItemEvaluator.cs index f5d52eed4ee..ada3da6a27f 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.cs @@ -29,7 +29,7 @@ internal partial class LazyItemEvaluator private readonly Expander _outerExpander; private readonly IEvaluatorData _evaluatorData; private readonly Expander _expander; - protected readonly IItemFactory _itemFactory; + private readonly IItemFactory _itemFactory; private readonly LoggingContext _loggingContext; private readonly EvaluationProfiler _evaluationProfiler; diff --git a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs index 34e35a566be..84c24fb02cc 100644 --- a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs +++ b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs @@ -101,35 +101,17 @@ private IEnumerable EnumerateFullFileSystemPaths(string path, string sea #region IFileSystem pass-through implementation - public FileAttributes GetAttributes(string path) - { - return _fileSystem.GetAttributes(path); - } + public FileAttributes GetAttributes(string path) => _fileSystem.GetAttributes(path); - public DateTime GetLastWriteTimeUtc(string path) - { - return _fileSystem.GetLastWriteTimeUtc(path); - } + public DateTime GetLastWriteTimeUtc(string path) => _fileSystem.GetLastWriteTimeUtc(path); - public TextReader ReadFile(string path) - { - return _fileSystem.ReadFile(path); - } + public TextReader ReadFile(string path) => _fileSystem.ReadFile(path); - public Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share) - { - return _fileSystem.GetFileStream(path, mode, access, share); - } + public Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share) => _fileSystem.GetFileStream(path, mode, access, share); - public string ReadFileAllText(string path) - { - return _fileSystem.ReadFileAllText(path); - } + public string ReadFileAllText(string path) => _fileSystem.ReadFileAllText(path); - public byte[] ReadFileAllBytes(string path) - { - return _fileSystem.ReadFileAllBytes(path); - } + public byte[] ReadFileAllBytes(string path) => _fileSystem.ReadFileAllBytes(path); #endregion } diff --git a/src/Build/FileSystem/IDirectoryCache.cs b/src/Build/FileSystem/IDirectoryCache.cs index 6567bf8a65d..3938bfc41d8 100644 --- a/src/Build/FileSystem/IDirectoryCache.cs +++ b/src/Build/FileSystem/IDirectoryCache.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; -using Microsoft.Build.Evaluation; - namespace Microsoft.Build.FileSystem { /// diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index 7eb3ca6700a..22a9572fc82 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -94,7 +94,7 @@ public FileMatcher(IFileSystem fileSystem, ConcurrentDictionary> fileEntryExpansionCache = null) + internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemEntries, ConcurrentDictionary> getFileSystemDirectoryEntriesCache = null) { if (Traits.Instance.MSBuildCacheFileEnumerations) { @@ -103,12 +103,12 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE } else { - _cachedGlobExpansions = fileEntryExpansionCache; + _cachedGlobExpansions = getFileSystemDirectoryEntriesCache; } _fileSystem = fileSystem; - _getFileSystemEntries = fileEntryExpansionCache == null + _getFileSystemEntries = getFileSystemDirectoryEntriesCache == null ? getFileSystemEntries : (type, path, pattern, directory, stripProjectDirectory) => { @@ -123,7 +123,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE FileSystemEntity.FilesAndDirectories => "A", _ => throw new NotImplementedException() } + ";" + path; - IReadOnlyList allEntriesForPath = fileEntryExpansionCache.GetOrAdd( + IReadOnlyList allEntriesForPath = getFileSystemDirectoryEntriesCache.GetOrAdd( cacheKey, s => getFileSystemEntries( type, @@ -144,7 +144,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE // Cache only directories, for files we won't hit the cache because the file name patterns tend to be unique if (type == FileSystemEntity.Directories) { - return fileEntryExpansionCache.GetOrAdd( + return getFileSystemDirectoryEntriesCache.GetOrAdd( $"D;{path};{pattern ?? "*"}", s => getFileSystemEntries( type, From 2348cd8ca67bef75fd039ad6e5eabaeae830ffd2 Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Wed, 20 Oct 2021 12:51:00 +0200 Subject: [PATCH 22/23] Remove unused parameter --- src/Build/Definition/Project.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Build/Definition/Project.cs b/src/Build/Definition/Project.cs index 731950b2405..ff29b8ec3a5 100644 --- a/src/Build/Definition/Project.cs +++ b/src/Build/Definition/Project.cs @@ -267,7 +267,7 @@ private Project(ProjectRootElement xml, IDictionary globalProper ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(toolsVersion, nameof(toolsVersion)); ErrorUtilities.VerifyThrowArgumentNull(projectCollection, nameof(projectCollection)); ProjectCollection = projectCollection; - var defaultImplementation = new ProjectImpl(this, xml, globalProperties, toolsVersion, subToolsetVersion, loadSettings, evaluationContext); + var defaultImplementation = new ProjectImpl(this, xml, globalProperties, toolsVersion, subToolsetVersion, loadSettings); implementationInternal = (IProjectLinkInternal)defaultImplementation; implementation = defaultImplementation; @@ -1857,8 +1857,7 @@ private class ProjectImpl : ProjectLink, IProjectLinkInternal /// Tools version to evaluate with. May be null. /// Sub-toolset version to explicitly evaluate the toolset with. May be null. /// The to use for evaluation. - /// The evaluation context to use in case reevaluation is required. - public ProjectImpl(Project owner, ProjectRootElement xml, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext) + public ProjectImpl(Project owner, ProjectRootElement xml, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectLoadSettings loadSettings) { ErrorUtilities.VerifyThrowArgumentNull(xml, nameof(xml)); ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(toolsVersion, nameof(toolsVersion)); From e30d2393d86cbf24b81a189ff9636fb9d77302ff Mon Sep 17 00:00:00 2001 From: Ladi Prosek Date: Thu, 21 Oct 2021 11:50:49 +0200 Subject: [PATCH 23/23] Add and update comments --- src/Build/Evaluation/Evaluator.cs | 3 +++ src/Build/FileSystem/IDirectoryCache.cs | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index db93150bdf1..e72f28469c6 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -144,6 +144,9 @@ internal class Evaluator /// private readonly int _submissionId; + /// + /// The evaluation context to use. + /// private readonly EvaluationContext _evaluationContext; /// diff --git a/src/Build/FileSystem/IDirectoryCache.cs b/src/Build/FileSystem/IDirectoryCache.cs index 3938bfc41d8..fb6e62c1b6a 100644 --- a/src/Build/FileSystem/IDirectoryCache.cs +++ b/src/Build/FileSystem/IDirectoryCache.cs @@ -12,6 +12,8 @@ namespace Microsoft.Build.FileSystem /// /// /// Unlike , file enumeration returns file/directory names, not full paths. + /// The host uses to specify the directory cache + /// factory per project. /// public interface IDirectoryCacheFactory { @@ -43,20 +45,20 @@ public interface IDirectoryCache /// /// Returns true if the given path points to an existing file on disk. /// - /// A normalized path. + /// A full and normalized path. bool FileExists(string path); /// /// Returns true if the given path points to an existing directory on disk. /// - /// A normalized path. + /// A full and normalized path. bool DirectoryExists(string path); /// /// Enumerates files in the given directory only (non-recursively). /// /// The desired return type. - /// The directory to enumerate. + /// The directory to enumerate, specified as a full normalized path. /// A search pattern supported by the platform which is guaranteed to return a superset of relevant files. /// A predicate to test whether a file should be included. /// A transform from ReadOnlySpan<char> to . @@ -72,7 +74,7 @@ public interface IDirectoryCache /// Enumerates subdirectories in the given directory only (non-recursively). ///
/// The desired return type. - /// The directory to enumerate. + /// The directory to enumerate, specified as a full normalized path. /// A search pattern supported by the platform which is guaranteed to return a superset of relevant directories. /// A predicate to test whether a directory should be included. /// A transform from ReadOnlySpan<char> to .