blob: 53358c8295e987715f1a22f0d475d729b9d2d361 [file] [log] [blame]
# Copyright 1998-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id$
__all__ = ["dbapi"]
import re
import portage
portage.proxy.lazyimport.lazyimport(globals(),
'portage.dep:match_from_list',
'portage.locks:unlockfile',
'portage.output:colorize',
'portage.util:cmp_sort_key,writemsg',
'portage.versions:catsplit,catpkgsplit,vercmp',
)
from portage import os
from portage import auxdbkeys, dep_expand
from portage.localization import _
class dbapi(object):
_category_re = re.compile(r'^\w[-.+\w]*$')
_pkg_dir_name_re = re.compile(r'^\w[-+\w]*$')
_categories = None
_iuse_implicit_re = None
_use_mutable = False
_known_keys = frozenset(x for x in auxdbkeys
if not x.startswith("UNUSED_0"))
def __init__(self):
pass
@property
def categories(self):
"""
Use self.cp_all() to generate a category list. Mutable instances
can delete the self._categories attribute in cases when the cached
categories become invalid and need to be regenerated.
"""
if self._categories is not None:
return self._categories
self._categories = tuple(sorted(set(catsplit(x)[0] \
for x in self.cp_all())))
return self._categories
def close_caches(self):
pass
def cp_list(self, cp, use_cache=1):
raise NotImplementedError(self)
def _cpv_sort_ascending(self, cpv_list):
"""
Use this to sort self.cp_list() results in ascending
order. It sorts in place and returns None.
"""
if len(cpv_list) > 1:
# If the cpv includes explicit -r0, it has to be preserved
# for consistency in findname and aux_get calls, so use a
# dict to map strings back to their original values.
ver_map = {}
for cpv in cpv_list:
ver_map[cpv] = '-'.join(catpkgsplit(cpv)[2:])
def cmp_cpv(cpv1, cpv2):
return vercmp(ver_map[cpv1], ver_map[cpv2])
cpv_list.sort(key=cmp_sort_key(cmp_cpv))
def cpv_all(self):
"""Return all CPVs in the db
Args:
None
Returns:
A list of Strings, 1 per CPV
This function relies on a subclass implementing cp_all, this is why the hasattr is there
"""
if not hasattr(self, "cp_all"):
raise NotImplementedError
cpv_list = []
for cp in self.cp_all():
cpv_list.extend(self.cp_list(cp))
return cpv_list
def cp_all(self):
""" Implement this in a child class
Args
None
Returns:
A list of strings 1 per CP in the datastore
"""
return NotImplementedError
def aux_get(self, mycpv, mylist):
"""Return the metadata keys in mylist for mycpv
Args:
mycpv - "sys-apps/foo-1.0"
mylist - ["SLOT","DEPEND","HOMEPAGE"]
Returns:
a list of results, in order of keys in mylist, such as:
["0",">=sys-libs/bar-1.0","http://www.foo.com"] or [] if mycpv not found'
"""
raise NotImplementedError
def aux_update(self, cpv, metadata_updates):
"""
Args:
cpv - "sys-apps/foo-1.0"
metadata_updates = { key : newvalue }
Returns:
None
"""
raise NotImplementedError
def match(self, origdep, use_cache=1):
"""Given a dependency, try to find packages that match
Args:
origdep - Depend atom
use_cache - Boolean indicating if we should use the cache or not
NOTE: Do we ever not want the cache?
Returns:
a list of packages that match origdep
"""
mydep = dep_expand(origdep, mydb=self, settings=self.settings)
return list(self._iter_match(mydep,
self.cp_list(mydep.cp, use_cache=use_cache)))
def _iter_match(self, atom, cpv_iter):
cpv_iter = iter(match_from_list(atom, cpv_iter))
if atom.slot:
cpv_iter = self._iter_match_slot(atom, cpv_iter)
if atom.use:
cpv_iter = self._iter_match_use(atom, cpv_iter)
return cpv_iter
def _iter_match_slot(self, atom, cpv_iter):
for cpv in cpv_iter:
try:
if self.aux_get(cpv, ["SLOT"])[0] == atom.slot:
yield cpv
except KeyError:
continue
def _iter_match_use(self, atom, cpv_iter):
"""
1) Check for required IUSE intersection (need implicit IUSE here).
2) Check enabled/disabled flag states.
"""
if self._iuse_implicit_re is None:
self._iuse_implicit_re = re.compile("^(%s)$" % \
"|".join(self.settings._get_implicit_iuse()))
iuse_implicit_re = self._iuse_implicit_re
for cpv in cpv_iter:
try:
iuse, slot, use = self.aux_get(cpv, ["IUSE", "SLOT", "USE"])
except KeyError:
continue
use = use.split()
iuse = frozenset(x.lstrip('+-') for x in iuse.split())
missing_iuse = False
for x in atom.use.required:
if x not in iuse and iuse_implicit_re.match(x) is None:
missing_iuse = True
break
if missing_iuse:
continue
if not self._use_mutable:
if atom.use.enabled.difference(use):
continue
if atom.use.disabled.intersection(use):
continue
else:
# Check masked and forced flags for repoman.
mysettings = getattr(self, "mysettings", None)
if mysettings is not None and not mysettings.local_config:
pkg = "%s:%s" % (cpv, slot)
usemask = mysettings._getUseMask(pkg)
if usemask.intersection(atom.use.enabled):
continue
useforce = mysettings._getUseForce(pkg).difference(usemask)
if useforce.intersection(atom.use.disabled):
continue
yield cpv
def invalidentry(self, mypath):
if mypath.endswith('portage_lockfile'):
if "PORTAGE_MASTER_PID" not in os.environ:
writemsg(_("Lockfile removed: %s\n") % mypath, 1)
unlockfile((mypath, None, None))
else:
# Nothing we can do about it. We're probably sandboxed.
pass
elif '/-MERGING-' in mypath:
if os.path.exists(mypath):
writemsg(colorize("BAD", _("INCOMPLETE MERGE:"))+" %s\n" % mypath,
noiselevel=-1)
else:
writemsg("!!! Invalid db entry: %s\n" % mypath, noiselevel=-1)
def update_ents(self, updates, onProgress=None, onUpdate=None):
"""
Update metadata of all packages for package moves.
@param updates: A list of move commands
@type updates: List
@param onProgress: A progress callback function
@type onProgress: a callable that takes 2 integer arguments: maxval and curval
@param onUpdate: A progress callback function called only
for packages that are modified by updates.
@type onUpdate: a callable that takes 2 integer arguments:
maxval and curval
"""
cpv_all = self.cpv_all()
cpv_all.sort()
maxval = len(cpv_all)
aux_get = self.aux_get
aux_update = self.aux_update
update_keys = ["DEPEND", "RDEPEND", "PDEPEND", "PROVIDE"]
from portage.update import update_dbentries
if onUpdate:
onUpdate(maxval, 0)
if onProgress:
onProgress(maxval, 0)
for i, cpv in enumerate(cpv_all):
metadata = dict(zip(update_keys, aux_get(cpv, update_keys)))
metadata_updates = update_dbentries(updates, metadata)
if metadata_updates:
aux_update(cpv, metadata_updates)
if onUpdate:
onUpdate(maxval, i+1)
if onProgress:
onProgress(maxval, i+1)
def move_slot_ent(self, mylist):
"""This function takes a sequence:
Args:
mylist: a sequence of (package, originalslot, newslot)
Returns:
The number of slotmoves this function did
"""
pkg = mylist[1]
origslot = mylist[2]
newslot = mylist[3]
origmatches = self.match(pkg)
moves = 0
if not origmatches:
return moves
for mycpv in origmatches:
slot = self.aux_get(mycpv, ["SLOT"])[0]
if slot != origslot:
continue
moves += 1
mydata = {"SLOT": newslot+"\n"}
self.aux_update(mycpv, mydata)
return moves