def handle_condition_conflict(self, decision_point, final_plan, primary_key, \ condition_set, result_set): conflict_condition, decision_condition = condition_set result = final_plan.check_pk(primary_key) if isinstance(result, int): conflict_id = final_plan[result].id if conflict_id not in self.conditional_packages: final_plan[result] = package return False for conflict_point in self.conditional_packages[conflict_id]: if conflict_point["type"].startswith(conflict_condition): if decision_point["type"].startswith(decision_condition): compare_result = utils.vercmp(decision_point["version"], \ conflict_point["version"]) if compare_result in result_set: self.conflict_point = conflict_point raise ConditionConflict(conflict_point)
def select_pkgs(self): for pkg in self.instdb.get_all_packages(): self.repo, self.category, self.name, self.version, self.slot = pkg # catch packages which are from the outside if not self.repodb.find_package(package_name=self.name, \ package_category=self.category): if not (self.category, self.name) in self.notfound_pkg: self.notfound_pkg.append((self.category, self.name)) # get version data from repository database repository_items = self.repodb.find_package(package_name=self.name, \ package_category=self.category) if not repository_items: # if the installed package could not found in the repository database # add the item to not-founds list self.notfound_pkg.append((self.category, self.name)) continue # collect available package version by slot value available_versions = {} for item in repository_items: if item.slot in available_versions: available_versions[item.slot].append(item.version) else: available_versions[item.slot] = [item.version] # comparise versions for item in repository_items: if item.slot == self.slot: best_version = utils.best_version( available_versions[item.slot]) result = utils.vercmp(best_version, self.version) if result != 0: self.packages.append( os.path.join(self.category, self.name) + ":" + self.slot) break if self.notfound_pkg: out.write( "%s: the following packages were installed but they could not be found in the database:\n\n" % out.color("WARNING", "brightyellow")) for no_category, no_name, in self.notfound_pkg: out.notify("%s/%s" % (no_category, no_name)) out.write("\n")
def select_pkgs(self): for pkg in self.instdb.get_all_packages(): self.repo, self.category, self.name, self.version, self.slot = pkg # catch packages which are from the outside if not self.repodb.find_package(package_name=self.name, \ package_category=self.category): if not (self.category, self.name) in self.notfound_pkg: self.notfound_pkg.append((self.category, self.name)) # get version data from repository database repository_items = self.repodb.find_package(package_name=self.name, \ package_category=self.category) if not repository_items: # if the installed package could not found in the repository database # add the item to not-founds list self.notfound_pkg.append((self.category, self.name)) continue # collect available package version by slot value available_versions = {} for item in repository_items: if item.slot in available_versions: available_versions[item.slot].append(item.version) else: available_versions[item.slot] = [item.version] # comparise versions for item in repository_items: if item.slot == self.slot: best_version = utils.best_version(available_versions[item.slot]) result = utils.vercmp(best_version, self.version) if result != 0: self.packages.append(os.path.join(self.category, self.name)+":"+self.slot) break if self.notfound_pkg: out.write("%s: the following packages were installed but they could not be found in the database:\n\n" % out.color("WARNING", "brightyellow")) for no_category, no_name, in self.notfound_pkg: out.notify("%s/%s" % (no_category, no_name)) out.write("\n")
def create_operation_plan(self): '''Resolve dependencies and prepares a convenient operation plan''' single_packages = PackageItem() for package in self.packages: self.parent_package = package self.current_package = None self.package_heap[package.id] = package dependencies = [] package_dependencies = self.collect_dependencies(package) if not package_dependencies: single_packages.add(package) continue # Create a list that consists of parent and child items for dependency in package_dependencies: dependency.parent = package.category+"/"+package.name+"/"+package.slot dependencies.append((package.id, dependency)) while True: buff = [] for parent, dependency in dependencies: self.current_package = dependency self.parent_package = None self.package_query.append((dependency.id, parent)) if dependency.id in self.processed: if self.processed[dependency.id] == self.package_options.get(dependency.id, None): # This package was processed and it has no option changes continue # Keep the package options to prevent extra transaction self.processed[dependency.id] = self.package_options.get(dependency.id, None) # Keep the package information for the next operations. # We don't want to create a new transaction for it. self.package_heap[dependency.id] = dependency # Get its dependencies package_collection = self.collect_dependencies(dependency) if not package_collection: # The item has no dependency continue # Create a list that consists of parent and child items for item in package_collection: item.parent = package.category+"/"+package.name+"/"+package.slot buff.append((dependency.id, item)) if not buff: # End of the node break dependencies = buff try: # Sort packages for building operation plan = sorter.topsort(self.package_query) except sorter.CycleError as err: answer, num_parents, children = err out.brightred("Circular dependency detected:\n") for items in sorter.find_cycles(parent_children=children): for item in items: package = self.repodb.find_package(package_id=item).get(0) out.write(package.repo+"/"+package.category+"/"+package.name+"-"\ +package.version+":"+package.slot+" ") out.write("\n") raise DependencyError # This part detects inline option conflicts removed = {} option_conflict = set() for package_id in self.inline_option_targets: for target in self.inline_option_targets[package_id]: for option in self.inline_option_targets[package_id][target]: if option.startswith("-"): if option in removed: removed[option].add((package_id, target)) else: removed[option] = set([(package_id, target)]) else: if "-"+option in removed: for (my_pkg_id, my_target) in removed["-"+option]: if my_target == target: option_conflict.add((my_target, \ self.package_heap[package_id], \ self.package_heap[my_pkg_id],\ option)) if option_conflict: out.error("option conflict detected:\n") for (pkg, add, remove, option)in option_conflict: out.error(out.color(option, "red")+" option on "+pkg+"\n") out.warn("%s/%s/%s/%s adds the option." % (add.repo, add.category, \ add.name, add.version)) out.warn("%s/%s/%s/%s removes the option." % (remove.repo, remove.category, \ remove.name, remove.version)) lpms.terminate() self.conditional_versions = {} for (key, values) in self.conditional_packages.items(): for value in values: target_package = self.package_heap[key] my_item = { "type": value["type"], "version": value["version"], "target": target_package.category+"/"+target_package.name+\ "/"+target_package.slot, } if not value["owner_id"] in self.conditional_versions: self.conditional_versions[value["owner_id"]] = [my_item] else: self.conditional_versions[value["owner_id"]].append(my_item) # TODO: I think I must use most professional way for ignore-depends feature. if lpms.getopt("--ignore-deps"): result = LCollect() result.packages = self.packages result.dependencies = self.package_dependencies result.options = self.package_options result.inline_option_targets = self.inline_option_targets result.conditional_versions = self.conditional_versions result.conflicts = self.conflicts return result # Workaround for postmerge dependencies for (id_dependency, id_package) in self.postmerge_dependencies: plan.remove(id_dependency) plan.insert(plan.index(id_package)+1, id_dependency) final_plan = PackageItem() required_package_ids = [package.id for package in self.packages] for package_id in plan: package = self.package_heap[package_id] continue_conditional = False # If a package has a conditional decision point, # we should consider the condition if package.id not in self.conditional_packages: for c_package_id in self.conditional_packages: c_package = self.package_heap[c_package_id] if package.pk == c_package.pk: continue_conditional = True if package_id in required_package_ids: final_plan.add_by_pk(c_package) break if package_id in required_package_ids: if continue_conditional is False: final_plan.add_by_pk(package) if continue_conditional: continue installed_package = self.instdb.find_package( package_category=package.category, package_name=package.name, package_slot=package.slot ) if installed_package: if package.id in self.inline_options: if installed_package.get(0).applied_options is None: final_plan.add_by_pk(package) continue continue_inline = False for inline_option in self.inline_options[package.id]: if not inline_option in installed_package.get(0).applied_options: final_plan.add_by_pk(package) continue_inline = True break if continue_inline: continue try: conditional_versions_query = self.instdb.find_conditional_versions( target=package.category+"/"+package.name+"/"+package.slot) if conditional_versions_query: for item in conditional_versions_query: item.decision_point["package_id"]=item.package_id if package.id in self.conditional_packages: if not item.decision_point in self.conditional_packages[package.id]: self.conditional_packages[package.id].append(item.decision_point) else: self.conditional_packages[package.id] = [item.decision_point] if package.id in self.conditional_packages: decision_points = self.conditional_packages[package.id] for decision_point in decision_points: comparison = utils.vercmp(installed_package.get(0).version, \ decision_point["version"]) if decision_point["type"] == ">=": if self.handle_condition_conflict(decision_point, final_plan, \ package.pk, ("<", ">"), (0, 1)) is False: continue if not comparison in (1, 0) or package.id in required_package_ids: final_plan.add_by_pk(package) elif decision_point["type"] == "<": if self.handle_condition_conflict(decision_point, final_plan, \ package.pk, (">", "<"), (0, -1)) is False: continue if comparison != -1: final_plan.add_by_pk(package) elif decision_point["type"] == ">": if self.handle_condition_conflict(decision_point, final_plan, \ package.pk, ("<", ">"), (0, 1)) is False: continue if comparison != 1 or package.id in required_package_ids: final_plan.add_by_pk(package) elif decision_point["type"] == "<=": if self.handle_condition_conflict(decision_point, final_plan, \ package.pk, (">", "<"), (0, -1)) is False: continue if not comparison in (-1, 0) or package.id in required_package_ids: final_plan.add_by_pk(package) elif decision_point["type"] == "==": if comparison != 0 or package.id in required_package_ids: final_plan.add_by_pk(package) except ConditionConflict: if not "owner_package" in decision_point: conflict_package = self.instdb.find_package(package_id=\ decision_point["package_id"]).get(0) decision_point["owner_package"] = conflict_package.repo+"/"+ \ conflict_package.category+"/"+ \ conflict_package.name+"/"+ \ conflict_package.version out.error("while selecting a convenient version of %s, a conflict detected:\n" % \ out.color(package.pk, "red")) out.notify(decision_point["owner_package"]+" wants "+\ decision_point["type"]+decision_point["version"]) out.notify(self.conflict_point["owner_package"]+" wants "+\ self.conflict_point["type"]+self.conflict_point["version"]) lpms.terminate("\nplease contact the package maintainers.") # Use new options if the package is effected if self.use_new_options and not package in final_plan: if package.id in self.package_options: for option in self.package_options[package.id]: if not option in installed_package.get(0).applied_options: final_plan.add_by_pk(package) break else: final_plan.add_by_pk(package) # Oh my god! Some packages have no dependency. if single_packages: for single_package in single_packages: for item_id in plan: if self.package_heap[item_id].pk == single_package.pk: single_packages.remove(single_package) break for single_package in single_packages: final_plan.insert_into(0, single_package) # Create LCollect object to manage package dependency data operation_plan = LCollect() operation_plan.packages = final_plan operation_plan.dependencies = self.package_dependencies operation_plan.options = self.package_options operation_plan.inline_option_targets = self.inline_option_targets operation_plan.conditional_versions = self.conditional_versions operation_plan.conflicts = self.conflicts return operation_plan
def get_convenient_package(self, package, instdb=False): def inline_options_management(inline_options): # TODO: inline_options variable must be a set # Check inline options, if an option is not available for the package, warn the user for inline_option in inline_options: if not inline_option in package.options: out.warn("%s option is not available for %s/%s/%s-%s. So that the option is removing..." % ( inline_option, package.repo, package.category, package.name, package.version )) inline_options.remove(inline_option) if inline_options: target = self.current_package.id if self.current_package is not \ None else self.parent_package.id my_package = package.category+"/"+package.name+"/"+package.slot if target in self.inline_option_targets: if my_package in self.inline_option_targets[target]: for option in inline_options: self.inline_option_targets[target][my_package].add(option) else: self.inline_option_targets[target][my_package] = set(inline_options) else: self.inline_option_targets[target] = {my_package: set(inline_options)} if package.id in self.inline_options: if not package.id in self.package_options: self.package_options[package.id] = set() for option in inline_options: if not option in self.inline_options[package.id]: self.inline_options[package.id].append(option) if package.id in self.package_options: self.package_options[package.id].add(option) else: self.inline_options[package.id] = inline_options if package.id in self.package_options: for inline_option in inline_options: self.package_options[package.id].add(inline_option) else: self.package_options[package.id] = set(inline_options) convenient_arches = utils.get_convenient_arches(self.conf.arch) current_package = self.parent_package if self.parent_package is not \ None else self.current_package result = LCollect() database = self.repodb if instdb is False else self.instdb slot = None gte, lte, lt, gt, et = False, False, False, False, False slot_parsed = package.split(":") if len(slot_parsed) == 2: data, slot = slot_parsed elif len(slot_parsed) > 2: out.error("%s invalid dependency in %s.py" % (data, self.current_package)) # Use and exception raise DependencyError else: data = package if ">=" == data[:2]: gte = True pkgname = data[2:] elif "<=" == data[:2]: lte = True pkgname = data[2:] elif "<" == data[:1]: lt = True pkgname = data[1:] elif ">" == data[:1]: gt = True pkgname = data[1:] elif "==" == data[:2]: et = True pkgname = data[2:] else: category, name = data.split("/") inline_options = self.parse_inline_options(name) if inline_options: name = name[:name.index("[")] if (category, name) in self.repository_cache: results = self.repository_cache[(category, name)] else: results = database.find_package(package_name=name, package_category=category) self.repository_cache[(category, name)] = results slot = self.get_convenient_slot(results, slot) if not results: if instdb: return current_package = current_package.repo+"/"+current_package.category+\ "/"+current_package.name+"-"+current_package.version+":"+current_package.slot out.error("unmet dependency: %s depends on %s" % (out.color(current_package, \ "red"), out.color(package, "red"))) raise DependencyError try: package = utils.get_convenient_package( results, self.locked_packages, self.custom_arch_requests, convenient_arches, self.instdb, slot ) except UnavailablePackage: for result in results: out.error("%s/%s/%s-%s:%s {%s} is unavailable for your arch(%s)." % (result.repo, result.category, \ result.name, result.version, result.slot, result.arch, self.conf.arch)) out.write("\n") out.write("%s %s/%s/%s-%s:%s {%s}\n" % (out.color("->", "brightyellow"), current_package.repo, \ current_package.category, current_package.name, current_package.version, \ current_package.slot, current_package.arch)) out.write(" %s %s/%s/%s-%s:%s {%s}\n" % (out.color("->", "brightyellow"), current_package.repo, \ current_package.category, current_package.name, current_package.version, \ current_package.slot, current_package.arch)) raise DependencyError except LockedPackage: out.error("these package(s) is/are locked by the system administrator:") for result in results: out.error_notify("%s/%s/%s-%s:%s {%s}" % (result.repo, result.category, \ result.name, result.version, result.slot, result.arch)) out.write("\n") out.write("%s %s/%s/%s-%s:%s {%s}\n" % (out.color("->", "brightyellow"), current_package.repo, \ current_package.category, current_package.name, current_package.version, \ current_package.slot, current_package.arch)) out.write(" %s %s/%s/%s-%s:%s {%s}\n" % (out.color("->", "brightyellow"), current_package.repo, \ current_package.category, current_package.name, current_package.version, \ current_package.slot, current_package.arch)) raise DependencyError # Set some variables to manage inline options inline_options_management(inline_options) return package category, name = pkgname.split("/") inline_options = self.parse_inline_options(name) if inline_options: name = name[:name.index("[")] name, version = utils.parse_pkgname(name) if (category, name) in self.repository_cache: results = self.repository_cache[(category, name)] else: results = database.find_package(package_name=name, package_category=category) self.repository_cache[(category, name)] = results slot = self.get_convenient_slot(results, slot) packages = [] decision_point = {} owner_package = current_package.repo+"/"+current_package.category+\ "/"+current_package.name+"-"+current_package.version if gte: decision_point = {"type": ">=", "version": version, \ "owner_package": owner_package, "owner_id": current_package.id} for result in results: comparison = utils.vercmp(result.version, version) if comparison == 1 or comparison == 0: packages.append(result) elif lte: decision_point = {"type": "<=", "version": version, \ "owner_package": owner_package, "owner_id": current_package.id} for result in results: comparison = utils.vercmp(result.version, version) if comparison == -1 or comparison == 0: packages.append(result) elif lt: decision_point = {"type": "<", "version": version, \ "owner_package": owner_package, "owner_id": current_package.id} for result in results: comparison = utils.vercmp(result.version, version) if comparison == -1: packages.append(result) elif gt: decision_point = {"type": ">", "version": version, \ "owner_package": owner_package, "owner_id": current_package.id} for result in results: comparison = utils.vercmp(result.version, version) if comparison == 1: packages.append(result) elif et: decision_point = {"type": "==", "version": version, \ "owner_package": owner_package, "owner_id": current_package.id} for result in results: comparison = utils.vercmp(result.version, version) if comparison == 0: packages.append(result) if not packages: out.error("unmet dependency: %s/%s/%s-%s:%s {%s} depends on %s" % \ (current_package.repo, \ current_package.category, \ current_package.name, \ current_package.version, \ current_package.slot, \ current_package.arch, \ out.color(package, "red"))) raise DependencyError try: package = utils.get_convenient_package( results if not packages else packages, self.locked_packages, self.custom_arch_requests, convenient_arches, self.instdb, slot ) except UnavailablePackage: for result in results: out.error("%s/%s/%s-%s:%s {%s}is unavailable for your arch(%s)." % (result.repo, result.category, \ result.name, result.version, result.slot, result.arch, self.conf.arch)) out.write("\n") out.write("%s %s/%s/%s-%s:%s {%s}\n" % (out.color("->", "brightyellow"), current_package.repo, \ current_package.category, current_package.name, current_package.version, \ current_package.slot, current_package.arch)) out.write(" %s %s/%s/%s-%s:%s {%s}\n" % (out.color("->", "brightyellow"), current_package.repo, \ current_package.category, current_package.name, current_package.version, \ current_package.slot, current_package.arch)) raise DependencyError except LockedPackage: out.error("these package(s) is/are locked by the system administrator:") for result in results: out.error_notify("%s/%s/%s-%s:%s {%s}" % (result.repo, result.category, \ result.name, result.version, result.slot, result.arch)) out.write("\n") out.write("%s %s/%s/%s-%s:%s {%s}\n" % (out.color("->", "brightyellow"), current_package.repo, \ current_package.category, current_package.name, current_package.version, \ current_package.slot, current_package.arch)) out.write(" %s %s/%s/%s-%s:%s {%s}\n" % (out.color("->", "brightyellow"), current_package.repo, \ current_package.category, current_package.name, current_package.version, \ current_package.slot, current_package.arch)) raise DependencyError # Set some variables to manage inline options inline_options_management(inline_options) if package.id in self.conditional_packages: self.conditional_packages[package.id].append(decision_point) else: self.conditional_packages[package.id] = [decision_point] return package
def show(packages, conflicts, options, installdb): '''Shows operation summary to the user''' # TODO: This can be splitted as a module for package in packages: status_bar = [' ', ' '] other_version = "" installed_packages = installdb.find_package( package_name=package.name, \ package_category=package.category) if not installed_packages: installed_package = None status_bar[0] = out.color("N", "brightgreen") else: if not [installed_package for installed_package in installed_packages \ if package.slot == installed_package.slot]: status_bar[1] = out.color("NS", "brightgreen") else: for installed_package in installed_packages: if installed_package.slot == package.slot: if package.version != installed_package.version: compare = utils.vercmp(package.version, \ installed_package.version) if compare == -1: status_bar[0] = out.color("D", "brightred") other_version = "[%s]" % out.color(\ installed_package.version, "brightred") elif compare == 1: status_bar[0] = out.color("U", "brightgreen") other_version = "[%s]" % out.color(\ installed_package.version, "green") elif package.version == installed_package.version: status_bar[0] = out.color("R", "brightyellow") class FormattedOptions(list): def __init__(self, data): super(FormattedOptions, self).extend(data) def append(self, item, color=None): self.remove(item) if color is not None: super(FormattedOptions, self).insert(0, out.color("*"+item, color)) else: super(FormattedOptions, self).insert(0, item) formatted_options = [] if package.options is not None: installed_package = None for item in installed_packages: if item.slot == package.slot: installed_package = item formatted_options = FormattedOptions(package.options) if package.id in options: if not status_bar[0].strip() or not status_bar[1].strip(): for applied_option in options[package.id]: if installed_package: if installed_package.applied_options is not None and not \ applied_option in installed_package.applied_options: formatted_options.append(applied_option, "brightgreen") continue elif installed_package.applied_options is None: formatted_options.append(applied_option, "brightgreen") continue formatted_options.append(applied_option, "red") if installed_package and installed_package.applied_options is not None: for applied_option in installed_package.applied_options: if not applied_option in options[package.id]: formatted_options.append(applied_option, "brightyellow") else: for option in package.options: if installed_package and installed_package.applied_options is not None and \ option in installed_package.applied_options: formatted_options.append(option, "brightyellow") else: formatted_options.append(option) else: if hasattr(installed_package, "applied_options") and installed_package.applied_options is not None: formatted_options = [out.color("%"+applied_option, "brightyellow") \ for applied_option in installed_package.applied_options] out.write(" [%s] %s/%s/%s {%s:%s} {%s} %s%s\n" % ( " ".join(status_bar), \ package.repo, \ package.category, \ package.name, \ out.color(package.slot, "yellow"),\ out.color(package.version, "green"),\ package.arch, \ other_version, \ " ("+", ".join(formatted_options)+")" if formatted_options else "" ) ) if package.id in conflicts: for conflict in conflicts[package.id]: category, name, slot = conflict.split("/") conflict = installdb.find_package(package_name=name, \ package_category=category, \ package_slot = slot).get(0) out.write("\t %s %s/%s/%s-%s" % (out.color(">> conflict:", "green"), \ conflict.repo,\ conflict.category, \ conflict.name, \ conflict.version ))