// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Xml; using System.Xml.Linq; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using NuGet.Frameworks; using NuGet.Packaging; using NuGet.Packaging.Core; namespace RepoTasks { // This is temporary until we can use FrameworkReference to build our own packages public class RemoveSharedFrameworkDependencies : Task { [Required] public ITaskItem[] Files { get; set; } [Required] public ITaskItem[] FrameworkOnlyPackages { get; set; } [Required] public string SharedFrameworkTargetFramework { get; set; } public override bool Execute() { Log.LogMessage("NuGet version = " + typeof(PackageArchiveReader).Assembly.GetName().Version); var dependencyToRemove = new HashSet(FrameworkOnlyPackages.Select(p => p.ItemSpec), StringComparer.OrdinalIgnoreCase); foreach (var file in Files) { FilterDependencies(file.ItemSpec, dependencyToRemove); } return !Log.HasLoggedErrors; } private void FilterDependencies(string targetPath, ISet dependencyToRemove) { var fileName = Path.GetFileName(targetPath); Log.LogMessage($"Updating {fileName}"); using (var fileStream = File.Open(targetPath, FileMode.Open)) using (var package = new ZipArchive(fileStream, ZipArchiveMode.Update)) using (var packageReader = new PackageArchiveReader(fileStream, leaveStreamOpen: true)) { var referencesFrameworkOnlyAssembly = false; var nuspecFile = packageReader.GetNuspecFile(); using (var stream = package.OpenFile(nuspecFile)) { var reader = Manifest.ReadFrom(stream, validateSchema: true); stream.Position = 0; var packageBuilder = new PackageBuilder(stream, basePath: null); var updatedGroups = new List(); foreach (var group in packageBuilder.DependencyGroups) { var packages = new List(); var updatedGroup = new PackageDependencyGroup(group.TargetFramework, packages); foreach (var dependency in group.Packages) { if (dependencyToRemove.Contains(dependency.Id)) { referencesFrameworkOnlyAssembly = true; Log.LogMessage($" Remove dependency on '{dependency.Id}'"); continue; } packages.Add(dependency); } updatedGroups.Add(updatedGroup); } if (referencesFrameworkOnlyAssembly) { packageBuilder.DependencyGroups.Clear(); packageBuilder.DependencyGroups.AddRange(updatedGroups); var updatedManifest = Manifest.Create(packageBuilder); var inMemory = new MemoryStream(); updatedManifest.Save(inMemory); inMemory.Position = 0; // Hack the raw nuspec to add the dependency var rawNuspec = XDocument.Load(inMemory, LoadOptions.PreserveWhitespace); var ns = rawNuspec.Root.GetDefaultNamespace(); var metadata = rawNuspec.Root.Descendants(ns + "metadata").Single(); metadata.Add( new XElement(ns + "frameworkReferences", new XElement(ns + "group", new XAttribute("targetFramework", NuGetFramework.Parse(SharedFrameworkTargetFramework).GetFrameworkString()), new XElement(ns + "frameworkReference", new XAttribute("name", "Microsoft.AspNetCore.App"))))); stream.Position = 0; stream.SetLength(0); rawNuspec.Save(stream); Log.LogMessage(MessageImportance.High, "Added to {0}", fileName); } else { Log.LogMessage($"No changes made to {fileName}"); } } } } } }