"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)",
"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",
" 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):
.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
.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
.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
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)
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
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:
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):
_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()
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")
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
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
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/"):
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/"):
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
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)