From e18c3b80e09f6abd096afa304a6555e5e450d8c6 Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Sun, 18 Oct 2009 13:27:06 +0000 Subject: [PATCH] Merged from trunk -r14625:14635 | 14626 | Inside dep_zapdeps(), detect cases such as || ( foo:1 foo:2 | | zmedico | ), 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). | | 14627 | Fix repoman breakage from previous commit. | | zmedico | | | 14628 | Make cpv_getkey() use catpkgsplit() insead of a separate | | zmedico | regex. | | 14629 | Bug #227225 - Add *DEPEND.badtilde warning for ~ operator | | zmedico | used with non-zero revision. Thanks to David Leverton | | | for this patch. | | 14630 | Automatically create a fallback setconfig, so emerge isn't | | zmedico | crippled due to misssing/corrupt/outdated sets.conf. This is | | | especially important since WorldSet has been renamed to | | | WorldSelectedSet, and thus new and old sets.conf files are | | | incompatible. | | 14631 | Call config._init_dirs() from emerge so that it's not called | | zmedico | every time the portage api is imported. | svn path=/main/branches/prefix/; revision=14670 --- bin/repoman | 12 +++++ man/repoman.1 | 9 ++++ pym/_emerge/actions.py | 1 + pym/_emerge/main.py | 76 +++++++++++++++++++---------- pym/portage/__init__.py | 103 ++++++++++++++++++++++++++++++---------- 5 files changed, 150 insertions(+), 51 deletions(-) diff --git a/bin/repoman b/bin/repoman index 7d0f325e8..961d013a4 100755 --- a/bin/repoman +++ b/bin/repoman @@ -293,6 +293,9 @@ qahelp={ "DEPEND.syntax":"Syntax error in DEPEND (usually an extra/missing space/parenthesis)", "RDEPEND.syntax":"Syntax error in RDEPEND (usually an extra/missing space/parenthesis)", "PDEPEND.syntax":"Syntax error in PDEPEND (usually an extra/missing space/parenthesis)", + "DEPEND.badtilde":"DEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored)", + "RDEPEND.badtilde":"RDEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored)", + "PDEPEND.badtilde":"PDEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored)", "LICENSE.syntax":"Syntax error in LICENSE (usually an extra/missing space/parenthesis)", "PROVIDE.syntax":"Syntax error in PROVIDE (usually an extra/missing space/parenthesis)", "PROPERTIES.syntax":"Syntax error in PROPERTIES (usually an extra/missing space/parenthesis)", @@ -348,6 +351,7 @@ qawarnings = set(( "DEPEND.badmasked","RDEPEND.badmasked","PDEPEND.badmasked", "DEPEND.badindev","RDEPEND.badindev","PDEPEND.badindev", "DEPEND.badmaskedindev","RDEPEND.badmaskedindev","PDEPEND.badmaskedindev", +"DEPEND.badtilde", "RDEPEND.badtilde", "PDEPEND.badtilde", "DESCRIPTION.toolong", "KEYWORDS.dropped", "KEYWORDS.stupid", @@ -1502,6 +1506,14 @@ for x in scanlist: " not supported with EAPI='%s':" + \ " '%s'") % (mytype, eapi, atom)) + if atom.operator == "~" and \ + portage.versions.catpkgsplit(atom.cpv)[3] != "r0": + stats[mytype + '.badtilde'] += 1 + fails[mytype + '.badtilde'].append( + (relative_path + ": %s uses the ~ operator" + " with a non-zero revision:" + \ + " '%s'") % (mytype, atom)) + type_list.extend([mytype] * (len(badsyntax) - len(type_list))) for m,b in zip(type_list, badsyntax): diff --git a/man/repoman.1 b/man/repoman.1 index dbb214d31..5fa1bdf8e 100644 --- a/man/repoman.1 +++ b/man/repoman.1 @@ -98,6 +98,9 @@ Masked ebuilds with bad DEPEND settings (matched against *all* ebuilds) .B DEPEND.badmaskedindev Masked ebuilds with bad DEPEND settings (matched against *all* ebuilds) in developing arch .TP +.B DEPEND.badtilde +DEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored) +.TP .B DEPEND.syntax Syntax error in DEPEND (usually an extra/missing space/parenthesis) .TP @@ -165,6 +168,9 @@ Masked ebuilds with PDEPEND settings (matched against *all* ebuilds) .B PDEPEND.badmaskedindev Masked ebuilds with PDEPEND settings (matched against *all* ebuilds) in developing arch .TP +.B PDEPEND.badtilde +PDEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored) +.TP .B PDEPEND.suspect PDEPEND contains a package that usually only belongs in DEPEND .TP @@ -186,6 +192,9 @@ Masked ebuilds with RDEPEND settings (matched against *all* ebuilds) .B RDEPEND.badmaskedindev Masked ebuilds with RDEPEND settings (matched against *all* ebuilds) in developing arch .TP +.B RDEPEND.badtilde +RDEPEND uses the ~ dep operator with a non-zero revision part, which is useless (the revision is ignored) +.TP .B RDEPEND.suspect RDEPEND contains a package that usually only belongs in DEPEND .TP diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py index c94ca3c5d..4f319dbf3 100644 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@ -2647,6 +2647,7 @@ def load_emerge_config(trees=None): for root, root_trees in trees.items(): settings = root_trees["vartree"].settings + settings._init_dirs() setconfig = load_default_config(settings, root_trees) root_trees["root_config"] = RootConfig(settings, root_trees, setconfig) diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py index e9afd255f..f79d4e1da 100644 --- a/pym/_emerge/main.py +++ b/pym/_emerge/main.py @@ -857,6 +857,55 @@ def ionice(settings): out.eerror("PORTAGE_IONICE_COMMAND returned %d" % (rval,)) out.eerror("See the make.conf(5) man page for PORTAGE_IONICE_COMMAND usage instructions.") +def setconfig_fallback(root_config): + from portage.sets.base import DummyPackageSet + from portage.sets.files import WorldSelectedSet + from portage.sets.profiles import PackagesSystemSet + setconfig = root_config.setconfig + setconfig.psets['world'] = DummyPackageSet(atoms=['@selected', '@system']) + setconfig.psets['selected'] = WorldSelectedSet(root_config.root) + setconfig.psets['system'] = \ + PackagesSystemSet(root_config.settings.profiles) + root_config.sets = setconfig.getSets() + +def get_missing_sets(root_config): + # emerge requires existence of "world", "selected", and "system" + missing_sets = [] + + for s in ("selected", "system", "world",): + if s not in root_config.sets: + missing_sets.append(s) + + return missing_sets + +def missing_sets_warning(root_config, missing_sets): + if len(missing_sets) > 2: + missing_sets_str = ", ".join('"%s"' % s for s in missing_sets[:-1]) + missing_sets_str += ', and "%s"' % missing_sets[-1] + elif len(missing_sets) == 2: + missing_sets_str = '"%s" and "%s"' % tuple(missing_sets) + else: + missing_sets_str = '"%s"' % missing_sets[-1] + msg = ["emerge: incomplete set configuration, " + \ + "missing set(s): %s" % missing_sets_str] + if root_config.sets: + msg.append(" sets defined: %s" % ", ".join(root_config.sets)) + msg.append(" This usually means that '%s'" % \ + (os.path.join(portage.const.GLOBAL_CONFIG_PATH, "sets.conf"),)) + msg.append(" is missing or corrupt.") + for line in msg: + writemsg_level(line + "\n", level=logging.ERROR, noiselevel=-1) + +def ensure_required_sets(trees): + warning_shown = False + for root_trees in trees.values(): + missing_sets = get_missing_sets(root_trees["root_config"]) + if missing_sets and not warning_shown: + warning_shown = True + missing_sets_warning(root_trees["root_config"], missing_sets) + if missing_sets: + setconfig_fallback(root_trees["root_config"]) + def expand_set_arguments(myfiles, myaction, root_config): retval = os.EX_OK setconfig = root_config.setconfig @@ -918,31 +967,6 @@ def expand_set_arguments(myfiles, myaction, root_config): for e in setconfig.errors: print(colorize("BAD", "Error during set creation: %s" % e)) - # emerge requires existence of "world", "selected", and "system" - required_sets = ("selected", "system", "world",) - missing_sets = [] - - for s in required_sets: - if s not in sets: - missing_sets.append(s) - if missing_sets: - if len(missing_sets) > 2: - missing_sets_str = ", ".join('"%s"' % s for s in missing_sets[:-1]) - missing_sets_str += ', and "%s"' % missing_sets[-1] - elif len(missing_sets) == 2: - missing_sets_str = '"%s" and "%s"' % tuple(missing_sets) - else: - missing_sets_str = '"%s"' % missing_sets[-1] - msg = ["emerge: incomplete set configuration, " + \ - "missing set(s): %s" % missing_sets_str] - if sets: - msg.append(" sets defined: %s" % ", ".join(sets)) - msg.append(" This usually means that '%s'" % \ - (os.path.join(portage.const.GLOBAL_CONFIG_PATH, "sets.conf"),)) - msg.append(" is missing or corrupt.") - for line in msg: - writemsg_level(line + "\n", level=logging.ERROR, noiselevel=-1) - return (None, 1) unmerge_actions = ("unmerge", "prune", "clean", "depclean") for a in myfiles: @@ -1167,6 +1191,8 @@ def emerge_main(): writemsg_stdout("".join("%s\n" % s for s in sorted(root_config.sets))) return os.EX_OK + ensure_required_sets(trees) + # only expand sets for actions taking package arguments oldargs = myfiles[:] if myaction in ("clean", "config", "depclean", "info", "prune", "unmerge", None): diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 95915e034..e247faa7a 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -2222,12 +2222,9 @@ class config(object): _validate_cache_for_unsupported_eapis = False _glep_55_enabled = True - # inject EPREFIX as it's in no single config file (I hope), - # but needs to be available using portageq + # inject EPREFIX as it needs to be available using portageq self["EPREFIX"] = EPREFIX - self._init_dirs() - for k in self._case_insensitive_vars: if k in self: self[k] = self[k].lower() @@ -7942,6 +7939,19 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): 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") @@ -7963,14 +7973,15 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): use_binaries=use_binaries, trees=trees) else: atoms = [x] - if not vardb: - # called by repoman - other.append((atoms, None, False)) - continue + 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 - versions = {} + slot_map = {} + cp_map = {} for atom in atoms: if atom.blocker: continue @@ -7998,9 +8009,15 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): avail_slot = dep.Atom("%s:%s" % (atom.cp, mydbapi.aux_get(avail_pkg, ["SLOT"])[0])) - versions[avail_slot] = avail_pkg + 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, versions, all_available) + 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 @@ -8015,7 +8032,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): all_installed_slots = False if all_installed: all_installed_slots = True - for slot_atom in versions: + 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/"): @@ -8037,7 +8054,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): unsat_use_non_installed.append(this_choice) else: all_in_graph = True - for slot_atom in versions: + 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/"): @@ -8089,17 +8106,52 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): else: other.append(this_choice) - # unsat_use_* must come after preferred_non_installed - # for correct ordering in cases like || ( foo[a] foo[b] ). - preferred = preferred_in_graph + preferred_installed + \ - preferred_any_slot + preferred_non_installed + \ - unsat_use_in_graph + unsat_use_installed + unsat_use_non_installed + \ - other + # 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 atoms, versions, all_available in preferred: - if all_available or allow_masked: - return atoms + 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 @@ -8272,12 +8324,11 @@ def dep_wordreduce(mydeplist,mysettings,mydbapi,mode,use_cache=1): return None return deplist -_cpv_key_re = re.compile('^' + versions._cpv + '$', re.VERBOSE) def cpv_getkey(mycpv): """Calls pkgsplit on a cpv and returns only the cp.""" - m = _cpv_key_re.match(mycpv) - if m is not None: - return m.group(2) + mysplit = versions.catpkgsplit(mycpv) + if mysplit is not None: + return mysplit[0] + '/' + mysplit[1] warnings.warn("portage.cpv_getkey() called with invalid cpv: '%s'" \ % (mycpv,), DeprecationWarning) -- 2.26.2