private static void AddDependenciesToQueue(IEnumerable <LibraryDependency> dependencies, Queue <DependencyNode> queue, NodeWarningProperties nodeWarningProperties) { // Add all the project's dependencies to the Queue with the merged WarningPropertiesCollection foreach (var dependency in dependencies) { var queueNode = new DependencyNode( dependency.Name, IsProject(dependency.LibraryRange.TypeConstraint), nodeWarningProperties); // Add the metadata from the parent project here. queue.Enqueue(queueNode); } }
/// <summary> /// Extracts the no warn codes for a libraryId from the warning properties at the node in the graph. /// </summary> /// <param name="nodeWarningProperties">warning properties at the node in the graph.</param> /// <param name="libraryId">libraryId for which the no warn codes have to be extracted.</param> /// <returns>HashSet of NuGetLogCodes containing the no warn codes for the libraryId.</returns> public static HashSet <NuGetLogCode> ExtractPathNoWarnProperties( NodeWarningProperties nodeWarningProperties, string libraryId) { var result = new HashSet <NuGetLogCode>(); if (nodeWarningProperties?.ProjectWide?.Count > 0) { result.UnionWith(nodeWarningProperties.ProjectWide); } if (nodeWarningProperties?.PackageSpecific?.Count > 0 && nodeWarningProperties.PackageSpecific.TryGetValue(libraryId, out var codes) && codes?.Count > 0) { result.UnionWith(codes); } return(result); }
/// <summary> /// Traverses a Dependency graph starting from the parent project in BF style. /// </summary> /// <param name="targetGraph">Parent project restore target graph.</param> /// <param name="parentProjectName">File path of the parent project.</param> /// <param name="parentProjectWideNoWarn">Project Wide NoWarn properties of the parent project.</param> /// <param name="parentPackageSpecificNoWarn">Package Specific NoWarn properties of the parent project.</param> /// <returns>PackageSpecificWarningProperties containing all the NoWarn's for each package seen in the graph accumulated while traversing the graph.</returns> private static PackageSpecificWarningProperties ExtractTransitiveNoWarnProperties( RestoreTargetGraph targetGraph, string parentProjectName, HashSet <NuGetLogCode> parentProjectWideNoWarn, Dictionary <string, HashSet <NuGetLogCode> > parentPackageSpecificNoWarn, Dictionary <string, Dictionary <NuGetFramework, WarningPropertiesCollection> > warningPropertiesCache) { var dependencyMapping = new Dictionary <string, LookUpNode>(StringComparer.OrdinalIgnoreCase); var queue = new Queue <DependencyNode>(); var seen = new Dictionary <string, NodeWarningProperties>(StringComparer.OrdinalIgnoreCase); var resultWarningProperties = new PackageSpecificWarningProperties(); var packageNoWarn = new Dictionary <string, HashSet <NuGetLogCode> >(StringComparer.OrdinalIgnoreCase); // All the packages in parent project's closure. // Once we have collected data for all of these, we can exit. var parentPackageDependencies = new HashSet <string>(StringComparer.OrdinalIgnoreCase); var parentTargetFramework = targetGraph.Framework; // Add all dependencies into a dict for a quick transitive lookup foreach (var dependencyGraphItem in targetGraph.Flattened) { WarningPropertiesCollection nodeWarningProperties = null; HashSet <NuGetLogCode> nodeProjectWideNoWarn = null; Dictionary <string, HashSet <NuGetLogCode> > nodePackageSpecificNoWarn = null; if (IsProject(dependencyGraphItem.Key.Type)) { var localMatch = (LocalMatch)dependencyGraphItem.Data.Match; var nodeProjectSpec = GetNodePackageSpec(localMatch); var nearestFramework = nodeProjectSpec.GetTargetFramework(parentTargetFramework).FrameworkName; if (nearestFramework != null) { // Get the WarningPropertiesCollection from the PackageSpec nodeWarningProperties = GetNodeWarningProperties(nodeProjectSpec, nearestFramework, warningPropertiesCache); nodeProjectWideNoWarn = nodeWarningProperties.ProjectWideWarningProperties.NoWarn.AsHashSet(); var nodePackageSpecificWarningProperties = ExtractPackageSpecificNoWarnForFramework( nodeWarningProperties.PackageSpecificWarningProperties, nearestFramework); if (nodePackageSpecificWarningProperties != null) { nodePackageSpecificNoWarn = nodePackageSpecificWarningProperties; } } } else { parentPackageDependencies.Add(dependencyGraphItem.Key.Name); } var lookUpNode = new LookUpNode() { Dependencies = dependencyGraphItem.Data.Dependencies, NodeWarningProperties = NodeWarningProperties.Create(nodeProjectWideNoWarn, nodePackageSpecificNoWarn) }; dependencyMapping[dependencyGraphItem.Key.Name] = lookUpNode; } // Get the direct dependencies for the parent project to seed the queue var parentDependencies = dependencyMapping[parentProjectName]; // Seed the queue with the parent project's direct dependencies var parentNoWarn = NodeWarningProperties.Create(parentProjectWideNoWarn, parentPackageSpecificNoWarn); AddDependenciesToQueue(parentDependencies.Dependencies, queue, parentNoWarn); // Add the parent project to the seen set to prevent adding it back to the queue AddToSeen(seen, new DependencyNode(id: parentProjectName, isProject: true, parentNoWarn)); // start taking one node from the queue and get all of it's dependencies while (queue.Count > 0) { var node = queue.Dequeue(); // Check if the node has already been visited, or if the node is a NoWarn superset of // an existing node. If this is a superset it will not provide any new paths where a // warning should be shown. if (AddToSeen(seen, node) && dependencyMapping.TryGetValue(node.Id, out var nodeLookUp)) { var nodeId = node.Id; var nodeIsProject = node.IsProject; var nodeDependencies = nodeLookUp.Dependencies; var pathWarningProperties = node.NodeWarningProperties; // If the node is a project then we need to extract the warning properties and // add those to the warning properties of the current path. if (nodeIsProject) { // Merge the node's no warn properties with the one in the path. NodeWarningProperties nodeWarningProperties = nodeLookUp.NodeWarningProperties; AddDependenciesToQueue(nodeDependencies, queue, nodeWarningProperties.Merge(node.NodeWarningProperties)); } else if (parentPackageDependencies.Contains(nodeId)) { // Evaluate the current path for package properties var packageNoWarnFromPath = pathWarningProperties.ExtractPathNoWarnProperties(nodeId); if (packageNoWarn.TryGetValue(nodeId, out var noWarnCodes)) { // We have seen atleast one path which contained a NoWarn for the package // We need to update the noWarnCodes.IntersectWith(packageNoWarnFromPath); } else { noWarnCodes = packageNoWarnFromPath; packageNoWarn.Add(nodeId, noWarnCodes); } // Check if there was any NoWarn in the path if (noWarnCodes.Count == 0) { // If the path does not "NoWarn" for this package then remove the path from parentPackageDependencies // This is done because if there are no "NoWarn" in one path, the the warnings must come through // We no longer care about this package in the graph parentPackageDependencies.Remove(nodeId); // If parentPackageDependencies is empty then exit the graph traversal if (parentPackageDependencies.Count == 0) { break; } } AddDependenciesToQueue(nodeDependencies, queue, pathWarningProperties); } } } // At the end of the graph traversal add the remaining package no warn lists into the result foreach (var packageId in packageNoWarn.Keys) { resultWarningProperties.AddRangeOfCodes(packageNoWarn[packageId], packageId, parentTargetFramework); } return(resultWarningProperties); }
public DependencyNode(string id, bool isProject, NodeWarningProperties nodeWarningProperties) { Id = id ?? throw new ArgumentNullException(nameof(id)); NodeWarningProperties = nodeWarningProperties ?? throw new ArgumentNullException(nameof(nodeWarningProperties)); IsProject = isProject; }
public DependencyNode(string id, bool isProject, HashSet <NuGetLogCode> projectWideNoWarn, Dictionary <string, HashSet <NuGetLogCode> > packageSpecificNoWarn) { Id = id ?? throw new ArgumentNullException(nameof(id)); NodeWarningProperties = NodeWarningProperties.Create(projectWideNoWarn, packageSpecificNoWarn); IsProject = isProject; }
/// <summary> /// Extracts the no warn codes for a libraryId from the warning properties at the node in the graph. /// </summary> /// <param name="nodeWarningProperties">warning properties at the node in the graph.</param> /// <param name="libraryId">libraryId for which the no warn codes have to be extracted.</param> /// <returns>HashSet of NuGetLogCodes containing the no warn codes for the libraryId.</returns> public static HashSet <NuGetLogCode> ExtractPathNoWarnProperties( NodeWarningProperties nodeWarningProperties, string libraryId) { return(nodeWarningProperties.ExtractPathNoWarnProperties(libraryId)); }