blob: c5e1f58bebeb2e01926d11c791ffc94fc2f7b64d [file] [log] [blame]
# Copyright 1999-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
import sys
from portage.dep import Atom, _repo_separator
from portage.exception import InvalidData
if sys.hexversion >= 0x3000000:
_unicode = str
_unicode = unicode
def create_world_atom(pkg, args_set, root_config, before_install=False):
"""Create a new atom for the world file if one does not exist. If the
argument atom is precise enough to identify a specific slot then a slot
atom will be returned. Atoms that are in the system set may also be stored
in world since system atoms can only match one slot while world atoms can
be greedy with respect to slots. Unslotted system packages will not be
stored in world."""
arg_atom = args_set.findAtomForPackage(pkg)
if not arg_atom:
return None
cp = arg_atom.cp
new_world_atom = cp
if arg_atom.repo:
new_world_atom += _repo_separator + arg_atom.repo
sets = root_config.sets
portdb = root_config.trees["porttree"].dbapi
vardb = root_config.trees["vartree"].dbapi
if arg_atom.repo is not None:
repos = [arg_atom.repo]
# Iterate over portdbapi.porttrees, since it's common to
# tweak this attribute in order to adjust match behavior.
repos = []
for tree in portdb.porttrees:
available_slots = set()
for cpv in portdb.match(Atom(cp)):
for repo in repos:
available_slots.add(portdb._pkg_str(_unicode(cpv), repo).slot)
except (KeyError, InvalidData):
slotted = len(available_slots) > 1 or \
(len(available_slots) == 1 and "0" not in available_slots)
if not slotted:
# check the vdb in case this is multislot
available_slots = set(vardb._pkg_str(cpv, None).slot \
for cpv in vardb.match(Atom(cp)))
slotted = len(available_slots) > 1 or \
(len(available_slots) == 1 and "0" not in available_slots)
if slotted and arg_atom.without_repo != cp:
# If the user gave a specific atom, store it as a
# slot atom in the world file.
slot_atom = pkg.slot_atom
# For USE=multislot, there are a couple of cases to
# handle here:
# 1) SLOT="0", but the real SLOT spontaneously changed to some
# unknown value, so just record an unslotted atom.
# 2) SLOT comes from an installed package and there is no
# matching SLOT in the ebuild repository.
# Make sure that the slot atom is available in either the
# portdb or the vardb, since otherwise the user certainly
# doesn't want the SLOT atom recorded in the world file
# (case 1 above). If it's only available in the vardb,
# the user may be trying to prevent a USE=multislot
# package from being removed by --depclean (case 2 above).
mydb = portdb
if not portdb.match(slot_atom):
# SLOT seems to come from an installed multislot package
mydb = vardb
# If there is no installed package matching the SLOT atom,
# it probably changed SLOT spontaneously due to USE=multislot,
# so just record an unslotted atom.
if vardb.match(slot_atom) or before_install:
# Now verify that the argument is precise
# enough to identify a specific slot.
matches = mydb.match(arg_atom)
matched_slots = set()
if before_install:
if mydb is vardb:
for cpv in matches:
matched_slots.add(mydb._pkg_str(cpv, None).slot)
for cpv in matches:
for repo in repos:
portdb._pkg_str(_unicode(cpv), repo).slot)
except (KeyError, InvalidData):
if len(matched_slots) == 1:
new_world_atom = slot_atom
if arg_atom.repo:
new_world_atom += _repo_separator + arg_atom.repo
if new_world_atom == sets["selected"].findAtomForPackage(pkg):
# Both atoms would be identical, so there's nothing to add.
return None
if not slotted and not arg_atom.repo:
# Unlike world atoms, system atoms are not greedy for slots, so they
# can't be safely excluded from world if they are slotted.
system_atom = sets["system"].findAtomForPackage(pkg)
if system_atom:
if not system_atom.cp.startswith("virtual/"):
return None
# System virtuals aren't safe to exclude from world since they can
# match multiple old-style virtuals but only one of them will be
# pulled in by update or depclean.
providers = portdb.settings.getvirtuals().get(system_atom.cp)
if providers and len(providers) == 1 and \
providers[0].cp == arg_atom.cp:
return None
return new_world_atom