public ModChange(CkanModule mod, GUIModChangeType changeType, SelectionReason reason) { Mod = mod; ChangeType = changeType; // If we don't have a Reason, the user probably wanted to install it Reason = reason ?? new SelectionReason.UserRequested(); }
/// <summary> /// Adds the specified module to the list of modules we're installing. /// This also adds its provides list to what we have available. /// </summary> private void Add(CkanModule module, SelectionReason reason) { if (module.IsMetapackage) { return; } if (module.IsDLC) { throw new ModuleIsDLCKraken(module); } log.DebugFormat("Adding {0} {1}", module.identifier, module.version); if (modlist.TryGetValue(module.identifier, out CkanModule possibleDup)) { if (possibleDup.identifier == module.identifier) { // We should never add the same module twice! log.ErrorFormat("Assertion failed: Adding {0} twice in relationship resolution", module.identifier); throw new ArgumentException("Already contains module: " + module.identifier); } else { // Duplicates via "provides" are OK though, we'll just replace it modlist.Remove(module.identifier); } } modlist.Add(module.identifier, module); if (!reasons.ContainsKey(module)) { reasons.Add(module, reason); } // Override Installed for upgrades else if (reasons[module] is SelectionReason.Installed) { reasons[module] = reason; } log.DebugFormat("Added {0}", module.identifier); // Stop here if it doesn't have any provide aliases. if (module.provides == null) { return; } // Handle provides/aliases if it does. // It's okay if there's already a key for one of our aliases // in the resolution list. In which case, we don't do anything. var aliases = module.provides.Where(alias => !modlist.ContainsKey(alias)); foreach (string alias in aliases) { log.DebugFormat("Adding {0} providing {1}", module.identifier, alias); modlist.Add(alias, module); } }
public ModChange(GUIMod mod, GUIModChangeType changeType, SelectionReason reason) { Mod = mod; ChangeType = changeType; Reason = reason; if (Reason == null) { // Hey, we don't have a Reason // Most likely the user wanted to install it Reason = new SelectionReason.UserRequested(); } }
/// <summary> /// Adds the specified module to the list of modules we're installing. /// This also adds its provides list to what we have available. /// </summary> private void Add(CkanModule module, SelectionReason reason) { if (module.IsMetapackage) { return; } log.DebugFormat("Adding {0} {1}", module.identifier, module.version); if (modlist.ContainsKey(module.identifier)) { // We should never be adding something twice! log.ErrorFormat("Assertion failed: Adding {0} twice in relationship resolution", module.identifier); throw new ArgumentException("Already contains module:" + module.identifier); } modlist.Add(module.identifier, module); if (!reasons.ContainsKey(module)) { reasons.Add(module, reason); } // Override Installed for upgrades else if (reasons[module] is SelectionReason.Installed) { reasons[module] = reason; } log.DebugFormat("Added {0}", module.identifier); // Stop here if it doesn't have any provide aliases. if (module.provides == null) { return; } // Handle provides/aliases if it does. // It's okay if there's already a key for one of our aliases // in the resolution list. In which case, we don't do anything. var aliases = module.provides.Where(alias => !modlist.ContainsKey(alias)); foreach (string alias in aliases) { log.DebugFormat("Adding {0} providing {1}", module.identifier, alias); modlist.Add(alias, module); } }
/// <summary> /// Resolve a relationship stanza (a list of relationships). /// This will add modules to be installed, if required. /// May recurse back to Resolve for those new modules. /// /// If `soft_resolve` is true, we warn rather than throw exceptions on mods we cannot find. /// If `soft_resolve` is false (default), we throw a ModuleNotFoundKraken if we can't find a dependency. /// /// Throws a TooManyModsProvideKraken if we have too many choices and /// options.without_toomanyprovides_kraken is not set. /// /// See RelationshipResolverOptions for further adjustments that can be made. /// /// </summary> private void ResolveStanza(IEnumerable<RelationshipDescriptor> stanza, SelectionReason reason, RelationshipResolverOptions options, bool soft_resolve = false) { if (stanza == null) { return; } foreach (var descriptor in stanza) { string dep_name = descriptor.name; log.DebugFormat("Considering {0}", dep_name); // If we already have this dependency covered, skip. // If it's already installed, skip. if (modlist.ContainsKey(dep_name)) { if (descriptor.version_within_bounds(modlist[dep_name].version)) continue; //TODO Ideally we could check here if it can be replaced by the version we want. throw new InconsistentKraken( string.Format( "{0} requires a version {1}. However a incompatible version, {2}, is in the resolver", dep_name, descriptor.RequiredVersion, modlist[dep_name].version)); } if (registry.IsInstalled(dep_name)) { if(descriptor.version_within_bounds(registry.InstalledVersion(dep_name))) continue; //TODO Ideally we could check here if it can be replaced by the version we want. throw new InconsistentKraken( string.Format( "{0} requires a version {1}. However a incompatible version, {2}, is already installed", dep_name, descriptor.RequiredVersion, registry.InstalledVersion(dep_name))); } List<CkanModule> candidates = registry.LatestAvailableWithProvides(dep_name, kspversion, descriptor) .Where(mod=>MightBeInstallable(mod)).ToList(); if (candidates.Count == 0) { if (!soft_resolve) { log.ErrorFormat("Dependency on {0} found, but nothing provides it.", dep_name); throw new ModuleNotFoundKraken(dep_name); } log.InfoFormat("{0} is recommended/suggested, but nothing provides it.", dep_name); continue; } if (candidates.Count > 1) { // Oh no, too many to pick from! // TODO: It would be great if instead we picked the one with the // most recommendations. if (options.without_toomanyprovides_kraken) { continue; } throw new TooManyModsProvideKraken(dep_name, candidates); } CkanModule candidate = candidates[0]; // Finally, check our candidate against everything which might object // to it being installed; that's all the mods which are fixed in our // list thus far, as well as everything on the system. var fixed_mods = new HashSet<Module>(modlist.Values); fixed_mods.UnionWith(installed_modules); var conflicting_mod = fixed_mods.FirstOrDefault(mod => mod.ConflictsWith(candidate)); if (conflicting_mod == null) { // Okay, looks like we want this one. Adding. Add(candidate, reason); Resolve(candidate, options); } else if (soft_resolve) { log.InfoFormat("{0} would cause conflicts, excluding it from consideration", candidate); } else { if (options.procede_with_inconsistencies) { Add(candidate, reason); conflicts.Add(new KeyValuePair<Module, Module>(conflicting_mod, candidate)); conflicts.Add(new KeyValuePair<Module, Module>(candidate, conflicting_mod)); } else { throw new InconsistentKraken(string.Format("{0} conflicts with {1}, can't install both.", conflicting_mod, candidate)); } } } }
/// <summary> /// Adds the specified module to the list of modules we're installing. /// This also adds its provides list to what we have available. /// </summary> private void Add(CkanModule module, SelectionReason reason) { if (module.IsMetapackage) return; log.DebugFormat("Adding {0} {1}", module.identifier, module.version); if (modlist.ContainsKey(module.identifier)) { // We should never be adding something twice! log.ErrorFormat("Assertion failed: Adding {0} twice in relationship resolution", module.identifier); throw new ArgumentException("Already contains module:" + module.identifier); } modlist.Add(module.identifier, module); if(!reasons.ContainsKey(module)) reasons.Add(module, reason); log.DebugFormat("Added {0}", module.identifier); // Stop here if it doesn't have any provide aliases. if (module.provides == null) { return; } // Handle provides/aliases if it does. // It's okay if there's already a key for one of our aliases // in the resolution list. In which case, we don't do anything. var aliases = module.provides.Where(alias => !modlist.ContainsKey(alias)); foreach (string alias in aliases) { log.DebugFormat("Adding {0} providing {1}", module.identifier, alias); modlist.Add(alias, module); } }
/// <summary> /// Resolve a relationship stanza (a list of relationships). /// This will add modules to be installed, if required. /// May recurse back to Resolve for those new modules. /// /// If `soft_resolve` is true, we warn rather than throw exceptions on mods we cannot find. /// If `soft_resolve` is false (default), we throw a ModuleNotFoundKraken if we can't find a dependency. /// /// Throws a TooManyModsProvideKraken if we have too many choices and /// options.without_toomanyprovides_kraken is not set. /// /// See RelationshipResolverOptions for further adjustments that can be made. /// </summary> private void ResolveStanza(IEnumerable <RelationshipDescriptor> stanza, SelectionReason reason, RelationshipResolverOptions options, bool soft_resolve = false, IEnumerable <RelationshipDescriptor> old_stanza = null) { if (stanza == null) { return; } stanza = stanza.Memoize(); foreach (RelationshipDescriptor descriptor in stanza) { log.DebugFormat("Considering {0}", descriptor.ToString()); // If we already have this dependency covered, skip. if (descriptor.MatchesAny(modlist.Values, null, null)) { continue; } else if (descriptor.ContainsAny(modlist.Keys)) { CkanModule module = modlist.Values .FirstOrDefault(m => descriptor.ContainsAny(new string[] { m.identifier })); if (options.proceed_with_inconsistencies) { conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(module, reason.Parent)); conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(reason.Parent, module)); continue; } else { throw new InconsistentKraken( $"{descriptor} required, but an incompatible version is in the resolver" ); } } // If it's already installed, skip. if (descriptor.MatchesAny( installed_modules, registry.InstalledDlls.ToHashSet(), registry.InstalledDlc)) { continue; } else if (descriptor.ContainsAny(installed_modules.Select(im => im.identifier))) { CkanModule module = installed_modules .FirstOrDefault(m => descriptor.ContainsAny(new string[] { m.identifier })); if (options.proceed_with_inconsistencies) { conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(module, reason.Parent)); conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(reason.Parent, module)); continue; } else { throw new InconsistentKraken( $"{descriptor} required, but an incompatible version is installed" ); } } // Pass mod list in case an older version of a module is conflict-free while later versions have conflicts var descriptor1 = descriptor; List <CkanModule> candidates = descriptor .LatestAvailableWithProvides(registry, GameVersion, modlist.Values) .Where(mod => !modlist.ContainsKey(mod.identifier) && descriptor1.WithinBounds(mod) && MightBeInstallable(mod)) .ToList(); if (candidates.Count == 0) { // Nothing found, try again without mod list // (conflicts will still be caught below) candidates = descriptor .LatestAvailableWithProvides(registry, GameVersion) .Where(mod => !modlist.ContainsKey(mod.identifier) && descriptor1.WithinBounds(mod) && MightBeInstallable(mod)) .ToList(); } if (candidates.Count == 0) { if (!soft_resolve) { log.InfoFormat("Dependency on {0} found but it is not listed in the index, or not available for your version of KSP.", descriptor.ToString()); throw new DependencyNotSatisfiedKraken(reason.Parent, descriptor.ToString()); } log.InfoFormat("{0} is recommended/suggested but it is not listed in the index, or not available for your version of KSP.", descriptor.ToString()); continue; } if (candidates.Count > 1) { // Oh no, too many to pick from! // TODO: It would be great if instead we picked the one with the // most recommendations. if (options.without_toomanyprovides_kraken) { continue; } // If we've got a parent stanza that has a relationship on a mod that provides what // we need, then select that. if (old_stanza != null) { List <CkanModule> provide = candidates .Where(cand => old_stanza.Any(rel => rel.WithinBounds(cand))) .ToList(); if (!provide.Any() || provide.Count() > 1) { //We still have either nothing, or too many to pick from //Just throw the TMP now throw new TooManyModsProvideKraken(descriptor.ToString(), candidates); } candidates[0] = provide.First(); } else { throw new TooManyModsProvideKraken(descriptor.ToString(), candidates); } } CkanModule candidate = candidates[0]; // Finally, check our candidate against everything which might object // to it being installed; that's all the mods which are fixed in our // list thus far, as well as everything on the system. var fixed_mods = new HashSet <CkanModule>(modlist.Values); fixed_mods.UnionWith(installed_modules); CkanModule conflicting_mod = fixed_mods.FirstOrDefault(mod => mod.ConflictsWith(candidate)); if (conflicting_mod == null) { // Okay, looks like we want this one. Adding. Add(candidate, reason); Resolve(candidate, options, stanza); } else if (soft_resolve) { log.InfoFormat("{0} would cause conflicts, excluding it from consideration", candidate); } else { if (options.proceed_with_inconsistencies) { Add(candidate, reason); conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(conflicting_mod, candidate)); conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(candidate, conflicting_mod)); } else { throw new InconsistentKraken( $"{conflicting_mod} conflicts with {candidate}"); } } } }
/// <summary> /// Resolve a relationship stanza (a list of relationships). /// This will add modules to be installed, if required. /// May recurse back to Resolve for those new modules. /// /// If `soft_resolve` is true, we warn rather than throw exceptions on mods we cannot find. /// If `soft_resolve` is false (default), we throw a ModuleNotFoundKraken if we can't find a dependency. /// /// Throws a TooManyModsProvideKraken if we have too many choices and /// options.without_toomanyprovides_kraken is not set. /// /// See RelationshipResolverOptions for further adjustments that can be made. /// /// </summary> private void ResolveStanza(IEnumerable <RelationshipDescriptor> stanza, SelectionReason reason, RelationshipResolverOptions options, bool soft_resolve = false, IEnumerable <RelationshipDescriptor> old_stanza = null) { if (stanza == null) { return; } foreach (var descriptor in stanza) { string dep_name = descriptor.name; log.DebugFormat("Considering {0}", dep_name); // If we already have this dependency covered, skip. // If it's already installed, skip. if (modlist.ContainsKey(dep_name)) { var module = modlist[dep_name]; if (descriptor.version_within_bounds(module.version)) { continue; } //TODO Ideally we could check here if it can be replaced by the version we want. if (options.procede_with_inconsistencies) { conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(module, reason.Parent)); conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(reason.Parent, module)); continue; } throw new InconsistentKraken( string.Format( "{0} requires a version {1}. However a incompatible version, {2}, is in the resolver", dep_name, descriptor.RequiredVersion, module.version)); } if (registry.IsInstalled(dep_name)) { if (descriptor.version_within_bounds(registry.InstalledVersion(dep_name))) { continue; } var module = registry.InstalledModule(dep_name).Module; //TODO Ideally we could check here if it can be replaced by the version we want. if (options.procede_with_inconsistencies) { conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(module, reason.Parent)); conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(reason.Parent, module)); continue; } throw new InconsistentKraken( string.Format( "{0} requires a version {1}. However a incompatible version, {2}, is already installed", dep_name, descriptor.RequiredVersion, registry.InstalledVersion(dep_name))); } var descriptor1 = descriptor; List <CkanModule> candidates = registry.LatestAvailableWithProvides(dep_name, kspversion, descriptor) .Where(mod => descriptor1.version_within_bounds(mod.version) && MightBeInstallable(mod)).ToList(); if (candidates.Count == 0) { if (!soft_resolve) { log.ErrorFormat("Dependency on {0} found, but nothing provides it.", dep_name); throw new ModuleNotFoundKraken(dep_name); } log.InfoFormat("{0} is recommended/suggested, but nothing provides it.", dep_name); continue; } if (candidates.Count > 1) { // Oh no, too many to pick from! // TODO: It would be great if instead we picked the one with the // most recommendations. if (options.without_toomanyprovides_kraken) { continue; } // If we've got a parent stanza that has a relationship on a mod that provides what // we need, then select that. if (old_stanza != null) { List <CkanModule> provide = candidates.Where(can => old_stanza.Where(relation => can.identifier == relation.name).Any()).ToList(); if (!provide.Any() || provide.Count() > 1) { //We still have either nothing, or too my to pick from //Just throw the TMP now throw new TooManyModsProvideKraken(dep_name, candidates); } candidates[0] = provide.First(); } else { throw new TooManyModsProvideKraken(dep_name, candidates); } } CkanModule candidate = candidates[0]; // Finally, check our candidate against everything which might object // to it being installed; that's all the mods which are fixed in our // list thus far, as well as everything on the system. var fixed_mods = new HashSet <CkanModule>(modlist.Values); fixed_mods.UnionWith(installed_modules); var conflicting_mod = fixed_mods.FirstOrDefault(mod => mod.ConflictsWith(candidate)); if (conflicting_mod == null) { // Okay, looks like we want this one. Adding. Add(candidate, reason); Resolve(candidate, options, stanza); } else if (soft_resolve) { log.InfoFormat("{0} would cause conflicts, excluding it from consideration", candidate); } else { if (options.procede_with_inconsistencies) { Add(candidate, reason); conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(conflicting_mod, candidate)); conflicts.Add(new KeyValuePair <CkanModule, CkanModule>(candidate, conflicting_mod)); } else { throw new InconsistentKraken(string.Format("{0} conflicts with {1}, can't install both.", conflicting_mod, candidate)); } } } }
public ModUpgrade(CkanModule mod, GUIModChangeType changeType, SelectionReason reason, CkanModule targetMod) : base(mod, changeType, reason) { this.targetMod = targetMod; }