static List <RemotePackage> FindDependencies(IDictionary <string, List <PackageDependency> > discoveredDependencies, Package[] packages, params List <RemotePackage>[] listsOfPackages) { var found = new List <RemotePackage>(); var packageIds = packages.Select(p => p.Id); bool throwIfMissing = (discoveredDependencies == null); // if this is null, then we're not recursing // TODO this should be pulled out into its own method that JUST returns a list of PackageDependency for us to find // // get ALL of the dependencies for these packages, grouped by package Id // eg. { "log4net" => ["log4net > 2.0", "log4net < 2.5"] } var allDependencies = new Dictionary <string, List <PackageDependency> >(); foreach (var package in packages) { foreach (var packageDependency in package.Dependencies) { if (packageIds.Contains(packageDependency.PackageId)) { continue; } if (!allDependencies.ContainsKey(packageDependency.PackageId)) { allDependencies[packageDependency.PackageId] = new List <PackageDependency>(); } if (!allDependencies[packageDependency.PackageId].Contains(packageDependency)) { allDependencies[packageDependency.PackageId].Add(packageDependency); } } } // add these packages' dependencies into discoveredDependencies. // we track these to know whether or not we're missing any dependencies for any of the packages found. if (discoveredDependencies == null) { discoveredDependencies = new Dictionary <string, List <PackageDependency> >(); } foreach (var packageDependency in allDependencies) { var dependencyId = packageDependency.Key; var dependencies = packageDependency.Value.ToArray(); if (!discoveredDependencies.ContainsKey(dependencyId)) { discoveredDependencies[dependencyId] = new List <PackageDependency>(); } foreach (var dependency in dependencies) { if (!discoveredDependencies[dependencyId].Contains(dependency)) { discoveredDependencies[dependencyId].Add(dependency); } } } foreach (var packageDependency in allDependencies) { var dependencyId = packageDependency.Key; var dependencies = packageDependency.Value.ToArray(); // go through all sources and get the *latest* version of this dependency (that matches) RemotePackage dependencyPackage = null; foreach (var sourcePackages in listsOfPackages) { var match = sourcePackages.Where(pkg => pkg.Id == dependencyId && PackageDependency.MatchesAll(pkg.Version, dependencies)).OrderBy(pkg => pkg.Version).Reverse().FirstOrDefault(); if (match != null) { if (dependencyPackage == null || dependencyPackage.Version < match.Version) { dependencyPackage = match; } } } if (dependencyPackage != null) { found.Add(dependencyPackage); if (dependencyPackage.Dependencies.Any()) { found.AddRange(Package.FindDependencies(discoveredDependencies, new Package[] { dependencyPackage }, listsOfPackages)); // <--- recurse! } } else { Console.WriteLine("Could not find dependency: {0}", dependencyId); } } // throw a MissingDependencyException if any of the discovered dependencies were not found if (throwIfMissing) { var foundIds = found.Select(pkg => pkg.Id); var missing = new List <PackageDependency>(); foreach (var dependencyPackage in discoveredDependencies) { if (!foundIds.Contains(dependencyPackage.Key)) { missing.AddRange(dependencyPackage.Value); } } if (missing.Count > 0) { throw new MissingDependencyException(missing); } } // TODO instead of just doing a Distinct(), we need to actually inspect the dependencies ... // do not include any of the packages that were passed in as dependencies return(found.Where(pkg => !packageIds.Contains(pkg.Id)).Distinct().ToList()); }
static RemotePackage PackageFromFeedEntry(XmlElement entry) { var package = new RemotePackage(); foreach (XmlNode node in entry.ChildNodes) { switch (node.Name.ToLower()) { case "id": break; case "pkg:packageid": package.Id = node.InnerText; break; case "pkg:version": package.VersionText = node.InnerText; break; case "pkg:language": package.Language = node.InnerText; break; case "title": package.Title = node.InnerText; break; case "content": package.Description = node.InnerText; break; case "author": package.Authors.Add(node.InnerText); break; case "owner": package.Owners.Add(node.InnerText); break; case "published": package.Created = DateTime.Parse(node.InnerText); break; case "updated": package.Modified = DateTime.Parse(node.InnerText); break; case "category": var term = node.Attributes["term"].Value; if (!package.Tags.Contains(term)) { package.Tags.Add(term); } break; case "pkg:requirelicenseacceptance": package.RequireLicenseAcceptance = bool.Parse(node.InnerText); break; case "pkg:keywords": // if there is 1 <string>, split it on spaces // else if there are many, each element is a tag var tagNodes = node.ChildNodes; if (tagNodes.Count == 1) { foreach (var tag in tagNodes[0].InnerText.Split(' ')) { if (!package.Tags.Contains(tag.Trim())) { package.Tags.Add(tag.Trim()); } } } else { foreach (XmlNode tagString in tagNodes) { if (!package.Tags.Contains(tagString.InnerText.Trim())) { package.Tags.Add(tagString.InnerText.Trim()); } } } break; case "link": switch (node.Attributes["rel"].Value) { case "enclosure": package.DownloadUrl = node.Attributes["href"].Value; break; case "license": package.LicenseUrl = node.Attributes["href"].Value; break; case "project": package.ProjectUrl = node.Attributes["href"].Value; break; case "icon": package.IconUrl = node.Attributes["href"].Value; break; default: Console.WriteLine("Unsupported <link> rel: {0}", node.Attributes["rel"].Value); break; } break; case "pkg:dependencies": foreach (XmlNode dependencyNode in node.ChildNodes) { var dependency = new PackageDependency(); foreach (XmlNode depNode in dependencyNode.ChildNodes) { switch (depNode.Name.ToLower()) { case "pkg:id": dependency.Id = depNode.InnerText; break; case "pkg:version": dependency.VersionText = depNode.InnerText; break; case "pkg:minversion": dependency.MinVersionText = depNode.InnerText; break; case "pkg:maxversion": dependency.MaxVersionText = depNode.InnerText; break; default: Console.WriteLine("Unknown dependency node: {0}", depNode.Name); break; } } package.Dependencies.Add(dependency); } break; default: Console.WriteLine("Unsupported <entry> element: {0} \"{1}\"", node.Name, node.InnerText); break; } } return(package); }