Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PrunePackageReference in the new resolver #6142

Merged
merged 3 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public async Task<ValueTuple<bool, List<RestoreTargetGraph>, RuntimeGraph>> Reso
hasInstallBeenCalledAlready = true;
}

TargetFrameworkInformation? projectTargetFramework = _request.Project.GetTargetFramework(pair.Framework);
TargetFrameworkInformation projectTargetFramework = _request.Project.GetTargetFramework(pair.Framework)!;
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved

var unresolvedPackages = new HashSet<LibraryRange>();

Expand All @@ -104,7 +104,7 @@ public async Task<ValueTuple<bool, List<RestoreTargetGraph>, RuntimeGraph>> Reso
// This is guaranteed to be computed before any graph with a RID, so we can assume this will return a value.

// PCL Projects with Supports have a runtime graph but no matching framework.
var runtimeGraphPath = projectTargetFramework?.RuntimeIdentifierGraphPath;
var runtimeGraphPath = projectTargetFramework.RuntimeIdentifierGraphPath;

RuntimeGraph? projectProviderRuntimeGraph = default;
if (runtimeGraphPath != null)
Expand Down Expand Up @@ -635,9 +635,20 @@ async static (state) =>
suppressions = currentSuppressions;
}

List<int>? prunedPackageIndices = null;
for (int i = 0; i < refItemResult.Item.Data.Dependencies.Count; i++)
{
LibraryDependency dep = refItemResult.Item.Data.Dependencies[i];
bool isPackage = dep.LibraryRange.TypeConstraintAllows(LibraryDependencyTarget.Package);
bool isDirectPackageReferenceFromRootProject = (currentRefRangeIndex == rootProjectRefItem.LibraryRangeIndex) && isPackage;

if (ShouldPrunePackage(projectTargetFramework!, refItemResult, dep, isPackage, isDirectPackageReferenceFromRootProject))
{
prunedPackageIndices ??= [];
prunedPackageIndices.Add(i);
continue;
}

LibraryDependencyIndex depIndex = refItemResult.GetDependencyIndexForDependency(i);

// Skip this node if the VersionRange is null or if its not transitively pinned and PrivateAssets=All
Expand All @@ -648,9 +659,6 @@ async static (state) =>

VersionRange? pinnedVersionRange = null;

bool isPackage = dep.LibraryRange.TypeConstraintAllows(LibraryDependencyTarget.Package);
bool isDirectPackageReferenceFromRootProject = (currentRefRangeIndex == rootProjectRefItem.LibraryRangeIndex) && isPackage;

if (!isDirectPackageReferenceFromRootProject && directPackageReferences?.Contains(depIndex) == true)
{
continue;
Expand Down Expand Up @@ -752,6 +760,14 @@ async static (state) =>
{
foreach (var dep in runtimeDependencies)
{
if (ShouldPrunePackage(projectTargetFramework!, refItemResult, dep, isPackage: true, isDirectPackageReferenceFromRootProject: false))
{
prunedPackageIndices ??= [];
prunedPackageIndices.Add(runtimeDependencyIndex);
runtimeDependencyIndex++;
continue;
}

DependencyGraphItem runtimeDependencyGraphItem = new()
{
LibraryDependency = dep,
Expand Down Expand Up @@ -786,6 +802,12 @@ async static (state) =>
}
}
}

// If the latest item was chosen, keep track of the pruned dependency indices.
if (chosenResolvedItems.TryGetValue(currentRefDependencyIndex, out ResolvedDependencyGraphItem? resolvedGraphItem))
{
resolvedGraphItem.PrunedDependencyIndices = prunedPackageIndices;
}
}

//Now that we've completed import, figure out the short real flattened list
Expand Down Expand Up @@ -829,13 +851,10 @@ async static (state) =>
LibraryRangeIndex[] pathToChosenRef = foundItem.Path;
bool directPackageReferenceFromRootProject = foundItem.IsDirectPackageReferenceFromRootProject;
List<HashSet<LibraryDependencyIndex>> chosenSuppressions = foundItem.Suppressions;

if (findLibraryEntryCache.TryGetValue(chosenRefRangeIndex, out Task<FindLibraryEntryResult>? nodeTask))
{
FindLibraryEntryResult node = await nodeTask;

flattenedGraphItems.Add(node.Item);

for (int i = 0; i < node.Item.Data.Dependencies.Count; i++)
{
var dep = node.Item.Data.Dependencies[i];
Expand All @@ -845,6 +864,11 @@ async static (state) =>
continue;
}

if (foundItem.PrunedDependencyIndices != null && foundItem.PrunedDependencyIndices.Contains(i))
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
{
continue;
}

if (StringComparer.OrdinalIgnoreCase.Equals(dep.Name, node.Item.Key.Name) || StringComparer.OrdinalIgnoreCase.Equals(dep.Name, rootGraphNode.Key.Name))
{
// Cycle
Expand Down Expand Up @@ -1034,6 +1058,31 @@ async static (state) =>
range: newGraphNode.Key.VersionRange,
child: newGraphNode.Item.Key));
}

if (foundItem.PrunedDependencyIndices != null && foundItem.PrunedDependencyIndices!.Count > 0)
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
{
int dependencyCount = node.Item.Data.Dependencies.Count - foundItem.PrunedDependencyIndices.Count;

List<LibraryDependency> dependencies = dependencyCount > 0 ? new(dependencyCount) : [];

for (int i = 0; dependencyCount > 0 && i < node.Item.Data.Dependencies.Count; i++)
{
if (!foundItem.PrunedDependencyIndices.Contains(i))
{
dependencies.Add(node.Item.Data.Dependencies[i]);
}
}

RemoteResolveResult remoteResolveResult = new RemoteResolveResult()
{
Match = node.Item.Data.Match,
Dependencies = dependencies,
};

node.Item.Data = remoteResolveResult;
}

flattenedGraphItems.Add(node.Item);
}
}

Expand Down Expand Up @@ -1204,6 +1253,42 @@ async static (state) =>
return (_success, allGraphs, allRuntimes);
}

private bool ShouldPrunePackage(TargetFrameworkInformation projectTargetFramework, FindLibraryEntryResult refItemResult, LibraryDependency dep, bool isPackage, bool isDirectPackageReferenceFromRootProject)
{
if (projectTargetFramework!.PackagesToPrune.TryGetValue(dep.Name, out PrunePackageReference? prunableVersion))
{
if (dep.LibraryRange!.VersionRange!.Satisfies(prunableVersion.VersionRange!.MaxVersion!))
{
if (!isPackage)
{
if (SdkAnalysisLevelMinimums.IsEnabled(
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
_request.Project!.RestoreMetadata!.SdkAnalysisLevel,
_request.Project.RestoreMetadata.UsingMicrosoftNETSdk,
SdkAnalysisLevelMinimums.PruningWarnings))
{
_logger.Log(RestoreLogMessage.CreateWarning(NuGetLogCode.NU1511, string.Format(CultureInfo.CurrentCulture, Strings.Error_RestorePruningProjectReference, dep.Name)));
}
}
else if (isDirectPackageReferenceFromRootProject)
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
{
if (SdkAnalysisLevelMinimums.IsEnabled(
_request.Project!.RestoreMetadata!.SdkAnalysisLevel,
_request.Project.RestoreMetadata.UsingMicrosoftNETSdk,
SdkAnalysisLevelMinimums.PruningWarnings))
{
_logger.Log(RestoreLogMessage.CreateWarning(NuGetLogCode.NU1510, string.Format(CultureInfo.CurrentCulture, Strings.Error_RestorePruningDirectPackageReference, dep.Name)));
}
}
else
{
_logger.LogDebug(string.Format(CultureInfo.CurrentCulture, Strings.RestoreDebugPruningPackageReference, $"{dep.Name} {dep.LibraryRange.VersionRange.OriginalString}", refItemResult.Item.Key, prunableVersion.VersionRange.MaxVersion));
return true;
}
}
}
return false;
}

private static bool EvictOnTypeConstraint(LibraryDependencyTarget current, LibraryDependencyTarget previous)
{
if (current == previous)
Expand Down Expand Up @@ -1295,6 +1380,8 @@ private class ResolvedDependencyGraphItem
public required LibraryRangeIndex[] Path { get; set; }

public required List<HashSet<LibraryDependencyIndex>> Suppressions { get; set; }

public List<int>? PrunedDependencyIndices { get; set; }
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
}

internal sealed class LibraryDependencyInterningTable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class LockFileBuilderCache
private readonly ConcurrentDictionary<CriteriaKey, List<(List<SelectionCriteria>, bool)>> _criteriaSets =
new();

private readonly ConcurrentDictionary<(CriteriaKey, string path, string aliases, LibraryIncludeFlags), Lazy<(LockFileTargetLibrary, bool)>> _lockFileTargetLibraryCache =
private readonly ConcurrentDictionary<(CriteriaKey, string path, string aliases, LibraryIncludeFlags, int), Lazy<(LockFileTargetLibrary, bool)>> _lockFileTargetLibraryCache =
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
new();

/// <summary>
Expand Down Expand Up @@ -104,7 +104,7 @@ public ContentItemCollection GetContentItems(LockFileLibrary library, LocalPacka
/// <summary>
/// Try to get a LockFileTargetLibrary from the cache.
/// </summary>
internal (LockFileTargetLibrary, bool) GetLockFileTargetLibrary(RestoreTargetGraph graph, NuGetFramework framework, LocalPackageInfo localPackageInfo, string aliases, LibraryIncludeFlags libraryIncludeFlags, Func<(LockFileTargetLibrary, bool)> valueFactory)
internal (LockFileTargetLibrary, bool) GetLockFileTargetLibrary(RestoreTargetGraph graph, NuGetFramework framework, LocalPackageInfo localPackageInfo, string aliases, LibraryIncludeFlags libraryIncludeFlags, List<LibraryDependency> dependencies, Func<(LockFileTargetLibrary, bool)> valueFactory)
{
// Comparing RuntimeGraph for equality is very expensive,
// so in case of a request where the RuntimeGraph is not empty we avoid using the cache.
Expand All @@ -114,7 +114,7 @@ public ContentItemCollection GetContentItems(LockFileLibrary library, LocalPacka
localPackageInfo = localPackageInfo ?? throw new ArgumentNullException(nameof(localPackageInfo));
var criteriaKey = new CriteriaKey(graph.TargetGraphName, framework);
var packagePath = localPackageInfo.ExpandedPath;
return _lockFileTargetLibraryCache.GetOrAdd((criteriaKey, packagePath, aliases, libraryIncludeFlags),
return _lockFileTargetLibraryCache.GetOrAdd((criteriaKey, packagePath, aliases, libraryIncludeFlags, dependencies.Count),
key => new Lazy<(LockFileTargetLibrary, bool)>(valueFactory)).Value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ internal static (LockFileTargetLibrary, bool) CreateLockFileTargetLibrary(
var runtimeIdentifier = targetGraph.RuntimeIdentifier;
var framework = targetFrameworkOverride ?? targetGraph.Framework;

return cache.GetLockFileTargetLibrary(targetGraph, framework, package, aliases, dependencyType,
return cache.GetLockFileTargetLibrary(targetGraph, framework, package, aliases, dependencyType, dependencies,
() =>
{
LockFileTargetLibrary lockFileLib = null;
Expand Down
27 changes: 27 additions & 0 deletions src/NuGet.Core/NuGet.Commands/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion src/NuGet.Core/NuGet.Commands/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1127,4 +1127,16 @@ NuGet requires HTTPS sources. Refer to https://aka.ms/nuget-https-everywhere for
<value>Audit source '{0}' did not provide any vulnerability data.</value>
<comment>{0} is the source name</comment>
</data>
</root>
<data name="RestoreDebugPruningPackageReference" xml:space="preserve">
<value>Pruning the package '{0}' as a dependency of '{1}'. The maximum prunable version is '{2}'</value>
<comment>0 - package id and version, 1 - version</comment>
</data>
<data name="Error_RestorePruningDirectPackageReference" xml:space="preserve">
<value>A direct PackageReference cannot be pruned, {0}. Consider removing this package from your dependencies, as it is likely unnecessary.</value>
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
<comment>0 - package id and version</comment>
</data>
<data name="Error_RestorePruningProjectReference" xml:space="preserve">
<value>A ProjectReference cannot be pruned, {0}.</value>
<comment>0 - project reference</comment>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ internal static class SdkAnalysisLevelMinimums
/// </summary>
internal static readonly NuGetVersion HttpErrorSdkAnalysisLevelMinimumValue = new("9.0.100");

/// <summary>
/// Minimum SDK Analysis Level required for warning for packages and projects that cannot be pruned.
/// </summary>
internal static readonly NuGetVersion PruningWarnings = new("10.0.100");
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Determines whether the feature is enabled based on the SDK analysis level.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,16 @@ public enum NuGetLogCode
/// </summary>
NU1509 = 1509,

/// <summary>
/// Direct reference to a package that cannot be pruned.
nkolev92 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
NU1510 = 1510,

/// <summary>
/// Project references cannot be pruned
/// </summary>
NU1511 = 1511,

/// <summary>
/// Dependency bumped up
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions src/NuGet.Core/NuGet.Common/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#nullable enable
NuGet.Common.NuGetLogCode.NU1509 = 1509 -> NuGet.Common.NuGetLogCode
NuGet.Common.NuGetLogCode.NU1510 = 1510 -> NuGet.Common.NuGetLogCode
NuGet.Common.NuGetLogCode.NU1511 = 1511 -> NuGet.Common.NuGetLogCode
static NuGet.Common.MSBuildStringUtility.GetNuGetLogCodes(string! s) -> System.Collections.Immutable.ImmutableArray<NuGet.Common.NuGetLogCode>
Loading