Skip to content

Commit fbcb617

Browse files
authored
Full restore support for aliasing - make the assets file aliasing aware (#6972)
1 parent 17fea19 commit fbcb617

File tree

71 files changed

+2412
-561
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+2412
-561
lines changed

src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/PackageReferenceProject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ private async ValueTask<IList<LockFileTarget>> GetTargetsListAsync(string assets
444444

445445
await TaskScheduler.Default;
446446

447-
LockFile lockFile = LockFileUtilities.GetLockFile(assetsFilePath, NullLogger.Instance, LockFileReadFlags.PackageFolders | LockFileReadFlags.Targets);
447+
LockFile lockFile = LockFileUtilities.GetLockFile(assetsFilePath, NullLogger.Instance, LockFileReadFlags.PackageFolders | LockFileReadFlags.Targets | LockFileReadFlags.PackageSpec);
448448
_packageFolders = lockFile?.PackageFolders ?? Array.Empty<LockFileItem>();
449449

450450
return lockFile?.Targets;

src/NuGet.Core/NuGet.Build.Tasks/GetReferenceNearestTargetFrameworkTask.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ public class GetReferenceNearestTargetFrameworkTask : Task
4242
public string CurrentProjectTargetPlatform { get; set; }
4343

4444

45+
/// <summary>
46+
/// The value of the current project's TargetFramework property.
47+
/// </summary>
48+
public string CurrentProjectTargetFrameworkProperty { get; set; }
49+
4550
/// <summary>
4651
/// Optional list of target frameworks to be used as Fallback target frameworks.
4752
/// </summary>
@@ -153,7 +158,7 @@ private ITaskItem AssignNearestFrameworkForSingleReference(
153158

154159
var packageSpec = new PackageSpec(targetFrameworkInformations);
155160

156-
var nearestNuGetFramework = packageSpec.GetTargetFramework(projectNuGetFramework);
161+
var nearestNuGetFramework = packageSpec.GetNearestTargetFramework(projectNuGetFramework, CurrentProjectTargetFrameworkProperty);
157162

158163
if (nearestNuGetFramework.FrameworkName != null)
159164
{
@@ -164,7 +169,7 @@ private ITaskItem AssignNearestFrameworkForSingleReference(
164169
// try project fallback frameworks
165170
foreach (var currentProjectTargetFramework in fallbackNuGetFrameworks)
166171
{
167-
nearestNuGetFramework = packageSpec.GetTargetFramework(currentProjectTargetFramework);
172+
nearestNuGetFramework = packageSpec.GetNearestTargetFramework(currentProjectTargetFramework, targetAlias: null);
168173

169174
if (nearestNuGetFramework.FrameworkName != null)
170175
{

src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ Copyright (c) .NET Foundation. All rights reserved.
5959
<PackageDownloadSupported>true</PackageDownloadSupported>
6060
<!-- Mark that this targets file GetReferenceNearestTargetFrameworkTask task supports the TargetPlatformMoniker -->
6161
<GetReferenceNearestTargetFrameworkTaskSupportsTargetPlatformParameter>true</GetReferenceNearestTargetFrameworkTaskSupportsTargetPlatformParameter>
62+
<!-- Mark that this targets file GetReferenceNearestTargetFrameworkTask task supports the TargetFramework -->
63+
<GetReferenceNearestTargetFrameworkTaskSupportsTargetFrameworkPropertyParameter>true</GetReferenceNearestTargetFrameworkTaskSupportsTargetFrameworkPropertyParameter>
6264
<!-- Flag if the Central package file is enabled -->
6365
<_CentralPackageVersionsEnabled Condition="'$(ManagePackageVersionsCentrally)' == 'true' AND '$(CentralPackageVersionsFileImported)' == 'true'">true</_CentralPackageVersionsEnabled>
6466
</PropertyGroup>

src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Why/DependencyGraphFinder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ internal static class DependencyGraphFinder
8080
if (useTargetAlias)
8181
{
8282
targetAlias = target.TargetAlias;
83-
directPackages = packageSpec.GetTargetFramework(targetAlias).Dependencies;
84-
directProjectReferences = packageSpec.GetRestoreMetadataFramework(targetAlias).ProjectReferences;
83+
directPackages = packageSpec.GetTargetFramework(targetAlias)!.Dependencies;
84+
directProjectReferences = packageSpec.GetRestoreMetadataFramework(targetAlias)!.ProjectReferences;
8585
}
8686
else
8787
{

src/NuGet.Core/NuGet.Commands/GlobalSuppressions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@
9696
[assembly: SuppressMessage("Build", "CA1303:Method 'void PackCommandRunner.PrintVerbose(string outputPath, PackageBuilder builder)' passes a literal string as parameter 'message' of a call to 'void PackCommandRunner.WriteLine(string message, object arg = null)'. Retrieve the following string(s) from a resource table instead: \"Version: {0}\".", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.PackCommandRunner.PrintVerbose(System.String,NuGet.Packaging.PackageBuilder)")]
9797
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void PackCommandRunner.SetupCurrentDirectory(PackArgs packArgs)', validate parameter 'packArgs' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.PackCommandRunner.SetupCurrentDirectory(NuGet.Commands.PackArgs)")]
9898
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void RemoveSourceRunner.Run(RemoveSourceArgs args, Func<ILogger> getLogger)', validate parameter 'getLogger' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.RemoveSourceRunner.Run(NuGet.Commands.RemoveSourceArgs,System.Func{NuGet.Common.ILogger})")]
99-
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void RestoreArgs.ApplyStandardProperties(RestoreRequest request)', validate parameter 'request' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.RestoreArgs.ApplyStandardProperties(NuGet.Commands.RestoreRequest)")]
10099
[assembly: SuppressMessage("Build", "CA1822:Member GetEffectiveFallbackPackageFolders does not access instance data and can be marked as static (Shared in VisualBasic)", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.RestoreArgs.GetEffectiveFallbackPackageFolders(NuGet.Configuration.ISettings)~System.Collections.Generic.IReadOnlyList{System.String}")]
101100
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void RestoreCollectorLogger.ApplyRestoreInputs(PackageSpec projectSpec)', validate parameter 'projectSpec' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.RestoreCollectorLogger.ApplyRestoreInputs(NuGet.ProjectModel.PackageSpec)")]
102101
[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'bool RestoreCollectorLogger.DisplayMessage(IRestoreLogMessage message)', validate parameter 'message' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "<Pending>", Scope = "member", Target = "~M:NuGet.Commands.RestoreCollectorLogger.DisplayMessage(NuGet.Common.IRestoreLogMessage)~System.Boolean")]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
#nullable enable
2+
~NuGet.Commands.SourceRepositoryDependencyProvider.FindLibraryAsync(NuGet.LibraryModel.LibraryRange! libraryRange, NuGet.Frameworks.NuGetFramework! targetFramework, string! targetAlias, NuGet.Protocol.Core.Types.SourceCacheContext! cacheContext, NuGet.Common.ILogger! logger, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<NuGet.LibraryModel.LibraryIdentity!>!

src/NuGet.Core/NuGet.Commands/RestoreCommand/DependencyGraphResolver.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ public async Task<ValueTuple<bool, List<RestoreTargetGraph>, RuntimeGraph>> Reso
195195
_rootProjectLibraryDependency.LibraryRange,
196196
frameworkRuntimeDefinition.Framework,
197197
runtimeIdentifier: string.IsNullOrWhiteSpace(frameworkRuntimeDefinition.RuntimeIdentifier) ? null : frameworkRuntimeDefinition.RuntimeIdentifier,
198+
frameworkRuntimeDefinition.TargetAlias,
198199
context,
199200
token),
200201
Parent = LibraryRangeIndex.None
@@ -1394,6 +1395,7 @@ .. chosenResolvedItem.Suppressions
13941395
childDependency.LibraryRange,
13951396
pair.Framework,
13961397
runtimeIdentifier: string.IsNullOrWhiteSpace(pair.RuntimeIdentifier) ? null : pair.RuntimeIdentifier,
1398+
pair.TargetAlias,
13971399
context,
13981400
token)
13991401
};

src/NuGet.Core/NuGet.Commands/RestoreCommand/LockFileBuilder.cs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,20 @@ public LockFile CreateLockFile(LockFile previousLockFile,
162162
.OrderBy(graph => graph.Framework.ToString(), StringComparer.Ordinal)
163163
.ThenBy(graph => graph.RuntimeIdentifier, StringComparer.Ordinal))
164164
{
165-
var target = new LockFileTarget
166-
{
167-
TargetFramework = targetGraph.Framework,
168-
RuntimeIdentifier = targetGraph.RuntimeIdentifier,
169-
TargetAlias = targetGraph.TargetAlias,
170-
};
165+
var target = lockFile.Version >= LockFileFormat.AliasedVersion ?
166+
new LockFileTarget
167+
{
168+
TargetFramework = targetGraph.Framework,
169+
RuntimeIdentifier = targetGraph.RuntimeIdentifier,
170+
TargetAlias = targetGraph.TargetAlias,
171+
Name = targetGraph.TargetGraphName
172+
} :
173+
new LockFileTarget
174+
{
175+
TargetFramework = targetGraph.Framework,
176+
RuntimeIdentifier = targetGraph.RuntimeIdentifier,
177+
TargetAlias = targetGraph.TargetAlias,
178+
};
171179

172180
var flattenedFlags = IncludeFlagUtils.FlattenDependencyTypes(_includeFlagGraphs, project, targetGraph);
173181

@@ -389,20 +397,24 @@ private static string GetFallbackFrameworkString(NuGetFramework framework)
389397
return string.Join(", ", frameworks);
390398
}
391399

392-
private static void AddProjectFileDependenciesForPackageReference(PackageSpec project, LockFile lockFile, IEnumerable<RestoreTargetGraph> targetGraphs)
400+
private static void AddProjectFileDependenciesForPackageReference(PackageSpec project, LockFile lockFile, List<RestoreTargetGraph> targetGraphs)
393401
{
394-
// For NETCore put everything under a TFM section
395-
// Projects are included for NETCore
402+
bool isAliasedLockFile = lockFile.Version >= LockFileFormat.AliasedVersion;
403+
396404
foreach (var frameworkInfo in project.TargetFrameworks
397-
.OrderBy(framework => framework.FrameworkName.ToString(),
405+
.OrderBy(framework => framework.TargetAlias,
398406
StringComparer.Ordinal))
399407
{
400408
var dependencies = new List<LibraryRange>();
401409
dependencies.AddRange(frameworkInfo.Dependencies.Select(e => e.LibraryRange));
402410

403-
var targetGraph = targetGraphs.SingleOrDefault(graph =>
404-
graph.Framework.Equals(frameworkInfo.FrameworkName)
405-
&& string.IsNullOrEmpty(graph.RuntimeIdentifier));
411+
RestoreTargetGraph targetGraph = !string.IsNullOrEmpty(frameworkInfo.TargetAlias) ?
412+
targetGraphs.SingleOrDefault(graph =>
413+
frameworkInfo.TargetAlias.Equals(graph.TargetAlias)
414+
&& string.IsNullOrEmpty(graph.RuntimeIdentifier)) :
415+
targetGraphs.SingleOrDefault(graph =>
416+
graph.Framework.Equals(frameworkInfo.FrameworkName)
417+
&& string.IsNullOrEmpty(graph.RuntimeIdentifier));
406418

407419
var resolvedEntry = targetGraph?
408420
.Flattened
@@ -428,8 +440,9 @@ private static void AddProjectFileDependenciesForPackageReference(PackageSpec pr
428440
}
429441

430442
// Add entry
443+
string framework = (isAliasedLockFile && !string.IsNullOrEmpty(frameworkInfo.TargetAlias)) ? frameworkInfo.TargetAlias : frameworkInfo.FrameworkName.ToString();
431444
var dependencyGroup = new ProjectFileDependencyGroup(
432-
frameworkInfo.FrameworkName.ToString(),
445+
framework,
433446
uniqueDependencies.Select(x => x.ToLockFileDependencyGroupString())
434447
.OrderBy(dependency => dependency, StringComparer.Ordinal));
435448

src/NuGet.Core/NuGet.Commands/RestoreCommand/RequestFactory/RestoreArgs.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ private List<SourceRepository> GetEffectiveSourcesCore(ISettings settings, IList
184184

185185
public void ApplyStandardProperties(RestoreRequest request)
186186
{
187+
if (request == null) throw new ArgumentNullException(nameof(request));
188+
187189
if (request.ProjectStyle == ProjectStyle.PackageReference)
188190
{
189191
request.LockFilePath = Path.Combine(request.RestoreOutputPath, LockFileFormat.AssetsFileName);
@@ -203,7 +205,8 @@ public void ApplyStandardProperties(RestoreRequest request)
203205

204206
request.RequestedRuntimes.UnionWith(Runtimes);
205207
request.FallbackRuntimes.UnionWith(FallbackRuntimes);
206-
request.LockFileVersion = LockFileFormat.Version;
208+
// Use legacy version for classic csproj.
209+
request.LockFileVersion = request.Project.RestoreMetadata?.UsingMicrosoftNETSdk == true ? LockFileFormat.Version : LockFileFormat.LegacyVersion;
207210

208211
// Run runtime asset checks for project.json, and for other types if enabled.
209212
if (ValidateRuntimeAssets == null)

src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
using NuGet.Protocol.Core.Types;
2424
using NuGet.Repositories;
2525
using NuGet.RuntimeModel;
26+
using NuGet.Shared;
2627
using NuGet.Versioning;
2728

2829
namespace NuGet.Commands
@@ -153,12 +154,12 @@ public RestoreCommand(RestoreRequest request)
153154
_request = request ?? throw new ArgumentNullException(nameof(request));
154155
_lockFileBuilderCache = request.LockFileBuilderCache;
155156

156-
// Validate the lock file version requested
157-
if (_request.LockFileVersion < 3 || _request.LockFileVersion > LockFileFormat.Version)
157+
// Validate the assets file version requested
158+
if (_request.LockFileVersion < LockFileFormat.LegacyVersion || _request.LockFileVersion > LockFileFormat.Version)
158159
{
159160
throw new ArgumentOutOfRangeException(
160161
paramName: nameof(request),
161-
message: $"Lock file version {_request.LockFileVersion} is not supported.");
162+
message: $"Assets file version {_request.LockFileVersion} is not supported.");
162163
}
163164

164165
var collectorLoggerHideWarningsAndErrors = request.Project.RestoreSettings.HideWarningsAndErrors
@@ -1462,8 +1463,10 @@ private LockFile BuildAssetsFile(
14621463
IReadOnlyList<NuGetv3LocalRepository> localRepositories,
14631464
RemoteWalkContext contextForProject)
14641465
{
1466+
int lockFileVersion = DetermineLockFileVersion();
1467+
14651468
// Build the lock file
1466-
var lockFile = new LockFileBuilder(_request.LockFileVersion, _logger, _includeFlagGraphs)
1469+
var lockFile = new LockFileBuilder(lockFileVersion, _logger, _includeFlagGraphs)
14671470
.CreateLockFile(
14681471
existingLockFile,
14691472
project,
@@ -1473,6 +1476,23 @@ private LockFile BuildAssetsFile(
14731476
_lockFileBuilderCache);
14741477

14751478
return lockFile;
1479+
1480+
int DetermineLockFileVersion()
1481+
{
1482+
int lockFileVersion = _request.LockFileVersion;
1483+
1484+
// We use the request provided version, unless we are using the Microsoft.NET.Sdk that does not support reading of aliased assets files.
1485+
if (_request.Project.RestoreMetadata.UsingMicrosoftNETSdk &&
1486+
!SdkAnalysisLevelMinimums.IsEnabled(
1487+
_request.Project.RestoreMetadata.SdkAnalysisLevel,
1488+
_request.Project.RestoreMetadata.UsingMicrosoftNETSdk,
1489+
SdkAnalysisLevelMinimums.V10_0_300))
1490+
{
1491+
lockFileVersion = LockFileFormat.LegacyVersion;
1492+
}
1493+
1494+
return lockFileVersion;
1495+
}
14761496
}
14771497

14781498
/// <summary>
@@ -1660,6 +1680,14 @@ private static async Task<IList<CompatibilityCheckResult>> VerifyCompatibilityAs
16601680
TelemetryActivity telemetryActivity)
16611681
{
16621682
bool success = true;
1683+
1684+
// Check for duplicate frameworks. The new dependency resolver supports aliasing, but the legacy one does not.
1685+
if (await ErrorForDuplicateFrameworks(_request, _logger))
1686+
{
1687+
success = false;
1688+
return (success, []);
1689+
}
1690+
16631691
if (_request.Project.TargetFrameworks.Count == 0)
16641692
{
16651693
var message = string.Format(CultureInfo.CurrentCulture, Strings.Log_ProjectDoesNotSpecifyTargetFrameworks, _request.Project.Name, _request.Project.FilePath);
@@ -1847,6 +1875,21 @@ private static async Task<IList<CompatibilityCheckResult>> VerifyCompatibilityAs
18471875
return (success, []);
18481876
}
18491877

1878+
bool shouldDoDuplicatesCheck = _request.Project.RestoreMetadata.UsingMicrosoftNETSdk &&
1879+
!SdkAnalysisLevelMinimums.IsEnabled(
1880+
_request.Project.RestoreMetadata.SdkAnalysisLevel,
1881+
_request.Project.RestoreMetadata.UsingMicrosoftNETSdk,
1882+
SdkAnalysisLevelMinimums.V10_0_300);
1883+
1884+
if (shouldDoDuplicatesCheck)
1885+
{
1886+
if (await ErrorForDuplicateFrameworks(_request, _logger))
1887+
{
1888+
success = false;
1889+
return (success, []);
1890+
}
1891+
}
1892+
18501893
var projectRestoreRequest = new ProjectRestoreRequest(_request, _request.Project, _request.ExistingLockFile, _logger)
18511894
{
18521895
ParentId = _operationId
@@ -1939,6 +1982,30 @@ private static async Task<IList<CompatibilityCheckResult>> VerifyCompatibilityAs
19391982
return (success, graphs);
19401983
}
19411984

1985+
// Check for duplicate frameworks and log an error if found.
1986+
// Returns true if duplicates exist, false otherwise.
1987+
private static async ValueTask<bool> ErrorForDuplicateFrameworks(RestoreRequest request, ILogger logger)
1988+
{
1989+
if (request.Project.TargetFrameworks.Count <= 1)
1990+
{
1991+
return false;
1992+
}
1993+
1994+
var seenFrameworks = new HashSet<NuGetFramework>(request.Project.TargetFrameworks.Count);
1995+
for (int i = 0; i < request.Project.TargetFrameworks.Count; i++)
1996+
{
1997+
if (!seenFrameworks.Add(request.Project.TargetFrameworks[i].FrameworkName))
1998+
{
1999+
// Duplicate found - log error and return immediately
2000+
var message = string.Format(CultureInfo.CurrentCulture, Strings.Log_AliasingSupportedInNewDependencyResolver, request.Project.Name, SdkAnalysisLevelMinimums.V10_0_300);
2001+
await logger.LogAsync(RestoreLogMessage.CreateError(NuGetLogCode.NU1018, message));
2002+
return true;
2003+
}
2004+
}
2005+
2006+
return false;
2007+
}
2008+
19422009
internal static List<ExternalProjectReference> GetProjectReferences(RestoreRequest request)
19432010
{
19442011
// External references

0 commit comments

Comments
 (0)