44using System ;
55using System . Collections . Generic ;
66using Microsoft . Build . Framework ;
7+ using Microsoft . Build . Shared ;
78using Microsoft . Build . Utilities ;
89using FrameworkNameVersioning = System . Runtime . Versioning . FrameworkName ;
910
1011#if FEATURE_GAC
11- using Microsoft . Build . Shared ;
12+ using System . Threading ;
1213using SystemProcessorArchitecture = System . Reflection . ProcessorArchitecture ;
1314#endif
1415
@@ -19,8 +20,12 @@ namespace Microsoft.Build.Tasks
1920 /// <summary>
2021 /// Returns the reference assembly paths to the various frameworks
2122 /// </summary>
22- public class GetReferenceAssemblyPaths : TaskExtension
23+ [ MSBuildMultiThreadableTask ]
24+ public class GetReferenceAssemblyPaths : TaskExtension , IMultiThreadableTask
2325 {
26+ /// <inheritdoc />
27+ public TaskEnvironment TaskEnvironment { get ; set ; } = TaskEnvironment . Fallback ;
28+
2429 #region Data
2530#if FEATURE_GAC
2631 /// <summary>
@@ -32,7 +37,23 @@ public class GetReferenceAssemblyPaths : TaskExtension
3237 /// <summary>
3338 /// Cache in a static whether or not we have found the 35sp1sentinel assembly.
3439 /// </summary>
35- private static bool ? s_net35SP1SentinelAssemblyFound ;
40+ private static readonly Lazy < bool > s_net35SP1SentinelAssemblyFound = new Lazy < bool > ( ( ) =>
41+ {
42+ // get an assemblyname from the string representation of the sentinel assembly name
43+ var sentinelAssemblyName = new AssemblyNameExtension ( NET35SP1SentinelAssemblyName ) ;
44+ string path = GlobalAssemblyCache . GetLocation (
45+ sentinelAssemblyName ,
46+ SystemProcessorArchitecture . MSIL ,
47+ runtimeVersion => "v2.0.50727" ,
48+ new Version ( "2.0.57027" ) ,
49+ false ,
50+ new FileExists ( p => FileUtilities . FileExistsNoThrow ( p ) ) ,
51+ GlobalAssemblyCache . pathFromFusionName ,
52+ GlobalAssemblyCache . gacEnumerator ,
53+ false ) ;
54+
55+ return ! string . IsNullOrEmpty ( path ) ;
56+ } , LazyThreadSafetyMode . PublicationOnly ) ;
3657#endif
3758
3859 /// <summary>
@@ -144,6 +165,11 @@ public string TargetFrameworkFallbackSearchPaths
144165 /// </summary>
145166 public override bool Execute ( )
146167 {
168+ AbsolutePath ? absoluteRootPath = ! string . IsNullOrEmpty ( RootPath )
169+ ? TaskEnvironment . GetAbsolutePath ( RootPath )
170+ : new AbsolutePath ( RootPath , ignoreRootedCheck : true ) ;
171+ IList < AbsolutePath > absoluteFallbackSearchPaths = ResolveAbsoluteFallbackSearchPaths ( TargetFrameworkFallbackSearchPaths ) ;
172+
147173 FrameworkNameVersioning moniker ;
148174 FrameworkNameVersioning monikerWithNoProfile = null ;
149175
@@ -169,16 +195,6 @@ public override bool Execute()
169195 if ( ! BypassFrameworkInstallChecks && moniker . Identifier . Equals ( ".NETFramework" , StringComparison . OrdinalIgnoreCase ) &&
170196 moniker . Version . Major < 4 )
171197 {
172- // We have not got a value for whether or not the 35 sentinel assembly has been found
173- if ( ! s_net35SP1SentinelAssemblyFound . HasValue )
174- {
175- // get an assemblyname from the string representation of the sentinel assembly name
176- var sentinelAssemblyName = new AssemblyNameExtension ( NET35SP1SentinelAssemblyName ) ;
177-
178- 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 ) ;
179- s_net35SP1SentinelAssemblyFound = ! String . IsNullOrEmpty ( path ) ;
180- }
181-
182198 // We did not find the SP1 sentinel assembly in the GAC. Therefore we must assume that SP1 isn't installed
183199 if ( ! s_net35SP1SentinelAssemblyFound . Value )
184200 {
@@ -195,7 +211,7 @@ public override bool Execute()
195211
196212 try
197213 {
198- _tfmPaths = GetPaths ( RootPath , TargetFrameworkFallbackSearchPaths , moniker ) ;
214+ _tfmPaths = GetPaths ( absoluteRootPath , absoluteFallbackSearchPaths , moniker ) ;
199215
200216 if ( _tfmPaths ? . Count > 0 )
201217 {
@@ -206,7 +222,7 @@ public override bool Execute()
206222 // There is no point in generating the full framework paths if profile path could not be found.
207223 if ( targetingProfile && _tfmPaths != null )
208224 {
209- _tfmPathsNoProfile = GetPaths ( RootPath , TargetFrameworkFallbackSearchPaths , monikerWithNoProfile ) ;
225+ _tfmPathsNoProfile = GetPaths ( absoluteRootPath , absoluteFallbackSearchPaths , monikerWithNoProfile ) ;
210226 }
211227
212228 // The path with out the profile is just the reference assembly paths.
@@ -236,14 +252,16 @@ public override bool Execute()
236252 /// <summary>
237253 /// Generate the set of chained reference assembly paths
238254 /// </summary>
239- private IList < String > GetPaths ( string rootPath , string targetFrameworkFallbackSearchPaths , FrameworkNameVersioning frameworkmoniker )
255+ private IList < String > GetPaths ( AbsolutePath ? rootPath , IList < AbsolutePath > fallbackSearchPaths , FrameworkNameVersioning frameworkmoniker )
240256 {
257+ string fallbackSearchPathsJoined = string . Join ( ";" , fallbackSearchPaths ) ;
258+
241259 IList < String > pathsToReturn = ToolLocationHelper . GetPathToReferenceAssemblies (
242260 frameworkmoniker . Identifier ,
243261 frameworkmoniker . Version . ToString ( ) ,
244262 frameworkmoniker . Profile ,
245- rootPath ,
246- targetFrameworkFallbackSearchPaths ) ;
263+ rootPath ? . Value ,
264+ fallbackSearchPathsJoined ) ;
247265
248266 if ( ! SuppressNotFoundError )
249267 {
@@ -267,6 +285,26 @@ private IList<String> GetPaths(string rootPath, string targetFrameworkFallbackSe
267285 return pathsToReturn ;
268286 }
269287
288+ /// <summary>
289+ /// Resolves each semicolon-separated fallback search path to absolute via TaskEnvironment.
290+ /// </summary>
291+ private IList < AbsolutePath > ResolveAbsoluteFallbackSearchPaths ( string fallbackSearchPaths )
292+ {
293+ if ( string . IsNullOrEmpty ( fallbackSearchPaths ) )
294+ {
295+ return [ ] ;
296+ }
297+
298+ string [ ] parts = fallbackSearchPaths . Split ( MSBuildConstants . SemicolonChar , StringSplitOptions . RemoveEmptyEntries ) ;
299+ var result = new AbsolutePath [ parts . Length ] ;
300+ for ( int i = 0 ; i < parts . Length ; i ++ )
301+ {
302+ result [ i ] = TaskEnvironment . GetAbsolutePath ( parts [ i ] ) ;
303+ }
304+
305+ return result ;
306+ }
307+
270308 #endregion
271309 }
272310}
0 commit comments