def getMinUpgrade(vulnerableList, unaffectedList, portdbapi, vardbapi, minimize=True): """ Checks if the systemstate is matching an atom in I{vulnerableList} and returns string describing the lowest version for the package that matches an atom in I{unaffectedList} and is greater than the currently installed version or None if the system is not affected. Both I{vulnerableList} and I{unaffectedList} should have the same base package. @type vulnerableList: List of Strings @param vulnerableList: atoms matching vulnerable package versions @type unaffectedList: List of Strings @param unaffectedList: atoms matching unaffected package versions @type portdbapi: portage.dbapi.porttree.portdbapi @param portdbapi: Ebuild repository @type vardbapi: portage.dbapi.vartree.vardbapi @param vardbapi: Installed package repository @type minimize: Boolean @param minimize: True for a least-change upgrade, False for emerge-like algorithm @rtype: String | None @return: the lowest unaffected version that is greater than the installed version. """ rValue = None v_installed = [] u_installed = [] for v in vulnerableList: v_installed += match(v, vardbapi) for u in unaffectedList: u_installed += match(u, vardbapi) install_unaffected = True for i in v_installed: if i not in u_installed: install_unaffected = False if install_unaffected: return rValue for u in unaffectedList: mylist = match(u, portdbapi, match_type="match-all") for c in mylist: c_pv = catpkgsplit(c) i_pv = catpkgsplit(best(v_installed)) if pkgcmp(c_pv[1:], i_pv[1:]) > 0 \ and (rValue == None \ or not match("="+rValue, portdbapi) \ or (minimize ^ (pkgcmp(c_pv[1:], catpkgsplit(rValue)[1:]) > 0)) \ and match("="+c, portdbapi)) \ and portdbapi.aux_get(c, ["SLOT"]) == vardbapi.aux_get(best(v_installed), ["SLOT"]): rValue = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2] if c_pv[3] != "r0": # we don't like -r0 for display rValue += "-"+c_pv[3] return rValue
def cpvequal(cpv1, cpv2): """ @param cpv1: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1" @type cpv1: String @param cpv2: CategoryPackageVersion (no operators) Example: "sys-apps/portage-2.1" @type cpv2: String @rtype: Boolean @returns: 1. True if cpv1 = cpv2 2. False Otherwise 3. Throws PortageException if cpv1 or cpv2 is not a CPV Example Usage: >>> from portage.dep import cpvequal >>> cpvequal("sys-apps/portage-2.1","sys-apps/portage-2.1") >>> True """ split1 = catpkgsplit(cpv1) split2 = catpkgsplit(cpv2) if not split1 or not split2: raise portage.exception.PortageException(_("Invalid data '%s, %s', parameter was not a CPV") % (cpv1, cpv2)) if split1[0] != split2[0]: return False return (pkgcmp(split1[1:], split2[1:]) == 0)
def _reduce(self, atomlist): mydict = {} for atom in atomlist[:]: cpv = self._portdbapi.xmatch("match-all", atom)[0] slot = self._portdbapi.aux_get(cpv, ["SLOT"])[0] cps = "/".join(catpkgsplit(cpv)[0:2]) + ":" + slot if not cps in mydict: mydict[cps] = (atom, cpv) else: other_cpv = mydict[cps][1] if pkgcmp(catpkgsplit(cpv)[1:], catpkgsplit(other_cpv)[1:]) > 0: atomlist.remove(mydict[cps][0]) mydict[cps] = (atom, cpv) return atomlist
def compare_strs(pkg1, pkg2): """Similar to the builtin cmp, but for package strings. Usually called as: package_list.sort(cpv.compare_strs) An alternative is to use the CPV descriptor from gentoolkit.cpv: >>> cpvs = sorted(CPV(x) for x in package_list) @see: >>> help(cmp) """ pkg1 = catpkgsplit(pkg1) pkg2 = catpkgsplit(pkg2) if pkg1[0] != pkg2[0]: return -1 if pkg1[0] < pkg2[0] else 1 elif pkg1[1] != pkg2[1]: return -1 if pkg1[1] < pkg2[1] else 1 else: return pkgcmp(pkg1[1:], pkg2[1:])
def compare_strs(pkg1, pkg2): """Similar to the builtin cmp, but for package strings. Usually called as: package_list.sort(cpv.compare_strs) An alternative is to use the CPV descriptor from gentoolkit.cpv: >>> package_list = ['sys-apps/portage-9999', 'media-video/ffmpeg-9999'] >>> cpvs = sorted(CPV(x) for x in package_list) @see: >>> help(cmp) """ pkg1 = catpkgsplit(pkg1) pkg2 = catpkgsplit(pkg2) if pkg1[0] != pkg2[0]: return -1 if pkg1[0] < pkg2[0] else 1 elif pkg1[1] != pkg2[1]: return -1 if pkg1[1] < pkg2[1] else 1 else: return pkgcmp(pkg1[1:], pkg2[1:])
def load(self): atoms = [] xmatch = self._portdb.xmatch xmatch_level = "bestmatch-visible" cp_list = self._vardb.cp_list aux_get = self._vardb.aux_get aux_keys = ["SLOT"] for cp in self._vardb.cp_all(): for cpv in cp_list(cp): slot, = aux_get(cpv, aux_keys) slot_atom = "%s:%s" % (cp, slot) ebuild = xmatch(xmatch_level, slot_atom) if not ebuild: continue ebuild_split = catpkgsplit(ebuild)[1:] installed_split = catpkgsplit(cpv)[1:] if pkgcmp(installed_split, ebuild_split) > 0: atoms.append(slot_atom) self._setAtoms(atoms)
def getMinUpgrade(vulnerableList, unaffectedList, portdbapi, vardbapi, minimize=True): """ Checks if the systemstate is matching an atom in I{vulnerableList} and returns string describing the lowest version for the package that matches an atom in I{unaffectedList} and is greater than the currently installed version or None if the system is not affected. Both I{vulnerableList} and I{unaffectedList} should have the same base package. @type vulnerableList: List of Strings @param vulnerableList: atoms matching vulnerable package versions @type unaffectedList: List of Strings @param unaffectedList: atoms matching unaffected package versions @type portdbapi: portage.dbapi.porttree.portdbapi @param portdbapi: Ebuild repository @type vardbapi: portage.dbapi.vartree.vardbapi @param vardbapi: Installed package repository @type minimize: Boolean @param minimize: True for a least-change upgrade, False for emerge-like algorithm @rtype: String | None @return: the lowest unaffected version that is greater than the installed version. """ rValue = None v_installed = [] u_installed = [] for v in vulnerableList: v_installed += match(v, vardbapi) for u in unaffectedList: u_installed += match(u, vardbapi) install_unaffected = True for i in v_installed: if i not in u_installed: install_unaffected = False if install_unaffected: return rValue for u in unaffectedList: mylist = match(u, portdbapi, match_type="match-all") for c in mylist: c_pv = catpkgsplit(c) i_pv = catpkgsplit(best(v_installed)) if pkgcmp(c_pv[1:], i_pv[1:]) > 0 \ and (rValue == None \ or not match("="+rValue, portdbapi) \ or (minimize ^ (pkgcmp(c_pv[1:], catpkgsplit(rValue)[1:]) > 0)) \ and match("="+c, portdbapi)) \ and portdbapi.aux_get(c, ["SLOT"]) == vardbapi.aux_get(best(v_installed), ["SLOT"]): rValue = c_pv[0] + "/" + c_pv[1] + "-" + c_pv[2] if c_pv[3] != "r0": # we don't like -r0 for display rValue += "-" + c_pv[3] return rValue
def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): """ Takes an unreduced and reduced deplist and removes satisfied dependencies. Returned deplist contains steps that must be taken to satisfy dependencies. """ if trees is None: trees = portage.db writemsg("ZapDeps -- %s\n" % (use_binaries), 2) if not reduced or unreduced == ["||"] or dep_eval(reduced): return [] if unreduced[0] != "||": unresolved = [] for x, satisfied in zip(unreduced, reduced): if isinstance(x, list): unresolved += dep_zapdeps(x, satisfied, myroot, use_binaries=use_binaries, trees=trees) elif not satisfied: unresolved.append(x) return unresolved # We're at a ( || atom ... ) type level and need to make a choice deps = unreduced[1:] satisfieds = reduced[1:] # Our preference order is for an the first item that: # a) contains all unmasked packages with the same key as installed packages # b) contains all unmasked packages # c) contains masked installed packages # d) is the first item preferred_installed = [] preferred_in_graph = [] preferred_any_slot = [] preferred_non_installed = [] unsat_use_in_graph = [] unsat_use_installed = [] unsat_use_non_installed = [] other = [] # unsat_use_* must come after preferred_non_installed # for correct ordering in cases like || ( foo[a] foo[b] ). choice_bins = ( preferred_in_graph, preferred_installed, preferred_any_slot, preferred_non_installed, unsat_use_in_graph, unsat_use_installed, unsat_use_non_installed, other, ) # Alias the trees we'll be checking availability against parent = trees[myroot].get("parent") priority = trees[myroot].get("priority") graph_db = trees[myroot].get("graph_db") vardb = None if "vartree" in trees[myroot]: vardb = trees[myroot]["vartree"].dbapi if use_binaries: mydbapi = trees[myroot]["bintree"].dbapi else: mydbapi = trees[myroot]["porttree"].dbapi # Sort the deps into installed, not installed but already # in the graph and other, not installed and not in the graph # and other, with values of [[required_atom], availablility] for x, satisfied in zip(deps, satisfieds): if isinstance(x, list): atoms = dep_zapdeps(x, satisfied, myroot, use_binaries=use_binaries, trees=trees) else: atoms = [x] if vardb is None: # When called by repoman, we can simply return the first choice # because dep_eval() handles preference selection. return atoms all_available = True all_use_satisfied = True slot_map = {} cp_map = {} for atom in atoms: if atom.blocker: continue # Ignore USE dependencies here since we don't want USE # settings to adversely affect || preference evaluation. avail_pkg = mydbapi.match(atom.without_use) if avail_pkg: avail_pkg = avail_pkg[-1] # highest (ascending order) avail_slot = Atom("%s:%s" % (atom.cp, mydbapi.aux_get(avail_pkg, ["SLOT"])[0])) if not avail_pkg: all_available = False all_use_satisfied = False break if atom.use: avail_pkg_use = mydbapi.match(atom) if not avail_pkg_use: all_use_satisfied = False else: # highest (ascending order) avail_pkg_use = avail_pkg_use[-1] if avail_pkg_use != avail_pkg: avail_pkg = avail_pkg_use avail_slot = Atom("%s:%s" % (atom.cp, mydbapi.aux_get(avail_pkg, ["SLOT"])[0])) slot_map[avail_slot] = avail_pkg pkg_cp = cpv_getkey(avail_pkg) highest_cpv = cp_map.get(pkg_cp) if highest_cpv is None or \ pkgcmp(catpkgsplit(avail_pkg)[1:], catpkgsplit(highest_cpv)[1:]) > 0: cp_map[pkg_cp] = avail_pkg this_choice = (atoms, slot_map, cp_map, all_available) if all_available: # The "all installed" criterion is not version or slot specific. # If any version of a package is already in the graph then we # assume that it is preferred over other possible packages choices. all_installed = True for atom in set(Atom(atom.cp) for atom in atoms \ if not atom.blocker): # New-style virtuals have zero cost to install. if not vardb.match(atom) and not atom.startswith("virtual/"): all_installed = False break all_installed_slots = False if all_installed: all_installed_slots = True for slot_atom in slot_map: # New-style virtuals have zero cost to install. if not vardb.match(slot_atom) and \ not slot_atom.startswith("virtual/"): all_installed_slots = False break if graph_db is None: if all_use_satisfied: if all_installed: if all_installed_slots: preferred_installed.append(this_choice) else: preferred_any_slot.append(this_choice) else: preferred_non_installed.append(this_choice) else: if all_installed_slots: unsat_use_installed.append(this_choice) else: unsat_use_non_installed.append(this_choice) else: all_in_graph = True for slot_atom in slot_map: # New-style virtuals have zero cost to install. if not graph_db.match(slot_atom) and \ not slot_atom.startswith("virtual/"): all_in_graph = False break circular_atom = None if all_in_graph: if parent is None or priority is None: pass elif priority.buildtime: # Check if the atom would result in a direct circular # dependency and try to avoid that if it seems likely # to be unresolvable. This is only relevant for # buildtime deps that aren't already satisfied by an # installed package. cpv_slot_list = [parent] for atom in atoms: if atom.blocker: continue if vardb.match(atom): # If the atom is satisfied by an installed # version then it's not a circular dep. continue if atom.cp != parent.cp: continue if match_from_list(atom, cpv_slot_list): circular_atom = atom break if circular_atom is not None: other.append(this_choice) else: if all_use_satisfied: if all_in_graph: preferred_in_graph.append(this_choice) elif all_installed: if all_installed_slots: preferred_installed.append(this_choice) else: preferred_any_slot.append(this_choice) else: preferred_non_installed.append(this_choice) else: if all_in_graph: unsat_use_in_graph.append(this_choice) elif all_installed_slots: unsat_use_installed.append(this_choice) else: unsat_use_non_installed.append(this_choice) else: other.append(this_choice) # Prefer choices which contain upgrades to higher slots. This helps # for deps such as || ( foo:1 foo:2 ), where we want to prefer the # atom which matches the higher version rather than the atom furthest # to the left. Sorting is done separately for each of choice_bins, so # as not to interfere with the ordering of the bins. Because of the # bin separation, the main function of this code is to allow # --depclean to remove old slots (rather than to pull in new slots). for choices in choice_bins: if len(choices) < 2: continue for choice_1 in choices[1:]: atoms_1, slot_map_1, cp_map_1, all_available_1 = choice_1 cps = set(cp_map_1) for choice_2 in choices: if choice_1 is choice_2: # choice_1 will not be promoted, so move on break atoms_2, slot_map_2, cp_map_2, all_available_2 = choice_2 intersecting_cps = cps.intersection(cp_map_2) if not intersecting_cps: continue has_upgrade = False has_downgrade = False for cp in intersecting_cps: version_1 = cp_map_1[cp] version_2 = cp_map_2[cp] difference = pkgcmp(catpkgsplit(version_1)[1:], catpkgsplit(version_2)[1:]) if difference != 0: if difference > 0: has_upgrade = True else: has_downgrade = True break if has_upgrade and not has_downgrade: # promote choice_1 in front of choice_2 choices.remove(choice_1) index_2 = choices.index(choice_2) choices.insert(index_2, choice_1) break for allow_masked in (False, True): for choices in choice_bins: for atoms, slot_map, cp_map, all_available in choices: if all_available or allow_masked: return atoms assert(False) # This point should not be reachable
def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): """ Takes an unreduced and reduced deplist and removes satisfied dependencies. Returned deplist contains steps that must be taken to satisfy dependencies. """ if trees is None: trees = portage.db writemsg("ZapDeps -- %s\n" % (use_binaries), 2) if not reduced or unreduced == ["||"] or dep_eval(reduced): return [] if unreduced[0] != "||": unresolved = [] for x, satisfied in zip(unreduced, reduced): if isinstance(x, list): unresolved += dep_zapdeps(x, satisfied, myroot, use_binaries=use_binaries, trees=trees) elif not satisfied: unresolved.append(x) return unresolved # We're at a ( || atom ... ) type level and need to make a choice deps = unreduced[1:] satisfieds = reduced[1:] # Our preference order is for an the first item that: # a) contains all unmasked packages with the same key as installed packages # b) contains all unmasked packages # c) contains masked installed packages # d) is the first item preferred_installed = [] preferred_in_graph = [] preferred_any_slot = [] preferred_non_installed = [] unsat_use_in_graph = [] unsat_use_installed = [] unsat_use_non_installed = [] other = [] # unsat_use_* must come after preferred_non_installed # for correct ordering in cases like || ( foo[a] foo[b] ). choice_bins = ( preferred_in_graph, preferred_installed, preferred_any_slot, preferred_non_installed, unsat_use_in_graph, unsat_use_installed, unsat_use_non_installed, other, ) # Alias the trees we'll be checking availability against parent = trees[myroot].get("parent") priority = trees[myroot].get("priority") graph_db = trees[myroot].get("graph_db") vardb = None if "vartree" in trees[myroot]: vardb = trees[myroot]["vartree"].dbapi if use_binaries: mydbapi = trees[myroot]["bintree"].dbapi else: mydbapi = trees[myroot]["porttree"].dbapi # Sort the deps into installed, not installed but already # in the graph and other, not installed and not in the graph # and other, with values of [[required_atom], availablility] for x, satisfied in zip(deps, satisfieds): if isinstance(x, list): atoms = dep_zapdeps(x, satisfied, myroot, use_binaries=use_binaries, trees=trees) else: atoms = [x] if vardb is None: # When called by repoman, we can simply return the first choice # because dep_eval() handles preference selection. return atoms all_available = True all_use_satisfied = True slot_map = {} cp_map = {} for atom in atoms: if atom.blocker: continue # Ignore USE dependencies here since we don't want USE # settings to adversely affect || preference evaluation. avail_pkg = mydbapi.match(atom.without_use) if avail_pkg: avail_pkg = avail_pkg[-1] # highest (ascending order) avail_slot = Atom( "%s:%s" % (atom.cp, mydbapi.aux_get(avail_pkg, ["SLOT"])[0])) if not avail_pkg: all_available = False all_use_satisfied = False break if atom.use: avail_pkg_use = mydbapi.match(atom) if not avail_pkg_use: all_use_satisfied = False else: # highest (ascending order) avail_pkg_use = avail_pkg_use[-1] if avail_pkg_use != avail_pkg: avail_pkg = avail_pkg_use avail_slot = Atom( "%s:%s" % (atom.cp, mydbapi.aux_get(avail_pkg, ["SLOT"])[0])) slot_map[avail_slot] = avail_pkg pkg_cp = cpv_getkey(avail_pkg) highest_cpv = cp_map.get(pkg_cp) if highest_cpv is None or \ pkgcmp(catpkgsplit(avail_pkg)[1:], catpkgsplit(highest_cpv)[1:]) > 0: cp_map[pkg_cp] = avail_pkg this_choice = (atoms, slot_map, cp_map, all_available) if all_available: # The "all installed" criterion is not version or slot specific. # If any version of a package is already in the graph then we # assume that it is preferred over other possible packages choices. all_installed = True for atom in set(Atom(atom.cp) for atom in atoms \ if not atom.blocker): # New-style virtuals have zero cost to install. if not vardb.match(atom) and not atom.startswith("virtual/"): all_installed = False break all_installed_slots = False if all_installed: all_installed_slots = True for slot_atom in slot_map: # New-style virtuals have zero cost to install. if not vardb.match(slot_atom) and \ not slot_atom.startswith("virtual/"): all_installed_slots = False break if graph_db is None: if all_use_satisfied: if all_installed: if all_installed_slots: preferred_installed.append(this_choice) else: preferred_any_slot.append(this_choice) else: preferred_non_installed.append(this_choice) else: if all_installed_slots: unsat_use_installed.append(this_choice) else: unsat_use_non_installed.append(this_choice) else: all_in_graph = True for slot_atom in slot_map: # New-style virtuals have zero cost to install. if not graph_db.match(slot_atom) and \ not slot_atom.startswith("virtual/"): all_in_graph = False break circular_atom = None if all_in_graph: if parent is None or priority is None: pass elif priority.buildtime: # Check if the atom would result in a direct circular # dependency and try to avoid that if it seems likely # to be unresolvable. This is only relevant for # buildtime deps that aren't already satisfied by an # installed package. cpv_slot_list = [parent] for atom in atoms: if atom.blocker: continue if vardb.match(atom): # If the atom is satisfied by an installed # version then it's not a circular dep. continue if atom.cp != parent.cp: continue if match_from_list(atom, cpv_slot_list): circular_atom = atom break if circular_atom is not None: other.append(this_choice) else: if all_use_satisfied: if all_in_graph: preferred_in_graph.append(this_choice) elif all_installed: if all_installed_slots: preferred_installed.append(this_choice) else: preferred_any_slot.append(this_choice) else: preferred_non_installed.append(this_choice) else: if all_in_graph: unsat_use_in_graph.append(this_choice) elif all_installed_slots: unsat_use_installed.append(this_choice) else: unsat_use_non_installed.append(this_choice) else: other.append(this_choice) # Prefer choices which contain upgrades to higher slots. This helps # for deps such as || ( foo:1 foo:2 ), where we want to prefer the # atom which matches the higher version rather than the atom furthest # to the left. Sorting is done separately for each of choice_bins, so # as not to interfere with the ordering of the bins. Because of the # bin separation, the main function of this code is to allow # --depclean to remove old slots (rather than to pull in new slots). for choices in choice_bins: if len(choices) < 2: continue for choice_1 in choices[1:]: atoms_1, slot_map_1, cp_map_1, all_available_1 = choice_1 cps = set(cp_map_1) for choice_2 in choices: if choice_1 is choice_2: # choice_1 will not be promoted, so move on break atoms_2, slot_map_2, cp_map_2, all_available_2 = choice_2 intersecting_cps = cps.intersection(cp_map_2) if not intersecting_cps: continue has_upgrade = False has_downgrade = False for cp in intersecting_cps: version_1 = cp_map_1[cp] version_2 = cp_map_2[cp] difference = pkgcmp( catpkgsplit(version_1)[1:], catpkgsplit(version_2)[1:]) if difference != 0: if difference > 0: has_upgrade = True else: has_downgrade = True break if has_upgrade and not has_downgrade: # promote choice_1 in front of choice_2 choices.remove(choice_1) index_2 = choices.index(choice_2) choices.insert(index_2, choice_1) break for allow_masked in (False, True): for choices in choice_bins: for atoms, slot_map, cp_map, all_available in choices: if all_available or allow_masked: return atoms assert (False) # This point should not be reachable
def available_portage_update(self, detected=False, init=False): """ Check if an update to portage is available. """ # TODO: be more verbose for debug ! name = 'available_portage_update' logger = logging.getLogger(f'{self.__logger_name}{name}::') logger.debug(f"Running with detected={detected}, init={init}") self.available = False self.latest = False self.current = False # First, any way get installed and latest current = vardbapi().match('portage')[0] latest = portdbapi().xmatch('bestmatch-visible', 'portage') # Then just compare # From site-packages/portage/versions.py # @param mypkg: either a pv or cpv # @return: # 1. None if input is invalid. # 2. (pn, ver, rev) if input is pv # 3. (cp, ver, rev) if input is a cpv result = pkgcmp(pkgsplit(latest), pkgsplit(current)) # From site-packages/portage/versions.py # Parameters: # pkg1 (list (example: ['test', '1.0', 'r1'])) - # package to compare with # pkg2 (list (example: ['test', '1.0', 'r1'])) - # package to compare againts # Returns: None or integer # None if package names are not the same # 1 if pkg1 is greater than pkg2 # -1 if pkg1 is less than pkg2 # 0 if pkg1 equals pkg2 if result == None or result == -1: msg = 'no result (package names are not the same ?!)' if result == -1: msg = ('the latest version available is lower than the' ' one installed...') logger.error("FAILED to compare versions when obtaining update " "informations for the portage package.") logger.error(f"Result is: {msg}") logger.error(f"Current portage version: {current}, latest:" f" {latest}.") # Return is ignored for the moment... # TODO ?? return # Split current version # as we don't know yet if latest > current split = pkgsplit(current) self.current = split[1] if not split[2] == 'r0': self.current = '-'.join(split[-2:]) # Check if an update to portage is available if result: # Now, split latest because > current split = pkgsplit(latest) self.latest = split[1] if not split[2] == 'r0': self.latest = '-'.join(split[-2:]) logger.debug(f"Found an update to portage (from {self.current}" f" to {self.latest}).") # Print only one time when program start if init: logger.info("Found an update to portage (from " f"{self.current} to {self.latest}).") self.available = True else: logger.debug("No update to portage package is available" f" (current version: {self.current})") self.available = False # For detected we have to compare current extracted # version and last current know version (so from # self.portage['current']. Both versions are already # split if detected: # From site-packages/portage/versions.py # Compare two versions # Example usage: # >>> from portage.versions import vercmp # >>> vercmp('1.0-r1','1.2-r3') # negative number # >>> vercmp('1.3','1.2-r3') # positive number # >>> vercmp('1.0_p3','1.0_p3') # 0 # @param pkg1: version to compare with # (see ver_regexp in portage.versions.py) # @type pkg1: string (example: "2.1.2-r3") # @param pkg2: version to compare againts # (see ver_regexp in portage.versions.py) # @type pkg2: string (example: "2.1.2_rc5") # @rtype: None or float # @return: # 1. positive if ver1 is greater than ver2 # 2. negative if ver1 is less than ver2 # 3. 0 if ver1 equals ver2 # 4. None if ver1 or ver2 are invalid # (see ver_regexp in portage.versions.py) compare = vercmp(self.portage['current'], self.current) msg = False add_msg = '' if compare < 0: # If not available and it have been updated # than it have been updated to latest one if not self.available: add_msg = 'latest ' msg = (f"The portage package has been updated (from " f"{self.portage['current']} to " f"{add_msg}{self.current}).") elif compare > 0: # Same here but reversed: if it was not # available (self.portage['available']) # and now it is (self.available) than # it have been downgraded from latest. if not self.portage['available'] and self.available: add_msg = 'latest ' msg = (f"The portage package has been downgraded (from " f"{add_msg}{self.portage['current']} to " f"{self.current}).") elif compare == 0: # This have been aborted msg = ("The portage package process has been aborted.") # Just skipp if msg = False # so that mean compare == None if msg: logger.info(msg) tosave = [] # Update only if change for key in 'current', 'latest', 'available': if not self.portage[key] == getattr(self, key): # This print if there a new version of portage available # even if there is already an older version available # TEST: if checking only for key latest than it could # be == to current so check also result. if key == 'latest' and result: logger.info("Found an update to portage (from " f"{self.current} to {self.latest}).") self.portage[key] = getattr(self, key) tosave.append([f'portage {key}', self.portage[key]]) if tosave: self.stateinfo.save(*tosave)
def match_from_list(mydep, candidate_list): """ Searches list for entries that matches the package. @param mydep: The package atom to match @type mydep: String @param candidate_list: The list of package atoms to compare against @param candidate_list: List @rtype: List @return: A list of package atoms that match the given package atom """ if not candidate_list: return [] from portage.util import writemsg if "!" == mydep[:1]: mydep = mydep[1:] if not isinstance(mydep, Atom): mydep = Atom(mydep) mycpv = mydep.cpv mycpv_cps = catpkgsplit(mycpv) # Can be None if not specific slot = mydep.slot if not mycpv_cps: cat, pkg = catsplit(mycpv) ver = None rev = None else: cat, pkg, ver, rev = mycpv_cps if mydep == mycpv: raise KeyError(_("Specific key requires an operator" " (%s) (try adding an '=')") % (mydep)) if ver and rev: operator = mydep.operator if not operator: writemsg(_("!!! Invalid atom: %s\n") % mydep, noiselevel=-1) return [] else: operator = None mylist = [] if operator is None: for x in candidate_list: cp = getattr(x, "cp", None) if cp is None: mysplit = catpkgsplit(remove_slot(x)) if mysplit is not None: cp = mysplit[0] + '/' + mysplit[1] if cp != mycpv: continue mylist.append(x) elif operator == "=": # Exact match for x in candidate_list: xcpv = getattr(x, "cpv", None) if xcpv is None: xcpv = remove_slot(x) if not cpvequal(xcpv, mycpv): continue mylist.append(x) elif operator == "=*": # glob match # XXX: Nasty special casing for leading zeros # Required as =* is a literal prefix match, so can't # use vercmp mysplit = catpkgsplit(mycpv) myver = mysplit[2].lstrip("0") if not myver or not myver[0].isdigit(): myver = "0"+myver mycpv = mysplit[0]+"/"+mysplit[1]+"-"+myver for x in candidate_list: xs = getattr(x, "cpv_split", None) if xs is None: xs = catpkgsplit(remove_slot(x)) myver = xs[2].lstrip("0") if not myver or not myver[0].isdigit(): myver = "0"+myver xcpv = xs[0]+"/"+xs[1]+"-"+myver if xcpv.startswith(mycpv): mylist.append(x) elif operator == "~": # version, any revision, match for x in candidate_list: xs = getattr(x, "cpv_split", None) if xs is None: xs = catpkgsplit(remove_slot(x)) if xs is None: raise InvalidData(x) if not cpvequal(xs[0]+"/"+xs[1]+"-"+xs[2], mycpv_cps[0]+"/"+mycpv_cps[1]+"-"+mycpv_cps[2]): continue if xs[2] != ver: continue mylist.append(x) elif operator in [">", ">=", "<", "<="]: mysplit = ["%s/%s" % (cat, pkg), ver, rev] for x in candidate_list: xs = getattr(x, "cpv_split", None) if xs is None: xs = catpkgsplit(remove_slot(x)) xcat, xpkg, xver, xrev = xs xs = ["%s/%s" % (xcat, xpkg), xver, xrev] try: result = pkgcmp(xs, mysplit) except ValueError: # pkgcmp may return ValueError during int() conversion writemsg(_("\nInvalid package name: %s\n") % x, noiselevel=-1) raise if result is None: continue elif operator == ">": if result > 0: mylist.append(x) elif operator == ">=": if result >= 0: mylist.append(x) elif operator == "<": if result < 0: mylist.append(x) elif operator == "<=": if result <= 0: mylist.append(x) else: raise KeyError(_("Unknown operator: %s") % mydep) else: raise KeyError(_("Unknown operator: %s") % mydep) if slot is not None: candidate_list = mylist mylist = [] for x in candidate_list: xslot = getattr(x, "slot", False) if xslot is False: xslot = dep_getslot(x) if xslot is not None and xslot != slot: continue mylist.append(x) if mydep.use: candidate_list = mylist mylist = [] for x in candidate_list: use = getattr(x, "use", None) if use is not None: is_valid_flag = x.iuse.is_valid_flag missing_iuse = False for y in mydep.use.required: if not is_valid_flag(y): missing_iuse = True break if missing_iuse: continue if mydep.use.enabled.difference(use.enabled): continue if mydep.use.disabled.intersection(use.enabled): continue mylist.append(x) return mylist