-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Migrate GetReferenceAssemblyPaths task to TaskEnvironment API #13495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -328,6 +328,93 @@ public void TestGeneralFrameworkMonikerGoodWithFrameworkInFallbackPaths() | |
| Assert.Equal(".NET Framework 4.1", displayName); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test that a relative RootPath resolved via TaskEnvironment produces the same result as an absolute RootPath. | ||
| /// </summary> | ||
| [Fact] | ||
| public void TestRelativeRootPathProducesSameResultAsAbsolute() | ||
| { | ||
| using (var env = TestEnvironment.Create()) | ||
| { | ||
| string baseDir = env.DefaultTestDirectory.Path; | ||
| string relativeDir = "framework-root"; | ||
| string absoluteDir = Path.Combine(baseDir, relativeDir); | ||
| var framework41Directory = env.CreateFolder(Path.Combine(absoluteDir, Path.Combine("MyFramework", "v4.1") + Path.DirectorySeparatorChar)); | ||
| var redistListDirectory = env.CreateFolder(Path.Combine(framework41Directory.Path, "RedistList")); | ||
| env.CreateFile(redistListDirectory, "FrameworkList.xml", | ||
| "<FileList Redist='Microsoft-Windows-CLRCoreComp' Name='.NET Framework 4.1'>" + | ||
| "<File AssemblyName='System.Xml' Version='2.0.0.0' PublicKeyToken='b03f5f7f11d50a3a' Culture='Neutral' FileVersion='2.0.50727.208' InGAC='true' />" + | ||
| "</FileList >"); | ||
|
|
||
| // Baseline: absolute RootPath | ||
| MockEngine absoluteEngine = new MockEngine(); | ||
| GetReferenceAssemblyPaths absoluteTask = new GetReferenceAssemblyPaths(); | ||
| absoluteTask.BuildEngine = absoluteEngine; | ||
| absoluteTask.TargetFrameworkMoniker = "MyFramework, Version=v4.1"; | ||
| absoluteTask.RootPath = absoluteDir; | ||
| absoluteTask.Execute(); | ||
|
|
||
| // Test: relative RootPath with TaskEnvironment | ||
| MockEngine relativeEngine = new MockEngine(); | ||
| GetReferenceAssemblyPaths relativeTask = new GetReferenceAssemblyPaths(); | ||
| relativeTask.BuildEngine = relativeEngine; | ||
| relativeTask.TargetFrameworkMoniker = "MyFramework, Version=v4.1"; | ||
| relativeTask.TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(baseDir); | ||
| relativeTask.RootPath = relativeDir; | ||
| relativeTask.Execute(); | ||
|
|
||
| Assert.Equal(absoluteTask.ReferenceAssemblyPaths, relativeTask.ReferenceAssemblyPaths); | ||
| Assert.Equal(absoluteTask.TargetFrameworkMonikerDisplayName, relativeTask.TargetFrameworkMonikerDisplayName); | ||
| Assert.Equal(0, relativeEngine.Errors); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test that a relative path in TargetFrameworkFallbackSearchPaths resolved via TaskEnvironment | ||
| /// produces the same result as an absolute fallback path. | ||
| /// </summary> | ||
| [Fact] | ||
| public void TestRelativeFallbackSearchPathProducesSameResultAsAbsolute() | ||
| { | ||
| using (var env = TestEnvironment.Create()) | ||
| { | ||
| string baseDir = env.DefaultTestDirectory.Path; | ||
|
Comment on lines
+378
to
+382
|
||
| string relativeDir = "framework-root"; | ||
| string absoluteDir = Path.Combine(baseDir, relativeDir); | ||
| var framework41Directory = env.CreateFolder(Path.Combine(absoluteDir, Path.Combine("MyFramework", "v4.1") + Path.DirectorySeparatorChar)); | ||
| var redistListDirectory = env.CreateFolder(Path.Combine(framework41Directory.Path, "RedistList")); | ||
| env.CreateFile(redistListDirectory, "FrameworkList.xml", | ||
| "<FileList Redist='Microsoft-Windows-CLRCoreComp' Name='.NET Framework 4.1'>" + | ||
| "<File AssemblyName='System.Xml' Version='2.0.0.0' PublicKeyToken='b03f5f7f11d50a3a' Culture='Neutral' FileVersion='2.0.50727.208' InGAC='true' />" + | ||
| "</FileList >"); | ||
|
|
||
| string nonExistentRoot = Path.Combine(baseDir, "nonexistent"); | ||
|
|
||
| // Baseline: absolute fallback path | ||
| MockEngine absoluteEngine = new MockEngine(); | ||
| GetReferenceAssemblyPaths absoluteTask = new GetReferenceAssemblyPaths(); | ||
| absoluteTask.BuildEngine = absoluteEngine; | ||
| absoluteTask.TargetFrameworkMoniker = "MyFramework, Version=v4.1"; | ||
| absoluteTask.RootPath = nonExistentRoot; | ||
| absoluteTask.TargetFrameworkFallbackSearchPaths = absoluteDir; | ||
| absoluteTask.Execute(); | ||
|
|
||
| // Test: relative fallback path with TaskEnvironment | ||
| MockEngine relativeEngine = new MockEngine(); | ||
| GetReferenceAssemblyPaths relativeTask = new GetReferenceAssemblyPaths(); | ||
| relativeTask.BuildEngine = relativeEngine; | ||
| relativeTask.TargetFrameworkMoniker = "MyFramework, Version=v4.1"; | ||
| relativeTask.TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(baseDir); | ||
| relativeTask.RootPath = nonExistentRoot; | ||
| relativeTask.TargetFrameworkFallbackSearchPaths = relativeDir; | ||
| relativeTask.Execute(); | ||
|
|
||
| Assert.Equal(absoluteTask.ReferenceAssemblyPaths, relativeTask.ReferenceAssemblyPaths); | ||
| Assert.Equal(absoluteTask.TargetFrameworkMonikerDisplayName, relativeTask.TargetFrameworkMonikerDisplayName); | ||
| Assert.Equal(0, relativeEngine.Errors); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,11 +4,12 @@ | |
| using System; | ||
| using System.Collections.Generic; | ||
| using Microsoft.Build.Framework; | ||
| using Microsoft.Build.Shared; | ||
| using Microsoft.Build.Utilities; | ||
| using FrameworkNameVersioning = System.Runtime.Versioning.FrameworkName; | ||
|
|
||
| #if FEATURE_GAC | ||
| using Microsoft.Build.Shared; | ||
| using System.Threading; | ||
| using SystemProcessorArchitecture = System.Reflection.ProcessorArchitecture; | ||
| #endif | ||
|
|
||
|
|
@@ -19,8 +20,12 @@ namespace Microsoft.Build.Tasks | |
| /// <summary> | ||
| /// Returns the reference assembly paths to the various frameworks | ||
| /// </summary> | ||
| public class GetReferenceAssemblyPaths : TaskExtension | ||
| [MSBuildMultiThreadableTask] | ||
| public class GetReferenceAssemblyPaths : TaskExtension, IMultiThreadableTask | ||
| { | ||
| /// <inheritdoc /> | ||
| public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback; | ||
|
|
||
| #region Data | ||
| #if FEATURE_GAC | ||
| /// <summary> | ||
|
|
@@ -32,7 +37,23 @@ public class GetReferenceAssemblyPaths : TaskExtension | |
| /// <summary> | ||
| /// Cache in a static whether or not we have found the 35sp1sentinel assembly. | ||
| /// </summary> | ||
| private static bool? s_net35SP1SentinelAssemblyFound; | ||
| private static readonly Lazy<bool> s_net35SP1SentinelAssemblyFound = new Lazy<bool>(() => | ||
| { | ||
| // get an assemblyname from the string representation of the sentinel assembly name | ||
| var sentinelAssemblyName = new AssemblyNameExtension(NET35SP1SentinelAssemblyName); | ||
| string path = GlobalAssemblyCache.GetLocation( | ||
| sentinelAssemblyName, | ||
| SystemProcessorArchitecture.MSIL, | ||
| runtimeVersion => "v2.0.50727", | ||
| new Version("2.0.57027"), | ||
| false, | ||
| new FileExists(p => FileUtilities.FileExistsNoThrow(p)), | ||
| GlobalAssemblyCache.pathFromFusionName, | ||
| GlobalAssemblyCache.gacEnumerator, | ||
| false); | ||
|
|
||
| return !string.IsNullOrEmpty(path); | ||
| }, LazyThreadSafetyMode.PublicationOnly); | ||
| #endif | ||
|
|
||
| /// <summary> | ||
|
|
@@ -144,6 +165,11 @@ public string TargetFrameworkFallbackSearchPaths | |
| /// </summary> | ||
| public override bool Execute() | ||
| { | ||
| AbsolutePath? absoluteRootPath = !string.IsNullOrEmpty(RootPath) | ||
| ? TaskEnvironment.GetAbsolutePath(RootPath) | ||
| : new AbsolutePath(RootPath, ignoreRootedCheck: true); | ||
| IList<AbsolutePath> absoluteFallbackSearchPaths = ResolveAbsoluteFallbackSearchPaths(TargetFrameworkFallbackSearchPaths); | ||
|
|
||
|
Comment on lines
+168
to
+172
|
||
| FrameworkNameVersioning moniker; | ||
| FrameworkNameVersioning monikerWithNoProfile = null; | ||
|
|
||
|
|
@@ -169,16 +195,6 @@ public override bool Execute() | |
| if (!BypassFrameworkInstallChecks && moniker.Identifier.Equals(".NETFramework", StringComparison.OrdinalIgnoreCase) && | ||
| moniker.Version.Major < 4) | ||
| { | ||
| // We have not got a value for whether or not the 35 sentinel assembly has been found | ||
| if (!s_net35SP1SentinelAssemblyFound.HasValue) | ||
| { | ||
| // get an assemblyname from the string representation of the sentinel assembly name | ||
| var sentinelAssemblyName = new AssemblyNameExtension(NET35SP1SentinelAssemblyName); | ||
|
|
||
| string path = GlobalAssemblyCache.GetLocation(sentinelAssemblyName, SystemProcessorArchitecture.MSIL, runtimeVersion => "v2.0.50727", new Version("2.0.57027"), false, new FileExists(p => FileUtilities.FileExistsNoThrow(p)), GlobalAssemblyCache.pathFromFusionName, GlobalAssemblyCache.gacEnumerator, false); | ||
| s_net35SP1SentinelAssemblyFound = !String.IsNullOrEmpty(path); | ||
| } | ||
|
|
||
| // We did not find the SP1 sentinel assembly in the GAC. Therefore we must assume that SP1 isn't installed | ||
| if (!s_net35SP1SentinelAssemblyFound.Value) | ||
| { | ||
|
|
@@ -195,7 +211,7 @@ public override bool Execute() | |
|
|
||
| try | ||
| { | ||
| _tfmPaths = GetPaths(RootPath, TargetFrameworkFallbackSearchPaths, moniker); | ||
| _tfmPaths = GetPaths(absoluteRootPath, absoluteFallbackSearchPaths, moniker); | ||
|
|
||
| if (_tfmPaths?.Count > 0) | ||
| { | ||
|
|
@@ -206,7 +222,7 @@ public override bool Execute() | |
| // There is no point in generating the full framework paths if profile path could not be found. | ||
| if (targetingProfile && _tfmPaths != null) | ||
| { | ||
| _tfmPathsNoProfile = GetPaths(RootPath, TargetFrameworkFallbackSearchPaths, monikerWithNoProfile); | ||
| _tfmPathsNoProfile = GetPaths(absoluteRootPath, absoluteFallbackSearchPaths, monikerWithNoProfile); | ||
| } | ||
|
|
||
| // The path with out the profile is just the reference assembly paths. | ||
|
|
@@ -236,14 +252,18 @@ public override bool Execute() | |
| /// <summary> | ||
| /// Generate the set of chained reference assembly paths | ||
| /// </summary> | ||
| private IList<String> GetPaths(string rootPath, string targetFrameworkFallbackSearchPaths, FrameworkNameVersioning frameworkmoniker) | ||
| private IList<String> GetPaths(AbsolutePath? rootPath, IList<AbsolutePath> fallbackSearchPaths, FrameworkNameVersioning frameworkmoniker) | ||
| { | ||
| string fallbackSearchPathsJoined = fallbackSearchPaths.Count > 0 | ||
| ? string.Join(";", fallbackSearchPaths) | ||
| : null; | ||
|
|
||
| IList<String> pathsToReturn = ToolLocationHelper.GetPathToReferenceAssemblies( | ||
| frameworkmoniker.Identifier, | ||
| frameworkmoniker.Version.ToString(), | ||
| frameworkmoniker.Profile, | ||
| rootPath, | ||
| targetFrameworkFallbackSearchPaths); | ||
| rootPath?.Value, | ||
| fallbackSearchPathsJoined); | ||
|
|
||
| if (!SuppressNotFoundError) | ||
| { | ||
|
|
@@ -267,6 +287,26 @@ private IList<String> GetPaths(string rootPath, string targetFrameworkFallbackSe | |
| return pathsToReturn; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Resolves each semicolon-separated fallback search path to absolute via TaskEnvironment. | ||
| /// </summary> | ||
| private IList<AbsolutePath> ResolveAbsoluteFallbackSearchPaths(string fallbackSearchPaths) | ||
| { | ||
| if (string.IsNullOrEmpty(fallbackSearchPaths)) | ||
| { | ||
| return []; | ||
| } | ||
|
|
||
| string[] parts = fallbackSearchPaths.Split(MSBuildConstants.SemicolonChar, StringSplitOptions.RemoveEmptyEntries); | ||
| var result = new AbsolutePath[parts.Length]; | ||
| for (int i = 0; i < parts.Length; i++) | ||
| { | ||
| result[i] = TaskEnvironment.GetAbsolutePath(parts[i]); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| #endregion | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test convention: new tests should inject
ITestOutputHelper(store as_output) and create the environment viaTestEnvironment.Create(_output)so diagnostic output is captured in CI. Also prefer Shouldly assertions over adding new xUnitAssertcalls in modified code.