Merged from trunk -r14625:14635
authorFabian Groffen <grobian@gentoo.org>
Sun, 18 Oct 2009 13:27:06 +0000 (13:27 -0000)
committerFabian Groffen <grobian@gentoo.org>
Sun, 18 Oct 2009 13:27:06 +0000 (13:27 -0000)
   | 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        |
   |         | <levertond@googlemail.com> 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
man/repoman.1
pym/_emerge/actions.py
pym/_emerge/main.py
pym/portage/__init__.py

index 7d0f325e8619e8311d13e77f9a85b277bada2c54..961d013a4fdee7161449f400b74630c0f072d941 100755 (executable)
@@ -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):
index dbb214d318574c18d3c316c5f5a5579e2b273d28..5fa1bdf8ed7af255bbad2a912a5870786e4cd741 100644 (file)
@@ -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
index c94ca3c5d80b52c41a8f7ae98e39c1f6afcaae13..4f319dbf3f3e576ddf942abb2ead4c6955a16037 100644 (file)
@@ -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)
 
index e9afd255f8d553010ac1cf56e858cf072afa5540..f79d4e1da56a2cde7b1dbfd3a1a20f170b7d2668 100644 (file)
@@ -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):
index 95915e034f822b9da7e5e8c170385fe6636f2458..e247faa7a505594637776b7f6ac8132d278138a3 100644 (file)
@@ -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)