| File: GetApplicableAssetsFromPackages.cs | Web Access |
| Project: src\arcade\src\Microsoft.DotNet.Build.Tasks.Packaging\src\Microsoft.DotNet.Build.Tasks.Packaging.csproj (Microsoft.DotNet.Build.Tasks.Packaging) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Newtonsoft.Json; using NuGet.Frameworks; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace Microsoft.DotNet.Build.Tasks.Packaging { public class GetApplicableAssetsFromPackages : BuildTask { private Dictionary<string, List<PackageItem>> _packageToPackageItems; private Dictionary<string, PackageItem> _targetPathToPackageItem; private AggregateNuGetAssetResolver _resolver; /// <summary> /// All items that make up packages to resolve from. /// Must have the following metadata /// Identity - path to the file in the binary directory /// TargetPath - path to the file in the package /// PackageId - ID of the package containing the file /// </summary> [Required] public ITaskItem[] PackageAssets { get; set; } /// <summary> /// TargetMoniker to use when resolving assets. EG: netcoreapp1.0, netstandard1.4 /// </summary> [Required] public string[] TargetMonikers { get; set; } /// <summary> /// runtime.json that defines the RID graph /// </summary> [Required] public string RuntimeFile { get; set; } /// <summary> /// If specified will be used when resolving runtime assets, otherwise TargetMoniker will be used. /// </summary> public string[] RuntimeTargetMonikers { get; set; } /// <summary> /// If specified will be used when resolving runtime assets, otherwise no RID will be used. /// </summary> public string TargetRuntime { get; set; } [Output] public ITaskItem[] CompileAssets { get; set; } [Output] public ITaskItem[] RuntimeAssets { get; set; } [Output] public ITaskItem[] BuildProjects { get; set; } /// <summary> /// Generates a table in markdown that lists the API version supported by /// various packages at all levels of NETStandard. /// </summary> /// <returns></returns> public override bool Execute() { if (PackageAssets == null || PackageAssets.Length == 0) { Log.LogError("PackageAssets argument must be specified"); return false; } if (TargetMonikers == null || TargetMonikers.Length == 0) { Log.LogError("TargetMoniker argument must be specified"); return false; } NuGetFramework[] compileFxs = TargetMonikers.Select(fx => NuGetFramework.Parse(fx)).ToArray(); NuGetFramework[] runtimeFxs = compileFxs; if (RuntimeTargetMonikers != null && RuntimeTargetMonikers.Length > 0) { runtimeFxs = RuntimeTargetMonikers.Select(fx => NuGetFramework.Parse(fx)).ToArray(); } LoadFiles(); var buildProjects = new List<BuildProject>(); CompileAssets = null; // find the best framework foreach (var compileFx in compileFxs) { var compileAssets = _resolver.ResolveCompileAssets(compileFx); if (compileAssets.Any()) { var compileItems = compileAssets.Where(ca => !NuGetAssetResolver.IsPlaceholder(ca)) .Select(ca => _targetPathToPackageItem[ca]); buildProjects.AddRange(compileItems.Select(ci => BuildProjectFromPackageItem(ci)).Where(bp => bp != null)); CompileAssets = compileItems.Select(ci => PackageItemAsResolvedAsset(ci)).ToArray(); Log.LogMessage($"Resolved compile assets from {compileFx.ToString()}: {String.Join(";", CompileAssets.Select(c => c.ItemSpec))}"); break; } } if (CompileAssets == null) { Log.LogError($"Could not locate compile assets for any of the frameworks {String.Join(";", compileFxs.Select(fx => fx.ToString()))}"); } RuntimeAssets = null; foreach (var runtimeFx in runtimeFxs) { var runtimeAssets = _resolver.ResolveRuntimeAssets(runtimeFx, TargetRuntime); if (runtimeAssets.Any()) { var runtimeItems = runtimeAssets.Where(ra => !NuGetAssetResolver.IsPlaceholder(ra)) .Select(ra => _targetPathToPackageItem[ra]); buildProjects.AddRange(runtimeItems.Select(ri => BuildProjectFromPackageItem(ri)).Where(bp => bp != null)); RuntimeAssets = runtimeItems.SelectMany(ri => PackageItemAndSymbolsAsResolvedAsset(ri)).ToArray(); Log.LogMessage($"Resolved runtime assets from {runtimeFx.ToString()}: {String.Join(";", RuntimeAssets.Select(r => r.ItemSpec))}"); break; } } if (!string.IsNullOrEmpty(TargetRuntime) && RuntimeAssets == null) { Log.LogError($"Could not locate runtime assets for any of the frameworks {String.Join(";", runtimeFxs.Select(fx => fx.ToString()))}"); } BuildProjects = buildProjects.Distinct().Select(bp => bp.ToItem()).ToArray(); return !Log.HasLoggedErrors; } private void LoadFiles() { _packageToPackageItems = new Dictionary<string, List<PackageItem>>(); foreach (var file in PackageAssets) { try { var packageItem = new PackageItem(file); if (String.IsNullOrWhiteSpace(packageItem.TargetPath)) { Log.LogError($"{packageItem.TargetPath} is missing TargetPath metadata"); } if (!_packageToPackageItems.ContainsKey(packageItem.Package)) { _packageToPackageItems[packageItem.Package] = new List<PackageItem>(); } _packageToPackageItems[packageItem.Package].Add(packageItem); } catch (Exception ex) { Log.LogError($"Could not parse File {file.ItemSpec}. {ex}"); // skip it. } } // build a map to translate back to source file from resolved asset // we use package-specific paths since we're resolving a set of packages. _targetPathToPackageItem = new Dictionary<string, PackageItem>(); foreach (var packageFiles in _packageToPackageItems) { foreach (PackageItem packageFile in packageFiles.Value) { string packageSpecificTargetPath = AggregateNuGetAssetResolver.AsPackageSpecificTargetPath(packageFiles.Key, packageFile.TargetPath); if (_targetPathToPackageItem.ContainsKey(packageSpecificTargetPath)) { Log.LogError($"Files {_targetPathToPackageItem[packageSpecificTargetPath].SourcePath} and {packageFile.SourcePath} have the same TargetPath {packageSpecificTargetPath}."); } _targetPathToPackageItem[packageSpecificTargetPath] = packageFile; } } _resolver = new AggregateNuGetAssetResolver(RuntimeFile); foreach (string packageId in _packageToPackageItems.Keys) { _resolver.AddPackageItems(packageId, _packageToPackageItems[packageId].Select(f => f.TargetPath)); } } private static ITaskItem PackageItemAsResolvedAsset(PackageItem packageItem) { return SetPackageMetadata(new TaskItem(packageItem.OriginalItem), packageItem); } private static IEnumerable<ITaskItem> PackageItemAndSymbolsAsResolvedAsset(PackageItem packageItem) { yield return PackageItemAsResolvedAsset(packageItem); string pdbPath = Path.ChangeExtension(packageItem.SourcePath, ".pdb"); if (File.Exists(pdbPath)) { var pdbItem = new TaskItem(Path.ChangeExtension(packageItem.OriginalItem.ItemSpec, ".pdb")); packageItem.OriginalItem.CopyMetadataTo(pdbItem); SetPackageMetadata(pdbItem, packageItem); if (!String.IsNullOrEmpty(packageItem.TargetPath)) { pdbItem.SetMetadata("TargetPath", Path.ChangeExtension(packageItem.TargetPath, ".pdb")); } yield return pdbItem; } } private static ITaskItem SetPackageMetadata(ITaskItem item, PackageItem packageItem) { item.SetMetadata("Private", "false"); item.SetMetadata("FromPkgProj", "true"); item.SetMetadata("NuGetPackageId", packageItem.Package); item.SetMetadata("NuGetPackageVersion", packageItem.PackageVersion); return item; } public static BuildProject BuildProjectFromPackageItem(PackageItem packageItem) { if (packageItem.SourceProject == null) { return null; } return new BuildProject() { Project = packageItem.SourceProject, AdditionalProperties = packageItem.AdditionalProperties, UndefineProperties = packageItem.UndefineProperties }; } } }