diff --git a/eng/Packages.props b/eng/Packages.props index b4e087118a4..1a2171602ba 100644 --- a/eng/Packages.props +++ b/eng/Packages.props @@ -13,6 +13,7 @@ + diff --git a/scripts/Deploy-MSBuild.ps1 b/scripts/Deploy-MSBuild.ps1 index 07b58d1f82d..f97b9ad94f5 100644 --- a/scripts/Deploy-MSBuild.ps1 +++ b/scripts/Deploy-MSBuild.ps1 @@ -98,6 +98,7 @@ if ($runtime -eq "Desktop") { FileToCopy "$bootstrapBinDirectory\Microsoft.Bcl.AsyncInterfaces.dll" FileToCopy "$bootstrapBinDirectory\Microsoft.Data.Entity.targets" + FileToCopy "$bootstrapBinDirectory\Microsoft.IO.Redist.dll" FileToCopy "$bootstrapBinDirectory\Microsoft.ServiceModel.targets" FileToCopy "$bootstrapBinDirectory\Microsoft.WinFx.targets" FileToCopy "$bootstrapBinDirectory\Microsoft.WorkflowBuildExtensions.targets" diff --git a/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj b/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj index 18ad711bac4..378c5127883 100644 --- a/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj +++ b/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj @@ -20,6 +20,8 @@ + + diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index d08e337c5da..f7540863381 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -36,6 +36,8 @@ + + diff --git a/src/Directory.BeforeCommon.targets b/src/Directory.BeforeCommon.targets index 913c97b1281..ce322f9833b 100644 --- a/src/Directory.BeforeCommon.targets +++ b/src/Directory.BeforeCommon.targets @@ -142,4 +142,9 @@ $(IntermediateOutputPath)\$(AssemblyName).xml + + $(DefineConstants);FEATURE_MSIOREDIST + true + + diff --git a/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj b/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj index b73c360d59d..4358e5a16a8 100644 --- a/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj +++ b/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj @@ -11,6 +11,7 @@ + diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj index 93185ce820a..17594f48d2f 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -229,6 +229,7 @@ + diff --git a/src/Package/MSBuild.Engine.Corext/MsBuild.Engine.Corext.nuspec b/src/Package/MSBuild.Engine.Corext/MsBuild.Engine.Corext.nuspec index 2918e172a5c..04a42dbfce4 100644 --- a/src/Package/MSBuild.Engine.Corext/MsBuild.Engine.Corext.nuspec +++ b/src/Package/MSBuild.Engine.Corext/MsBuild.Engine.Corext.nuspec @@ -31,6 +31,7 @@ + @@ -87,6 +88,7 @@ + diff --git a/src/Package/MSBuild.VSSetup/files.swr b/src/Package/MSBuild.VSSetup/files.swr index 67e5764974d..fc7164e4899 100644 --- a/src/Package/MSBuild.VSSetup/files.swr +++ b/src/Package/MSBuild.VSSetup/files.swr @@ -32,6 +32,7 @@ folder InstallDir:\MSBuild\Current\Bin file source=$(X86BinPath)Microsoft.Build.Framework.tlb file source=$(X86BinPath)Microsoft.Build.Tasks.Core.dll vs.file.ngenApplications="[installDir]\Common7\IDE\vsn.exe" vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 file source=$(X86BinPath)Microsoft.Build.Utilities.Core.dll vs.file.ngenApplications="[installDir]\Common7\IDE\vsn.exe" vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)Microsoft.IO.Redist.dll vs.file.ngenApplications="[installDir]\Common7\IDE\vsn.exe" vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 file source=$(X86BinPath)MSBuild.exe vs.file.ngenArchitecture=x86 vs.file.ngenPriority=1 file source=$(X86BinPath)MSBuild.exe.config file source=$(TaskHostBinPath)MSBuildTaskHost.exe vs.file.ngenArchitecture=x86 @@ -184,6 +185,7 @@ folder InstallDir:\MSBuild\Current\Bin\amd64 file source=$(X86BinPath)System.Memory.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)System.Text.Json.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)Microsoft.Bcl.AsyncInterfaces.dll vs.file.ngenArchitecture=all + file source=$(X86BinPath)Microsoft.IO.Redist.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)System.Text.Encodings.Web.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)System.Threading.Tasks.Extensions.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)System.Numerics.Vectors.dll vs.file.ngenArchitecture=all diff --git a/src/Shared/FileSystem/ManagedFileSystem.cs b/src/Shared/FileSystem/ManagedFileSystem.cs index 6d8bd32fb4e..d5d80a07e35 100644 --- a/src/Shared/FileSystem/ManagedFileSystem.cs +++ b/src/Shared/FileSystem/ManagedFileSystem.cs @@ -1,6 +1,7 @@ // 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.Utilities; using System; using System.Collections.Generic; using System.IO; @@ -16,6 +17,20 @@ internal class ManagedFileSystem : IFileSystem public static ManagedFileSystem Singleton() => ManagedFileSystem.Instance; + private static bool ShouldUseMicrosoftIO + { + get + { +#if !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS + return ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0); +#else + // We need to mock usage of ChangeWaves class, + // because Microsoft.Build.Engine.OM.UnitTests should not have access to internals of Microsoft.Build.Framework. + return true; +#endif + } + } + protected ManagedFileSystem() { } public TextReader ReadFile(string path) @@ -38,19 +53,78 @@ public byte[] ReadFileAllBytes(string path) return File.ReadAllBytes(path); } +#if FEATURE_MSIOREDIST + private static IEnumerable HandleFileLoadException( + Func> enumerateFunctionDelegate, + string path, + string searchPattern, + Microsoft.IO.SearchOption searchOption + ) + { + try + { + return enumerateFunctionDelegate(path, searchPattern, searchOption); + } + // Microsoft.IO.Redist has a dependency on System.Buffers and if System.Buffers assembly is not found the line above throws an exception. + // However, FileMatcher class (that in most cases calls the enumeration) does not allow to fail on a IO-related exception. Such behavior hides the actual exception and makes it obscure. + // We rethrow it to make it fail with a proper error message and call stack. + catch (FileLoadException ex) + { + throw new InvalidOperationException(ex.Message, ex); + } + // Sometimes FileNotFoundException is thrown when there is an assembly load failure. In this case it should have FusionLog. + catch (FileNotFoundException ex) when (ex.FusionLog != null) + { + throw new InvalidOperationException(ex.Message, ex); + } + } +#endif + public virtual IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) { +#if FEATURE_MSIOREDIST + return ShouldUseMicrosoftIO + ? HandleFileLoadException( + (path, searchPattern, searchOption) => Microsoft.IO.Directory.EnumerateFiles(path, searchPattern, searchOption), + path, + searchPattern, + (Microsoft.IO.SearchOption)searchOption + ) + : Directory.EnumerateFiles(path, searchPattern, searchOption); +#else return Directory.EnumerateFiles(path, searchPattern, searchOption); +#endif } public virtual IEnumerable EnumerateDirectories(string path, string searchPattern, SearchOption searchOption) { +#if FEATURE_MSIOREDIST + return ShouldUseMicrosoftIO + ? HandleFileLoadException( + (path, searchPattern, searchOption) => Microsoft.IO.Directory.EnumerateDirectories(path, searchPattern, searchOption), + path, + searchPattern, + (Microsoft.IO.SearchOption)searchOption + ) + : Directory.EnumerateDirectories(path, searchPattern, searchOption); +#else return Directory.EnumerateDirectories(path, searchPattern, searchOption); +#endif } public virtual IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption) { +#if FEATURE_MSIOREDIST + return ShouldUseMicrosoftIO + ? HandleFileLoadException( + (path, searchPattern, searchOption) => Microsoft.IO.Directory.EnumerateFileSystemEntries(path, searchPattern, searchOption), + path, + searchPattern, (Microsoft.IO.SearchOption)searchOption + ) + : Directory.EnumerateFileSystemEntries(path, searchPattern, searchOption); +#else return Directory.EnumerateFileSystemEntries(path, searchPattern, searchOption); +#endif } public FileAttributes GetAttributes(string path) diff --git a/src/Shared/UnitTests/FileMatcher_Tests.cs b/src/Shared/UnitTests/FileMatcher_Tests.cs index d8cf2eeed4c..471e1536b63 100644 --- a/src/Shared/UnitTests/FileMatcher_Tests.cs +++ b/src/Shared/UnitTests/FileMatcher_Tests.cs @@ -12,6 +12,7 @@ using Microsoft.Build.Shared.FileSystem; using Xunit; using Xunit.Abstractions; +using Microsoft.Build.Utilities; namespace Microsoft.Build.UnitTests { @@ -1244,13 +1245,20 @@ public void IllegalPaths() [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Nothing's too long for Unix [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp)] - public void IllegalTooLongPath() + public void IllegalTooLongPathOptOutWave17_0() { - string longString = new string('X', 500) + "*"; // need a wildcard to do anything - string[] result = FileMatcher.Default.GetFiles(@"c:\", longString); + using (var env = TestEnvironment.Create()) + { + ChangeWaves.ResetStateForTests(); + env.SetEnvironmentVariable("MSBUILDDISABLEFEATURESFROMVERSION", ChangeWaves.Wave17_0.ToString()); + BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(); - Assert.Equal(longString, result[0]); // Does not throw + string longString = new string('X', 500) + "*"; // need a wildcard to do anything + string[] result = FileMatcher.Default.GetFiles(@"c:\", longString); + Assert.Equal(longString, result[0]); // Does not throw + ChangeWaves.ResetStateForTests(); + } // Not checking that GetFileSpecMatchInfo returns the illegal-path flag, // not certain that won't break something; this fix is merely to avoid a crash. } diff --git a/src/Tasks/Microsoft.Build.Tasks.csproj b/src/Tasks/Microsoft.Build.Tasks.csproj index f7c4b9555f1..69933efefbf 100644 --- a/src/Tasks/Microsoft.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.Build.Tasks.csproj @@ -988,6 +988,8 @@ + + diff --git a/src/Utilities/Microsoft.Build.Utilities.csproj b/src/Utilities/Microsoft.Build.Utilities.csproj index 09634e72315..da7f065bffe 100644 --- a/src/Utilities/Microsoft.Build.Utilities.csproj +++ b/src/Utilities/Microsoft.Build.Utilities.csproj @@ -21,6 +21,7 @@ +