commit:     3553e8bc274ae5b736a47ed8f1acc4bf472a7af4
Author:     Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Thu Jul 24 22:20:16 2014 +0000
Commit:     Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Thu Aug 28 08:59:03 2014 +0000
URL:        
http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=3553e8bc

sets: Introduce @changed-deps to update packages which need dep changes

The @changed-deps set compares the RDEPEND and PDEPEND entries of
installed packages with the metadata of matching ebuilds, triggering
rebuilds whenever a difference in dependencies is detected.

This set can be used to force installed package metadata updates when
ebuild dependencies are changed in-place. In particular, it can be used
to restore dependency tree integrity after disabling --dynamic-deps.

Acked-by: Alexander Berntsen <bernalex <AT> gentoo.org>
Acked-by: Brian Dolbec <dolsen <AT> gentoo.org>

---
 cnf/sets/portage.conf      |  5 +++
 pym/portage/_sets/dbapi.py | 81 ++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/cnf/sets/portage.conf b/cnf/sets/portage.conf
index b73afb1..fd2c387 100644
--- a/cnf/sets/portage.conf
+++ b/cnf/sets/portage.conf
@@ -84,3 +84,8 @@ class = portage.sets.dbapi.UnavailableSet
 # are not available.
 [unavailable-binaries]
 class = portage.sets.dbapi.UnavailableBinaries
+
+# Installed packages for which vdb *DEPEND entries are outdated compared
+# to the matching portdb entry.
+[changed-deps]
+class = portage.sets.dbapi.ChangedDepsSet

diff --git a/pym/portage/_sets/dbapi.py b/pym/portage/_sets/dbapi.py
index 817bcd7..299cb81 100644
--- a/pym/portage/_sets/dbapi.py
+++ b/pym/portage/_sets/dbapi.py
@@ -3,17 +3,19 @@
 
 from __future__ import division
 
+import re
 import time
 
 from portage import os
 from portage.versions import best, catsplit, vercmp
-from portage.dep import Atom
+from portage.dep import Atom, use_reduce
+from portage.exception import InvalidAtom
 from portage.localization import _
 from portage._sets.base import PackageSet
 from portage._sets import SetConfigError, get_boolean
 import portage
 
-__all__ = ["CategorySet", "DowngradeSet",
+__all__ = ["CategorySet", "ChangedDepsSet", "DowngradeSet",
        "EverythingSet", "OwnerSet", "VariableSet"]
 
 class EverythingSet(PackageSet):
@@ -458,3 +460,78 @@ class RebuiltBinaries(EverythingSet):
                        bindb=trees["bintree"].dbapi)
 
        singleBuilder = classmethod(singleBuilder)
+
+class ChangedDepsSet(PackageSet):
+
+       _operations = ["merge", "unmerge"]
+
+       description = "Package set which contains all installed " + \
+               "packages for which the vdb *DEPEND entries are outdated " + \
+               "compared to corresponding portdb entries."
+
+       def __init__(self, portdb=None, vardb=None):
+               super(ChangedDepsSet, self).__init__()
+               self._portdb = portdb
+               self._vardb = vardb
+
+       def load(self):
+               depvars = ('RDEPEND', 'PDEPEND')
+
+               # regexp used to match atoms using subslot operator :=
+               subslot_repl_re = re.compile(r':[^[]*=')
+
+               atoms = []
+               for cpv in self._vardb.cpv_all():
+                       # no ebuild, no update :).
+                       if not self._portdb.cpv_exists(cpv):
+                               continue
+
+                       # USE flags used to build the ebuild and EAPI
+                       # (needed for Atom & use_reduce())
+                       use, eapi = self._vardb.aux_get(cpv, ('USE', 'EAPI'))
+                       usel = use.split()
+
+                       # function used to recursively process atoms in nested 
lists.
+                       def clean_subslots(depatom, usel=None):
+                               if isinstance(depatom, list):
+                                       # process the nested list.
+                                       return [clean_subslots(x, usel) for x 
in depatom]
+                               else:
+                                       try:
+                                               # this can be either an atom or 
some special operator.
+                                               # in the latter case, we get 
InvalidAtom and pass it as-is.
+                                               a = Atom(depatom)
+                                       except InvalidAtom:
+                                               return depatom
+                                       else:
+                                               # if we're processing portdb, 
we need to evaluate USE flag
+                                               # dependency conditionals to 
make them match vdb. this
+                                               # requires passing the list of 
USE flags, so we reuse it
+                                               # as conditional for the 
operation as well.
+                                               if usel is not None:
+                                                       a = 
a.evaluate_conditionals(usel)
+
+                                               # replace slot operator := 
dependencies with plain :=
+                                               # since we can't properly 
compare expanded slots
+                                               # in vardb to abstract slots in 
portdb.
+                                               return 
subslot_repl_re.sub(':=', a)
+
+                       # get all *DEPEND variables from vdb & portdb and 
compare them.
+                       # we need to do some cleaning up & expansion to make 
matching
+                       # meaningful since vdb dependencies are 
conditional-free.
+                       vdbvars = [clean_subslots(use_reduce(x, uselist=usel, 
eapi=eapi))
+                                       for x in self._vardb.aux_get(cpv, 
depvars)]
+                       pdbvars = [clean_subslots(use_reduce(x, uselist=usel, 
eapi=eapi), usel)
+                                       for x in self._portdb.aux_get(cpv, 
depvars)]
+
+                       # if dependencies don't match, trigger the rebuild.
+                       if vdbvars != pdbvars:
+                               atoms.append('=%s' % cpv)
+
+               self._setAtoms(atoms)
+
+       def singleBuilder(cls, options, settings, trees):
+               return cls(portdb=trees["porttree"].dbapi,
+                       vardb=trees["vartree"].dbapi)
+
+       singleBuilder = classmethod(singleBuilder)

Reply via email to