| File: Sdk\SdkResolver.cs | Web Access |
| Project: src\msbuild\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; namespace Microsoft.Build.Framework { /// <summary> /// An abstract interface for classes that can resolve a Software Development Kit (SDK). /// </summary> public abstract class SdkResolver { /// <summary> /// Gets the name of the <see cref="SdkResolver"/> to be displayed in build output log. /// </summary> public abstract string Name { get; } /// <summary> /// Gets the self-described resolution priority order. MSBuild will sort resolvers /// by this value. /// </summary> public abstract int Priority { get; } /// <summary> /// Resolves the specified SDK reference. /// </summary> /// <param name="sdkReference">A <see cref="SdkReference" /> containing the referenced SDKs be resolved.</param> /// <param name="resolverContext">Context for resolving the SDK.</param> /// <param name="factory">Factory class to create an <see cref="SdkResult" /></param> /// <returns> /// An <see cref="SdkResult" /> containing the resolved SDKs or associated error / reason /// the SDK could not be resolved. Return <see langword="null"/> if the resolver is not /// applicable for a particular <see cref="SdkReference"/>. /// </returns> /// <remarks> /// Note: You must use <see cref="SdkResultFactory"/> to return a result. /// </remarks> public abstract SdkResult? Resolve(SdkReference sdkReference, SdkResolverContext resolverContext, SdkResultFactory factory); /// <summary> /// The set of resolvers registered in-process via <see cref="Register"/>. /// </summary> private static readonly List<SdkResolver> s_registeredResolvers = new(); /// <summary> /// Registers an <see cref="SdkResolver"/> to be consulted during SDK resolution by a host that runs /// the MSBuild engine in-process (for example the .NET SDK CLI), without MSBuild discovering and /// loading it from disk by reflection. /// </summary> /// <param name="resolver">The resolver instance to register.</param> /// <remarks> /// <para> /// This is the supported way to provide SDK resolvers in a trimmed or Native AOT host, where the /// on-disk <c>SdkResolvers</c> probing and reflection-based loading used for plugin resolvers are /// unavailable. The registered resolver is consulted on the same reflection-free code path as /// MSBuild's built-in resolver, so it never triggers the dynamic-loading failure (MSB4282). /// </para> /// <para> /// The registered resolver participates in resolution in <see cref="Priority"/> order alongside /// MSBuild's built-in resolver, with no assembly loading or reflection. It is consulted for every /// SDK reference in the process. /// </para> /// <para> /// Intended to be called once per resolver during host initialization, before the first project is /// evaluated. The set of registered resolvers is captured the first time an SDK is resolved in the /// process; registrations performed after that point are not guaranteed to take effect. /// </para> /// <para> /// This method is thread-safe. Registering the same instance more than once has no additional effect. /// </para> /// </remarks> /// <exception cref="ArgumentNullException"><paramref name="resolver"/> is <see langword="null"/>.</exception> public static void Register(SdkResolver resolver) { if (resolver is null) { throw new ArgumentNullException(nameof(resolver)); } lock (s_registeredResolvers) { bool alreadyRegistered = false; foreach (SdkResolver registeredResolver in s_registeredResolvers) { if (ReferenceEquals(registeredResolver, resolver)) { alreadyRegistered = true; break; } } if (!alreadyRegistered) { s_registeredResolvers.Add(resolver); } } } /// <summary> /// Gets a snapshot of the resolvers registered via <see cref="Register"/>, for the engine to fold /// into its reflection-free default-resolver pass. /// </summary> internal static IReadOnlyList<SdkResolver> RegisteredResolvers { get { lock (s_registeredResolvers) { return s_registeredResolvers.Count == 0 ? Array.Empty<SdkResolver>() : s_registeredResolvers.ToArray(); } } } /// <summary> /// Clears all registered resolvers. For test use only, to reset the process-global registration state. /// </summary> internal static void ClearRegisteredResolversForTests() { lock (s_registeredResolvers) { s_registeredResolvers.Clear(); } } } }