File: Commands\New\SdkInfoProvider.cs
Web Access
Project: ..\..\..\src\Cli\dotnet\dotnet.csproj (dotnet)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable disable
 
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.MSBuildSdkResolver;
using Microsoft.DotNet.NativeWrapper;
using Microsoft.TemplateEngine.Abstractions.Components;
using Microsoft.TemplateEngine.Utils;
 
namespace Microsoft.DotNet.Cli.Commands.New;
 
internal class SdkInfoProvider : ISdkInfoProvider
{
    private readonly Func<string> _getCurrentProcessPath;
 
    public Guid Id { get; } = Guid.Parse("{A846C4E2-1E85-4BF5-954D-17655D916928}");
 
    public SdkInfoProvider()
        : this(null)
    { }
 
    internal SdkInfoProvider(Func<string> getCurrentProcessPath)
    {
        _getCurrentProcessPath = getCurrentProcessPath;
    }
 
    public Task<string> GetCurrentVersionAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(Product.Version);
    }
 
    public Task<IEnumerable<string>> GetInstalledVersionsAsync(CancellationToken cancellationToken)
    {
        // Get the dotnet directory, while ignoring custom msbuild resolvers
        string dotnetDir = NativeWrapper.EnvironmentProvider.GetDotnetExeDirectory(
            key =>
                key.Equals("DOTNET_MSBUILD_SDK_RESOLVER_CLI_DIR", StringComparison.InvariantCultureIgnoreCase)
                    ? null
                    : Environment.GetEnvironmentVariable(key),
            _getCurrentProcessPath);
 
        IEnumerable<string> sdks;
        try
        {
            // sdks contain the full path, version is the last part
            //  details: https://github.com/dotnet/runtime/blob/5098d45cc1bf9649fab5df21f227da4b80daa084/src/native/corehost/fxr/sdk_info.cpp#L76
            sdks = NETCoreSdkResolverNativeWrapper.GetAvailableSdks(dotnetDir).Select(Path.GetFileName);
        }
        // The NETCoreSdkResolverNativeWrapper is not properly initialized (case of OSx in test env) - let's manually perform what
        //  sdk_info::get_all_sdk_infos does
        catch (Exception e) when (e is HostFxrRuntimePropertyNotSetException or HostFxrNotFoundException)
        {
            string sdkDir = Path.Combine(dotnetDir, "sdk");
            sdks = Directory.Exists(sdkDir) ? Directory.GetDirectories(sdkDir).Select(Path.GetFileName).Where(IsValidFxVersion) : [];
        }
        return Task.FromResult(sdks);
    }
 
    public string ProvideConstraintRemedySuggestion(IReadOnlyList<string> supportedVersions,
        IReadOnlyList<string> viableInstalledVersions)
    {
        if (viableInstalledVersions.Any())
        {
            return string.Format(CliCommandStrings.SdkInfoProvider_Message_SwitchSdk, viableInstalledVersions.ToCsvString());
        }
        else
        {
            return string.Format(CliCommandStrings.SdkInfoProvider_Message_InstallSdk, supportedVersions.ToCsvString());
        }
    }
 
    private static bool IsValidFxVersion(string versionString)
    {
        return FXVersion.TryParse(versionString, out _);
    }
}