|
// 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 Microsoft.Build.Utilities;
using Microsoft.Extensions.DependencyModel;
using Newtonsoft.Json;
using NuGet.Packaging.Core;
using NuGet.ProjectModel;
using NuGet.RuntimeModel;
namespace Microsoft.NET.Build.Tasks
{
/// <summary>
/// Generates the $(project).deps.json file.
/// </summary>
public class GenerateDepsFile : TaskBase
{
[Required]
public string ProjectPath { get; set; }
public string AssetsFilePath { get; set; }
[Required]
public string DepsFilePath { get; set; }
[Required]
public string TargetFramework { get; set; }
public string RuntimeIdentifier { get; set; }
/// <summary>
/// Strips the RID if it's any, because that's not reasonable
/// </summary>
private string EffectiveRuntimeIdentifier => string.IsNullOrEmpty(RuntimeIdentifier) ? null : RuntimeIdentifier == "any" ? null : RuntimeIdentifier;
public string PlatformLibraryName { get; set; }
public ITaskItem[] RuntimeFrameworks { get; set; }
[Required]
public string AssemblyName { get; set; }
[Required]
public string AssemblyExtension { get; set; }
[Required]
public string AssemblyVersion { get; set; }
public ITaskItem[] AssemblySatelliteAssemblies { get; set; } = Array.Empty<ITaskItem>();
[Required]
public bool IncludeMainProject { get; set; }
public bool TrimDepsJsonLibrariesWithoutAssets { get; set; }
// @(ReferencePath) that will be passed to
public ITaskItem[] ReferencePaths { get; set; } = Array.Empty<ITaskItem>();
// Full set of @(ReferenceDependencyPaths) found by RAR
public ITaskItem[] ReferenceDependencyPaths { get; set; } = Array.Empty<ITaskItem>();
// Full set of @(ReferenceSatellitePaths) found by RAR
public ITaskItem[] ReferenceSatellitePaths { get; set; } = Array.Empty<ITaskItem>();
// Subset of @(ReferencePath) that is not CopyLocal, used for compilation, but not runtime assets
public ITaskItem[] ReferenceAssemblies { get; set; } = Array.Empty<ITaskItem>();
// Runtime assets for self-contained deployment from runtime pack
public ITaskItem[] RuntimePackAssets { get; set; } = Array.Empty<ITaskItem>();
public ITaskItem CompilerOptions { get; set; }
public ITaskItem[] RuntimeStorePackages { get; set; }
// NuGet compilation assets
[Required]
public ITaskItem[] CompileReferences { get; set; }
// NuGet runtime assets for root directory: @(NativeCopyLocalItems), @(ResourceCopyLocalItems), @(RuntimeCopyLocalItems)
[Required]
public ITaskItem[] ResolvedNuGetFiles { get; set; }
// NuGet runtime assets for runtimes* directory
[Required]
public ITaskItem[] ResolvedRuntimeTargetsFiles { get; set; }
// CopyLocal subset ot of @(ReferencePath), @(ReferenceDependencyPath)
// Used to filter out non-runtime assemblies from deps file. Only project and direct references in this
// set will be written to deps file as runtime dependencies.
public string[] UserRuntimeAssemblies { get; set; }
public bool IsSelfContained { get; set; }
public bool IsSingleFile { get; set; }
public bool IncludeRuntimeFileVersions { get; set; }
public bool IncludeProjectsNotInAssetsFile { get; set; }
// List of runtime identifier (platform part only) to validate for runtime assets
// If set, the task will warn on any RIDs that aren't in the list
public string[] ValidRuntimeIdentifierPlatformsForAssets { get; set; }
[Required]
public string RuntimeGraphPath { get; set; }
List<ITaskItem> _filesWritten = new();
[Output]
public ITaskItem[] FilesWritten
{
get { return _filesWritten.ToArray(); }
}
private Dictionary<PackageIdentity, string> GetFilteredPackages()
{
Dictionary<PackageIdentity, string> filteredPackages = null;
if (RuntimeStorePackages != null && RuntimeStorePackages.Length > 0)
{
filteredPackages = new Dictionary<PackageIdentity, string>();
foreach (var package in RuntimeStorePackages)
{
filteredPackages.Add(
ItemUtilities.GetPackageIdentity(package),
package.GetMetadata(MetadataKeys.RuntimeStoreManifestNames));
}
}
return filteredPackages;
}
private void WriteDepsFile(string depsFilePath)
{
ProjectContext projectContext = null;
LockFileLookup lockFileLookup = null;
if (AssetsFilePath != null)
{
LockFile lockFile = new LockFileCache(this).GetLockFile(AssetsFilePath);
projectContext = lockFile.CreateProjectContext(
TargetFramework,
EffectiveRuntimeIdentifier,
PlatformLibraryName,
RuntimeFrameworks,
IsSelfContained);
lockFileLookup = new LockFileLookup(lockFile);
}
CompilationOptions compilationOptions = CompilationOptionsConverter.ConvertFrom(CompilerOptions);
SingleProjectInfo mainProject = SingleProjectInfo.Create(
ProjectPath,
AssemblyName,
AssemblyExtension,
AssemblyVersion,
AssemblySatelliteAssemblies);
var userRuntimeAssemblySet = new HashSet<string>(UserRuntimeAssemblies ?? Enumerable.Empty<string>(), StringComparer.OrdinalIgnoreCase);
Func<ITaskItem, bool> isUserRuntimeAssembly = item => userRuntimeAssemblySet.Contains(item.ItemSpec);
IEnumerable<ReferenceInfo> referenceAssemblyInfos =
ReferenceInfo.CreateReferenceInfos(ReferenceAssemblies);
// If there is a generated asset file, the projectContext will contain most of the project references.
// So remove any project reference contained within projectContext from directReferences to avoid duplication
IEnumerable<ReferenceInfo> directReferences =
ReferenceInfo.CreateDirectReferenceInfos(
ReferencePaths,
ReferenceSatellitePaths,
lockFileLookup,
isUserRuntimeAssembly,
IncludeProjectsNotInAssetsFile);
IEnumerable<ReferenceInfo> dependencyReferences =
ReferenceInfo.CreateDependencyReferenceInfos(ReferenceDependencyPaths, ReferenceSatellitePaths, isUserRuntimeAssembly);
Dictionary<string, SingleProjectInfo> referenceProjects =
SingleProjectInfo.CreateProjectReferenceInfos(ReferencePaths, ReferenceSatellitePaths,
isUserRuntimeAssembly);
bool ShouldIncludeRuntimeAsset(ITaskItem item)
{
if (IsSelfContained)
{
if (!IsSingleFile || !item.GetMetadata(MetadataKeys.DropFromSingleFile).Equals("true"))
{
return true;
}
}
else if (item.HasMetadataValue(MetadataKeys.RuntimePackAlwaysCopyLocal, "true"))
{
return true;
}
return false;
}
IEnumerable<RuntimePackAssetInfo> runtimePackAssets =
RuntimePackAssets.Where(ShouldIncludeRuntimeAsset).Select(RuntimePackAssetInfo.FromItem);
DependencyContextBuilder builder;
if (projectContext != null)
{
// Generate the RID-fallback for self-contained builds.
//
// In order to support loading components with RID-specific assets,
// the AssemblyDependencyResolver requires a RID fallback graph.
// The component itself should not carry the RID fallback graph with it, because
// it would need to carry graph of all the RIDs and needs updates for newer RIDs.
// For framework dependent apps, the RID fallback graph comes from the core framework Microsoft.NETCore.App,
// so there is no need to write it into the app.
// If self-contained apps, the (applicable subset of) RID fallback graph needs to be written to the deps.json manifest.
//
// If a RID-graph is provided to the DependencyContextBuilder, it generates a RID-fallback
// graph with respect to the target RuntimeIdentifier.
RuntimeGraph runtimeGraph =
IsSelfContained ? new RuntimeGraphCache(this).GetRuntimeGraph(RuntimeGraphPath) : null;
builder = new DependencyContextBuilder(mainProject, IncludeRuntimeFileVersions, runtimeGraph, projectContext, lockFileLookup);
}
else
{
builder = new DependencyContextBuilder(
mainProject,
IncludeRuntimeFileVersions,
RuntimeFrameworks,
isSelfContained: IsSelfContained,
platformLibraryName: PlatformLibraryName,
runtimeIdentifier: EffectiveRuntimeIdentifier,
targetFramework: TargetFramework);
}
builder = builder
.WithMainProjectInDepsFile(IncludeMainProject)
.WithTrimLibrariesWithoutAssets(TrimDepsJsonLibrariesWithoutAssets)
.WithReferenceAssemblies(referenceAssemblyInfos)
.WithDirectReferences(directReferences)
.WithDependencyReferences(dependencyReferences)
.WithReferenceProjectInfos(referenceProjects)
.WithRuntimePackAssets(runtimePackAssets)
.WithCompilationOptions(compilationOptions)
.WithReferenceAssembliesPath(FrameworkReferenceResolver.GetDefaultReferenceAssembliesPath())
.WithPackagesThatWereFiltered(GetFilteredPackages());
if (CompileReferences.Length > 0)
{
builder = builder.WithCompileReferences(ReferenceInfo.CreateReferenceInfos(CompileReferences));
}
var resolvedNuGetFiles = ResolvedNuGetFiles.Select(f => new ResolvedFile(f, false))
.Concat(ResolvedRuntimeTargetsFiles.Select(f => new ResolvedFile(f, true)));
builder = builder.WithResolvedNuGetFiles(resolvedNuGetFiles);
DependencyContext dependencyContext = builder.Build(UserRuntimeAssemblies);
var writer = new DependencyContextWriter();
using (var fileStream = File.Create(depsFilePath))
{
writer.Write(dependencyContext, fileStream);
}
_filesWritten.Add(new TaskItem(depsFilePath));
if (ValidRuntimeIdentifierPlatformsForAssets != null)
{
var affectedLibs = new List<string>();
var affectedRids = new List<string>();
foreach (var lib in dependencyContext.RuntimeLibraries)
{
var warnOnRids = lib.RuntimeAssemblyGroups.Select(g => g.Runtime).Where(ShouldWarnOnRuntimeIdentifer)
.Concat(lib.NativeLibraryGroups.Select(g => g.Runtime).Where(ShouldWarnOnRuntimeIdentifer));
if (warnOnRids.Any())
{
affectedLibs.Add(lib.Name);
affectedRids.AddRange(warnOnRids);
}
}
if (affectedRids.Count > 0)
{
affectedLibs.Sort();
affectedRids.Sort();
Log.LogWarning(Strings.NonPortableRuntimeIdentifierDetected, string.Join(", ", affectedRids.Distinct()), string.Join(", ", affectedLibs.Distinct()));
}
}
}
private bool ShouldWarnOnRuntimeIdentifer(string runtimeIdentifier)
{
if (string.IsNullOrEmpty(runtimeIdentifier))
return false;
int separator = runtimeIdentifier.LastIndexOf('-');
string platform = separator < 0
? runtimeIdentifier
: runtimeIdentifier.Substring(0, separator);
return Array.IndexOf(ValidRuntimeIdentifierPlatformsForAssets, platform.ToLowerInvariant()) == -1;
}
protected override void ExecuteCore()
{
WriteDepsFile(DepsFilePath);
}
}
}
|