Merged from trunk 7269:7283
authorFabian Groffen <grobian@gentoo.org>
Mon, 16 Jul 2007 17:34:24 +0000 (17:34 -0000)
committerFabian Groffen <grobian@gentoo.org>
Mon, 16 Jul 2007 17:34:24 +0000 (17:34 -0000)
   +------------------------------------------------------------------------+
   | 7270   | sync MAILSUBJECT in make.conf with make.globals               |
   | genone |                                                               |
   |--------+---------------------------------------------------------------|
   | 7271   | s/node/atom/ and other minor updates                          |
   | genone |                                                               |
   |--------+---------------------------------------------------------------|
   | 7272   | Make PackageSet to behave more like a real set, and change    |
   | genone | loading logic a bit to avoid unnecessary reloads for editable |
   |        | sets                                                          |
   |--------+---------------------------------------------------------------|
   | 7273   | use a more logical approach for editable sets                 |
   | genone |                                                               |
   |--------+---------------------------------------------------------------|
   | 7274   | fix load logic for editable sets once more                    |
   | genone |                                                               |
   |--------+---------------------------------------------------------------|
   | 7275   | ignore already applied glsas when loading the security set    |
   | genone |                                                               |
   |--------+---------------------------------------------------------------|
   | 7276   | add support for metadata                                      |
   | genone |                                                               |
   |--------+---------------------------------------------------------------|
   | 7277   | fix metadata parsing for file-based sets, add support for     |
   | genone | user-defined file sets in test code                           |
   |--------+---------------------------------------------------------------|
   | 7278   | disable FEATURES=gpg as it's unmaintained, useless and likely |
   | genone | broken for many people                                        |
   |--------+---------------------------------------------------------------|
   | 7279   | replace emerge.AtomSet with portage.sets.InternalPackageSet   |
   | genone |                                                               |
   |--------+---------------------------------------------------------------|
   | 7280   | break recursion cycle                                         |
   | genone |                                                               |
   |--------+---------------------------------------------------------------|
   | 7281   | Replace emerge.SystemSet with                                 |
   | genone | portage.sets.profiles.PackagesSystemSet                       |
   |--------+---------------------------------------------------------------|
   | 7282   | break another recursion cycle, add special subclass for       |
   | genone | "world" to handle locking                                     |
   |--------+---------------------------------------------------------------|
   | 7283   | Replace emerge.WorldSet with portage.sets.files.WorldSet      |
   | genone |                                                               |
   +------------------------------------------------------------------------+

svn path=/main/branches/prefix/; revision=7284

12 files changed:
bin/prepstrip
cnf/make.conf
pym/emerge/__init__.py
pym/portage/__init__.py
pym/portage/elog/mod_mail.py
pym/portage/glsa.py
pym/portage/sets/__init__.py
pym/portage/sets/dbapi.py
pym/portage/sets/files.py
pym/portage/sets/profiles.py
pym/portage/sets/security.py
pym/portage/sets/shell.py

index ca184f064ec7c4fb99937ea235fc518d726e1e2b..8fce32b1c4cfbfd081adcc305acf97a8ac4e2102 100755 (executable)
@@ -45,7 +45,7 @@ save_elf_debug() {
        hasq splitdebug ${FEATURES} || return 0
 
        local x=$1
-       local y="${ED}usr/lib/debug/${x:${#ED}}.debug"
+       local y="${ED}usr/lib/debug/${x:${#D}}.debug"
 
        # dont save debug info twice
        [[ ${x} == *".debug" ]] && return 0
@@ -116,7 +116,7 @@ do
                        ${STRIP} ${PORTAGE_STRIP_FLAGS} "${x}"
                fi
        elif [[ ${f} == *"SB relocatable"* ]] ; then
-               vecho "   ${x:${#D}}"
+               vecho "   ${x:${#ED}}"
                save_elf_sources "${x}"
                if ${strip_this} ; then
                        [[ ${x} == *.ko ]] && save_elf_debug "${x}"
index d51f09f3b49739ca42f5f27dcf363cd7327647d1..73a4419285cadde8269774f8f8155f6e8dd21a9a 100644 (file)
@@ -382,4 +382,4 @@ PORTAGE_ELOG_CLASSES="warn error log"
 #                           variables are expanded:
 #                               ${PACKAGE} - see description of PORTAGE_ELOG_COMMAND
 #                               ${HOST} - FQDN of the host portage is running on
-#PORTAGE_ELOG_MAILSUBJECT="package \${PACKAGE} merged on \${HOST} with notice"
+#PORTAGE_ELOG_MAILSUBJECT="[portage] ebuild log for \${PACKAGE} on \${HOST}"
index b64dbd66e30e13f0273fad33fc5ac20c7666c5ae..2017786d6336a4bffa885b5bf3dc8edc4eda2900 100644 (file)
@@ -53,6 +53,9 @@ from portage.const import EPREFIX
 from portage.data import secpass
 from portage.util import normalize_path as normpath
 from portage.util import writemsg
+from portage.sets import InternalPackageSet
+from portage.sets.profiles import PackagesSystemSet as SystemSet
+from portage.sets.files import WorldSet
 
 from itertools import chain, izip
 from UserDict import DictMixin
@@ -565,9 +568,8 @@ def getlist(settings, mode):
 
 def clean_world(vardb, cpv):
        """Remove a package from the world file when unmerged."""
-       world_set = WorldSet(vardb.settings)
+       world_set = WorldSet("world", vardb.settings["ROOT"])
        world_set.lock()
-       world_set.load()
        worldlist = list(world_set)
        mykey = portage.cpv_getkey(cpv)
        newworldlist = []
@@ -587,91 +589,9 @@ def clean_world(vardb, cpv):
                        #this doesn't match the package we're unmerging; keep it.
                        newworldlist.append(x)
 
-       world_set.clear()
-       world_set.update(newworldlist)
-       world_set.save()
+       world_set.replace(newworldlist)
        world_set.unlock()
 
-class AtomSet(object):
-       def __init__(self, atoms=None):
-               self._atoms = {}
-               if atoms:
-                       self.update(atoms)
-       def clear(self):
-               self._atoms.clear()
-       def add(self, atom):
-               cp = portage.dep_getkey(atom)
-               cp_list = self._atoms.get(cp)
-               if cp_list is None:
-                       cp_list = []
-                       self._atoms[cp] = cp_list
-               if atom not in cp_list:
-                       cp_list.append(atom)
-       def update(self, atoms):
-               for atom in atoms:
-                       self.add(atom)
-       def __contains__(self, atom):
-               cp = portage.dep_getkey(atom)
-               if cp in self._atoms and atom in self._atoms[cp]:
-                       return True
-               return False
-       def findAtomForPackage(self, cpv, metadata):
-               """Return the best match for a given package from the arguments, or
-               None if there are no matches.  This matches virtual arguments against
-               the PROVIDE metadata.  This can raise an InvalidDependString exception
-               if an error occurs while parsing PROVIDE."""
-               cpv_slot = "%s:%s" % (cpv, metadata["SLOT"])
-               cp = portage.dep_getkey(cpv)
-               atoms = self._atoms.get(cp)
-               if atoms:
-                       best_match = portage.best_match_to_list(cpv_slot, atoms)
-                       if best_match:
-                               return best_match
-               if not metadata["PROVIDE"]:
-                       return None
-               provides = portage.flatten(portage.dep.use_reduce(
-                       portage.dep.paren_reduce(metadata["PROVIDE"]),
-                       uselist=metadata["USE"].split()))
-               for provide in provides:
-                       provided_cp = portage.dep_getkey(provide)
-                       atoms = self._atoms.get(provided_cp)
-                       if atoms:
-                               transformed_atoms = [atom.replace(provided_cp, cp) for atom in atoms]
-                               best_match = portage.best_match_to_list(cpv_slot, transformed_atoms)
-                               if best_match:
-                                       return atoms[transformed_atoms.index(best_match)]
-               return None
-       def __iter__(self):
-               for atoms in self._atoms.itervalues():
-                       for atom in atoms:
-                               yield atom
-
-class SystemSet(AtomSet):
-       def __init__(self, settings, **kwargs):
-               AtomSet.__init__(self, **kwargs)
-               self.update(getlist(settings, "system"))
-
-class WorldSet(AtomSet):
-       def __init__(self, settings, **kwargs):
-               AtomSet.__init__(self, **kwargs)
-               self.world_file = os.path.join(settings["ROOT"], portage.WORLD_FILE)
-               self._lock = None
-       def _ensure_dirs(self):
-               portage.util.ensure_dirs(os.path.dirname(self.world_file),
-                       gid=portage.portage_gid, mode=02750, mask=02)
-       def load(self):
-               self.clear()
-               self.update(portage.util.grabfile_package(self.world_file))
-       def save(self):
-               self._ensure_dirs()
-               portage.write_atomic(self.world_file,
-                       "\n".join(sorted(self)) + "\n")
-       def lock(self):
-               self._ensure_dirs()
-               self._lock = portage.locks.lockfile(self.world_file, wantnewlockfile=1)
-       def unlock(self):
-               portage.locks.unlockfile(self._lock)
-               self._lock = None
 
 class RootConfig(object):
        """This is used internally by depgraph to track information about a
@@ -681,10 +601,9 @@ class RootConfig(object):
                self.settings = trees["vartree"].settings
                self.root = self.settings["ROOT"]
                self.sets = {}
-               world_set = WorldSet(self.settings)
-               world_set.load()
+               world_set = WorldSet("world", self.root)
                self.sets["world"] = world_set
-               system_set = SystemSet(self.settings)
+               system_set = SystemSet("system", self.settings.profiles)
                self.sets["system"] = system_set
 
 def create_world_atom(pkg_key, metadata, args_set, root_config):
@@ -1145,10 +1064,10 @@ class depgraph(object):
                # contains all sets added to the graph
                self._sets = {}
                # contains atoms given as arguments
-               self._sets["args"] = AtomSet()
+               self._sets["args"] = InternalPackageSet()
                # contains all atoms from all sets added to the graph, including
                # atoms given as arguments
-               self._set_atoms = AtomSet()
+               self._set_atoms = InternalPackageSet()
                # contains all nodes pulled in by self._set_atoms
                self._set_nodes = set()
                self.blocker_digraph = digraph()
@@ -2589,8 +2508,7 @@ class depgraph(object):
                if verbosity is None:
                        verbosity = ("--quiet" in self.myopts and 1 or \
                                "--verbose" in self.myopts and 3 or 2)
-               favorites_set = AtomSet()
-               favorites_set.update(favorites)
+               favorites_set = InternalPackageSet(favorites)
                changelogs=[]
                p=[]
                blockers = []
@@ -3070,10 +2988,10 @@ class depgraph(object):
                                pkg_system = False
                                pkg_world = False
                                try:
-                                       pkg_system = system_set.findAtomForPackage(pkg_key, metadata)
-                                       pkg_world  = world_set.findAtomForPackage(pkg_key, metadata)
+                                       pkg_system = system_set.containsCPV(pkg_key)
+                                       pkg_world  = world_set.containsCPV(pkg_key)
                                        if not pkg_world and myroot == self.target_root and \
-                                               favorites_set.findAtomForPackage(pkg_key, metadata):
+                                               favorites_set.containsCPV(pkg_key):
                                                # Maybe it will be added to world now.
                                                if create_world_atom(pkg_key, metadata,
                                                        favorites_set, root_config):
@@ -3279,7 +3197,6 @@ class depgraph(object):
                root_config = self.roots[self.target_root]
                world_set = root_config.sets["world"]
                world_set.lock()
-               world_set.load()
                args_set = self._sets["args"]
                portdb = self.trees[self.target_root]["porttree"].dbapi
                added_favorites = set()
@@ -3304,8 +3221,6 @@ class depgraph(object):
                                writemsg("!!! see '%s'\n\n" % os.path.join(
                                        root, portage.VDB_PATH, pkg_key, "PROVIDE"), noiselevel=-1)
                                del e
-               if added_favorites:
-                       world_set.save()
                world_set.unlock()
 
        def loadResumeCommand(self, resume_data):
@@ -3538,7 +3453,7 @@ class MergeTask(object):
 
                root_config = RootConfig(self.trees[self.target_root])
                system_set = root_config.sets["system"]
-               args_set = AtomSet(favorites)
+               args_set = InternalPackageSet(favorites)
                world_set = root_config.sets["world"]
                if "--resume" not in self.myopts:
                        mymergelist = mylist
@@ -3635,7 +3550,7 @@ class MergeTask(object):
                        #buildsyspkg: Check if we need to _force_ binary package creation
                        issyspkg = ("buildsyspkg" in myfeat) \
                                        and x[0] != "blocks" \
-                                       and system_set.findAtomForPackage(pkg_key, metadata) \
+                                       and system_set.containsCPV(pkg_key) \
                                        and "--buildpkg" not in self.myopts
                        if x[0] in ["ebuild","blocks"]:
                                if x[0] == "blocks" and "--fetchonly" not in self.myopts:
@@ -3827,19 +3742,17 @@ class MergeTask(object):
                                self.trees[x[1]]["vartree"].inject(x[2])
                                myfavkey = portage.cpv_getkey(x[2])
                                if not fetchonly and not pretend and \
-                                       args_set.findAtomForPackage(pkg_key, metadata):
+                                       args_set.containsCPV(pkg_key):
                                        world_set.lock()
-                                       world_set.load()
                                        myfavkey = create_world_atom(pkg_key, metadata,
                                                args_set, root_config)
                                        if myfavkey:
-                                               world_set.add(myfavkey)
                                                print ">>> Recording",myfavkey,"in \"world\" favorites file..."
                                                emergelog(xterm_titles, " === ("+\
                                                        str(mergecount)+" of "+\
                                                        str(len(mymergelist))+\
                                                        ") Updating world file ("+x[pkgindex]+")")
-                                               world_set.save()
+                                               world_set.add(myfavkey)
                                        world_set.unlock()
 
                                if "--pretend" not in self.myopts and \
@@ -5398,10 +5311,9 @@ def action_depclean(settings, trees, ldpath_mtimes,
        vardb = dep_check_trees[myroot]["vartree"].dbapi
        # Constrain dependency selection to the installed packages.
        dep_check_trees[myroot]["porttree"] = dep_check_trees[myroot]["vartree"]
-       system_set = SystemSet(settings)
+       system_set = SystemSet("system", settings.profiles)
        syslist = list(system_set)
-       world_set = WorldSet(settings)
-       world_set.load()
+       world_set = WorldSet("world", myroot)
        worldlist = list(world_set)
        fakedb = portage.fakedbapi(settings=settings)
        myvarlist = vardb.cpv_all()
index 34d723b11619a31d09514152289a102293258d8d..be46009d4e5bdb0e8ec8a74beae40578700cfb18 100644 (file)
@@ -1483,6 +1483,9 @@ class config(object):
                                        self.features.remove("usersandbox")
 
                        self.features.sort()
+                       if "gpg" in self.features:
+                               writemsg(colorize("WARN", "!!! FEATURES=gpg is unmaintained, incomplete and broken. Disabling it."), noiselevel=-1)
+                               self.features.remove("gpg")
                        self["FEATURES"] = " ".join(self.features)
                        self.backup_changes("FEATURES")
 
index d0d2b33ac32edc4377f67ed57894ff6ae35a2dbd..c9fa5b5639b1bb874db7c4217ae1ba8ac6b0a09d 100644 (file)
@@ -16,6 +16,21 @@ def process(mysettings, key, logentries, fulltext):
        mysubject = mysubject.replace("${PACKAGE}", key)
        mysubject = mysubject.replace("${HOST}", socket.getfqdn())
 
+       # look at the phases listed in our logentries to figure out what action was performed
+       action = "merged"
+       for phase in logentries.keys():
+               # if we found a *rm phase assume that the package was unmerged
+               if phase in ["postrm", "prerm"]:
+                       action = "unmerged"
+       # if we think that the package was unmerged, make sure there was no unexpected
+       # phase recorded to avoid misinformation
+       if action == "unmerged":
+               for phase in logentries.keys():
+                       if phase not in ["postrm", "prerm", "other"]:
+                               action = "unknown"
+
+       mysubject = mysubject.replace("${ACTION}", action)
+
        mymessage = portage.mail.create_message(myfrom, myrecipient, mysubject, fulltext)
        portage.mail.send_mail(mysettings, mymessage)
 
index ee6343640e1ebba6ee88b91bd7592913b99c53a3..d8053a8204bafc0b796120a1451abd1a162adf6e 100644 (file)
@@ -556,7 +556,7 @@ class Glsa:
                @rtype:         Boolean
                @returns:       True if the GLSA was applied, False if not
                """
-               aList = grabfile(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep)))
+               aList = grabfile(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep), "glsa"))
                return (self.nr in aList)
 
        def inject(self):
@@ -569,7 +569,7 @@ class Glsa:
                @returns:       None
                """
                if not self.isApplied():
-                       checkfile = open(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep)), "a+")
+                       checkfile = open(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep), "glsa"), "a+")
                        checkfile.write(self.nr+"\n")
                        checkfile.close()
                return None
index e0be0047c975722ef4ae5a7e02bc0ab790efdfb8..91ddc357383d83358129af032d381900002b244d 100644 (file)
@@ -6,90 +6,187 @@ import os
 
 from portage.const import PRIVATE_PATH, USER_CONFIG_PATH
 from portage.exception import InvalidAtom
-from portage.dep import isvalidatom
+from portage.dep import isvalidatom, match_from_list, best_match_to_list, dep_getkey, use_reduce, paren_reduce
+from portage import flatten
 
-OPERATIONS = ["merge", "unmerge", "edit"]
+OPERATIONS = ["merge", "unmerge"]
 DEFAULT_SETS = ["world", "system", "everything", "security"] \
        +["package_"+x for x in ["mask", "unmask", "use", "keywords"]]
 
 class PackageSet(object):
+       # Set this to operations that are supported by your subclass. While 
+       # technically there is no difference between "merge" and "unmerge" regarding
+       # package sets, the latter doesn't make sense for some sets like "system"
+       # or "security" and therefore isn't supported by them.
        _operations = ["merge"]
-
+       _atommap = {}
+       description = "generic package set"
+       
        def __init__(self, name):
-               self._name = name
-               self._nodes = []
+               self.name = name
+               self._atoms = set()
+               self._atommap = {}
                self._loaded = False
+               self._loading = False
+
+       def __contains__(self, atom):
+               return atom in self.getAtoms()
+       
+       def __iter__(self):
+               for x in self.getAtoms():
+                       yield x
        
        def supportsOperation(self, op):
                if not op in OPERATIONS:
                        raise ValueError(op)
                return op in self._operations
        
-       def getNodes(self):
-               if not self._loaded:
+       def getAtoms(self):
+               if not (self._loaded or self._loading):
+                       self._loading = True
                        self.load()
                        self._loaded = True
-               return self._nodes
-
-       def _setNodes(self, nodes):
-               nodes = map(str.strip, nodes)
-               for n in nodes[:]:
-                       if n == "":
-                               nodes.remove(n)
-                       elif not isvalidatom(n):
-                               raise InvalidAtom(n)
-               self._nodes = nodes
-       
-       def getName(self):
-               return self._name
+                       self._loading = False
+               return self._atoms
+
+       def _setAtoms(self, atoms):
+               atoms = map(str.strip, atoms)
+               for a in atoms[:]:
+                       if a == "":
+                               atoms.remove(a)
+                       elif not isvalidatom(a):
+                               raise InvalidAtom(a)
+               self._atoms = atoms
+               self._updateAtomMap()
+
+       def load(self):
+               # This method must be overwritten by subclasses
+               # Editable sets should use the value of self._mtime to determine if they
+               # need to reload themselves
+               raise NotImplementedError()
+
+       def containsCPV(self, cpv):
+               for a in self.getAtoms():
+                       if match_from_list(a, [cpv]):
+                               return True
+               return False
        
-       def addNode(self, node):
-               if self.supportsOperation("edit"):
-                       self.load()
-                       self._nodes.append(node)
-                       self.write()
+       def getMetadata(self, key):
+               if hasattr(self, key.lower()):
+                       return getattr(self, key.lower())
                else:
-                       raise NotImplementedError()
+                       return ""
+       
+       def _updateAtomMap(self):
+               for a in self._atoms:
+                       cp = dep_getkey(a)
+                       self._atommap.setdefault(cp, set())
+                       self._atommap[cp].add(a)
+       
+       # Not sure if this one should really be in PackageSet
+       def findAtomForPackage(self, cpv, metadata):
+               """Return the best match for a given package from the arguments, or
+               None if there are no matches.  This matches virtual arguments against
+               the PROVIDE metadata.  This can raise an InvalidDependString exception
+               if an error occurs while parsing PROVIDE."""
+               cpv_slot = "%s:%s" % (cpv, metadata["SLOT"])
+               cp = dep_getkey(cpv)
+               atoms = self._atommap.get(cp)
+               if atoms:
+                       best_match = best_match_to_list(cpv_slot, atoms)
+                       if best_match:
+                               return best_match
+               if not metadata["PROVIDE"]:
+                       return None
+               provides = flatten(use_reduce(paren_reduce(metadata["PROVIDE"]),
+                                                               uselist=metadata["USE"].split()))
+               for provide in provides:
+                       provided_cp = dep_getkey(provide)
+                       atoms = self._atommap.get(provided_cp)
+                       if atoms:
+                               atoms = list(atoms)
+                               transformed_atoms = [atom.replace(provided_cp, cp) for atom in atoms]
+                               best_match = best_match_to_list(cpv_slot, transformed_atoms)
+                               if best_match:
+                                       return atoms[transformed_atoms.index(best_match)]
+               return None
 
-       def removeNode(self, node):
-               if self.supportsOperation("edit"):
-                       self.load()
-                       self._nodes.remove(node)
-                       self.write()
-               else:
-                       raise NotImplementedError()
+class EditablePackageSet(PackageSet):
+       def getAtoms(self):
+               self.load()
+               return self._atoms
+
+       def update(self, atoms):
+               self.load()
+               self._atoms.update(atoms)
+               self._updateAtomMap()
+               self.write()
+       
+       def add(self, atom):
+               self.update([atom])
+
+       def replace(self, atoms):
+               self._setAtoms(atoms)
+               self.write()
+
+       def remove(self, atom):
+               self.load()
+               self._atoms.discard(atom)
+               self._updateAtomMap()
+               self.write()
+
+       def removePackageAtoms(self, cp):
+               self.load()
+               for a in self.getAtoms():
+                       if dep_getkey(a) == cp:
+                               self.remove(a)
+               self.write()
 
        def write(self):
+               # This method must be overwritten in subclasses that should be editable
                raise NotImplementedError()
 
+
+class InternalPackageSet(EditablePackageSet):
+       def __init__(self, initial_atoms=None):
+               super(InternalPackageSet, self).__init__("")
+               if initial_atoms != None:
+                       self.update(initial_atoms)
+
+       def clear(self):
+               self._atoms.clear()
+               self._updateAtomMap()
+       
        def load(self):
-               raise NotImplementedError()
+               pass
 
-class EmptyPackageSet(PackageSet):
-       _operations = ["merge", "unmerge"]
+       def write(self):
+               pass
 
 def make_default_sets(configroot, root, profile_paths, settings=None, 
                vdbapi=None, portdbapi=None):
        from portage.sets.files import StaticFileSet, ConfigFileSet
        from portage.sets.profiles import PackagesSystemSet
-       from portage.sets.security import AffectedSet
+       from portage.sets.security import NewAffectedSet
        from portage.sets.dbapi import EverythingSet
        
        rValue = set()
-       rValue.add(StaticFileSet("world", os.path.join(root, PRIVATE_PATH, "world")))
+       worldset = StaticFileSet("world", os.path.join(root, PRIVATE_PATH, "world"))
+       worldset.description = "Set of packages that were directly installed"
+       rValue.add(worldset)
        for suffix in ["mask", "unmask", "keywords", "use"]:
                myname = "package_"+suffix
                myset = ConfigFileSet(myname, os.path.join(configroot, USER_CONFIG_PATH.lstrip(os.sep), "package."+suffix))
                rValue.add(myset)
        rValue.add(PackagesSystemSet("system", profile_paths))
        if settings != None and portdbapi != None:
-               rValue.add(AffectedSet("security", settings, vdbapi, portdbapi))
+               rValue.add(NewAffectedSet("security", settings, vdbapi, portdbapi))
        else:
-               rValue.add(EmptyPackageSet("security"))
+               rValue.add(InternalPackageSet("security"))
        if vdbapi != None:
                rValue.add(EverythingSet("everything", vdbapi))
        else:
-               rValue.add(EmptyPackageSet("everything"))
+               rValue.add(InternalPackageSet("everything"))
 
        return rValue
 
@@ -108,13 +205,34 @@ def make_extra_static_sets(configroot):
                rValue.add(StaticFileSet(fname, os.path.join(mydir, myname)))
        return rValue
 
+def make_category_sets(portdbapi, settings, only_visible=True):
+       from portage.sets.dbapi import CategorySet
+       rValue = set()
+       for c in settings.categories:
+               rValue.add(CategorySet("category_%s" % c, c, portdbapi, only_visible=only_visible))
+       return rValue
+
 # adhoc test code
 if __name__ == "__main__":
-       import portage
+       import portage, sys, os
+       from portage.sets.dbapi import CategorySet
+       from portage.sets.files import StaticFileSet
        l = make_default_sets("/", "/", portage.settings.profiles, portage.settings, portage.db["/"]["vartree"].dbapi, portage.db["/"]["porttree"].dbapi)
        l.update(make_extra_static_sets("/"))
+       if len(sys.argv) > 1:
+               for s in sys.argv[1:]:
+                       if s.startswith("category_"):
+                               c = s[9:]
+                               l.add(CategorySet("category_%s" % c, c, portdbapi, only_visible=only_visible))
+                       elif os.path.exists(s):
+                               l.add(StaticFileSet(os.path.basename(s), s))
+                       elif s != "*":
+                               print "ERROR: could not create set '%s'" % s
+               if not "*" in sys.argv:
+                       l = [s for s in l if s.name in sys.argv[1:]]
        for x in l:
-               print x.getName()+":"
-               for n in sorted(x.getNodes()):
+               print x.name+":"
+               print "DESCRIPTION = %s" % x.getMetadata("Description")
+               for n in sorted(x.getAtoms()):
                        print "- "+n
                print
index dcc0552ef893a2da8cd2f0289d0988d082e5104f..1b2520ef6049146fa1defbc9f097978c8fa06eae 100644 (file)
@@ -8,21 +8,22 @@ from portage.sets import PackageSet
 
 class EverythingSet(PackageSet):
        _operations = ["merge", "unmerge"]
-
+       description = "Package set containing all installed packages"
+       
        def __init__(self, name, vdbapi):
                super(EverythingSet, self).__init__(name)
                self._db = vdbapi
        
        def load(self):
-               mynodes = []
+               myatoms = []
                for cp in self._db.cp_all():
                        if len(self._db.cp_list(cp)) > 1:
                                for cpv in self._db.cp_list(cp):
                                        myslot = self._db.aux_get(cpv, ["SLOT"])[0]
-                                       mynodes.append(cp+":"+myslot)
+                                       myatoms.append(cp+":"+myslot)
                        else:
-                               mynodes.append(cp)
-               self._setNodes(mynodes)
+                               myatoms.append(cp)
+               self._setAtoms(myatoms)
 
 class CategorySet(PackageSet):
        _operations = ["merge", "unmerge"]
@@ -32,12 +33,17 @@ class CategorySet(PackageSet):
                self._db = portdbapi
                self._category = category
                self._check = only_visible
+               if only_visible:
+                       s="visible"
+               else:
+                       s="all"
+               self.description = "Package set containing %s packages of category %s" % (s, self._category)
                        
        def load(self):
-               mynodes = []
+               myatoms = []
                for cp in self._db.cp_all():
                        if catsplit(cp)[0] == self._category:
                                if (not self._check) or len(self._db.match(cp)) > 0:
-                                       mynodes.append(cp)
-               self._setNodes(mynodes)
+                                       myatoms.append(cp)
+               self._setAtoms(myatoms)
        
index 8e1bca99cacf8783d3dd5785159b8792ddaaf44e..7fa5fb24d6d79db3c470f11811e11de2d16e3919 100644 (file)
@@ -2,29 +2,75 @@
 # Distributed under the terms of the GNU General Public License v2
 # $Id$
 
-from portage.util import grabfile_package, grabdict_package, write_atomic
+from portage.util import grabfile, grabfile_package, grabdict_package, write_atomic, ensure_dirs
+from portage.const import PRIVATE_PATH
+from portage.locks import lockfile, unlockfile
+from portage import portage_gid
+import os
 
-from portage.sets import PackageSet
+from portage.sets import PackageSet, EditablePackageSet
 
-class StaticFileSet(PackageSet):
-       _operations = ["merge", "unmerge", "edit"]
+class StaticFileSet(EditablePackageSet):
+       _operations = ["merge", "unmerge"]
        
        def __init__(self, name, filename):
                super(StaticFileSet, self).__init__(name)
                self._filename = filename
+               self._mtime = None
+               self.description = "Package set loaded from file %s" % self._filename
+               metadata = grabfile(self._filename + ".metadata")
+               key = None
+               for line in metadata:
+                       line = line.strip()
+                       if len(line) == 0 and key != None:
+                               setattr(self, key, " ".join(value))
+                               key = None
+                       elif line[-1] == ":" and key == None:
+                               key = line[:-1].lower()
+                               value = []
+                       elif key != None:
+                               value.append(line)
+                       else:
+                               pass
+               else:
+                       if key != None:
+                               setattr(self, key, " ".join(value))
        
        def write(self):
-               write_atomic(self._filename, "\n".join(self._nodes)+"\n")
+               write_atomic(self._filename, "\n".join(self._atoms)+"\n")
        
        def load(self):
-               self._setNodes(grabfile_package(self._filename, recursive=True))
+               try:
+                       mtime = os.stat(self._filename).st_mtime
+               except (OSError, IOError):
+                       mtime = None
+               if (not self._loaded or self._mtime != mtime):
+                       self._setAtoms(grabfile_package(self._filename, recursive=True))
+                       self._mtime = mtime
        
-class ConfigFileSet(StaticFileSet):
-       _operations = ["merge", "unmerge"]
+class ConfigFileSet(PackageSet):
+       def __init__(self, name, filename):
+               super(ConfigFileSet, self).__init__(name)
+               self._filename = filename
+               self.description = "Package set generated from %s" % self._filename
 
-       def write(self):
-               raise NotImplementedError()
-       
        def load(self):
-               self._setNodes(grabdict_package(self._filename, recursive=True).keys())
+               self._setAtoms(grabdict_package(self._filename, recursive=True).keys())
+
+class WorldSet(StaticFileSet):
+       description = "Set of packages that were directly installed by the user"
+       
+       def __init__(self, name, root):
+               super(WorldSet, self).__init__(name, os.path.join(os.sep, root, PRIVATE_PATH, "world"))
+               self._lock = None
+
+       def _ensure_dirs(self):
+               ensure_dirs(os.path.dirname(self._filename), gid=portage_gid, mode=02750, mask=02)
+
+       def lock(self):
+               self._ensure_dirs()
+               self._lock = lockfile(self._filename, wantnewlockfile=1)
 
+       def unlock(self):
+               unlockfile(self._lock)
+               self._lock = None
index fe7ab201a89a00912067be08c133bb9f2ba8ccb9..55b7942f594f2a9b747c0be53a4eab8dc6b39af2 100644 (file)
@@ -13,8 +13,9 @@ class PackagesSystemSet(PackageSet):
        def __init__(self, name, profile_paths):
                super(PackagesSystemSet, self).__init__(name)
                self._profile_paths = profile_paths
+               self.description = "System packages for profile %s" % self._profile_paths[-1]
        
        def load(self):
                mylist = [grabfile_package(os.path.join(x, "packages")) for x in self._profile_paths]
                mylist = stack_lists(mylist, incremental=1)
-               self._setNodes([x[1:] for x in mylist if x[0] == "*"])
+               self._setAtoms([x[1:] for x in mylist if x[0] == "*"])
index d7db89d9584f688a6b561589bf5d102c0cc19473..3bc9cbca4862e5a489052a1b639b3f224c0ad2dc 100644 (file)
@@ -3,41 +3,66 @@
 # $Id$
 
 import portage.glsa as glsa
+from portage.util import grabfile
+from portage.const import CACHE_PATH
+import os
 
 from portage.sets import PackageSet
 
 class SecuritySet(PackageSet):
        _operations = ["merge"]
-
+       _skip_applied = False
+       
+       description = "package set that includes all packages possibly affected by a GLSA"
+               
        def __init__(self, name, settings, vardbapi, portdbapi):
                super(SecuritySet, self).__init__(name)
                self._settings = settings
                self._vardbapi = vardbapi
                self._portdbapi = portdbapi
+               self._checkfile = os.path.join(os.sep, self._settings["ROOT"], CACHE_PATH.lstrip(os.sep), "glsa")
+
+       def getGlsaList(self, skip_applied):
+               glsaindexlist = glsa.get_glsa_list(self._settings)
+               if skip_applied:
+                       applied_list = grabfile(self._checkfile)
+                       glsaindexlist = set(glsaindexlist).difference(applied_list)
+                       glsaindexlist = list(glsaindexlist)
+               glsaindexlist.sort()
+               return glsaindexlist
                
-       def loadCheckfile(self):
-               self._appliedlist = grabfile(os.path.join(os.sep, self._settings["ROOT"], CACHE_PATH, "glsa"))
-       
        def load(self):
-               glsaindexlist = glsa.get_glsa_list(self._settings)
-               nodelist = []
+               glsaindexlist = self.getGlsaList(self._skip_applied)
+               atomlist = []
                for glsaid in glsaindexlist:
                        myglsa = glsa.Glsa(glsaid, self._settings, self._vardbapi, self._portdbapi)
                        #print glsaid, myglsa.isVulnerable(), myglsa.isApplied(), myglsa.getMergeList()
                        if self.useGlsa(myglsa):
-                               nodelist += myglsa.getMergeList(least_change=False)
-               self._setNodes(nodelist)
-       
-       def useGlsaId(self, glsaid):
-               return True
+                               atomlist += myglsa.getMergeList(least_change=False)
+               self._setAtoms(atomlist)
        
        def useGlsa(self, myglsa):
                return True
+
+       def updateAppliedList(self):
+               glsaindexlist = self.getGlsaList(True)
+               applied_list = grabfile(self._checkfile)
+               for glsaid in glsaindexlist:
+                       myglsa = glsa.Glsa(glsaid, self._settings, self._vardbapi, self._portdbapi)
+                       if not myglsa.isVulnerable():
+                               applied_list.append(glsaid)
+               write_atomic(self._checkfile, "\n".join(applied_list))
        
 class NewGlsaSet(SecuritySet):
-       def useGlsa(self, myglsa):
-               return not myglsa.isApplied()
+       _skip_applied = True
+       description = "Package set that includes all packages possibly affected by an unapplied GLSA"
 
 class AffectedSet(SecuritySet):
+       description = "Package set that includes all packages affected by an unapplied GLSA"
+
        def useGlsa(self, myglsa):
                return myglsa.isVulnerable()
+
+class NewAffectedSet(AffectedSet):
+       _skip_applied = True
+       description = "Package set that includes all packages affected by an unapplied GLSA"
index 8874743c9855411bb6393ec2945bbc88fe8e974a..2383dba81cf1463e94698f398f2eb39ea29e1d8b 100644 (file)
@@ -12,10 +12,11 @@ class CommandOutputSet(PackageSet):
        def __init__(self, name, command):
                super(CommandOutputSet, self).__init__(name)
                self._command = command
+               self.description = "Package set generated from output of '%s'" % self._command
        
        def load(self):
                pipe = subprocess.Popen(self._command, stdout=subprocess.PIPE, shell=True)
                if pipe.wait() == os.EX_OK:
                        text = pipe.stdout.read()
-                       self._setNodes(text.split("\n"))
+                       self._setAtoms(text.split("\n"))