refactor to handle multiple projects
This commit is contained in:
@@ -57,86 +57,102 @@ public static class ProjectEvaluation
|
||||
return Guid.Parse(userSecretsId);
|
||||
}
|
||||
|
||||
public static async Task<List<InstalledPackage>> GetPackageReferencesForProject(SharpIdeProjectModel projectModel, bool includeTransitive = true)
|
||||
public static async Task<List<InstalledPackage>> GetPackageReferencesForProjects(List<SharpIdeProjectModel> projectModels, bool includeTransitive = true)
|
||||
{
|
||||
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(ProjectEvaluation)}.{nameof(GetPackageReferencesForProject)}");
|
||||
Guard.Against.Null(projectModel, nameof(projectModel));
|
||||
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(ProjectEvaluation)}.{nameof(GetPackageReferencesForProjects)}");
|
||||
Guard.Against.Null(projectModels, nameof(projectModels));
|
||||
|
||||
var project = _projectCollection.GetLoadedProjects(projectModel.FilePath).Single();
|
||||
|
||||
var assetsPath = project.GetPropertyValue("ProjectAssetsFile");
|
||||
|
||||
if (File.Exists(assetsPath) is false)
|
||||
var projects = projectModels.Select(s =>
|
||||
{
|
||||
throw new FileNotFoundException("Could not find project.assets.json file", assetsPath);
|
||||
}
|
||||
var lockFileFormat = new LockFileFormat();
|
||||
var lockFile = lockFileFormat.Read(assetsPath);
|
||||
var packages = GetPackagesFromAssetsFile(lockFile, includeTransitive);
|
||||
return packages;
|
||||
var proj = _projectCollection.GetLoadedProjects(s.FilePath).Single();
|
||||
var assetsPath = proj.GetPropertyValue("ProjectAssetsFile");
|
||||
|
||||
if (File.Exists(assetsPath) is false)
|
||||
{
|
||||
throw new FileNotFoundException("Could not find project.assets.json file", assetsPath);
|
||||
}
|
||||
var lockFileFormat = new LockFileFormat();
|
||||
var lockFile = lockFileFormat.Read(assetsPath);
|
||||
return (LockFile: lockFile, Project: s);
|
||||
}).ToList();
|
||||
|
||||
var result = await GetPackagesFromAssetsFiles(projects);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ChatGPT Special
|
||||
private static List<InstalledPackage> GetPackagesFromAssetsFile(LockFile assetsFile, bool includeTransitive)
|
||||
public static async Task<List<InstalledPackage>> GetPackagesFromAssetsFiles(List<(LockFile, SharpIdeProjectModel)> projects, bool includeTransitive = true)
|
||||
{
|
||||
var packages = new List<InstalledPackage>();
|
||||
var dependencyMap = NugetDependencyGraph.GetPackageDependencyMap(assetsFile);
|
||||
|
||||
// We currently do not handle multi-targeted projects
|
||||
var target = assetsFile.Targets.SingleOrDefault(t => t.RuntimeIdentifier == null);
|
||||
if (target == null) return packages;
|
||||
|
||||
var tfm = target.TargetFramework.GetShortFolderName();
|
||||
var tfmInfo = assetsFile.PackageSpec.TargetFrameworks
|
||||
.FirstOrDefault(t => t.FrameworkName.Equals(target.TargetFramework));
|
||||
|
||||
if (tfmInfo == null) return packages;
|
||||
|
||||
var topLevelDependencies = tfmInfo.Dependencies
|
||||
.DistinctBy(s => s.Name)
|
||||
.Select(s => s.Name)
|
||||
.ToHashSet();
|
||||
|
||||
foreach (var lockFileTargetLibrary in target.Libraries.Where(l => l.Type == "package"))
|
||||
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(ProjectEvaluation)}.{nameof(GetPackagesFromAssetsFiles)}");
|
||||
var allPackages = new Dictionary<string, InstalledPackage>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var (assetsFile, project) in projects)
|
||||
{
|
||||
var isTopLevel = topLevelDependencies.Contains(lockFileTargetLibrary.Name);
|
||||
if (!includeTransitive && !isTopLevel) continue;
|
||||
var dependencyMap = NugetDependencyGraph.GetPackageDependencyMap(assetsFile);
|
||||
|
||||
var dependency = tfmInfo.Dependencies
|
||||
.FirstOrDefault(d => d.Name.Equals(lockFileTargetLibrary.Name, StringComparison.OrdinalIgnoreCase));
|
||||
// We currently do not handle multi-targeted projects
|
||||
var target = assetsFile.Targets.SingleOrDefault(t => t.RuntimeIdentifier == null);
|
||||
if (target == null) continue;
|
||||
|
||||
var dependents = dependencyMap.GetValueOrDefault(lockFileTargetLibrary.Name, []);
|
||||
var mappedDependents = dependents.Select(d => new DependentPackage
|
||||
var tfm = target.TargetFramework.GetShortFolderName();
|
||||
var tfmInfo = assetsFile.PackageSpec.TargetFrameworks
|
||||
.FirstOrDefault(t => t.FrameworkName.Equals(target.TargetFramework));
|
||||
|
||||
if (tfmInfo == null) continue;
|
||||
|
||||
var topLevelDependencies = tfmInfo.Dependencies
|
||||
.DistinctBy(s => s.Name)
|
||||
.Select(s => s.Name)
|
||||
.ToHashSet();
|
||||
|
||||
foreach (var lockFileTargetLibrary in target.Libraries.Where(l => l.Type == "package"))
|
||||
{
|
||||
PackageName = d.PackageName,
|
||||
RequestedVersion = d.PackageDependency.VersionRange
|
||||
}).ToList();
|
||||
if (string.IsNullOrEmpty(lockFileTargetLibrary.Name)) continue;
|
||||
|
||||
packages.Add(new InstalledPackage
|
||||
{
|
||||
Name = lockFileTargetLibrary.Name,
|
||||
RequestedVersion = dependency?.LibraryRange.VersionRange?.ToString() ?? "",
|
||||
ResolvedVersion = lockFileTargetLibrary.Version?.ToString(),
|
||||
TargetFramework = tfm,
|
||||
IsTopLevel = isTopLevel,
|
||||
IsAutoReferenced = dependency?.AutoReferenced ?? false,
|
||||
DependentPackages = mappedDependents
|
||||
});
|
||||
var isTopLevel = topLevelDependencies.Contains(lockFileTargetLibrary.Name);
|
||||
if (!includeTransitive && !isTopLevel) continue;
|
||||
|
||||
var dependency = tfmInfo.Dependencies
|
||||
.FirstOrDefault(d => d.Name.Equals(lockFileTargetLibrary.Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var dependents = dependencyMap.GetValueOrDefault(lockFileTargetLibrary.Name, []);
|
||||
var mappedDependents = dependents.Select(d => new DependentPackage
|
||||
{
|
||||
PackageName = d.PackageName,
|
||||
RequestedVersion = d.PackageDependency.VersionRange
|
||||
}).ToList();
|
||||
|
||||
var existingPackage = allPackages.GetValueOrDefault(lockFileTargetLibrary.Name) ?? new InstalledPackage { Name = lockFileTargetLibrary.Name, ProjectPackageReferences = [] };
|
||||
existingPackage.ProjectPackageReferences.Add(new ProjectPackageReference
|
||||
{
|
||||
Project = project,
|
||||
InstalledVersion = lockFileTargetLibrary.Version,
|
||||
IsTopLevel = isTopLevel,
|
||||
IsAutoReferenced = dependency?.AutoReferenced ?? false,
|
||||
DependentPackages = mappedDependents
|
||||
});
|
||||
allPackages[lockFileTargetLibrary.Name] = existingPackage;
|
||||
}
|
||||
}
|
||||
|
||||
return packages;
|
||||
return allPackages.Values.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public class InstalledPackage
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
public required string RequestedVersion { get; set; }
|
||||
public required string? ResolvedVersion { get; set; }
|
||||
public required string TargetFramework { get; set; }
|
||||
//public required NuGetVersion LatestVersion { get; set; }
|
||||
public required List<ProjectPackageReference> ProjectPackageReferences { get; set; }
|
||||
}
|
||||
|
||||
public class ProjectPackageReference
|
||||
{
|
||||
public required SharpIdeProjectModel Project { get; set; }
|
||||
public required NuGetVersion InstalledVersion { get; set; }
|
||||
public required bool IsTopLevel { get; set; }
|
||||
public required bool IsAutoReferenced { get; set; }
|
||||
public List<DependentPackage>? DependentPackages { get; set; }
|
||||
public bool IsTransitive => !IsTopLevel && !IsAutoReferenced;
|
||||
}
|
||||
|
||||
public class DependentPackage
|
||||
{
|
||||
public required string PackageName { get; set; }
|
||||
|
||||
@@ -9,7 +9,7 @@ using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||
|
||||
namespace SharpIDE.Application.Features.Nuget;
|
||||
|
||||
public record InstalledNugetPackageInfo(bool IsTransitive, NuGetVersion Version, List<DependentPackage>? DependentPackages);
|
||||
public record InstalledNugetPackageInfo(List<ProjectPackageReference> ProjectPackageReferences);
|
||||
public record IdePackageResult(string PackageId, List<IdePackageFromSourceResult> PackageFromSources, InstalledNugetPackageInfo? InstalledNugetPackageInfo);
|
||||
public record struct IdePackageFromSourceResult(IPackageSearchMetadata PackageSearchMetadata, PackageSource Source);
|
||||
public class NugetClientService
|
||||
@@ -118,11 +118,7 @@ public class NugetClientService
|
||||
var packagesResult = new List<IdePackageResult>();
|
||||
foreach (var installedPackage in installedPackages)
|
||||
{
|
||||
var isTransitive = installedPackage.IsTopLevel is false;
|
||||
var nugetVersionString = installedPackage.ResolvedVersion ?? installedPackage.RequestedVersion;
|
||||
var nugetVersion = NuGetVersion.Parse(nugetVersionString);
|
||||
|
||||
var installedNugetPackageInfo = new InstalledNugetPackageInfo(isTransitive, nugetVersion, installedPackage.DependentPackages);
|
||||
var installedNugetPackageInfo = new InstalledNugetPackageInfo(installedPackage.ProjectPackageReferences);
|
||||
var idePackageResult = new IdePackageResult(installedPackage.Name, [], installedNugetPackageInfo);
|
||||
|
||||
foreach (var source in packageSources)
|
||||
|
||||
@@ -26,8 +26,7 @@ public partial class NugetPanel : Control
|
||||
|
||||
private readonly PackedScene _packageEntryScene = ResourceLoader.Load<PackedScene>("uid://cqc2xlt81ju8s");
|
||||
private readonly Texture2D _csprojIcon = ResourceLoader.Load<Texture2D>("uid://cqt30ma6xgder");
|
||||
|
||||
private IdePackageResult? _selectedPackage;
|
||||
|
||||
// we use this to access the project for the dropdown
|
||||
private List<SharpIdeProjectModel?> _projects = null!;
|
||||
|
||||
@@ -81,7 +80,6 @@ public partial class NugetPanel : Control
|
||||
|
||||
private async Task OnPackageSelected(IdePackageResult packageResult)
|
||||
{
|
||||
_selectedPackage = packageResult;
|
||||
await _nugetPackageDetails.SetPackage(packageResult);
|
||||
}
|
||||
|
||||
@@ -113,6 +111,7 @@ public partial class NugetPanel : Control
|
||||
|
||||
private async Task PopulateSearchResults()
|
||||
{
|
||||
return;
|
||||
var result = await _nugetClientService.GetTop100Results(_solution!.DirectoryPath);
|
||||
var scenes = result.Select(s =>
|
||||
{
|
||||
@@ -135,7 +134,7 @@ public partial class NugetPanel : Control
|
||||
{
|
||||
var project = _solution!.AllProjects.First(s => s.Name == "ProjectA");
|
||||
await project.MsBuildEvaluationProjectTask;
|
||||
var installedPackages = await ProjectEvaluation.GetPackageReferencesForProject(project);
|
||||
var installedPackages = await ProjectEvaluation.GetPackageReferencesForProjects([project]);
|
||||
var idePackageResult = await _nugetClientService.GetPackagesForInstalledPackages(project.ChildNodeBasePath, installedPackages);
|
||||
var scenes = idePackageResult.Select(s =>
|
||||
{
|
||||
@@ -144,7 +143,7 @@ public partial class NugetPanel : Control
|
||||
scene.PackageSelected += OnPackageSelected;
|
||||
return scene;
|
||||
}).ToList();
|
||||
var transitiveScenes = scenes.Where(s => s.PackageResult.InstalledNugetPackageInfo!.IsTransitive).ToList();
|
||||
var transitiveScenes = scenes.Where(s => s.PackageResult.InstalledNugetPackageInfo!.ProjectPackageReferences.Any(x => x.IsTransitive)).ToList();
|
||||
var directScenes = scenes.Except(transitiveScenes).ToList();
|
||||
await this.InvokeAsync(() =>
|
||||
{
|
||||
|
||||
@@ -52,9 +52,10 @@ public partial class PackageEntry : MarginContainer
|
||||
if (PackageResult is null) return;
|
||||
_packageNameLabel.Text = PackageResult.PackageId;
|
||||
var installedPackagedInfo = PackageResult.InstalledNugetPackageInfo;
|
||||
if (installedPackagedInfo?.DependentPackages is not null && installedPackagedInfo.IsTransitive)
|
||||
var isTransitive = installedPackagedInfo?.ProjectPackageReferences.Any(p => p.IsTransitive) ?? false;
|
||||
if (isTransitive && installedPackagedInfo?.ProjectPackageReferences.Any(p => p.DependentPackages?.Count is not 0) is true)
|
||||
{
|
||||
var transitiveOriginsGroupedByVersion = installedPackagedInfo.DependentPackages
|
||||
var transitiveOriginsGroupedByVersion = installedPackagedInfo.ProjectPackageReferences.SelectMany(s => s.DependentPackages ?? [])
|
||||
.GroupBy(t => t.RequestedVersion)
|
||||
.Select(g => new
|
||||
{
|
||||
@@ -67,10 +68,10 @@ public partial class PackageEntry : MarginContainer
|
||||
{string.Join("\n", transitiveOriginsGroupedByVersion.Select(t => $"{t.RequestedVersion.ToString("p", VersionRangeFormatter.Instance)} by {string.Join(", ", t.PackageNames)}"))}
|
||||
""";
|
||||
}
|
||||
_installedVersionLabel.Text = installedPackagedInfo?.IsTransitive is true ? $"({installedPackagedInfo?.Version.ToNormalizedString()})" : installedPackagedInfo?.Version.ToNormalizedString();
|
||||
_installedVersionLabel.Text = GetInstalledVersionsText(installedPackagedInfo);
|
||||
var highestVersionPackageFromSource = PackageResult.PackageFromSources
|
||||
.MaxBy(p => p.PackageSearchMetadata.Identity.Version);
|
||||
if (installedPackagedInfo?.Version != highestVersionPackageFromSource.PackageSearchMetadata.Identity.Version)
|
||||
if (installedPackagedInfo?.ProjectPackageReferences.TrueForAll(s => s.InstalledVersion != highestVersionPackageFromSource.PackageSearchMetadata.Identity.Version) is true)
|
||||
{
|
||||
_latestVersionLabel.Text = highestVersionPackageFromSource.PackageSearchMetadata.Identity.Version.ToNormalizedString();
|
||||
}
|
||||
@@ -94,4 +95,17 @@ public partial class PackageEntry : MarginContainer
|
||||
_sourceNamesContainer.AddChild(label);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetInstalledVersionsText(InstalledNugetPackageInfo? packageInfo)
|
||||
{
|
||||
if (packageInfo is null) return string.Empty;
|
||||
|
||||
var versions = packageInfo.ProjectPackageReferences
|
||||
.Select(p => p.InstalledVersion.ToNormalizedString())
|
||||
.Distinct()
|
||||
.ToList();
|
||||
var isTransitive = packageInfo.ProjectPackageReferences.Any(p => p.IsTransitive);
|
||||
var text = isTransitive ? $"({string.Join(", ", versions)})" : string.Join(", ", versions);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user