/// <summary> /// detects issues and prints them to the log. use filterPackages if its wanted to filter the issues based on newly installed packages. /// </summary> private static Issue CheckPackages(IEnumerable <PackageDef> packages, IEnumerable <PackageDef> newPackages, LogEventType severity) { var tree = DependencyAnalyzer.BuildAnalyzerContext(packages.ToList()); if (newPackages != null) { tree = tree.FilterRelated(newPackages.ToList()); } if (tree.BrokenPackages.Count == 0) { return(Issue.None); } foreach (var pkg in tree.BrokenPackages) { foreach (var issue in tree.GetIssues(pkg)) { switch (issue.IssueType) { case DependencyIssueType.Missing: log.TraceEvent(severity, 0, $"Package '{pkg.Name}' depends on '{issue.PackageName}' which will not be installed."); break; case DependencyIssueType.DependencyMissing: log.TraceEvent(severity, 0, $"Package '{pkg.Name}' depends on '{issue.PackageName} which itself will be broken (See message related to other plugin)."); break; case DependencyIssueType.IncompatibleVersion: log.TraceEvent(severity, 0, $"Package '{pkg.Name}' depends on '{issue.PackageName}' version '{issue.ExpectedVersion}', but '{issue.LoadedVersion}' will be installed."); break; } } } return(Issue.BrokenPackages); }
/// <summary> /// detects issues and prints them to the log. use filterPackages if its wanted to filter the issues based on newly installed packages. /// </summary> public static Issue CheckInstalledPackages(string installDir) { var packages = new Installation(installDir).GetPackages(); var tree = DependencyAnalyzer.BuildAnalyzerContext(packages.ToList()); if (tree.BrokenPackages.Count == 0) { return(Issue.None); } log.Error("Package Dependency Warning"); foreach (var pkg in tree.BrokenPackages) { log.Warning("The Package '{0}' has the following dependency issues:", pkg.Name); foreach (var issue in tree.GetIssues(pkg)) { switch (issue.IssueType) { case DependencyIssueType.Missing: log.Info(" * The package depends on '{0}' (v{1}) which is not installed.", issue.PackageName, issue.ExpectedVersion); break; case DependencyIssueType.DependencyMissing: log.Info(" * The package depends on '{0} which has issues.", issue.PackageName); log.Info(" See message related to other packages."); break; case DependencyIssueType.IncompatibleVersion: log.Info(" * The package depends on '{0}' version '{1}', but '{2}' was installed.", issue.PackageName, issue.ExpectedVersion, issue.LoadedVersion); break; } } } return(Issue.BrokenPackages); }
/// <summary> /// Builds a DependencyTree based on the dependencies between the packages. /// </summary> /// <param name="packages"></param> /// <returns></returns> public static DependencyAnalyzer BuildAnalyzerContext(List <PackageDef> packages) { Dictionary <string, PackageDef> packagesLookup = packages.GroupBy(p => p.Name).Select(p => p.First()).ToDictionary(pkg => pkg.Name); Dictionary <PackageDef, List <PackageDef> > dependers = packages.ToDictionary(pkg => pkg, pkg => new List <PackageDef>()); HashSet <PackageDef> broken_packages = new HashSet <PackageDef>(); foreach (var package in packages) { foreach (var dep in package.Dependencies) { if (false == packagesLookup.ContainsKey(dep.Name)) { // missing: create placeholder. null means missing. packagesLookup[dep.Name] = new PackageDef { Dependencies = new List <PackageDependency>(), Name = dep.Name, Version = null, Files = new List <PackageFile>() }; dependers[packagesLookup[dep.Name]] = new List <PackageDef>(); } var loadedpackage = packagesLookup[dep.Name]; if (false == dep.Version.IsCompatible(loadedpackage.Version) || null == loadedpackage.Version) { broken_packages.Add(package); } dependers[packagesLookup[dep.Name]].Add(package); } } // find nth order broken packages // if a dependency is broken, the depender is also broken. Stack <PackageDef> newBroken = new Stack <PackageDef>(broken_packages); while (newBroken.Count > 0) { var item = newBroken.Pop(); var deps = dependers[item]; foreach (var dep in deps) { if (!broken_packages.Contains(dep)) { broken_packages.Add(dep); newBroken.Push(dep); } } } DependencyAnalyzer deptree = new DependencyAnalyzer(); deptree.BrokenPackages = broken_packages.ToList().AsReadOnly(); deptree.packagesLookup = packagesLookup; deptree.dependers = dependers; return(deptree); }
private bool CheckPackageAndDependencies(List <PackageDef> installed, List <string> packagePaths) { var packages = packagePaths.Select(PackageDef.FromXml).ToList(); installed.RemoveIf(i => packages.Any(u => u.Name == i.Name && u.Version == i.Version)); var analyzer = DependencyAnalyzer.BuildAnalyzerContext(installed); var packagesWithIssues = new List <PackageDef>(); foreach (var inst in installed) { if (analyzer.GetIssues(inst).Any(i => packages.Any(p => p.Name == i.PackageName))) { packagesWithIssues.Add(inst); } } if (packages.Any(p => p.Files.Any(f => f.FileName.ToLower().EndsWith("OpenTap.dll")))) { log.Error("OpenTAP cannot be uninstalled."); return(false); } if (packagesWithIssues.Any()) { log.Warning("Plugin Dependecy Conflict."); var question = string.Format("One or more installed packages depend on {0}. Uninstalling might cause these packages to break:\n{1}", string.Join(" and ", packages.Select(p => p.Name)), string.Join("\n", packagesWithIssues.Select(p => p.Name + " " + p.Version))); var req = new ContinueRequest { message = question, Response = ContinueResponse.Continue }; UserInput.Request(req, true); return(req.Response == ContinueResponse.Continue); } else { foreach (var bundle in packages.Where(p => p.IsBundle()).ToList()) { log.Warning("Package '{0}' is a bundle and has installed:\n{1}\n\nThese packages must be uninstalled separately.\n", bundle.Name, string.Join("\n", bundle.Dependencies.Select(d => d.Name))); } return(true); } }
private void GetPackageInfo(PackageDef package, List <PackageVersion> packageVersions, Installation installation) { var allPackages = installation.GetPackages(); var latestVersion = packageVersions?.Max(p => p.Version); var packageInstalled = allPackages.Contains(package); if (!packageInstalled) { allPackages.Add(package); } var tree = DependencyAnalyzer.BuildAnalyzerContext(allPackages); var issues = tree.GetIssues(package); ParseDescription(package.Description); // Get similar releases to get available platforms and architectures var similarReleases = packageVersions.Where(x => x.Version.Major == package.Version.Major && x.Version.Minor == package.Version.Minor).ToList(); AddWritePair("Package Name", package.Name); AddWritePair("Group", package.Group); var installedVersion = installation.GetPackages().Where(x => x.Name == package.Name)?.FirstOrDefault()?.Version; var installedString = installedVersion == null ? "(not installed)" : $"({installedVersion} installed)"; AddWritePair("Version", $"{package.Version} {installedString}"); if (latestVersion != null && installedVersion != null && latestVersion != installedVersion) { AddWritePair("Newest Version in Repository", $"{latestVersion}"); } AddWritePair("Compatible Architectures", string.Join(Environment.NewLine, similarReleases.Select(x => x.Architecture).Distinct())); AddWritePair("Compatible Platforms", string.Join(Environment.NewLine, similarReleases.Select(x => x.OS).Distinct())); if (packageInstalled == false) { AddWritePair("Compatibility Issues", issues.Any() ? $"{(string.Join(Environment.NewLine, issues.Select(x => $"{x.PackageName} - {x.IssueType.ToString()}")))}" : "none"); } AddWritePair("Owner", package.Owner); var licenseString = (package.LicenseRequired ?? "").Replace("&", " and ").Replace("|", " or "); AddWritePair("License Required", licenseString); AddWritePair("SourceUrl", package.SourceUrl); if (package.PackageSource is IRepositoryPackageDefSource repoPkg) { AddWritePair("Repository", repoPkg.RepositoryUrl); } AddWritePair("Package Type", package.FileType); AddWritePair("Package Class", package.Class); var tags = package.Tags?.Split(new string[] { " ", "," }, StringSplitOptions.RemoveEmptyEntries); if (tags?.Length > 0) { AddWritePair("Package Tags", string.Join(" ", tags)); } if (package.Dependencies.Count > 0) { AddWritePair("Dependencies", string.Join(Environment.NewLine, package.Dependencies.Select(x => x.Name))); } foreach (var(key, value) in SubTags) { AddWritePair(key, value); } AddWritePair("Description", Description); if (IncludeFiles) { if (package.Files.Count > 0) { AddWritePair("Files", string.Join(Environment.NewLine, package.Files.Select(x => x.RelativeDestinationPath.Replace("\\", "/")))); } } if (IncludePlugins) { var plugins = package.Files.Select(x => x.Plugins); var sb = new StringBuilder(); foreach (var plugin in plugins) { foreach (var p in plugin) { var name = p.Name; var desc = p.Description; sb.Append(!string.IsNullOrWhiteSpace(desc) ? $"{name} ({desc}){Environment.NewLine}" : $"{name}{Environment.NewLine}"); } } AddWritePair("Plugins", sb.ToString()); } WritePairs(); }