Skip to content

Read PackageDefinitions and PackageDependencies from the cache assets file instead of ResolvePackageDependencies #28057

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

Merged
merged 3 commits into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@ -21,9 +21,9 @@ public class GivenAResolvePackageDependenciesTask

[Theory]
[MemberData(nameof(ItemCounts))]
public void ItRaisesLockFileToMSBuildItems(string projectName, int[] counts, bool emitLegacyAssetsFileItems)
public void ItRaisesLockFileToMSBuildItems(string projectName, int[] counts)
{
var task = GetExecutedTaskFromPrefix(projectName, out _, emitLegacyAssetsFileItems);
var task = GetExecutedTaskFromPrefix(projectName, out _);

task.PackageDefinitions .Count().Should().Be(counts[0]);
task.FileDefinitions .Count().Should().Be(counts[1]);
Expand All @@ -40,36 +40,16 @@ public static IEnumerable<object[]> ItemCounts
{
new object[] {
"dotnet.new",
new int[] { 110, 2536, 1, 846, 73 },
true
},
new object[] {
"dotnet.new",
new int[] { 110, 0, 0, 846, 0 },
false
},
new object[] {
"simple.dependencies",
new int[] { 113, 2613, 1, 878, 94 },
true
new int[] { 110, 2536, 1, 845, 75 },
},
new object[] {
"simple.dependencies",
new int[] { 113, 0, 0, 878, 0 },
false
new int[] { 113, 2613, 1, 877, 96 },
},
};
}
}

[Fact]
public void ItOmitsLegacyItemsByDefault()
{
var task = new ResolvePackageDependencies();

task.EmitLegacyAssetsFileItems.Should().Be(false);
}

[Theory]
[InlineData("dotnet.new")]
[InlineData("simple.dependencies")]
Expand Down Expand Up @@ -584,8 +564,7 @@ public void ItAddsAnalyzerMetadataAndFileDependencies()
{
ProjectAssetsFile = lockFile.Path,
ProjectPath = null,
ProjectLanguage = projectLanguage, // set language
EmitLegacyAssetsFileItems = true
ProjectLanguage = projectLanguage // set language
};
task.Execute().Should().BeTrue();

Expand Down Expand Up @@ -669,8 +648,7 @@ public void ItFiltersAnalyzersByProjectLanguage()
{
ProjectAssetsFile = lockFile.Path,
ProjectPath = null,
ProjectLanguage = projectLanguage, // set language
EmitLegacyAssetsFileItems = true
ProjectLanguage = projectLanguage // set language
};
task.Execute().Should().BeTrue();

Expand Down Expand Up @@ -839,19 +817,19 @@ public void ItDoesNotThrowOnCrossTargetingWithTargetPlatforms()
GetExecutedTaskFromContents(lockFileContent, out _); // Task should not fail on matching framework names
}

private static ResolvePackageDependencies GetExecutedTaskFromPrefix(string lockFilePrefix, out LockFile lockFile, bool emitLegacyAssetsFileItems = true, string target = null)
private static ResolvePackageDependencies GetExecutedTaskFromPrefix(string lockFilePrefix, out LockFile lockFile, string target = null)
{
lockFile = TestLockFiles.GetLockFile(lockFilePrefix);
return GetExecutedTask(lockFile, emitLegacyAssetsFileItems, target);
return GetExecutedTask(lockFile, target);
}

private static ResolvePackageDependencies GetExecutedTaskFromContents(string lockFileContents, out LockFile lockFile, bool emitLegacyAssetsFileItems = true, string target = null)
private static ResolvePackageDependencies GetExecutedTaskFromContents(string lockFileContents, out LockFile lockFile, string target = null)
{
lockFile = TestLockFiles.CreateLockFile(lockFileContents);
return GetExecutedTask(lockFile, emitLegacyAssetsFileItems, target);
return GetExecutedTask(lockFile, target);
}

private static ResolvePackageDependencies GetExecutedTask(LockFile lockFile, bool emitLegacyAssetsFileItems, string target)
private static ResolvePackageDependencies GetExecutedTask(LockFile lockFile, string target)
{
var resolver = new MockPackageResolver(_packageRoot);

Expand All @@ -860,7 +838,6 @@ private static ResolvePackageDependencies GetExecutedTask(LockFile lockFile, boo
ProjectAssetsFile = lockFile.Path,
ProjectPath = _projectPath,
ProjectLanguage = null,
EmitLegacyAssetsFileItems = emitLegacyAssetsFileItems,
TargetFramework = target
};

Expand Down
187 changes: 181 additions & 6 deletions src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NuGet.Common;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.ProjectModel;
using NuGet.Versioning;

Expand All @@ -27,6 +29,8 @@ namespace Microsoft.NET.Build.Tasks
/// </summary>
public sealed class ResolvePackageAssets : TaskBase
{
#region Input Items

/// <summary>
/// Path to assets.json.
/// </summary>
Expand Down Expand Up @@ -160,6 +164,10 @@ public sealed class ResolvePackageAssets : TaskBase
/// </summary>
public bool DesignTimeBuild { get; set; }

#endregion

#region Output Items

/// <summary>
/// Full paths to assemblies from packages to pass to compiler as analyzers.
/// </summary>
Expand Down Expand Up @@ -230,9 +238,22 @@ public sealed class ResolvePackageAssets : TaskBase
[Output]
public ITaskItem[] ApphostsForShimRuntimeIdentifiers { get; private set; }

/// <summary>
/// All the libraries/packages in the lock file.
/// </summary>
[Output]
public ITaskItem[] PackageDefinitions { get; private set; }

[Output]
public ITaskItem[] PackageDependencies { get; private set; }

/// <summary>
/// All the dependencies between packages. Each package has metadata 'ParentPackage'
/// to refer to the package that depends on it. For top level packages this value is blank.
/// </summary>
[Output]
public ITaskItem[] PackageDependenciesBetweenPackages { get; private set; }

/// <summary>
/// List of symbol files (.pdb) related to NuGet packages.
/// </summary>
Expand All @@ -251,6 +272,8 @@ public sealed class ResolvePackageAssets : TaskBase
[Output]
public ITaskItem[] ReferenceDocumentationFiles { get; private set; }

#endregion

/// <summary>
/// Messages from the assets file.
/// These are logged directly and therefore not returned to the targets (note private here).
Expand Down Expand Up @@ -335,7 +358,9 @@ private void ReadItemGroups()
FrameworkAssemblies = reader.ReadItemGroup();
FrameworkReferences = reader.ReadItemGroup();
NativeLibraries = reader.ReadItemGroup();
PackageDefinitions = reader.ReadItemGroup();
PackageDependencies = reader.ReadItemGroup();
PackageDependenciesBetweenPackages = reader.ReadItemGroup();
PackageFolders = reader.ReadItemGroup();
ReferenceDocumentationFiles = reader.ReadItemGroup();
ResourceAssemblies = reader.ReadItemGroup();
Expand Down Expand Up @@ -654,6 +679,8 @@ internal sealed class CacheWriter : IDisposable
{
private const int InitialStringTableCapacity = 32;

private HashSet<string> _projectFileDependencies;
private Dictionary<string, string> _targetNameToAliasMap;
private ResolvePackageAssets _task;
private BinaryWriter _writer;
private LockFile _lockFile;
Expand Down Expand Up @@ -684,7 +711,8 @@ public CacheWriter(ResolvePackageAssets task)
_task = task;
_lockFile = new LockFileCache(task).GetLockFile(task.ProjectAssetsFile);
_packageResolver = NuGetPackageResolver.CreateResolver(_lockFile);

_targetNameToAliasMap = CreateTargetNameToAlisMap();
ReadProjectFileDependencies(string.IsNullOrEmpty(_task.TargetFramework) || !_targetNameToAliasMap.ContainsKey(_task.TargetFramework) ? null : _targetNameToAliasMap[_task.TargetFramework]);

// If we are doing a design-time build, we do not want to fail the build if we can't find the
// target framework and/or runtime identifier in the assets file. This is because the design-time
Expand Down Expand Up @@ -726,8 +754,26 @@ public CacheWriter(ResolvePackageAssets task)
{
ComputePackageExclusions();
}

void ReadProjectFileDependencies(string frameworkAlias)
{
_projectFileDependencies = _lockFile.GetProjectFileDependencySet(frameworkAlias);
}
}

private Dictionary<string, string> CreateTargetNameToAlisMap() => _lockFile.Targets.ToDictionary(t => t.Name, t =>
{
var alias = _lockFile.GetLockFileTargetAlias(t);
if (string.IsNullOrEmpty(t.RuntimeIdentifier))
{
return alias;
}
else
{
return alias + "/" + t.RuntimeIdentifier;
}
});

public void WriteToCacheFile()
{
Directory.CreateDirectory(Path.GetDirectoryName(_task.ProjectAssetsCacheFile));
Expand Down Expand Up @@ -806,7 +852,9 @@ private void WriteItemGroups()
WriteItemGroup(WriteFrameworkAssemblies);
WriteItemGroup(WriteFrameworkReferences);
WriteItemGroup(WriteNativeLibraries);
WriteItemGroup(WritePackageDefinitions);
WriteItemGroup(WritePackageDependencies);
WriteItemGroup(WritePackageDependenciesBetweenPackages);
WriteItemGroup(WritePackageFolders);
WriteItemGroup(WriteReferenceDocumentationFiles);
WriteItemGroup(WriteResourceAssemblies);
Expand Down Expand Up @@ -1157,17 +1205,13 @@ private void WriteDebugItems(

foreach (string fileExtension in relatedExtensions.Split(RelatedPropertySeparator))
{
if (fileExtension.ToLower() == extension)
if (fileExtension.ToLowerInvariant() == extension)
{
string xmlFilePath = Path.ChangeExtension(itemSpec, fileExtension);
if (File.Exists(xmlFilePath))
{
WriteItem(xmlFilePath, library);
}
else
{
_task.Log.LogWarning(Strings.AssetsFileNotFound, xmlFilePath);
}
}
}
}
Expand Down Expand Up @@ -1394,6 +1438,72 @@ private void WritePackageFolders()
}
}

private void WritePackageDefinitions()
{
// Get library and file definitions
foreach (var package in _lockFile.Libraries)
{
var packageName = package.Name;
var packageVersion = package.Version.ToNormalizedString();
string packageId = $"{packageName}/{packageVersion}";

WriteItem(packageId);
WriteMetadata(MetadataKeys.Name, packageName);
WriteMetadata(MetadataKeys.Type, package.Type);
WriteMetadata(MetadataKeys.Version, packageVersion);
WriteMetadata(MetadataKeys.Path, package.Path ?? string.Empty);

string resolvedPackagePath = ResolvePackagePath(package);
WriteMetadata(MetadataKeys.ResolvedPath, resolvedPackagePath ?? string.Empty);

WriteMetadata(MetadataKeys.DiagnosticLevel, GetPackageDiagnosticLevel(package));
}

string ResolvePackagePath(LockFileLibrary package)
{
if (package.IsProject())
{
var relativeMSBuildProjectPath = package.MSBuildProject;

if (string.IsNullOrEmpty(relativeMSBuildProjectPath))
{
throw new BuildErrorException(Strings.ProjectAssetsConsumedWithoutMSBuildProjectPath, package.Name, _task.ProjectAssetsFile);
}

return GetAbsolutePathFromProjectRelativePath(relativeMSBuildProjectPath);
}
else
{
return _packageResolver.GetPackageDirectory(package.Name, package.Version);
}
}

string GetAbsolutePathFromProjectRelativePath(string path)
{
return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(_task.ProjectPath), path));
}

string GetPackageDiagnosticLevel(LockFileLibrary package)
{
string target = _task.TargetFramework ?? "";

var messages = _lockFile.LogMessages.Where(log => log.LibraryId == package.Name && log.TargetGraphs
.Select(tg =>
{
var parsedTargetGraph = NuGetFramework.Parse(tg);
var alias = _lockFile.PackageSpec.TargetFrameworks.FirstOrDefault(tf => tf.FrameworkName == parsedTargetGraph)?.TargetAlias;
return alias ?? tg;
}).Contains(target));

if (!messages.Any())
{
return string.Empty;
}

return messages.Max(log => log.Level).ToString();
}
}

private void WritePackageDependencies()
{
foreach (var library in _runtimeTarget.Libraries)
Expand All @@ -1405,6 +1515,71 @@ private void WritePackageDependencies()
}
}

private void WritePackageDependenciesBetweenPackages()
{
foreach(var target in _lockFile.Targets)
{
GetPackageDependencies(target);
}

void GetPackageDependencies(LockFileTarget target)
{
var resolvedPackageVersions = target.Libraries
.ToDictionary(pkg => pkg.Name, pkg => pkg.Version.ToNormalizedString(), StringComparer.OrdinalIgnoreCase);

string frameworkAlias = _targetNameToAliasMap[target.Name];

var transitiveProjectRefs = new HashSet<string>(
target.Libraries
.Where(lib => lib.IsTransitiveProjectReference(_lockFile, ref _projectFileDependencies, frameworkAlias))
.Select(pkg => pkg.Name),
StringComparer.OrdinalIgnoreCase);

foreach (var package in target.Libraries)
{
string packageId = $"{package.Name}/{package.Version.ToNormalizedString()}";

if (_projectFileDependencies.Contains(package.Name))
{
WriteItem(packageId);
WriteMetadata(MetadataKeys.ParentTarget, frameworkAlias); // Foreign Key
WriteMetadata(MetadataKeys.ParentPackage, string.Empty); // Foreign Key
}

// get sub package dependencies
GetDependencies(package, target.Name, resolvedPackageVersions, transitiveProjectRefs);
}
}

void GetDependencies(
LockFileTargetLibrary package,
string targetName,
Dictionary<string, string> resolvedPackageVersions,
HashSet<string> transitiveProjectRefs)
{
string packageId = $"{package.Name}/{package.Version.ToNormalizedString()}";
string frameworkAlias = _targetNameToAliasMap[targetName];
foreach (var deps in package.Dependencies)
{
if (!resolvedPackageVersions.TryGetValue(deps.Id, out string version))
{
continue;
}

string depsName = $"{deps.Id}/{version}";

WriteItem(depsName);
WriteMetadata(MetadataKeys.ParentTarget, frameworkAlias); // Foreign Key
WriteMetadata(MetadataKeys.ParentPackage, packageId); // Foreign Key

if (transitiveProjectRefs.Contains(deps.Id))
{
WriteMetadata(MetadataKeys.TransitiveProjectReference, "true");
}
}
}
}

private void WriteResourceAssemblies()
{
WriteItems(
Expand Down
Loading