|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using Microsoft.Build.Framework;
using NuGet.Packaging.Core;
using NuGet.ProjectModel;
namespace Microsoft.NET.Build.Tasks
{
internal static class LockFileExtensions
{
public static LockFileTarget GetTargetAndReturnNullIfNotFound(this LockFile lockFile, string frameworkAlias, string runtimeIdentifier)
{
LockFileTarget lockFileTarget = lockFile.GetTarget(frameworkAlias, runtimeIdentifier);
if (lockFileTarget == null &&
lockFile.PackageSpec.TargetFrameworks.All(tfi => string.IsNullOrEmpty(tfi.TargetAlias)))
{
var nuGetFramework = NuGetUtils.ParseFrameworkName(frameworkAlias);
lockFileTarget = lockFile.GetTarget(nuGetFramework, runtimeIdentifier);
}
return lockFileTarget;
}
public static LockFileTarget GetTargetAndThrowIfNotFound(this LockFile lockFile, string frameworkAlias, string runtimeIdentifier)
{
LockFileTarget lockFileTarget = lockFile.GetTargetAndReturnNullIfNotFound(frameworkAlias, runtimeIdentifier);
if (lockFileTarget == null)
{
string frameworkString = frameworkAlias;
string targetMoniker = string.IsNullOrEmpty(runtimeIdentifier) ?
frameworkString :
$"{frameworkString}/{runtimeIdentifier}";
string message;
if (string.IsNullOrEmpty(runtimeIdentifier))
{
message = string.Format(Strings.AssetsFileMissingTarget, lockFile.Path, targetMoniker, frameworkString);
}
else
{
message = string.Format(Strings.AssetsFileMissingRuntimeIdentifier, lockFile.Path, targetMoniker, frameworkString, runtimeIdentifier);
}
throw new BuildErrorException(message);
}
return lockFileTarget;
}
public static string GetLockFileTargetAlias(this LockFile lockFile, LockFileTarget lockFileTarget)
{
var frameworkAlias = lockFile.PackageSpec.TargetFrameworks.FirstOrDefault(tfi => tfi.FrameworkName == lockFileTarget.TargetFramework)?.TargetAlias;
if (frameworkAlias == null)
{
throw new ArgumentException("Could not find TargetFramework alias in lock file for " + lockFileTarget.TargetFramework);
}
return frameworkAlias;
}
public static ProjectContext CreateProjectContext(
this LockFile lockFile,
string frameworkAlias,
string runtime,
// Trimmed from publish output, and if there are no runtimeFrameworks, written to runtimeconfig.json
string platformLibraryName,
// Written to runtimeconfig.json
Microsoft.Build.Framework.ITaskItem[] runtimeFrameworks,
bool isSelfContained)
{
if (lockFile == null)
{
throw new ArgumentNullException(nameof(lockFile));
}
if (frameworkAlias == null)
{
throw new ArgumentNullException(nameof(frameworkAlias));
}
var lockFileTarget = lockFile.GetTargetAndThrowIfNotFound(frameworkAlias, runtime);
LockFileTargetLibrary platformLibrary = lockFileTarget.GetLibrary(platformLibraryName);
bool isFrameworkDependent = IsFrameworkDependent(runtimeFrameworks, isSelfContained, lockFileTarget.RuntimeIdentifier, platformLibrary != null);
return new ProjectContext(lockFile, lockFileTarget, platformLibrary,
runtimeFrameworks?.Select(i => new ProjectContext.RuntimeFramework(i))?.ToArray(),
isFrameworkDependent);
}
public static bool IsFrameworkDependent(ITaskItem[] runtimeFrameworks, bool isSelfContained, string runtimeIdentifier, bool hasPlatformLibrary)
{
return (hasPlatformLibrary || runtimeFrameworks?.Any() == true) &&
(!isSelfContained || (string.IsNullOrEmpty(runtimeIdentifier) || runtimeIdentifier == "any"));
}
public static LockFileTargetLibrary GetLibrary(this LockFileTarget lockFileTarget, string libraryName)
{
if (string.IsNullOrEmpty(libraryName))
{
return null;
}
return lockFileTarget
.Libraries
.FirstOrDefault(e => e.Name.Equals(libraryName, StringComparison.OrdinalIgnoreCase));
}
public static HashSet<string> GetProjectFileDependencySet(this LockFile lockFile, string frameworkAlias)
{
// Get package name from e.g. Microsoft.VSSDK.BuildTools >= 15.0.25604-Preview4
static string GetPackageNameFromDependency(string dependency)
{
int indexOfWhiteSpace = IndexOfWhiteSpace(dependency);
if (indexOfWhiteSpace < 0)
{
return dependency;
}
return dependency.Substring(0, indexOfWhiteSpace);
}
static int IndexOfWhiteSpace(string s)
{
for (int i = 0; i < s.Length; i++)
{
if (char.IsWhiteSpace(s[i]))
{
return i;
}
}
return -1;
}
var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var group in lockFile.ProjectFileDependencyGroups)
{
var groupFrameworkAlias = GetFrameworkAliasForDependencyGroup(group);
if (string.IsNullOrEmpty(groupFrameworkAlias) || string.IsNullOrEmpty(frameworkAlias) || groupFrameworkAlias.Equals(frameworkAlias) ||
NuGetUtils.ParseFrameworkName(groupFrameworkAlias.Split('/').First()).DotNetFrameworkName.Equals(NuGetUtils.ParseFrameworkName(frameworkAlias).DotNetFrameworkName))
{
foreach (string dependency in group.Dependencies)
{
string packageName = GetPackageNameFromDependency(dependency);
set.Add(packageName);
}
}
}
return set;
}
private static string GetFrameworkAliasForDependencyGroup(ProjectFileDependencyGroup group)
{
return group.FrameworkName;
}
public static HashSet<string> GetPlatformExclusionList(
this LockFileTarget lockFileTarget,
LockFileTargetLibrary platformLibrary,
IDictionary<string, LockFileTargetLibrary> libraryLookup)
{
var exclusionList = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
exclusionList.Add(platformLibrary.Name);
CollectDependencies(libraryLookup, platformLibrary.Dependencies, exclusionList);
return exclusionList;
}
public static HashSet<PackageIdentity> GetTransitivePackagesList(
this LockFileTarget lockFileTarget,
LockFileTargetLibrary package,
IDictionary<string, LockFileTargetLibrary> libraryLookup)
{
var exclusionList = new HashSet<PackageIdentity>();
exclusionList.Add(new PackageIdentity(package.Name, package.Version));
CollectDependencies(libraryLookup, package.Dependencies, exclusionList);
return exclusionList;
}
private static void CollectDependencies(
IDictionary<string, LockFileTargetLibrary> libraryLookup,
IEnumerable<PackageDependency> dependencies,
HashSet<string> exclusionList)
{
var excludedPackages = new HashSet<PackageIdentity>();
CollectDependencies(libraryLookup, dependencies, excludedPackages);
foreach (var pkg in excludedPackages)
{
exclusionList.Add(pkg.Id);
}
}
private static void CollectDependencies(
IDictionary<string, LockFileTargetLibrary> libraryLookup,
IEnumerable<PackageDependency> dependencies,
HashSet<PackageIdentity> exclusionList)
{
foreach (PackageDependency dependency in dependencies)
{
LockFileTargetLibrary library = libraryLookup[dependency.Id];
if (library.Version.Equals(dependency.VersionRange.MinVersion))
{
if (exclusionList.Add(new PackageIdentity(library.Name, library.Version)))
{
CollectDependencies(libraryLookup, library.Dependencies, exclusionList);
}
}
}
}
public static IEnumerable<LockFileTargetLibrary> Filter(
this IEnumerable<LockFileTargetLibrary> libraries,
HashSet<string> exclusionList)
{
return libraries.Where(e => !exclusionList.Contains(e.Name));
}
public static IEnumerable<IGrouping<string, LockFileRuntimeTarget>> GetRuntimeTargetsGroups(
this LockFileTargetLibrary library,
string assetType)
{
return library.RuntimeTargets
.FilterPlaceholderFiles()
.Cast<LockFileRuntimeTarget>()
.Where(t => string.Equals(t.AssetType, assetType, StringComparison.OrdinalIgnoreCase))
.GroupBy(t => t.Runtime);
}
// A package is a TransitiveProjectReference if it is a project, is not directly referenced,
// and does not contain a placeholder compile time assembly
public static bool IsTransitiveProjectReference(this LockFileTargetLibrary library, LockFile lockFile, ref HashSet<string> directProjectDependencies, string frameworkAlias)
{
if (!library.IsProject())
{
return false;
}
if (directProjectDependencies == null)
{
directProjectDependencies = lockFile.GetProjectFileDependencySet(frameworkAlias);
}
return !directProjectDependencies.Contains(library.Name)
&& !library.CompileTimeAssemblies.Any(f => f.IsPlaceholderFile());
}
public static IEnumerable<LockFileItem> FilterPlaceholderFiles(this IEnumerable<LockFileItem> files)
=> files.Where(f => !f.IsPlaceholderFile());
public static bool IsPlaceholderFile(this LockFileItem item)
=> NuGetUtils.IsPlaceholderFile(item.Path);
public static bool IsPackage(this LockFileTargetLibrary library)
=> library.Type == "package";
public static bool IsPackage(this LockFileLibrary library)
=> library.Type == "package";
public static bool IsProject(this LockFileTargetLibrary library)
=> library.Type == "project";
public static bool IsProject(this LockFileLibrary library)
=> library.Type == "project";
}
}
|