| File: Commands\Workload\WorkloadInfoHelper.cs | Web Access |
| Project: src\sdk\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. using System.CommandLine; using Microsoft.Deployment.DotNet.Releases; using Microsoft.DotNet.Cli.Commands.Workload.Install; using Microsoft.DotNet.Cli.Commands.Workload.Install.WorkloadInstallRecords; using Microsoft.DotNet.Cli.Commands.Workload.List; #if !CLI_AOT using Microsoft.DotNet.Cli.NuGetPackageDownloader; #endif using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Configurer; using Microsoft.NET.Sdk.WorkloadManifestReader; using Product = Microsoft.DotNet.Cli.Utils.Product; namespace Microsoft.DotNet.Cli.Commands.Workload; #if CLI_AOT // Under NativeAOT the workload installer (and its NuGet/MSI machinery) is not compiled in; this type // only provides the read-only info-reporting surface (workload version + installed workloads), so it // does not implement IWorkloadInfoHelper (which exposes the installer) in the AOT build. internal class WorkloadInfoHelper #else internal class WorkloadInfoHelper : IWorkloadInfoHelper #endif { public readonly SdkFeatureBand _currentSdkFeatureBand; private readonly string? _targetSdkVersion; public string DotnetPath { get; } public string UserLocalPath { get; } public WorkloadInfoHelper( bool isInteractive, VerbosityOptions verbosity = VerbosityOptions.normal, string? targetSdkVersion = null, bool? verifyMsiSignature = null, IReporter? reporter = null, IWorkloadInstallationRecordRepository? workloadRecordRepo = null, string? currentSdkVersion = null, string? dotnetDir = null, string? userProfileDir = null, IWorkloadResolver? workloadResolver = null) { DotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath)!; ReleaseVersion currentSdkReleaseVersion = new(currentSdkVersion ?? Product.Version); _currentSdkFeatureBand = new SdkFeatureBand(currentSdkReleaseVersion); _targetSdkVersion = targetSdkVersion; userProfileDir ??= CliFolderPathCalculator.DotnetUserProfileFolderPath; ManifestProvider = new SdkDirectoryWorkloadManifestProvider(DotnetPath, string.IsNullOrWhiteSpace(_targetSdkVersion) ? currentSdkReleaseVersion.ToString() : _targetSdkVersion, userProfileDir, SdkDirectoryWorkloadManifestProvider.GetGlobalJsonPath(Environment.CurrentDirectory)); WorkloadResolver = workloadResolver ?? NET.Sdk.WorkloadManifestReader.WorkloadResolver.Create( ManifestProvider, DotnetPath, currentSdkReleaseVersion.ToString(), userProfileDir); #if CLI_AOT // The NativeAOT build does not compile in the workload installer (NuGet/MSI machinery). The info // path only needs the installation-record repository, so build it directly here - mirroring the // read-only construction in WorkloadInstallDetector. WorkloadRecordRepo = workloadRecordRepo ?? CreateInstallationRecordRepository(DotnetPath, _currentSdkFeatureBand, userProfileDir); UserLocalPath = dotnetDir ?? (FileBasedWorkloadInstallationRecordRepositoryFactory.IsUserLocal(DotnetPath, _currentSdkFeatureBand) ? userProfileDir : DotnetPath); #else var restoreConfig = new RestoreActionConfig(Interactive: isInteractive); // When verifyMsiSignature is not specified by the caller, we delegate to // WorkloadUtilities.ShouldVerifySignatures() which respects the registry policy // and dotnet host signing status. Installer = WorkloadInstallerFactory.GetWorkloadInstaller( reporter, _currentSdkFeatureBand, WorkloadResolver, verbosity, userProfileDir, verifyMsiSignature: verifyMsiSignature ?? WorkloadUtilities.ShouldVerifySignatures(), restoreActionConfig: restoreConfig, elevationRequired: false, shouldLog: false); WorkloadRecordRepo = workloadRecordRepo ?? Installer.GetWorkloadInstallationRecordRepository(); UserLocalPath = dotnetDir ?? (WorkloadFileBasedInstall.IsUserLocal(DotnetPath, _currentSdkFeatureBand.ToString()) ? userProfileDir : DotnetPath); #endif } #if CLI_AOT // Build the installation-record repository without the workload installer (not compiled into the // AOT build). Mirrors WorkloadInstallDetector: registry-backed for MSI installs on Windows, // file-based otherwise. private static IWorkloadInstallationRecordRepository CreateInstallationRecordRepository(string dotnetDir, SdkFeatureBand sdkFeatureBand, string userProfileDir) => WorkloadInstallType.GetWorkloadInstallType(sdkFeatureBand, dotnetDir) switch { InstallType.Msi => new ReadOnlyWindowsWorkloadInstallationRecordRepository(), _ => FileBasedWorkloadInstallationRecordRepositoryFactory.Create(dotnetDir, sdkFeatureBand, userProfileDir), }; #endif #if !CLI_AOT public IInstaller Installer { get; private init; } #endif public SdkDirectoryWorkloadManifestProvider ManifestProvider { get; } public IWorkloadInstallationRecordRepository WorkloadRecordRepo { get; private init; } public IWorkloadResolver WorkloadResolver { get; private init; } public IEnumerable<WorkloadId> InstalledSdkWorkloadIds => WorkloadRecordRepo.GetInstalledWorkloads(_currentSdkFeatureBand); public InstalledWorkloadsCollection AddInstalledVsWorkloads(IEnumerable<WorkloadId> sdkWorkloadIds) { InstalledWorkloadsCollection installedWorkloads = new(sdkWorkloadIds, $"SDK {_currentSdkFeatureBand}"); #if TARGET_WINDOWS if (OperatingSystem.IsWindows()) { VisualStudioWorkloads.GetInstalledWorkloads(WorkloadResolver, installedWorkloads); } #endif return installedWorkloads; } #if !CLI_AOT public void CheckTargetSdkVersionIsValid() { if (!string.IsNullOrWhiteSpace(_targetSdkVersion)) { if (new SdkFeatureBand(_targetSdkVersion).CompareTo(_currentSdkFeatureBand) < 0) { throw new ArgumentException( $"Version band of {_targetSdkVersion} --- {new SdkFeatureBand(_targetSdkVersion)} should not be smaller than current version band {_currentSdkFeatureBand}"); } } } /// <inheritdoc/> public IEnumerable<WorkloadResolver.WorkloadInfo> InstalledAndExtendedWorkloads { get { var installed = AddInstalledVsWorkloads(InstalledSdkWorkloadIds); return WorkloadResolver.GetExtendedWorkloads( installed.AsEnumerable().Select(t => new WorkloadId(t.Key))); } } #endif internal static string GetWorkloadsVersion(WorkloadInfoHelper? workloadInfoHelper = null) { workloadInfoHelper ??= new WorkloadInfoHelper(false); var versionInfo = workloadInfoHelper.ManifestProvider.GetWorkloadVersion(); // The explicit space here is intentional, as it's easy to miss in localization and crucial for parsing return versionInfo.Version + (versionInfo.IsInstalled ? string.Empty : ' ' + CliCommandStrings.WorkloadVersionNotInstalledShort); } internal void ShowWorkloadsInfo(IReporter? reporter = null, string? dotnetDir = null, bool showVersion = true) { reporter ??= Reporter.Output; var versionInfo = ManifestProvider.GetWorkloadVersion(); void WriteUpdateModeAndAnyError(string indent = "") { var useWorkloadSets = InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(_currentSdkFeatureBand, UserLocalPath), "default.json")).ShouldUseWorkloadSets(); var configurationMessage = useWorkloadSets ? CliCommandStrings.WorkloadManifestInstallationConfigurationWorkloadSets : CliCommandStrings.WorkloadManifestInstallationConfigurationLooseManifests; reporter.WriteLine(indent + configurationMessage); if (!versionInfo.IsInstalled) { reporter.WriteLine(indent + string.Format(CliCommandStrings.WorkloadSetFromGlobalJsonNotInstalled, versionInfo.Version, versionInfo.GlobalJsonPath)); } else if (versionInfo.WorkloadSetsEnabledWithoutWorkloadSet) { reporter.WriteLine(indent + CliCommandStrings.ShouldInstallAWorkloadSet); } } if (showVersion) { reporter.WriteLine($" Workload version: {GetWorkloadsVersion()}"); WriteUpdateModeAndAnyError(indent: " "); reporter.WriteLine(); } if (versionInfo.IsInstalled) { var installedList = InstalledSdkWorkloadIds; var installedWorkloads = AddInstalledVsWorkloads(installedList); var dotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); if (installedWorkloads.Count == 0) { reporter.WriteLine(CliCommandStrings.NoWorkloadsInstalledInfoWarning); } else { var manifestInfoDict = WorkloadResolver.GetInstalledManifests().ToDictionary(info => info.Id, StringComparer.OrdinalIgnoreCase); foreach (var workload in installedWorkloads.AsEnumerable()) { var workloadManifest = WorkloadResolver.GetManifestFromWorkload(new WorkloadId(workload.Key)); var workloadFeatureBand = manifestInfoDict[workloadManifest.Id].ManifestFeatureBand; const int align = 10; const string separator = " "; reporter.WriteLine($" [{workload.Key}]"); reporter.Write($"{separator}{CliCommandStrings.WorkloadSourceColumn}:"); reporter.WriteLine($" {workload.Value,align}"); reporter.Write($"{separator}{CliCommandStrings.WorkloadManifestVersionColumn}:"); reporter.WriteLine($" {workloadManifest.Version + '/' + workloadFeatureBand,align}"); reporter.Write($"{separator}{CliCommandStrings.WorkloadManifestPathColumn}:"); reporter.WriteLine($" {workloadManifest.ManifestPath,align}"); reporter.Write($"{separator}{CliCommandStrings.WorkloadInstallTypeColumn}:"); reporter.WriteLine($" {WorkloadInstallType.GetWorkloadInstallType(new SdkFeatureBand(Product.Version), dotnetPath).ToString(),align}" ); reporter.WriteLine(""); } } } if (!showVersion) { WriteUpdateModeAndAnyError(); } } }