Merged from trunk 10069:10086
authorFabian Groffen <grobian@gentoo.org>
Fri, 2 May 2008 14:40:49 +0000 (14:40 -0000)
committerFabian Groffen <grobian@gentoo.org>
Fri, 2 May 2008 14:40:49 +0000 (14:40 -0000)
   | 10070   | Skip group/owner tarsync options for tarsync if chown on     |
   | zmedico | /usr/portage fails, since the user might still be able to    |
   |         | sync even though the chown call is not permitted.            |

   | 10072   | Fix broken variable references found by pyflakes.            |
   | zmedico |                                                              |

   | 10073   | remove 'sets/' name-prefix for file-based package sets       |
   | genone  |                                                              |

   | 10075   | check if a given package set is defined more than once       |
   | genone  |                                                              |

   | 10077   | add config option for package sets to not be recorded in     |
   | genone  | 'world'                                                      |

   | 10078   | properly remove sets from 'world' when unmerging             |
   | genone  |                                                              |

   | 10079   | move cleaning of sets out of package loop                    |
   | genone  |                                                              |

   | 10080   | do not consider sets to be installed that are about to be    |
   | genone  | unmerged                                                     |

   | 10081   | v2 of FEATURES=preserved-libs, using LinkageMap instead of   |
   | genone  | the now removed LibraryPackageMap class                      |

   | 10082   | use special symlink comparison code (original patch from     |
   | genone  | prefix r9499)                                                |

   | 10083   | fix typo                                                     |
   | genone  |                                                              |

   | 10085   | rename NEEDED.2 to NEEDED.ELF.2 to allow other ABI formats   |
   | genone  | to use a different syntax                                    |

   | 10086   | Update documentation for package set configuration. Original |
   | genone  | patch by Arfrever Frehtes Taifersar Arahesis                 |

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

bin/emerge-webrsync
bin/misc-functions.sh
cnf/sets.conf
doc/config/sets.docbook
pym/_emerge/__init__.py
pym/portage/dbapi/vartree.py
pym/portage/sets/__init__.py
pym/portage/sets/base.py
pym/portage/sets/dbapi.py
pym/portage/sets/files.py
pym/portage/sets/libs.py [new file with mode: 0644]

index 91a1375d6e4543a74e2446c6223a082787cc7215..5cc7fe80ca4631e370d48b306c7aa024da0680d2 100755 (executable)
@@ -174,7 +174,10 @@ sync_local() {
        vecho "Syncing local tree ..."
 
        if type -P tarsync > /dev/null ; then
-               if ! tarsync $(vvecho -v) -s 1 -o portage -g portage -e /distfiles -e /packages -e /local "${file}" "${PORTDIR}"; then
+               local chown_opts="-o portage -g portage"
+               chown portage:portage portage > /dev/null 2>&1 || chown_opts=""
+               if ! tarsync $(vvecho -v) -s 1 ${chown_opts} \
+                       -e /distfiles -e /packages -e /local "${file}" "${PORTDIR}"; then
                        eecho "tarsync failed; tarball is corrupt? (${file})"
                        return 1
                fi
index 58e773b2804bdcf2425154c339b7ca8a6b982665..b5988dbdad17b2e284ff050fc7bb7ddcc01bfea4 100644 (file)
@@ -169,7 +169,7 @@ install_qa_check() {
                        if [ -z "${rpath}" -o -n "${rpath//*ORIGIN*}" ]; then
                                # object doesn't contain $ORIGIN in its runpath attribute
                                echo "${obj} ${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED
-                               echo "${arch:3};${obj};${soname};${rpath};${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.2
+                               echo "${arch:3};${obj};${soname};${rpath};${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.ELF.2
                        else
                                dir=$(dirname ${obj})
                                # replace $ORIGIN with the dirname of the current object for the lookup
@@ -186,7 +186,7 @@ install_qa_check() {
                                rneeded=${rneeded:1}
                                if [ -n "${rneeded}" ]; then
                                        echo "${obj} ${rneeded}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED
-                                       echo "${arch:3};${obj};${soname};${rpath};${rneeded}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.2
+                                       echo "${arch:3};${obj};${soname};${rpath};${rneeded}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.ELF.2
                                fi
                        fi
                done }
index 46d7c769fe786bba1e761573e5bf719c45c76f2c..7d852cae59c42db381ea657b463546bac6a30f01 100644 (file)
@@ -7,6 +7,7 @@
 # Not much that could be changed for world, so better leave it alone
 [world]
 class = portage.sets.files.WorldSet
+world-candidate = False
 
 # Same as for world, though later portage versions might use a different class
 [system]
@@ -21,13 +22,15 @@ class = portage.sets.profiles.PackagesSystemSet
 # - SecuritySet: include all GLSAs
 [security]
 class = portage.sets.security.NewAffectedSet
+world-candidate = False
 
 # Again, not much to change here, though people might prefer a different name
 [everything]
 class = portage.sets.dbapi.EverythingSet
+world-candidate = False
 
 # The following treats all files in /etc/portage/sets as a package set called
-# 'sets/$filename'.
+# '$filename'.
 [usersets]
 class = portage.sets.files.StaticFileSet
 multiset = true
@@ -36,4 +39,5 @@ directory = @DOMAIN_PREFIX@/etc/portage/sets
 # Set to rebuild all packages that need a preserved lib that only remains due
 # to FEATURES=preserve-libs
 [preserved-rebuild]
-class = portage.sets.dbapi.PreservedLibraryConsumerSet
+class = portage.sets.libs.PreservedLibraryConsumerSet
+world-candidate = False
index c7fc37c21696acc0b3335837960a567babaa8f64..40274a426b5158a105e5ab1d1a4ff3d52a11e2c2 100644 (file)
                        isn't stricly required, but it should always be used as the default 
                        handler might be changed in future versions.</para></footnote>.
                        That option defines which handler class should be used to 
-                       create the set. Another universal option available for single sets is
-                       <varname>name</varname>, however it's usually not needed as the name
+                       create the set. Other universal options available for single sets are
+                       <varname>name</varname> (which is usually not needed as the name
                        of the set is generated from the section name if <varname>name</varname>
-                       is missing. Some handler classes might require additional
-                       options for their configuration, these will be covered later in 
-                       this chapter.
+                       is missing) and <varname>world-candidate</varname>, which determines if
+                       given package should be added to the <varname>world</varname> set. Some
+                       handler classes might require additional options for their configuration,
+                       these will be covered later in this chapter.
                        </para>
                        <para>
                        Here are a few examples for single sets taken from the default 
@@ -63,6 +64,7 @@
                        # The classic world set
                        [world]
                        class = portage.sets.files.WorldSet
+                       world-candidate = False
                        
                        # The classic system set
                        [system]
@@ -81,7 +83,8 @@
                        sets each section still requires the <varname>class</varname> option,
                        but to indicate that the section should generate multiple sets it's
                        also necessary to set the <varname>multiset</varname> option to 
-                       <parameter>true</parameter>.
+                       <parameter>true</parameter>. The <varname>world-candidate</varname>
+                       option is also supported like with single sets.
                        </para>
                        <para>
                        As it doesn't make much sense to specify a single name for multiple sets
                        <itemizedlist>
                        <listitem><varname>filename</varname>: Required. Specifies the path to the file
                                that should be used for the package set.</listitem>
+                       <listitem><varname>greedy</varname>: Optional, defaults to <parameter>false</parameter>.
+                               Determines if atoms in the package should include all installed slots (when set to
+                               <parameter>true</parameter>) or if no slot expansion is wanted (when set to 
+                               <parameter>false</parameter>). This option only affects packages that have multiple
+                               slots available (e.g. <parameter>sys-kernel/gentoo-sources</parameter>).</listitem>
                        </itemizedlist>
                        </para>
                        </sect3>
                                that location a separate package set is created.
                        </listitem>
                        <listitem><varname>name_pattern</varname>: Optional, defaults to 
-                               <parameter>sets/$name</parameter>. This describes the naming pattern
+                               <parameter>$name</parameter>. This describes the naming pattern
                                to be used for creating the sets. It must contain either 
                                <parameter>$name</parameter> or <parameter>${name}</parameter>, which 
                                will be replaced by the filename (without any directory components).
                                No other files in that directory will be used.
                        </listitem>
                        <listitem><varname>name_pattern</varname>: Optional, defaults to 
-                               <parameter>sets/package_$suffix</parameter>. This describes the naming 
+                               <parameter>package_$suffix</parameter>. This describes the naming 
                                pattern to be used for creating the sets. It must contain either
                                <parameter>$suffix</parameter> or <parameter>${suffix}</parameter>, 
                                which will be replaced by the file suffix (e.g. 
                        <sect3>
                        <title>Single Set Configuraton</title>
                        <para>
-                       This class does not support any options.
+                       This class doesn't support any extra options.
                        </para>
                        </sect3>
                </sect2>
                        <sect3>
                        <title>Single Set Configuration</title>
                        <para>
-                       This class does not support any options.
+                       This class doesn't support any extra options.
                        </para>
                        </sect3>
                </sect2>
                        <sect3>
                        <title>Single Set Configuration</title>
                        <para>
-                       This class does not support any options.
+                       This class doesn't support any extra options.
+                       </para>
+                       </sect3>
+               </sect2>
+               <sect2 id='config-set-classes-PreservedLibraryConsumerSet'>
+               <title>portage.sets.libs.PreservedLibraryConsumerSet</title>
+               <para>
+               A special set used to rebuild all packages that need a preserved library that only
+               remains due to <varname>FEATURES="preserve-libs"</varname>.
+               </para>
+
+                       <sect3>
+                       <title>Single Set Configuration</title>
+                       <para>
+                       This class supports the following option:
+                       <itemizedlist>
+                       <listitem><varname>debug</varname>: Generate extra output useful to figure out why
+                               certain packages are included in the set, as it's not always obvious.
+                       </listitem>
+                       </itemizedlist>
                        </para>
                        </sect3>
                </sect2>
        <listitem><varname>world</varname>: uses <classname>WorldSet</classname></listitem>
        <listitem><varname>security</varname>: uses <classname>NewAffectedSet</classname> with default options</listitem>
        <listitem><varname>everything</varname>: uses <classname>EverythingSet</classname></listitem>
+       <listitem><varname>preserved-rebuild</varname>: uses <classname>PreservedLibraryConsumerSet</classname></listitem>
        </itemizedlist>
        Additionally the default configuration includes a multi set section based on
        the <classname>StaticFileSet</classname> defaults that creates a set for each 
index 77281f3fc5cdcb5191bca4cfb07c80ca6e09a5fa..69df7da4a9720c9fe6356b6fb0b5af0e5bd99d16 100644 (file)
@@ -3207,7 +3207,7 @@ class depgraph(object):
                                                blockers is not None:
                                                # Re-use the blockers from the graph.
                                                blocker_atoms = sorted(blockers)
-                                               counter = long(node.metadata["COUNTER"])
+                                               counter = long(pkg.metadata["COUNTER"])
                                                blocker_data = \
                                                        blocker_cache.BlockerData(counter, blocker_atoms)
                                                blocker_cache[pkg.cpv] = blocker_data
@@ -4238,9 +4238,7 @@ class depgraph(object):
                                                        counters.newslot += 1
 
                                        if "--changelog" in self.myopts:
-                                               slot_atom = "%s:%s" % (portage.dep_getkey(pkg_key),
-                                                       mydbapi.aux_get(pkg_key, ["SLOT"])[0])
-                                               inst_matches = vardb.match(slot_atom)
+                                               inst_matches = vardb.match(pkg.slot_atom)
                                                if inst_matches:
                                                        changelogs.extend(self.calc_changelog(
                                                                portdb.findname(pkg_key),
@@ -4770,7 +4768,7 @@ class depgraph(object):
                                del e
                all_added = []
                for k in self._sets:
-                       if k in ("args", "world"):
+                       if k in ("args", "world") or not root_config.sets[k].world_candidate:
                                continue
                        s = SETPREFIX + k
                        if s in world_set:
@@ -5889,6 +5887,7 @@ def unmerge(root_config, myopts, unmerge_action,
                        if candidates:
                                stop = False
                                installed_sets += candidates
+       installed_sets = [x for x in installed_sets if x not in root_config.setconfig.active]
        del stop, pos
 
        # we don't want to unmerge packages that are still listed in user-editable package sets
@@ -6029,6 +6028,9 @@ def unmerge(root_config, myopts, unmerge_action,
                                if clean_world:
                                        sets["world"].cleanPackage(vartree.dbapi, y)
                                emergelog(xterm_titles, " >>> unmerge success: "+y)
+       if clean_world:
+               for s in root_config.setconfig.active:
+                       sets["world"].remove(SETPREFIX+s)
        return 1
 
 def chk_updated_info_files(root, infodirs, prev_mtimes, retval):
@@ -7959,6 +7961,7 @@ def action_build(settings, trees, mtimedb,
                                portage.writemsg_stdout(colorize("WARN", "WARNING:")
                                        + " AUTOCLEAN is disabled.  This can cause serious"
                                        + " problems due to overlapping packages.\n")
+                       trees[settings["ROOT"]]["vartree"].dbapi.plib_registry.pruneNonExisting()
 
                if merge_count and not (buildpkgonly or fetchonly or pretend):
                        post_emerge(trees, mtimedb, retval)
index 3e941d5410c9b1a9ed1be1b047b4dcddade7d9fd..0aaa158ad41af3e1589eb089e882ab7311c110d2 100644 (file)
@@ -27,7 +27,7 @@ from portage.elog import elog_process
 from portage.elog.messages import ewarn
 from portage.elog.filtering import filter_mergephases, filter_unmergephases
 
-import os, re, sys, stat, errno, commands, copy, time
+import os, re, sys, stat, errno, commands, copy, time, subprocess
 from itertools import izip
 
 try:
@@ -135,38 +135,63 @@ class LinkageMap(object):
        def rebuild(self):
                libs = {}
                obj_properties = {}
+               lines = []
                for cpv in self._dbapi.cpv_all():
-                       lines = grabfile(self._dbapi.getpath(cpv, filename="NEEDED.2"))
-                       for l in lines:
-                               fields = l.strip("\n").split(";")
-                               if len(fields) < 5:
-                                       print "Error", fields
-                                       # insufficient field length
-                                       continue
-                               arch = fields[0]
-                               obj = fields[1]
-                               soname = fields[2]
-                               path = fields[3].replace("${ORIGIN}", os.path.dirname(obj)).replace("$ORIGIN", os.path.dirname(obj)).split(":")
-                               needed = fields[4].split(",")
-                               if soname:
-                                       libs.setdefault(soname, {arch: {"providers": [], "consumers": []}})
-                                       libs[soname].setdefault(arch, {"providers": [], "consumers": []})
-                                       libs[soname][arch]["providers"].append(obj)
-                               for x in needed:
-                                       libs.setdefault(x, {arch: {"providers": [], "consumers": []}})
-                                       libs[x].setdefault(arch, {"providers": [], "consumers": []})
-                                       libs[x][arch]["consumers"].append(obj)
-                               obj_properties[obj] = (arch, path, needed, soname)
+                       lines += grabfile(self._dbapi.getpath(cpv, filename="NEEDED.ELF.2"))
+
+               # have to call scanelf for preserved libs here as they aren't 
+               # registered in NEEDED.ELF.2 files
+               if self._dbapi.plib_registry and self._dbapi.plib_registry.getPreservedLibs():
+                       args = ["/usr/bin/scanelf", "-yqF", "%a;%F;%S;%r;%n"]
+                       for items in self._dbapi.plib_registry.getPreservedLibs().values():
+                               args += items
+                       proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+                       output = [l[3:] for l in proc.communicate()[0].split("\n")]
+                       lines += output
+
+               for l in lines:
+                       if l.strip() == "":
+                               continue
+                       fields = l.strip("\n").split(";")
+                       if len(fields) < 5:
+                               print "Error", fields
+                               # insufficient field length
+                               continue
+                       arch = fields[0]
+                       obj = os.path.realpath(fields[1])
+                       soname = fields[2]
+                       path = fields[3].replace("${ORIGIN}", os.path.dirname(obj)).replace("$ORIGIN", os.path.dirname(obj)).split(":")
+                       needed = fields[4].split(",")
+                       if soname:
+                               libs.setdefault(soname, {arch: {"providers": [], "consumers": []}})
+                               libs[soname].setdefault(arch, {"providers": [], "consumers": []})
+                               libs[soname][arch]["providers"].append(obj)
+                       for x in needed:
+                               libs.setdefault(x, {arch: {"providers": [], "consumers": []}})
+                               libs[x].setdefault(arch, {"providers": [], "consumers": []})
+                               libs[x][arch]["consumers"].append(obj)
+                       obj_properties[obj] = (arch, needed, path, soname)
                
                self._libs = libs
                self._obj_properties = obj_properties
+
+       def listLibraryObjects(self):
+               rValue = []
+               if not self._libs:
+                       self.rebuild()
+               for soname in self._libs:
+                       for arch in self._libs[soname]:
+                               rValue.extend(self._libs[soname][arch]["providers"])
+               return rValue
        
        def findProviders(self, obj):
+               if not self._libs:
+                       self.rebuild()
                obj = os.path.realpath(obj)
                rValue = {}
                if obj not in self._obj_properties:
                        raise KeyError("%s not in object list" % obj)
-               arch, path, needed, soname = self._obj_properties[obj]
+               arch, needed, path, soname = self._obj_properties[obj]
                path.extend(self._defpath)
                path = [os.path.realpath(x) for x in path]
                for x in needed:
@@ -181,13 +206,15 @@ class LinkageMap(object):
                return rValue
        
        def findConsumers(self, obj):
+               if not self._libs:
+                       self.rebuild()
                obj = os.path.realpath(obj)
                rValue = set()
                for soname in self._libs:
                        for arch in self._libs[soname]:
                                if obj in self._libs[soname][arch]["providers"]:
                                        for x in self._libs[soname][arch]["consumers"]:
-                                               path = self._obj_properties[x][1]
+                                               path = self._obj_properties[x][2]
                                                path = [os.path.realpath(y) for y in path+self._defpath]
                                                if soname[0] == os.sep and os.path.realpath(soname) == os.path.realpath(obj):
                                                        rValue.add(x)
@@ -293,7 +320,6 @@ class vardbapi(dbapi):
                self._counter_path = os.path.join(root,
                        CACHE_PATH.lstrip(os.path.sep), "counter")
 
-               self.libmap = LibraryPackageMap(os.path.join(self.root, CACHE_PATH.lstrip(os.sep), "library_consumers"), self)
                try:
                        self.plib_registry = PreservedLibsRegistry(
                                os.path.join(self.root, PRIVATE_PATH, "preserved_libs_registry"))
@@ -301,6 +327,8 @@ class vardbapi(dbapi):
                        # apparently this user isn't allowed to access PRIVATE_PATH
                        self.plib_registry = None
 
+               self.linkmap = LinkageMap(self)
+
        def getpath(self, mykey, filename=None):
                rValue = os.path.join(self.root, VDB_PATH, mykey)
                if filename != None:
@@ -1271,31 +1299,35 @@ class dblink(object):
                                        return retval
 
                        # regenerate reverse NEEDED map
-                       self.vartree.dbapi.libmap.update()
+                       self.vartree.dbapi.linkmap.rebuild()
                        
                        # remove preserved libraries that don't have any consumers left
                        # FIXME: this code is quite ugly and can likely be optimized in several ways
                        plib_dict = plib_registry.getPreservedLibs()
                        for cpv in plib_dict:
-                               keeplist = []
                                plib_dict[cpv].sort()
+                               # for the loop below to work correctly, we need all
+                               # symlinks to come before the actual files, such that
+                               # the recorded symlinks (sonames) will be resolved into
+                               # their real target before the object is found not to be
+                               # in the reverse NEEDED map
+                               def symlink_compare(x, y):
+                                       if os.path.islink(x):
+                                               if os.path.islink(y):
+                                                       return 0
+                                               else:
+                                                       return -1
+                                       elif os.path.islink(y):
+                                               return 1
+                                       else:
+                                               return 0
+
+                               plib_dict[cpv].sort(symlink_compare)
                                for f in plib_dict[cpv]:
-                                       if not os.path.exists(f) or os.path.realpath(f) in keeplist:
+                                       if not os.path.exists(f):
                                                continue
                                        unlink_list = []
-                                       while os.path.islink(f):
-                                               if os.path.basename(f) in self.vartree.dbapi.libmap.get():
-                                                       unlink_list = []
-                                                       keeplist.append(os.path.realpath(f))
-                                                       break
-                                               else:
-                                                       unlink_list.append(f)
-                                                       # only follow symlinks if the target is also a preserved lib object
-                                                       if os.readlink(f) in plib_dict[cpv]:
-                                                               f = os.readlink(f)
-                                                       else:
-                                                               break
-                                       if not os.path.islink(f) and not os.path.basename(f) in self.vartree.dbapi.libmap.get():
+                                       if not self.vartree.dbapi.linkmap.findConsumers(f):
                                                unlink_list.append(f)
                                        for obj in unlink_list:
                                                try:
@@ -1682,22 +1714,27 @@ class dblink(object):
 
        def _preserve_libs(self, srcroot, destroot, mycontents, counter):
                # read global reverse NEEDED map
-               libmap = self.vartree.dbapi.libmap.get()
+               linkmap = self.vartree.dbapi.linkmap
+               linkmap.rebuild()
+               liblist = linkmap.listLibraryObjects()
 
                # get list of libraries from old package instance
                old_contents = self._installed_instance.getcontents().keys()
-               old_libs = set([os.path.basename(x) for x in old_contents]).intersection(libmap)
+               old_libs = set(old_contents).intersection(liblist)
 
                # get list of libraries from new package instance
-               mylibs = set([os.path.basename(x) for x in mycontents]).intersection(libmap)
+               mylibs = set(mycontents).intersection(liblist)
 
                # check which libs are present in the old, but not the new package instance
-               preserve_libs = old_libs.difference(mylibs)
+               candidates = old_libs.difference(mylibs)
+               for x in old_contents:
+                       if os.path.islink(x) and os.path.realpath(x) in candidates:
+                               candidates.add(x)
 
                # ignore any libs that are only internally used by the package
                def has_external_consumers(lib, contents, otherlibs):
-                       consumers = set(libmap[lib])
-                       contents_without_libs = [x for x in contents if not os.path.basename(x) in otherlibs]
+                       consumers = linkmap.findConsumers(lib)
+                       contents_without_libs = [x for x in contents if x not in otherlibs]
                        
                        # just used by objects that will be autocleaned
                        if len(consumers.difference(contents_without_libs)) == 0:
@@ -1714,23 +1751,34 @@ class dblink(object):
                        else:
                                return True
 
-               for lib in list(preserve_libs):
-                       if not has_external_consumers(lib, old_contents, preserve_libs):
-                               preserve_libs.remove(lib)
-                       # only preserve the lib if there is no other copy in the search path
-                       for path in getlibpaths():
-                               fullname = os.path.join(path, lib)
-                               if fullname not in old_contents and os.path.exists(fullname) and lib in preserve_libs:
-                                       preserve_libs.remove(lib)
-                       
-               # get the real paths for the libs
-               preserve_paths = [x for x in old_contents if os.path.basename(x) in preserve_libs]
-               del old_contents, old_libs, mylibs, preserve_libs
-
+               for lib in list(candidates):
+                       if not has_external_consumers(lib, old_contents, candidates):
+                               candidates.remove(lib)
+                       # only preserve the lib if there is no other copy to use for each consumer
+                       keep = False
+                       for c in linkmap.findConsumers(lib):
+                               localkeep = True
+                               providers = linkmap.findProviders(c)
+                               for soname in providers:
+                                       if lib in providers[soname]:
+                                               for p in providers[soname]:
+                                                       if p not in candidates:
+                                                               localkeep = False
+                                                               break
+                                               break
+                               if localkeep:
+                                       keep = True
+               
+               del mylibs, mycontents, old_contents, liblist
+               
                # inject files that should be preserved into our image dir
                import shutil
                missing_paths = []
-               for x in preserve_paths:
+               for x in candidates:
+                       # skip existing files so the 'new' libs aren't overwritten
+                       if os.path.exists(os.path.join(srcroot, x.lstrip(os.sep))):
+                               missing_paths.append(x)
+                               continue
                        print "injecting %s into %s" % (x, srcroot)
                        if not os.path.exists(os.path.join(destroot, x.lstrip(os.sep))):
                                print "%s does not exist so can't be preserved" % x
@@ -1748,14 +1796,14 @@ class dblink(object):
                                os.symlink(linktarget, os.path.join(srcroot, x.lstrip(os.sep)))
                                if linktarget[0] != os.sep:
                                        linktarget = os.path.join(os.path.dirname(x), linktarget)
-                               preserve_paths.append(linktarget)
+                               candidates.add(linktarget)
                        else:
                                shutil.copy2(os.path.join(destroot, x.lstrip(os.sep)),
                                        os.path.join(srcroot, x.lstrip(os.sep)))
 
-               preserve_paths = [x for x in preserve_paths if x not in missing_paths]
+               preserve_paths = [x for x in candidates if x not in missing_paths]
 
-               del missing_paths
+               del missing_paths, candidates
 
                # keep track of the libs we preserved
                self.vartree.dbapi.plib_registry.register(self.mycpv, self.settings["SLOT"], counter, preserve_paths)
@@ -2293,7 +2341,7 @@ class dblink(object):
                del conf_mem_file
 
                # regenerate reverse NEEDED map
-               self.vartree.dbapi.libmap.update()
+               self.vartree.dbapi.linkmap.rebuild()
 
                #do postinst script
                self.settings["PORTAGE_UPDATE_ENV"] = \
index 476aea4cab86c6de935e6a85a27c384066833ba3..d688164b58246c5ebde28f1000c37a9bfcaacecb 100644 (file)
@@ -32,7 +32,6 @@ class SetConfig(SafeConfigParser):
                self.settings = settings
                self._parsed = False
                self.active = []
-               self.aliases = {}
 
        def _parse(self):
                if self._parsed:
@@ -58,11 +57,19 @@ class SetConfig(SafeConfigParser):
                        # create single or multiple instances of the given class depending on configuration
                        if self.has_option(sname, "multiset") and self.getboolean(sname, "multiset"):
                                if hasattr(setclass, "multiBuilder"):
+                                       newsets = {}
                                        try:
-                                               self.psets.update(setclass.multiBuilder(optdict, self.settings, self.trees))
+                                               newsets = setclass.multiBuilder(optdict, self.settings, self.trees)
                                        except SetConfigError, e:
                                                self.errors.append("Configuration error in section '%s': %s" % (sname, str(e)))
                                                continue
+                                       for x in newsets:
+                                               if x in self.psets:
+                                                       self.errors.append("Redefinition of set '%s' (sections: '%s', '%s')" % (setname, self.psets[setname].creator, sname))
+                                               newsets[x].creator = sname
+                                               if self.has_option(sname, "world-candidate") and not self.getboolean(sname, "world-candidate"):
+                                                       newsets[x].world_candidate = False
+                                       self.psets.update(newsets)
                                else:
                                        self.errors.append("Section '%s' is configured as multiset, but '%s' doesn't support that configuration" % (sname, classname))
                                        continue
@@ -71,9 +78,14 @@ class SetConfig(SafeConfigParser):
                                        setname = self.get(sname, "name")
                                except NoOptionError:
                                        setname = sname
+                               if setname in self.psets:
+                                       self.errors.append("Redefinition of set '%s' (sections: '%s', '%s')" % (setname, self.psets[setname].creator, sname))
                                if hasattr(setclass, "singleBuilder"):
                                        try:
                                                self.psets[setname] = setclass.singleBuilder(optdict, self.settings, self.trees)
+                                               self.psets[setname].creator = sname
+                                               if self.has_option(sname, "world-candidate") and not self.getboolean(sname, "world-candidate"):
+                                                       self.psets[setname].world_candidate = False
                                        except SetConfigError, e:
                                                self.errors.append("Configuration error in section '%s': %s" % (sname, str(e)))
                                                continue
index 50702cff39c9d97c951fe31d9bf538a5b99838ff..71547813e37bb135a3d19cb11fdc0d3d7b87764a 100644 (file)
@@ -24,6 +24,7 @@ class PackageSet(object):
                self._loading = False
                self.errors = []
                self._nonatoms = set()
+               self.world_candidate = True
 
        def __contains__(self, atom):
                self._load()
@@ -182,6 +183,7 @@ class EditablePackageSet(PackageSet):
        def remove(self, atom):
                self._load()
                self._atoms.discard(atom)
+               self._nonatoms.discard(atom)
                self._updateAtomMap()
                self.write()
 
index c4f974aa9fcfe0bb3ff47c9fac70b00b1ecd0865..fca7425527b3729f696f0dce786499d5db782c09 100644 (file)
@@ -2,13 +2,9 @@
 # Distributed under the terms of the GNU General Public License v2
 # $Id$
 
-from portage.versions import catsplit, catpkgsplit
+from portage.versions import catsplit
 from portage.sets.base import PackageSet
 from portage.sets import SetConfigError, get_boolean
-from portage.dbapi.vartree import dblink
-from portage.util import grabfile
-
-import os
 
 __all__ = ["CategorySet", "EverythingSet"]
 
@@ -108,71 +104,3 @@ class CategorySet(PackageSet):
                return rValue
        multiBuilder = classmethod(multiBuilder)
 
-class LibraryConsumerSet(PackageSet):
-       _operations = ["merge", "unmerge"]
-
-       def __init__(self, vardbapi, debug=False):
-               super(LibraryConsumerSet, self).__init__()
-               self.dbapi = vardbapi
-               self.debug = debug
-
-       def mapPathsToAtoms(self, paths):
-               rValue = set()
-               for cpv in self.dbapi.cpv_all():
-                       mysplit = catsplit(cpv)
-                       link = dblink(mysplit[0], mysplit[1], myroot=self.dbapi.root, \
-                                       mysettings=self.dbapi.settings, treetype='vartree', \
-                                       vartree=self.dbapi.vartree)
-                       if paths.intersection(link.getcontents()):
-                               cat, pn = catpkgsplit(cpv)[:2]
-                               slot = self.dbapi.aux_get(cpv, ["SLOT"])[0]
-                               rValue.add("%s/%s:%s" % (cat, pn, slot))
-               return rValue
-       
-
-class PreservedLibraryConsumerSet(LibraryConsumerSet):
-       def load(self):
-               reg = self.dbapi.plib_registry
-               libmap = self.dbapi.libmap.get()
-               consumers = set()
-               if reg:
-                       for libs in reg.getPreservedLibs().values():
-                               for lib in libs:
-                                       paths = libmap.get(os.path.basename(lib), [])
-                                       consumers.update(paths)
-               else:
-                       return
-               if not consumers:
-                       return
-               self._setAtoms(self.mapPathsToAtoms(consumers))
-
-       def singleBuilder(cls, options, settings, trees):
-               return PreservedLibraryConsumerSet(trees["vartree"].dbapi)
-       singleBuilder = classmethod(singleBuilder)
-
-class MissingLibraryConsumerSet(LibraryConsumerSet):
-       _operations = ["merge", "unmerge"]
-       
-       def load(self):
-               atoms = set()
-               consumers = set()
-               for lib in self.dbapi.libmap.get():
-                       found=False
-                       for searchdir in grabfile(os.path.join(os.sep, self.dbapi.root, "etc/ld.so.conf")):
-                               if os.path.exists(os.path.join(searchdir, lib)):
-                                       found=True
-                                       break
-                       if not found:
-                               print "missing library: %s" % lib
-                               print "consumers:"
-                               for x in self.dbapi.libmap.get()[lib]:
-                                       print "    ", x
-                               consumers.update(self.dbapi.libmap.get()[lib])
-               if not consumers:
-                       return
-               self._setAtoms(self.mapPathsToAtoms(consumers))
-       
-       def singleBuilder(cls, options, settings, trees):
-               debug = get_boolean(options, "debug", False)
-               return MissingLibraryConsumerSet(trees["vartree"].dbapi, debug=debug)
-       singleBuilder = classmethod(singleBuilder)
index cfc594ffbfd6dc9baae5bd85f9bb2ecb10219eb3..7ed29e2ecf666cd53359fabf1104d58e138e1b98 100644 (file)
@@ -107,7 +107,7 @@ class StaticFileSet(EditablePackageSet):
        def multiBuilder(self, options, settings, trees):
                rValue = {}
                directory = options.get("directory", os.path.join(settings["PORTAGE_CONFIGROOT"], USER_CONFIG_PATH.lstrip(os.sep), "sets"))
-               name_pattern = options.get("name_pattern", "sets/${name}")
+               name_pattern = options.get("name_pattern", "${name}")
                if not "$name" in name_pattern and not "${name}" in name_pattern:
                        raise SetConfigError("name_pattern doesn't include ${name} placeholder")
                greedy = get_boolean(options, "greedy", False)
diff --git a/pym/portage/sets/libs.py b/pym/portage/sets/libs.py
new file mode 100644 (file)
index 0000000..4ab8a33
--- /dev/null
@@ -0,0 +1,50 @@
+# Copyright 2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+from portage.sets.base import PackageSet
+from portage.dbapi.vartree import dblink
+from portage.versions import catsplit, catpkgsplit
+
+import os
+
+class LibraryConsumerSet(PackageSet):
+       _operations = ["merge", "unmerge"]
+
+       def __init__(self, vardbapi, debug=False):
+               super(LibraryConsumerSet, self).__init__()
+               self.dbapi = vardbapi
+               self.debug = debug
+
+       def mapPathsToAtoms(self, paths):
+               rValue = set()
+               for cpv in self.dbapi.cpv_all():
+                       mysplit = catsplit(cpv)
+                       link = dblink(mysplit[0], mysplit[1], myroot=self.dbapi.root, \
+                                       mysettings=self.dbapi.settings, treetype='vartree', \
+                                       vartree=self.dbapi.vartree)
+                       if paths.intersection(link.getcontents()):
+                               cat, pn = catpkgsplit(cpv)[:2]
+                               slot = self.dbapi.aux_get(cpv, ["SLOT"])[0]
+                               rValue.add("%s/%s:%s" % (cat, pn, slot))
+               return rValue
+       
+
+class PreservedLibraryConsumerSet(LibraryConsumerSet):
+       def load(self):
+               reg = self.dbapi.plib_registry
+               consumers = set()
+               if reg:
+                       for libs in reg.getPreservedLibs().values():
+                               for lib in libs:
+                                       #print lib, self.dbapi.linkmap.findConsumers(lib)
+                                       consumers.update(self.dbapi.linkmap.findConsumers(lib))
+               else:
+                       return
+               if not consumers:
+                       return
+               self._setAtoms(self.mapPathsToAtoms(consumers))
+
+       def singleBuilder(cls, options, settings, trees):
+               return PreservedLibraryConsumerSet(trees["vartree"].dbapi)
+       singleBuilder = classmethod(singleBuilder)